@angular/benchpress
Version:
Benchpress - a framework for e2e performance tests
234 lines • 36.2 kB
JavaScript
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
var ChromeDriverExtension_1;
import { __decorate, __metadata, __param } from "tslib";
import { Inject, Injectable } from '@angular/core';
import * as fs from 'fs';
import { Options } from '../common_options';
import { WebDriverAdapter } from '../web_driver_adapter';
import { PerfLogFeatures, WebDriverExtension } from '../web_driver_extension';
/**
* Set the following 'traceCategories' to collect metrics in Chrome:
* 'v8,blink.console,disabled-by-default-devtools.timeline,devtools.timeline,blink.user_timing'
*
* In order to collect the frame rate related metrics, add 'benchmark'
* to the list above.
*/
let ChromeDriverExtension = ChromeDriverExtension_1 = class ChromeDriverExtension extends WebDriverExtension {
constructor(driver, userAgent, rawPerflogPath) {
super();
this.driver = driver;
this._firstRun = true;
this._majorChromeVersion = this._parseChromeVersion(userAgent);
this._rawPerflogPath = rawPerflogPath;
}
_parseChromeVersion(userAgent) {
if (!userAgent) {
return -1;
}
let v = userAgent.split(/Chrom(e|ium)\//g)[2];
if (!v) {
return -1;
}
v = v.split('.')[0];
if (!v) {
return -1;
}
return parseInt(v, 10);
}
gc() {
return this.driver.executeScript('window.gc()');
}
async timeBegin(name) {
if (this._firstRun) {
this._firstRun = false;
// Before the first run, read out the existing performance logs
// so that the chrome buffer does not fill up.
await this.driver.logs('performance');
}
return this.driver.executeScript(`performance.mark('${name}-bpstart');`);
}
timeEnd(name, restartName = null) {
let script = `performance.mark('${name}-bpend');`;
if (restartName) {
script += `performance.mark('${restartName}-bpstart');`;
}
return this.driver.executeScript(script);
}
// See [Chrome Trace Event
// Format](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit)
readPerfLog() {
// TODO(tbosch): Chromedriver bug https://code.google.com/p/chromedriver/issues/detail?id=1098
// Need to execute at least one command so that the browser logs can be read out!
return this.driver.executeScript('1+1')
.then((_) => this.driver.logs('performance'))
.then((entries) => {
const events = [];
entries.forEach((entry) => {
const message = JSON.parse(entry['message'])['message'];
if (message['method'] === 'Tracing.dataCollected') {
events.push(message['params']);
}
if (message['method'] === 'Tracing.bufferUsage') {
throw new Error('The DevTools trace buffer filled during the test!');
}
});
if (this._rawPerflogPath && events.length) {
fs.appendFileSync(this._rawPerflogPath, JSON.stringify(events));
}
return this._convertPerfRecordsToEvents(events);
});
}
_convertPerfRecordsToEvents(chromeEvents, normalizedEvents = null) {
if (!normalizedEvents) {
normalizedEvents = [];
}
chromeEvents.forEach((event) => {
const categories = this._parseCategories(event['cat']);
const normalizedEvent = this._convertEvent(event, categories);
if (normalizedEvent != null)
normalizedEvents.push(normalizedEvent);
});
return normalizedEvents;
}
_convertEvent(event, categories) {
const name = event['name'];
const args = event['args'];
if (this._isEvent(categories, name, ['blink.console'])) {
return normalizeEvent(event, { 'name': name });
}
else if (this._isEvent(categories, name, ['blink.user_timing'])) {
return normalizeEvent(event, { 'name': name });
}
else if (this._isEvent(categories, name, ['benchmark'], 'BenchmarkInstrumentation::ImplThreadRenderingStats')) {
// TODO(goderbauer): Instead of BenchmarkInstrumentation::ImplThreadRenderingStats the
// following events should be used (if available) for more accurate measurements:
// 1st choice: vsync_before - ground truth on Android
// 2nd choice: BenchmarkInstrumentation::DisplayRenderingStats - available on systems with
// new surfaces framework (not broadly enabled yet)
// 3rd choice: BenchmarkInstrumentation::ImplThreadRenderingStats - fallback event that is
// always available if something is rendered
const frameCount = event['args']['data']['frame_count'];
if (frameCount > 1) {
throw new Error('multi-frame render stats not supported');
}
if (frameCount == 1) {
return normalizeEvent(event, { 'name': 'frame' });
}
}
else if (this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'Rasterize') ||
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'CompositeLayers')) {
return normalizeEvent(event, { 'name': 'render' });
}
else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) {
const normArgs = {
'majorGc': true,
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
args['usedHeapSizeBefore']
};
return normalizeEvent(event, { 'name': 'gc', 'args': normArgs });
}
else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MinorGC')) {
const normArgs = {
'majorGc': false,
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
args['usedHeapSizeBefore']
};
return normalizeEvent(event, { 'name': 'gc', 'args': normArgs });
}
else if (this._isEvent(categories, name, ['devtools.timeline'], 'FunctionCall') &&
(!args || !args['data'] ||
(args['data']['scriptName'] !== 'InjectedScript' && args['data']['scriptName'] !== ''))) {
return normalizeEvent(event, { 'name': 'script' });
}
else if (this._isEvent(categories, name, ['devtools.timeline'], 'EvaluateScript')) {
return normalizeEvent(event, { 'name': 'script' });
}
else if (this._isEvent(categories, name, ['devtools.timeline', 'blink'], 'UpdateLayoutTree')) {
return normalizeEvent(event, { 'name': 'render' });
}
else if (this._isEvent(categories, name, ['devtools.timeline'], 'UpdateLayerTree') ||
this._isEvent(categories, name, ['devtools.timeline'], 'Layout') ||
this._isEvent(categories, name, ['devtools.timeline'], 'Paint')) {
return normalizeEvent(event, { 'name': 'render' });
}
else if (this._isEvent(categories, name, ['devtools.timeline'], 'ResourceReceivedData')) {
const normArgs = { 'encodedDataLength': args['data']['encodedDataLength'] };
return normalizeEvent(event, { 'name': 'receivedData', 'args': normArgs });
}
else if (this._isEvent(categories, name, ['devtools.timeline'], 'ResourceSendRequest')) {
const data = args['data'];
const normArgs = { 'url': data['url'], 'method': data['requestMethod'] };
return normalizeEvent(event, { 'name': 'sendRequest', 'args': normArgs });
}
else if (this._isEvent(categories, name, ['blink.user_timing'], 'navigationStart')) {
return normalizeEvent(event, { 'name': 'navigationStart' });
}
return null; // nothing useful in this event
}
_parseCategories(categories) {
return categories.split(',');
}
_isEvent(eventCategories, eventName, expectedCategories, expectedName = null) {
const hasCategories = expectedCategories.reduce((value, cat) => value && eventCategories.indexOf(cat) !== -1, true);
return !expectedName ? hasCategories : hasCategories && eventName === expectedName;
}
perfLogFeatures() {
return new PerfLogFeatures({ render: true, gc: true, frameCapture: true, userTiming: true });
}
supports(capabilities) {
return this._majorChromeVersion >= 44 && capabilities['browserName'].toLowerCase() === 'chrome';
}
};
ChromeDriverExtension.PROVIDERS = [{
provide: ChromeDriverExtension_1,
deps: [WebDriverAdapter, Options.USER_AGENT, Options.RAW_PERFLOG_PATH]
}];
ChromeDriverExtension = ChromeDriverExtension_1 = __decorate([
Injectable(),
__param(1, Inject(Options.USER_AGENT)),
__param(2, Inject(Options.RAW_PERFLOG_PATH)),
__metadata("design:paramtypes", [WebDriverAdapter, String, Object])
], ChromeDriverExtension);
export { ChromeDriverExtension };
function normalizeEvent(chromeEvent, data) {
let ph = chromeEvent['ph'].toUpperCase();
if (ph === 'S') {
ph = 'B';
}
else if (ph === 'F') {
ph = 'E';
}
else if (ph === 'R') {
// mark events from navigation timing
ph = 'I';
// Chrome 65+ doesn't allow user timing measurements across page loads.
// Instead, we use performance marks with special names.
if (chromeEvent['name'].match(/-bpstart/)) {
data['name'] = chromeEvent['name'].slice(0, -8);
ph = 'B';
}
else if (chromeEvent['name'].match(/-bpend$/)) {
data['name'] = chromeEvent['name'].slice(0, -6);
ph = 'E';
}
}
const result = { 'pid': chromeEvent['pid'], 'ph': ph, 'cat': 'timeline', 'ts': chromeEvent['ts'] / 1000 };
if (ph === 'X') {
let dur = chromeEvent['dur'];
if (dur === undefined) {
dur = chromeEvent['tdur'];
}
result['dur'] = !dur ? 0.0 : dur / 1000;
}
for (const prop in data) {
result[prop] = data[prop];
}
return result;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hyb21lX2RyaXZlcl9leHRlbnNpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9wYWNrYWdlcy9iZW5jaHByZXNzL3NyYy93ZWJkcml2ZXIvY2hyb21lX2RyaXZlcl9leHRlbnNpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HOzs7QUFFSCxPQUFPLEVBQUMsTUFBTSxFQUFFLFVBQVUsRUFBaUIsTUFBTSxlQUFlLENBQUM7QUFDakUsT0FBTyxLQUFLLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFFekIsT0FBTyxFQUFDLE9BQU8sRUFBQyxNQUFNLG1CQUFtQixDQUFDO0FBQzFDLE9BQU8sRUFBQyxnQkFBZ0IsRUFBQyxNQUFNLHVCQUF1QixDQUFDO0FBQ3ZELE9BQU8sRUFBZSxlQUFlLEVBQUUsa0JBQWtCLEVBQUMsTUFBTSx5QkFBeUIsQ0FBQztBQUUxRjs7Ozs7O0dBTUc7QUFFSCxJQUFhLHFCQUFxQiw2QkFBbEMsTUFBYSxxQkFBc0IsU0FBUSxrQkFBa0I7SUFVM0QsWUFDWSxNQUF3QixFQUE4QixTQUFpQixFQUM3QyxjQUEyQjtRQUMvRCxLQUFLLEVBQUUsQ0FBQztRQUZFLFdBQU0sR0FBTixNQUFNLENBQWtCO1FBSjVCLGNBQVMsR0FBRyxJQUFJLENBQUM7UUFPdkIsSUFBSSxDQUFDLG1CQUFtQixHQUFHLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUMvRCxJQUFJLENBQUMsZUFBZSxHQUFHLGNBQWMsQ0FBQztJQUN4QyxDQUFDO0lBRU8sbUJBQW1CLENBQUMsU0FBaUI7UUFDM0MsSUFBSSxDQUFDLFNBQVMsRUFBRTtZQUNkLE9BQU8sQ0FBQyxDQUFDLENBQUM7U0FDWDtRQUNELElBQUksQ0FBQyxHQUFHLFNBQVMsQ0FBQyxLQUFLLENBQUMsaUJBQWlCLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM5QyxJQUFJLENBQUMsQ0FBQyxFQUFFO1lB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V0FBVyxDQUFDLE1BQU0sQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNoRCxFQUFFLEdBQUcsR0FBRyxDQUFDO1NBQ1Y7S0FDRjtJQUNELE1BQU0sTUFBTSxHQUNSLEVBQUMsS0FBSyxFQUFFLFdBQVcsQ0FBQyxLQUFLLENBQUMsRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLFdBQVcsQ0FBQyxJQUFJLENBQUMsR0FBRyxJQUFJLEVBQUMsQ0FBQztJQUM3RixJQUFJLEVBQUUsS0FBSyxHQUFHLEVBQUU7UUFDZCxJQUFJLEdBQUcsR0FBRyxXQUFXLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDN0IsSUFBSSxHQUFHLEtBQUssU0FBUyxFQUFFO1lBQ3JCLEdBQUcsR0FBRyxXQUFXLENBQUMsTUFBTSxDQUFDLENBQUM7U0FDM0I7UUFDRCxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxHQUFHLElBQUksQ0FBQztLQUN6QztJQUNELEtBQUssTUFBTSxJQUFJLElBQUksSUFBSSxFQUFFO1FBQ3ZCLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7S0FDM0I7SUFDRCxPQUFPLE1BQU0sQ0FBQztBQUNoQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IEdvb2dsZSBMTEMgQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqXG4gKiBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhbiBNSVQtc3R5bGUgbGljZW5zZSB0aGF0IGNhbiBiZVxuICogZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZSBhdCBodHRwczovL2FuZ3VsYXIuaW8vbGljZW5zZVxuICovXG5cbmltcG9ydCB7SW5qZWN0LCBJbmplY3RhYmxlLCBTdGF0aWNQcm92aWRlcn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgKiBhcyBmcyBmcm9tICdmcyc7XG5cbmltcG9ydCB7T3B0aW9uc30gZnJvbSAnLi4vY29tbW9uX29wdGlvbnMnO1xuaW1wb3J0IHtXZWJEcml2ZXJBZGFwdGVyfSBmcm9tICcuLi93ZWJfZHJpdmVyX2FkYXB0ZXInO1xuaW1wb3J0IHtQZXJmTG9nRXZlbnQsIFBlcmZMb2dGZWF0dXJlcywgV2ViRHJpdmVyRXh0ZW5zaW9ufSBmcm9tICcuLi93ZWJfZHJpdmVyX2V4dGVuc2lvbic7XG5cbi8qKlxuICogU2V0IHRoZSBmb2xsb3dpbmcgJ3RyYWNlQ2F0ZWdvcmllcycgdG8gY29sbGVjdCBtZXRyaWNzIGluIENocm9tZTpcbiAqICd2OCxibGluay5jb25zb2xlLGRpc2FibGVkLWJ5LWRlZmF1bHQtZGV2dG9vbHMudGltZWxpbmUsZGV2dG9vbHMudGltZWxpbmUsYmxpbmsudXNlcl90aW1pbmcnXG4gKlxuICogSW4gb3JkZXIgdG8gY29sbGVjdCB0aGUgZnJhbWUgcmF0ZSByZWxhdGVkIG1ldHJpY3MsIGFkZCAnYmVuY2htYXJrJ1xuICogdG8gdGhlIGxpc3QgYWJvdmUuXG4gKi9cbkBJbmplY3RhYmxlKClcbmV4cG9ydCBjbGFzcyBDaHJvbWVEcml2ZXJFeHRlbnNpb24gZXh0ZW5kcyBXZWJEcml2ZXJFeHRlbnNpb24ge1xuICBzdGF0aWMgUFJPVklERVJTID0gPFN0YXRpY1Byb3ZpZGVyPlt7XG4gICAgcHJvdmlkZTogQ2hyb21lRHJpdmVyRXh0ZW5zaW9uLFxuICAgIGRlcHM6IFtXZWJEcml2ZXJBZGFwdGVyLCBPcHRpb25zLlVTRVJfQUdFTlQsIE9wdGlvbnMuUkFXX1BFUkZMT0dfUEFUSF1cbiAgfV07XG5cbiAgcHJpdmF0ZSBfbWFqb3JDaHJvbWVWZXJzaW9uOiBudW1iZXI7XG4gIHByaXZhdGUgX2ZpcnN0UnVuID0gdHJ1ZTtcbiAgcHJpdmF0ZSBfcmF3UGVyZmxvZ1BhdGg6IHN0cmluZ3xudWxsO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSBkcml2ZXI6IFdlYkRyaXZlckFkYXB0ZXIsIEBJbmplY3QoT3B0aW9ucy5VU0VSX0FHRU5UKSB1c2VyQWdlbnQ6IHN0cmluZyxcbiAgICAgIEBJbmplY3QoT3B0aW9ucy5SQVdfUEVSRkxPR19QQVRIKSByYXdQZXJmbG9nUGF0aDogc3RyaW5nfG51bGwpIHtcbiAgICBzdXBlcigpO1xuICAgIHRoaXMuX21ham9yQ2hyb21lVmVyc2lvbiA9IHRoaXMuX3BhcnNlQ2hyb21lVmVyc2lvbih1c2VyQWdlbnQpO1xuICAgIHRoaXMuX3Jhd1BlcmZsb2dQYXRoID0gcmF3UGVyZmxvZ1BhdGg7XG4gIH1cblxuICBwcml2YXRlIF9wYXJzZUNocm9tZVZlcnNpb24odXNlckFnZW50OiBzdHJpbmcpOiBudW1iZXIge1xuICAgIGlmICghdXNlckFnZW50KSB7XG4gICAgICByZXR1cm4gLTE7XG4gICAgfVxuICAgIGxldCB2ID0gdXNlckFnZW50LnNwbGl0KC9DaHJvbShlfGl1bSlcXC8vZylbMl07XG4gICAgaWYgKCF2KSB7XG4gICAgICByZXR1cm4gLTE7XG4gICAgfVxuICAgIHYgPSB2LnNwbGl0KCcuJylbMF07XG4gICAgaWYgKCF2KSB7XG4gICAgICByZXR1cm4gLTE7XG4gICAgfVxuICAgIHJldHVybiBwYXJzZUludCh2LCAxMCk7XG4gIH1cblxuICBvdmVycmlkZSBnYygpIHtcbiAgICByZXR1cm4gdGhpcy5kcml2ZXIuZXhlY3V0ZVNjcmlwdCgnd2luZG93LmdjKCknKTtcbiAgfVxuXG4gIG92ZXJyaWRlIGFzeW5jIHRpbWVCZWdpbihuYW1lOiBzdHJpbmcpOiBQcm9taXNlPGFueT4ge1xuICAgIGlmICh0aGlzLl9maXJzdFJ1bikge1xuICAgICAgdGhpcy5fZmlyc3RSdW4gPSBmYWxzZTtcbiAgICAgIC8vIEJlZm9yZSB0aGUgZmlyc3QgcnVuLCByZWFkIG91dCB0aGUgZXhpc3RpbmcgcGVyZm9ybWFuY2UgbG9nc1xuICAgICAgLy8gc28gdGhhdCB0aGUgY2hyb21lIGJ1ZmZlciBkb2VzIG5vdCBmaWxsIHVwLlxuICAgICAgYXdhaXQgdGhpcy5kcml2ZXIubG9ncygncGVyZm9ybWFuY2UnKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZHJpdmVyLmV4ZWN1dGVTY3JpcHQoYHBlcmZvcm1hbmNlLm1hcmsoJyR7bmFtZX0tYnBzdGFydCcpO2ApO1xuICB9XG5cbiAgb3ZlcnJpZGUgdGltZUVuZChuYW1lOiBzdHJpbmcsIHJlc3RhcnROYW1lOiBzdHJpbmd8bnVsbCA9IG51bGwpOiBQcm9taXNlPGFueT4ge1xuICAgIGxldCBzY3JpcHQgPSBgcGVyZm9ybWFuY2UubWFyaygnJHtuYW1lfS1icGVuZCcpO2A7XG4gICAgaWYgKHJlc3RhcnROYW1lKSB7XG4gICAgICBzY3JpcHQgKz0gYHBlcmZvcm1hbmNlLm1hcmsoJyR7cmVzdGFydE5hbWV9LWJwc3RhcnQnKTtgO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5kcml2ZXIuZXhlY3V0ZVNjcmlwdChzY3JpcHQpO1xuICB9XG5cbiAgLy8gU2VlIFtDaHJvbWUgVHJhY2UgRXZlbnRcbiAgLy8gRm9ybWF0XShodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9kb2N1bWVudC9kLzFDdkFDbHZGZnlBNVItUGhZVW1uNU9PUXRZTUg0aDZJMG5Tc0tjaE5BeVNVL2VkaXQpXG4gIG92ZXJyaWRlIHJlYWRQZXJmTG9nKCk6IFByb21pc2U8UGVyZkxvZ0V2ZW50W10+IHtcbiAgICAvLyBUT0RPKHRib3NjaCk6IENocm9tZWRyaXZlciBidWcgaHR0cHM6Ly9jb2RlLmdvb2dsZS5jb20vcC9jaHJvbWVkcml2ZXIvaXNzdWVzL2RldGFpbD9pZD0xMDk4XG4gICAgLy8gTmVlZCB0byBleGVjdXRlIGF0IGxlYXN0IG9uZSBjb21tYW5kIHNvIHRoYXQgdGhlIGJyb3dzZXIgbG9ncyBjYW4gYmUgcmVhZCBvdXQhXG4gICAgcmV0dXJuIHRoaXMuZHJpdmVyLmV4ZWN1dGVTY3JpcHQoJzErMScpXG4gICAgICAgIC50aGVuKChfKSA9PiB0aGlzLmRyaXZlci5sb2dzKCdwZXJmb3JtYW5jZScpKVxuICAgICAgICAudGhlbigoZW50cmllcykgPT4ge1xuICAgICAgICAgIGNvbnN0IGV2ZW50czogUGVyZkxvZ0V2ZW50W10gPSBbXTtcbiAgICAgICAgICBlbnRyaWVzLmZvckVhY2goKGVudHJ5OiBhbnkpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IG1lc3NhZ2UgPVxuICAgICAgICAgICAgICAgIChKU09OLnBhcnNlKGVudHJ5WydtZXNzYWdlJ10pIGFzXG4gICAgICAgICAgICAgICAgIHttZXNzYWdlOiB7bWV0aG9kOiBzdHJpbmcsIHBhcmFtczogUGVyZkxvZ0V2ZW50fX0pWydtZXNzYWdlJ107XG4gICAgICAgICAgICBpZiAobWVzc2FnZVsnbWV0aG9kJ10gPT09ICdUcmFjaW5nLmRhdGFDb2xsZWN0ZWQnKSB7XG4gICAgICAgICAgICAgIGV2ZW50cy5wdXNoKG1lc3NhZ2VbJ3BhcmFtcyddKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChtZXNzYWdlWydtZXRob2QnXSA9PT0gJ1RyYWNpbmcuYnVmZmVyVXNhZ2UnKSB7XG4gICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignVGhlIERldlRvb2xzIHRyYWNlIGJ1ZmZlciBmaWxsZWQgZHVyaW5nIHRoZSB0ZXN0IScpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgaWYgKHRoaXMuX3Jhd1BlcmZsb2dQYXRoICYmIGV2ZW50cy5sZW5ndGgpIHtcbiAgICAgICAgICAgIGZzLmFwcGVuZEZpbGVTeW5jKHRoaXMuX3Jhd1BlcmZsb2dQYXRoLCBKU09OLnN0cmluZ2lmeShldmVudHMpKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIHRoaXMuX2NvbnZlcnRQZXJmUmVjb3Jkc1RvRXZlbnRzKGV2ZW50cyk7XG4gICAgICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBfY29udmVydFBlcmZSZWNvcmRzVG9FdmVudHMoXG4gICAgICBjaHJvbWVFdmVudHM6IEFycmF5PHtba2V5OiBzdHJpbmddOiBhbnl9Piwgbm9ybWFsaXplZEV2ZW50czogUGVyZkxvZ0V2ZW50W118bnVsbCA9IG51bGwpIHtcbiAgICBpZiAoIW5vcm1hbGl6ZWRFdmVudHMpIHtcbiAgICAgIG5vcm1hbGl6ZWRFdmVudHMgPSBbXTtcbiAgICB9XG4gICAgY2hyb21lRXZlbnRzLmZvckVhY2goKGV2ZW50KSA9PiB7XG4gICAgICBjb25zdCBjYXRlZ29yaWVzID0gdGhpcy5fcGFyc2VDYXRlZ29yaWVzKGV2ZW50WydjYXQnXSk7XG4gICAgICBjb25zdCBub3JtYWxpemVkRXZlbnQgPSB0aGlzLl9jb252ZXJ0RXZlbnQoZXZlbnQsIGNhdGVnb3JpZXMpO1xuICAgICAgaWYgKG5vcm1hbGl6ZWRFdmVudCAhPSBudWxsKSBub3JtYWxpemVkRXZlbnRzIS5wdXNoKG5vcm1hbGl6ZWRFdmVudCk7XG4gICAgfSk7XG4gICAgcmV0dXJuIG5vcm1hbGl6ZWRFdmVudHM7XG4gIH1cblxuICBwcml2YXRlIF9jb252ZXJ0RXZlbnQoZXZlbnQ6IHtba2V5OiBzdHJpbmddOiBhbnl9LCBjYXRlZ29yaWVzOiBzdHJpbmdbXSkge1xuICAgIGNvbnN0IG5hbWUgPSBldmVudFsnbmFtZSddO1xuICAgIGNvbnN0IGFyZ3MgPSBldmVudFsnYXJncyddO1xuICAgIGlmICh0aGlzLl9pc0V2ZW50KGNhdGVnb3JpZXMsIG5hbWUsIFsnYmxpbmsuY29uc29sZSddKSkge1xuICAgICAgcmV0dXJuIG5vcm1hbGl6ZUV2ZW50KGV2ZW50LCB7J25hbWUnOiBuYW1lfSk7XG4gICAgfSBlbHNlIGlmICh0aGlzLl9pc0V2ZW50KGNhdGVnb3JpZXMsIG5hbWUsIFsnYmxpbmsudXNlcl90aW1pbmcnXSkpIHtcbiAgICAgIHJldHVybiBub3JtYWxpemVFdmVudChldmVudCwgeyduYW1lJzogbmFtZX0pO1xuICAgIH0gZWxzZSBpZiAodGhpcy5faXNFdmVudChcbiAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWVzLCBuYW1lLCBbJ2JlbmNobWFyayddLFxuICAgICAgICAgICAgICAgICAgICdCZW5jaG1hcmtJbnN0cnVtZW50YXRpb246OkltcGxUaHJlYWRSZW5kZXJpbmdTdGF0cycpKSB7XG4gICAgICAvLyBUT0RPKGdvZGVyYmF1ZXIpOiBJbnN0ZWFkIG9mIEJlbmNobWFya0luc3RydW1lbnRhdGlvbjo6SW1wbFRocmVhZFJlbmRlcmluZ1N0YXRzIHRoZVxuICAgICAgLy8gZm9sbG93aW5nIGV2ZW50cyBzaG91bGQgYmUgdXNlZCAoaWYgYXZhaWxhYmxlKSBmb3IgbW9yZSBhY2N1cmF0ZSBtZWFzdXJlbWVudHM6XG4gICAgICAvLyAgIDFzdCBjaG9pY2U6IHZzeW5jX2JlZm9yZSAtIGdyb3VuZCB0cnV0aCBvbiBBbmRyb2lkXG4gICAgICAvLyAgIDJuZCBjaG9pY2U6IEJlbmNobWFya0luc3RydW1lbnRhdGlvbjo6RGlzcGxheVJlbmRlcmluZ1N0YXRzIC0gYXZhaWxhYmxlIG9uIHN5c3RlbXMgd2l0aFxuICAgICAgLy8gICAgICAgICAgICAgICBuZXcgc3VyZmFjZXMgZnJhbWV3b3JrIChub3QgYnJvYWRseSBlbmFibGVkIHlldClcbiAgICAgIC8vICAgM3JkIGNob2ljZTogQmVuY2htYXJrSW5zdHJ1bWVudGF0aW9uOjpJbXBsVGhyZWFkUmVuZGVyaW5nU3RhdHMgLSBmYWxsYmFjayBldmVudCB0aGF0IGlzXG4gICAgICAvLyAgICAgICAgICAgICAgIGFsd2F5cyBhdmFpbGFibGUgaWYgc29tZXRoaW5nIGlzIHJlbmRlcmVkXG4gICAgICBjb25zdCBmcmFtZUNvdW50ID0gZXZlbnRbJ2FyZ3MnXVsnZGF0YSddWydmcmFtZV9jb3VudCddO1xuICAgICAgaWYgKGZyYW1lQ291bnQgPiAxKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignbXVsdGktZnJhbWUgcmVuZGVyIHN0YXRzIG5vdCBzdXBwb3J0ZWQnKTtcbiAgICAgIH1cbiAgICAgIGlmIChmcmFtZUNvdW50ID09IDEpIHtcbiAgICAgICAgcmV0dXJuIG5vcm1hbGl6ZUV2ZW50KGV2ZW50LCB7J25hbWUnOiAnZnJhbWUnfSk7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChcbiAgICAgICAgdGhpcy5faXNFdmVudChjYXRlZ29yaWVzLCBuYW1lLCBbJ2Rpc2FibGVkLWJ5LWRlZmF1bHQtZGV2dG9vbHMudGltZWxpbmUnXSwgJ1Jhc3Rlcml6ZScpIHx8XG4gICAgICAgIHRoaXMuX2lzRXZlbnQoXG4gICAgICAgICAgICBjYXRlZ29yaWVzLCBuYW1lLCBbJ2Rpc2FibGVkLWJ5LWRlZmF1bHQtZGV2dG9vbHMudGltZWxpbmUnXSwgJ0NvbXBvc2l0ZUxheWVycycpKSB7XG4gICAgICByZXR1cm4gbm9ybWFsaXplRXZlbnQoZXZlbnQsIHsnbmFtZSc6ICdyZW5kZXInfSk7XG4gICAgfSBlbHNlIGlmICh0aGlzLl9pc0V2ZW50KGNhdGVnb3JpZXMsIG5hbWUsIFsnZGV2dG9vbHMudGltZWxpbmUnLCAndjgnXSwgJ01ham9yR0MnKSkge1xuICAgICAgY29uc3Qgbm9ybUFyZ3MgPSB7XG4gICAgICAgICdtYWpvckdjJzogdHJ1ZSxcbiAgICAgICAgJ3VzZWRIZWFwU2l6ZSc6IGFyZ3NbJ3VzZWRIZWFwU2l6ZUFmdGVyJ10gIT09IHVuZGVmaW5lZCA/IGFyZ3NbJ3VzZWRIZWFwU2l6ZUFmdGVyJ10gOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJnc1sndXNlZEhlYXBTaXplQmVmb3JlJ11cbiAgICAgIH07XG4gICAgICByZXR1cm4gbm9ybWFsaXplRXZlbnQoZXZlbnQsIHsnbmFtZSc6ICdnYycsICdhcmdzJzogbm9ybUFyZ3N9KTtcbiAgICB9IGVsc2UgaWYgKHRoaXMuX2lzRXZlbnQoY2F0ZWdvcmllcywgbmFtZSwgWydkZXZ0b29scy50aW1lbGluZScsICd2OCddLCAnTWlub3JHQycpKSB7XG4gICAgICBjb25zdCBub3JtQXJncyA9IHtcbiAgICAgICAgJ21ham9yR2MnOiBmYWxzZSxcbiAgICAgICAgJ3VzZWRIZWFwU2l6ZSc6IGFyZ3NbJ3VzZWRIZWFwU2l6ZUFmdGVyJ10gIT09IHVuZGVmaW5lZCA/IGFyZ3NbJ3VzZWRIZWFwU2l6ZUFmdGVyJ10gOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJnc1sndXNlZEhlYXBTaXplQmVmb3JlJ11cbiAgICAgIH07XG4gICAgICByZXR1cm4gbm9ybWFsaXplRXZlbnQoZXZlbnQsIHsnbmFtZSc6ICdnYycsICdhcmdzJzogbm9ybUFyZ3N9KTtcbiAgICB9IGVsc2UgaWYgKFxuICAgICAgICB0aGlzLl9pc0V2ZW50KGNhdGVnb3JpZXMsIG5hbWUsIFsnZGV2dG9vbHMudGltZWxpbmUnXSwgJ0Z1bmN0aW9uQ2FsbCcpICYmXG4gICAgICAgICghYXJncyB8fCAhYXJnc1snZGF0YSddIHx8XG4gICAgICAgICAoYXJnc1snZGF0YSddWydzY3JpcHROYW1lJ10gIT09ICdJbmplY3RlZFNjcmlwdCcgJiYgYXJnc1snZGF0YSddWydzY3JpcHROYW1lJ10gIT09ICcnKSkpIHtcbiAgICAgIHJldHVybiBub3JtYWxpemVFdmVudChldmVudCwgeyduYW1lJzogJ3NjcmlwdCd9KTtcbiAgICB9IGVsc2UgaWYgKHRoaXMuX2lzRXZlbnQoY2F0ZWdvcmllcywgbmFtZSwgWydkZXZ0b29scy50aW1lbGluZSddLCAnRXZhbHVhdGVTY3JpcHQnKSkge1xuICAgICAgcmV0dXJuIG5vcm1hbGl6ZUV2ZW50KGV2ZW50LCB7J25hbWUnOiAnc2NyaXB0J30pO1xuICAgIH0gZWxzZSBpZiAodGhpcy5faXNFdmVudChcbiAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWVzLCBuYW1lLCBbJ2RldnRvb2xzLnRpbWVsaW5lJywgJ2JsaW5rJ10sICdVcGRhdGVMYXlvdXRUcmVlJykpIHtcbiAgICAgIHJldHVybiBub3JtYWxpemVFdmVudChldmVudCwgeyduYW1lJzogJ3JlbmRlcid9KTtcbiAgICB9IGVsc2UgaWYgKFxuICAgICAgICB0aGlzLl9pc0V2ZW50KGNhdGVnb3JpZXMsIG5hbWUsIFsnZGV2dG9vbHMudGltZWxpbmUnXSwgJ1VwZGF0ZUxheWVyVHJlZScpIHx8XG4gICAgICAgIHRoaXMuX2lzRXZlbnQoY2F0ZWdvcmllcywgbmFtZSwgWydkZXZ0b29scy50aW1lbGluZSddLCAnTGF5b3V0JykgfHxcbiAgICAgICAgdGhpcy5faXNFdmVudChjYXRlZ29yaWVzLCBuYW1lLCBbJ2RldnRvb2xzLnRpbWVsaW5lJ10sICdQYWludCcpKSB7XG4gICAgICByZXR1cm4gbm9ybWFsaXplRXZlbnQoZXZlbnQsIHsnbmFtZSc6ICdyZW5kZXInfSk7XG4gICAgfSBlbHNlIGlmICh0aGlzLl9pc0V2ZW50KGNhdGVnb3JpZXMsIG5hbWUsIFsnZGV2dG9vbHMudGltZWxpbmUnXSwgJ1Jlc291cmNlUmVjZWl2ZWREYXRhJykpIHtcbiAgICAgIGNvbnN0IG5vcm1BcmdzID0geydlbmNvZGVkRGF0YUxlbmd0aCc6IGFyZ3NbJ2RhdGEnXVsnZW5jb2RlZERhdGFMZW5ndGgnXX07XG4gICAgICByZXR1cm4gbm9ybWFsaXplRXZlbnQoZXZlbnQsIHsnbmFtZSc6ICdyZWNlaXZlZERhdGEnLCAnYXJncyc6IG5vcm1BcmdzfSk7XG4gICAgfSBlbHNlIGlmICh0aGlzLl9pc0V2ZW50KGNhdGVnb3JpZXMsIG5hbWUsIFsnZGV2dG9vbHMudGltZWxpbmUnXSwgJ1Jlc291cmNlU2VuZFJlcXVlc3QnKSkge1xuICAgICAgY29uc3QgZGF0YSA9IGFyZ3NbJ2RhdGEnXTtcbiAgICAgIGNvbnN0IG5vcm1BcmdzID0geyd1cmwnOiBkYXRhWyd1cmwnXSwgJ21ldGhvZCc6IGRhdGFbJ3JlcXVlc3RNZXRob2QnXX07XG4gICAgICByZXR1cm4gbm9ybWFsaXplRXZlbnQoZXZlbnQsIHsnbmFtZSc6ICdzZW5kUmVxdWVzdCcsICdhcmdzJzogbm9ybUFyZ3N9KTtcbiAgICB9IGVsc2UgaWYgKHRoaXMuX2lzRXZlbnQoY2F0ZWdvcmllcywgbmFtZSwgWydibGluay51c2VyX3RpbWluZyddLCAnbmF2aWdhdGlvblN0YXJ0JykpIHtcbiAgICAgIHJldHVybiBub3JtYWxpemVFdmVudChldmVudCwgeyduYW1lJzogJ25hdmlnYXRpb25TdGFydCd9KTtcbiAgICB9XG4gICAgcmV0dXJuIG51bGw7ICAvLyBub3RoaW5nIHVzZWZ1bCBpbiB0aGlzIGV2ZW50XG4gIH1cblxuICBwcml2YXRlIF9wYXJzZUNhdGVnb3JpZXMoY2F0ZWdvcmllczogc3RyaW5nKTogc3RyaW5nW10ge1xuICAgIHJldHVybiBjYXRlZ29yaWVzLnNwbGl0KCcsJyk7XG4gIH1cblxuICBwcml2YXRlIF9pc0V2ZW50KFxuICAgICAgZXZlbnRDYXRlZ29yaWVzOiBzdHJpbmdbXSwgZXZlbnROYW1lOiBzdHJpbmcsIGV4cGVjdGVkQ2F0ZWdvcmllczogc3RyaW5nW10sXG4gICAgICBleHBlY3RlZE5hbWU6IHN0cmluZ3xudWxsID0gbnVsbCk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IGhhc0NhdGVnb3JpZXMgPSBleHBlY3RlZENhdGVnb3JpZXMucmVkdWNlKFxuICAgICAgICAodmFsdWUsIGNhdCkgPT4gdmFsdWUgJiYgZXZlbnRDYXRlZ29yaWVzLmluZGV4T2YoY2F0KSAhPT0gLTEsIHRydWUpO1xuICAgIHJldHVybiAhZXhwZWN0ZWROYW1lID8gaGFzQ2F0ZWdvcmllcyA6IGhhc0NhdGVnb3JpZXMgJiYgZXZlbnROYW1lID09PSBleHBlY3RlZE5hbWU7XG4gIH1cblxuICBvdmVycmlkZSBwZXJmTG9nRmVhdHVyZXMoKTogUGVyZkxvZ0ZlYXR1cmVzIHtcbiAgICByZXR1cm4gbmV3IFBlcmZMb2dGZWF0dXJlcyh7cmVuZGVyOiB0cnVlLCBnYzogdHJ1ZSwgZnJhbWVDYXB0dXJlOiB0cnVlLCB1c2VyVGltaW5nOiB0cnVlfSk7XG4gIH1cblxuICBvdmVycmlkZSBzdXBwb3J0cyhjYXBhYmlsaXRpZXM6IHtba2V5OiBzdHJpbmddOiBhbnl9KTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuX21ham9yQ2hyb21lVmVyc2lvbiA+PSA0NCAmJiBjYXBhYmlsaXRpZXNbJ2Jyb3dzZXJOYW1lJ10udG9Mb3dlckNhc2UoKSA9PT0gJ2Nocm9tZSc7XG4gIH1cbn1cblxuZnVuY3Rpb24gbm9ybWFsaXplRXZlbnQoY2hyb21lRXZlbnQ6IHtba2V5OiBzdHJpbmddOiBhbnl9LCBkYXRhOiBQZXJmTG9nRXZlbnQpOiBQZXJmTG9nRXZlbnQge1xuICBsZXQgcGggPSBjaHJvbWVFdmVudFsncGgnXS50b1VwcGVyQ2FzZSgpO1xuICBpZiAocGggPT09ICdTJykge1xuICAgIHBoID0gJ0InO1xuICB9IGVsc2UgaWYgKHBoID09PSAnRicpIHtcbiAgICBwaCA9ICdFJztcbiAgfSBlbHNlIGlmIChwaCA9PT0gJ1InKSB7XG4gICAgLy8gbWFyayBldmVudHMgZnJvbSBuYXZpZ2F0aW9uIHRpbWluZ1xuICAgIHBoID0gJ0knO1xuICAgIC8vIENocm9tZSA2NSsgZG9lc24ndCBhbGxvdyB1c2VyIHRpbWluZyBtZWFzdXJlbWVudHMgYWNyb3NzIHBhZ2UgbG9hZHMuXG4gICAgLy8gSW5zdGVhZCwgd2UgdXNlIHBlcmZvcm1hbmNlIG1hcmtzIHdpdGggc3BlY2lhbCBuYW1lcy5cbiAgICBpZiAoY2hyb21lRXZlbnRbJ25hbWUnXS5tYXRjaCgvLWJwc3RhcnQvKSkge1xuICAgICAgZGF0YVsnbmFtZSddID0gY2hyb21lRXZlbnRbJ25hbWUnXS5zbGljZSgwLCAtOCk7XG4gICAgICBwaCA9ICdCJztcbiAgICB9IGVsc2UgaWYgKGNocm9tZUV2ZW50WyduYW1lJ10ubWF0Y2goLy1icGVuZCQvKSkge1xuICAgICAgZGF0YVsnbmFtZSddID0gY2hyb21lRXZlbnRbJ25hbWUnXS5zbGljZSgwLCAtNik7XG4gICAgICBwaCA9ICdFJztcbiAgICB9XG4gIH1cbiAgY29uc3QgcmVzdWx0OiB7W2tleTogc3RyaW5nXTogYW55fSA9XG4gICAgICB7J3BpZCc6IGNocm9tZUV2ZW50WydwaWQnXSwgJ3BoJzogcGgsICdjYXQnOiAndGltZWxpbmUnLCAndHMnOiBjaHJvbWVFdmVudFsndHMnXSAvIDEwMDB9O1xuICBpZiAocGggPT09ICdYJykge1xuICAgIGxldCBkdXIgPSBjaHJvbWVFdmVudFsnZHVyJ107XG4gICAgaWYgKGR1ciA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICBkdXIgPSBjaHJvbWVFdmVudFsndGR1ciddO1xuICAgIH1cbiAgICByZXN1bHRbJ2R1ciddID0gIWR1ciA/IDAuMCA6IGR1ciAvIDEwMDA7XG4gIH1cbiAgZm9yIChjb25zdCBwcm9wIGluIGRhdGEpIHtcbiAgICByZXN1bHRbcHJvcF0gPSBkYXRhW3Byb3BdO1xuICB9XG4gIHJldHVybiByZXN1bHQ7XG59XG4iXX0=