UNPKG

testcafe

Version:

Automated browser testing for the modern web development stack.

355 lines 62 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const debug_1 = __importDefault(require("debug")); const testcafe_browser_tools_1 = __importDefault(require("testcafe-browser-tools")); const os_family_1 = __importDefault(require("os-family")); const path_1 = require("path"); const make_dir_1 = __importDefault(require("make-dir")); const connection_1 = __importDefault(require("../connection")); const delay_1 = __importDefault(require("../../utils/delay")); const client_functions_1 = require("./utils/client-functions"); const warning_message_1 = __importDefault(require("../../notifications/warning-message")); const get_os_info_1 = __importDefault(require("get-os-info")); const DEBUG_LOGGER = (0, debug_1.default)('testcafe:browser:provider'); const BROWSER_OPENING_DELAY = 2000; const RESIZE_DIFF_SIZE = { width: 100, height: 100, }; function sumSizes(sizeA, sizeB) { return { width: sizeA.width + sizeB.width, height: sizeA.height + sizeB.height, }; } function subtractSizes(sizeA, sizeB) { return { width: sizeA.width - sizeB.width, height: sizeA.height - sizeB.height, }; } class BrowserProvider { constructor(plugin) { this.plugin = plugin; this.initPromise = Promise.resolve(false); this.isMultiBrowser = this.plugin.isMultiBrowser; // HACK: The browser window has different border sizes in normal and maximized modes. So, we need to be sure that the window is // not maximized before resizing it in order to keep the mechanism of correcting the client area size working. When browser is started, // we are resizing it for the first time to switch the window to normal mode, and for the second time - to restore the client area size. this.localBrowsersInfo = {}; } _ensureLocalBrowserInfo(browserId) { if (this.localBrowsersInfo[browserId]) return; this.localBrowsersInfo[browserId] = { windowDescriptor: null, maxScreenSize: null, resizeCorrections: null, }; } async _findWindow(browserId) { const pageTitle = this._getPageTitle(browserId); return testcafe_browser_tools_1.default.findWindow(pageTitle); } _getPageTitle(browserId) { if (this.plugin.getPageTitle) return this.plugin.getPageTitle(browserId); return browserId; } _getWindowDescriptor(browserId) { if (this.plugin.getWindowDescriptor) return this.plugin.getWindowDescriptor(browserId); return this.localBrowsersInfo[browserId] && this.localBrowsersInfo[browserId].windowDescriptor; } _setWindowDescriptor(browserId, windowDescriptor) { if (this.plugin.setWindowDescriptor) { this.plugin.setWindowDescriptor(browserId, windowDescriptor); return; } this.localBrowsersInfo[browserId].windowDescriptor = windowDescriptor; } _getMaxScreenSize(browserId) { return this.localBrowsersInfo[browserId] && this.localBrowsersInfo[browserId].maxScreenSize; } _getResizeCorrections(browserId) { return this.localBrowsersInfo[browserId] && this.localBrowsersInfo[browserId].resizeCorrections; } _isBrowserIdle(browserId) { const connection = connection_1.default.getById(browserId); return connection.idle; } async _calculateResizeCorrections(browserId) { if (!this._isBrowserIdle(browserId)) return; const title = await this.plugin.runInitScript(browserId, client_functions_1.GET_TITLE_SCRIPT); if (!await testcafe_browser_tools_1.default.isMaximized(title)) return; const currentSize = await this.plugin.runInitScript(browserId, client_functions_1.GET_WINDOW_DIMENSIONS_INFO_SCRIPT); const etalonSize = subtractSizes(currentSize, RESIZE_DIFF_SIZE); await testcafe_browser_tools_1.default.resize(title, currentSize.width, currentSize.height, etalonSize.width, etalonSize.height); let resizedSize = await this.plugin.runInitScript(browserId, client_functions_1.GET_WINDOW_DIMENSIONS_INFO_SCRIPT); let correctionSize = subtractSizes(resizedSize, etalonSize); await testcafe_browser_tools_1.default.resize(title, resizedSize.width, resizedSize.height, etalonSize.width, etalonSize.height); resizedSize = await this.plugin.runInitScript(browserId, client_functions_1.GET_WINDOW_DIMENSIONS_INFO_SCRIPT); correctionSize = sumSizes(correctionSize, subtractSizes(resizedSize, etalonSize)); if (this.localBrowsersInfo[browserId]) this.localBrowsersInfo[browserId].resizeCorrections = correctionSize; await testcafe_browser_tools_1.default.maximize(title); } async _calculateMacSizeLimits(browserId) { if (!this._isBrowserIdle(browserId)) return; const sizeInfo = await this.plugin.runInitScript(browserId, client_functions_1.GET_WINDOW_DIMENSIONS_INFO_SCRIPT); if (this.localBrowsersInfo[browserId]) { this.localBrowsersInfo[browserId].maxScreenSize = { width: sizeInfo.availableWidth - (sizeInfo.outerWidth - sizeInfo.width), height: sizeInfo.availableHeight - (sizeInfo.outerHeight - sizeInfo.height), }; } } async _ensureBrowserWindowDescriptor(browserId) { if (this._getWindowDescriptor(browserId)) return; await this._ensureLocalBrowserInfo(browserId); // NOTE: delay to ensure the window finished the opening await this.plugin.waitForConnectionReady(browserId); await (0, delay_1.default)(BROWSER_OPENING_DELAY); if (this.localBrowsersInfo[browserId]) { const connection = connection_1.default.getById(browserId); let windowDescriptor = null; try { windowDescriptor = await this._findWindow(browserId); } catch (err) { // NOTE: We can suppress the error here since we can just disable window manipulation functions // when we cannot find a local window descriptor DEBUG_LOGGER(err); connection.addWarning(warning_message_1.default.cannotFindWindowDescriptorError, connection.browserInfo.alias, err.message); } this._setWindowDescriptor(browserId, windowDescriptor); } } async _ensureBrowserWindowParameters(browserId) { await this._ensureBrowserWindowDescriptor(browserId); if (os_family_1.default.win && !this._getResizeCorrections(browserId)) await this._calculateResizeCorrections(browserId); else if (os_family_1.default.mac && !this._getMaxScreenSize(browserId)) await this._calculateMacSizeLimits(browserId); } async _closeLocalBrowser(browserId) { if (this.plugin.needCleanUpBrowserInfo) this.plugin.cleanUpBrowserInfo(browserId); const windowDescriptor = this._getWindowDescriptor(browserId); await testcafe_browser_tools_1.default.close(windowDescriptor); } async _resizeLocalBrowserWindow(browserId, width, height, currentWidth, currentHeight) { await this._ensureBrowserWindowDescriptor(browserId); const resizeCorrections = this._getResizeCorrections(browserId); if (resizeCorrections && await testcafe_browser_tools_1.default.isMaximized(this._getWindowDescriptor(browserId))) { width -= resizeCorrections.width; height -= resizeCorrections.height; } await testcafe_browser_tools_1.default.resize(this._getWindowDescriptor(browserId), currentWidth, currentHeight, width, height); } async _takeLocalBrowserScreenshot(browserId, screenshotPath) { await testcafe_browser_tools_1.default.screenshot(this._getWindowDescriptor(browserId), screenshotPath); } async _canResizeLocalBrowserWindowToDimensions(browserId, width, height) { if (!os_family_1.default.mac) return true; const maxScreenSize = this._getMaxScreenSize(browserId); return width <= maxScreenSize.width && height <= maxScreenSize.height; } async _maximizeLocalBrowserWindow(browserId) { await this._ensureBrowserWindowDescriptor(browserId); await testcafe_browser_tools_1.default.maximize(this._getWindowDescriptor(browserId)); } async _ensureRetryTestPagesWarning(browserId) { const connection = connection_1.default.getById(browserId); if (connection === null || connection === void 0 ? void 0 : connection.retryTestPages) { const isServiceWorkerEnabled = await this.plugin.runInitScript(browserId, client_functions_1.GET_IS_SERVICE_WORKER_ENABLED); if (!isServiceWorkerEnabled) connection.addWarning(warning_message_1.default.retryTestPagesIsNotSupported, connection.browserInfo.alias, connection.browserInfo.alias); } } async canUseDefaultWindowActions(browserId) { const isLocalBrowser = await this.plugin.isLocalBrowser(browserId); const isHeadlessBrowser = await this.plugin.isHeadlessBrowser(browserId); return isLocalBrowser && !isHeadlessBrowser; } async init() { const initialized = await this.initPromise; if (initialized) return; this.initPromise = this.plugin .init() .then(() => true); try { await this.initPromise; } catch (error) { this.initPromise = Promise.resolve(false); throw error; } } async dispose() { const initialized = await this.initPromise; if (!initialized) return; this.initPromise = this.plugin .dispose() .then(() => false); try { await this.initPromise; } catch (error) { this.initPromise = Promise.resolve(false); throw error; } } async isLocalBrowser(browserId, browserName) { return await this.plugin.isLocalBrowser(browserId, browserName); } isHeadlessBrowser(browserId, browserName) { return this.plugin.isHeadlessBrowser(browserId, browserName); } async getOSInfo(browserId) { if (await this.isLocalBrowser(browserId)) return await (0, get_os_info_1.default)(); return await this.plugin.getOSInfo(browserId); } async openBrowser(browserId, pageUrl, browserOption, additionalOptions) { await this.plugin.openBrowser(browserId, pageUrl, browserOption, additionalOptions); await this._ensureRetryTestPagesWarning(browserId); if (await this.canUseDefaultWindowActions(browserId)) await this._ensureBrowserWindowParameters(browserId); } async closeBrowser(browserId, data) { const canUseDefaultWindowActions = await this.canUseDefaultWindowActions(browserId); const customActionsInfo = await this.hasCustomActionForBrowser(browserId); const hasCustomCloseBrowser = customActionsInfo.hasCloseBrowser; const usePluginsCloseBrowser = hasCustomCloseBrowser || !canUseDefaultWindowActions; if (usePluginsCloseBrowser) await this.plugin.closeBrowser(browserId, data); else await this._closeLocalBrowser(browserId); if (canUseDefaultWindowActions) delete this.localBrowsersInfo[browserId]; } async getBrowserList() { return await this.plugin.getBrowserList(); } async isValidBrowserName(browserName) { return await this.plugin.isValidBrowserName(browserName); } async resizeWindow(browserId, width, height, currentWidth, currentHeight, isNativeAutomation) { const canUseDefaultWindowActions = await this.canUseDefaultWindowActions(browserId); const customActionsInfo = await this.hasCustomActionForBrowser(browserId); const hasCustomResizeWindow = customActionsInfo.hasResizeWindow; if (canUseDefaultWindowActions && !hasCustomResizeWindow && isNativeAutomation) { await this.plugin.resizeWindowNativeAutomation(browserId, width, height, currentWidth, currentHeight); return; } if (canUseDefaultWindowActions && !hasCustomResizeWindow) { await this._resizeLocalBrowserWindow(browserId, width, height, currentWidth, currentHeight); return; } await this.plugin.resizeWindow(browserId, width, height, currentWidth, currentHeight); } async canResizeWindowToDimensions(browserId, width, height) { const canUseDefaultWindowActions = await this.canUseDefaultWindowActions(browserId); const customActionsInfo = await this.hasCustomActionForBrowser(browserId); const hasCustomCanResizeToDimensions = customActionsInfo.hasCanResizeWindowToDimensions; if (canUseDefaultWindowActions && !hasCustomCanResizeToDimensions) return await this._canResizeLocalBrowserWindowToDimensions(browserId, width, height); return await this.plugin.canResizeWindowToDimensions(browserId, width, height); } async maximizeWindow(browserId, isNativeAutomation) { const canUseDefaultWindowActions = await this.canUseDefaultWindowActions(browserId); const customActionsInfo = await this.hasCustomActionForBrowser(browserId); const hasCustomMaximizeWindow = customActionsInfo.hasMaximizeWindow; if (canUseDefaultWindowActions && !hasCustomMaximizeWindow && isNativeAutomation) return await this.plugin.maximizeWindowNativeAutomation(browserId); if (canUseDefaultWindowActions && !hasCustomMaximizeWindow) return await this._maximizeLocalBrowserWindow(browserId); return await this.plugin.maximizeWindow(browserId); } async takeScreenshot(browserId, screenshotPath, pageWidth, pageHeight, fullPage) { const canUseDefaultWindowActions = await this.canUseDefaultWindowActions(browserId); const customActionsInfo = await this.hasCustomActionForBrowser(browserId); const hasCustomTakeScreenshot = customActionsInfo.hasTakeScreenshot; const connection = connection_1.default.getById(browserId); const takeLocalBrowsersScreenshot = canUseDefaultWindowActions && !hasCustomTakeScreenshot; const isLocalFullPageMode = takeLocalBrowsersScreenshot && fullPage; if (isLocalFullPageMode) { connection.addWarning(warning_message_1.default.screenshotsFullPageNotSupported, connection.browserInfo.alias); return; } await (0, make_dir_1.default)((0, path_1.dirname)(screenshotPath)); if (takeLocalBrowsersScreenshot) await this._takeLocalBrowserScreenshot(browserId, screenshotPath); else await this.plugin.takeScreenshot(browserId, screenshotPath, pageWidth, pageHeight, fullPage); } async getVideoFrameData(browserId) { return this.plugin.getVideoFrameData(browserId); } async startCapturingVideo(browserId) { await this.plugin.startCapturingVideo(browserId); } async stopCapturingVideo(browserId) { await this.plugin.stopCapturingVideo(browserId); } async hasCustomActionForBrowser(browserId) { return this.plugin.hasCustomActionForBrowser(browserId); } async reportJobResult(browserId, status, data) { await this.plugin.reportJobResult(browserId, status, data); } getActiveWindowId(browserId) { if (!this.plugin.supportMultipleWindows) return null; return this.plugin.getActiveWindowId(browserId); } resetActiveWindowId(browserId) { if (!this.plugin.supportMultipleWindows) return null; return this.plugin.resetActiveWindowId(browserId); } getNewActiveWindowId(browserId) { return this.plugin.getNewActiveWindowId(browserId); } setActiveWindowId(browserId, val) { this.plugin.setActiveWindowId(browserId, val); } async openFileProtocol(browserId, url) { await this.plugin.openFileProtocol(browserId, url); } async closeBrowserChildWindow(browserId, windowId) { await this.plugin.closeBrowserChildWindow(browserId, windowId); } async dispatchNativeAutomationEvent(browserId, type, options) { await this.plugin.dispatchNativeAutomationEvent(browserId, type, options); } async dispatchNativeAutomationEventSequence(browserId, sequence) { await this.plugin.dispatchNativeAutomationEventSequence(browserId, sequence); } supportNativeAutomation() { return this.plugin.supportNativeAutomation(); } getNativeAutomation(browserId) { return this.plugin.getNativeAutomation(browserId); } async getCurrentCDPSession(browserId) { return this.plugin.getCurrentCDPSession(browserId); } getNewWindowIdInNativeAutomation(browserId, windowId) { return this.plugin.getNewWindowIdInNativeAutomation(browserId, windowId); } } exports.default = BrowserProvider; module.exports = exports.default; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYnJvd3Nlci9wcm92aWRlci9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLGtEQUEwQjtBQUMxQixvRkFBa0Q7QUFDbEQsMERBQTJCO0FBQzNCLCtCQUErQjtBQUMvQix3REFBK0I7QUFDL0IsK0RBQXNFO0FBQ3RFLDhEQUFzQztBQUN0QywrREFJa0M7QUFDbEMsMEZBQWtFO0FBR2xFLDhEQUFxRDtBQU9yRCxNQUFNLFlBQVksR0FBRyxJQUFBLGVBQUssRUFBQywyQkFBMkIsQ0FBQyxDQUFDO0FBRXhELE1BQU0scUJBQXFCLEdBQUcsSUFBSSxDQUFDO0FBRW5DLE1BQU0sZ0JBQWdCLEdBQUc7SUFDckIsS0FBSyxFQUFHLEdBQUc7SUFDWCxNQUFNLEVBQUUsR0FBRztDQUNkLENBQUM7QUFhRixTQUFTLFFBQVEsQ0FBRSxLQUFXLEVBQUUsS0FBVztJQUN2QyxPQUFPO1FBQ0gsS0FBSyxFQUFHLEtBQUssQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDLEtBQUs7UUFDakMsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDLE1BQU07S0FDdEMsQ0FBQztBQUNOLENBQUM7QUFFRCxTQUFTLGFBQWEsQ0FBRSxLQUFXLEVBQUUsS0FBVztJQUM1QyxPQUFPO1FBQ0gsS0FBSyxFQUFHLEtBQUssQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDLEtBQUs7UUFDakMsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDLE1BQU07S0FDdEMsQ0FBQztBQUNOLENBQUM7QUFFRCxNQUFxQixlQUFlO0lBTWhDLFlBQW9CLE1BQVc7UUFDM0IsSUFBSSxDQUFDLE1BQU0sR0FBVyxNQUFNLENBQUM7UUFDN0IsSUFBSSxDQUFDLFdBQVcsR0FBTSxPQUFPLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzdDLElBQUksQ0FBQyxj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QUMsdUJBQXVCLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQ25FLENBQUM7SUFFTSxLQUFLLENBQUMsNkJBQTZCLENBQUUsU0FBaUIsRUFBRSxJQUFlLEVBQUUsT0FBWTtRQUN4RixNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsNkJBQTZCLENBQUMsU0FBUyxFQUFFLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQztJQUM5RSxDQUFDO0lBRU0sS0FBSyxDQUFDLHFDQUFxQyxDQUFFLFNBQWlCLEVBQUUsUUFBWTtRQUMvRSxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMscUNBQXFDLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQ2pGLENBQUM7SUFFTSx1QkFBdUI7UUFDMUIsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLHVCQUF1QixFQUFFLENBQUM7SUFDakQsQ0FBQztJQUVNLG1CQUFtQixDQUFFLFNBQWlCO1FBQ3pDLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUN0RCxDQUFDO0lBRU0sS0FBSyxDQUFDLG9CQUFvQixDQUFFLFNBQWlCO1FBQ2hELE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxvQkFBb0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUN2RCxDQUFDO0lBRU0sZ0NBQWdDLENBQUUsU0FBaUIsRUFBRSxRQUFnQjtRQUN4RSxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsZ0NBQWdDLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQzdFLENBQUM7Q0FDSjtBQXhiRCxrQ0F3YkMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgZGVidWcgZnJvbSAnZGVidWcnO1xuaW1wb3J0IGJyb3dzZXJUb29scyBmcm9tICd0ZXN0Y2FmZS1icm93c2VyLXRvb2xzJztcbmltcG9ydCBPUyBmcm9tICdvcy1mYW1pbHknO1xuaW1wb3J0IHsgZGlybmFtZSB9IGZyb20gJ3BhdGgnO1xuaW1wb3J0IG1ha2VEaXIgZnJvbSAnbWFrZS1kaXInO1xuaW1wb3J0IEJyb3dzZXJDb25uZWN0aW9uLCB7IEJyb3dzZXJDbG9zaW5nSW5mbyB9IGZyb20gJy4uL2Nvbm5lY3Rpb24nO1xuaW1wb3J0IGRlbGF5IGZyb20gJy4uLy4uL3V0aWxzL2RlbGF5JztcbmltcG9ydCB7XG4gICAgR0VUX0lTX1NFUlZJQ0VfV09SS0VSX0VOQUJMRUQsXG4gICAgR0VUX1RJVExFX1NDUklQVCxcbiAgICBHRVRfV0lORE9XX0RJTUVOU0lPTlNfSU5GT19TQ1JJUFQsXG59IGZyb20gJy4vdXRpbHMvY2xpZW50LWZ1bmN0aW9ucyc7XG5pbXBvcnQgV0FSTklOR19NRVNTQUdFIGZyb20gJy4uLy4uL25vdGlmaWNhdGlvbnMvd2FybmluZy1tZXNzYWdlJztcbmltcG9ydCB7IERpY3Rpb25hcnkgfSBmcm9tICcuLi8uLi9jb25maWd1cmF0aW9uL2ludGVyZmFjZXMnO1xuaW1wb3J0IHsgV2luZG93RGltZW50aW9uc0luZm8gfSBmcm9tICcuLi9pbnRlcmZhY2VzJztcbmltcG9ydCBnZXRMb2NhbE9TSW5mbywgeyBPU0luZm8gfSBmcm9tICdnZXQtb3MtaW5mbyc7XG5pbXBvcnQgeyBPcGVuQnJvd3NlckFkZGl0aW9uYWxPcHRpb25zIH0gZnJvbSAnLi4vLi4vc2hhcmVkL3R5cGVzJztcbmltcG9ydCB7IEV2ZW50VHlwZSB9IGZyb20gJy4uLy4uL25hdGl2ZS1hdXRvbWF0aW9uL3R5cGVzJztcbmltcG9ydCB7IE5hdGl2ZUF1dG9tYXRpb25CYXNlIH0gZnJvbSAnLi4vLi4vbmF0aXZlLWF1dG9tYXRpb24nO1xuaW1wb3J0IHJlbW90ZUNocm9tZSBmcm9tICdjaHJvbWUtcmVtb3RlLWludGVyZmFjZSc7XG5cblxuY29uc3QgREVCVUdfTE9HR0VSID0gZGVidWcoJ3Rlc3RjYWZlOmJyb3dzZXI6cHJvdmlkZXInKTtcblxuY29uc3QgQlJPV1NFUl9PUEVOSU5HX0RFTEFZID0gMjAwMDtcblxuY29uc3QgUkVTSVpFX0RJRkZfU0laRSA9IHtcbiAgICB3aWR0aDogIDEwMCxcbiAgICBoZWlnaHQ6IDEwMCxcbn07XG5cbmludGVyZmFjZSBTaXplIHtcbiAgICB3aWR0aDogbnVtYmVyO1xuICAgIGhlaWdodDogbnVtYmVyO1xufVxuXG5pbnRlcmZhY2UgTG9jYWxCcm93c2VySW5mbyB7XG4gICAgd2luZG93RGVzY3JpcHRvcjogbnVsbCB8IHN0cmluZztcbiAgICBtYXhTY3JlZW5TaXplOiBudWxsIHwgU2l6ZTtcbiAgICByZXNpemVDb3JyZWN0aW9uczogbnVsbCB8IFNpemU7XG59XG5cbmZ1bmN0aW9uIHN1bVNpemVzIChzaXplQTogU2l6ZSwgc2l6ZUI6IFNpemUpOiBTaXplIHtcbiAgICByZXR1cm4ge1xuICAgICAgICB3aWR0aDogIHNpemVBLndpZHRoICsgc2l6ZUIud2lkdGgsXG4gICAgICAgIGhlaWdodDogc2l6ZUEuaGVpZ2h0ICsgc2l6ZUIuaGVpZ2h0LFxuICAgIH07XG59XG5cbmZ1bmN0aW9uIHN1YnRyYWN0U2l6ZXMgKHNpemVBOiBTaXplLCBzaXplQjogU2l6ZSk6IFNpemUge1xuICAgIHJldHVybiB7XG4gICAgICAgIHdpZHRoOiAgc2l6ZUEud2lkdGggLSBzaXplQi53aWR0aCxcbiAgICAgICAgaGVpZ2h0OiBzaXplQS5oZWlnaHQgLSBzaXplQi5oZWlnaHQsXG4gICAgfTtcbn1cblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQnJvd3NlclByb3ZpZGVyIHtcbiAgICBwcml2YXRlIHBsdWdpbjogYW55O1xuICAgIHByaXZhdGUgaW5pdFByb21pc2U6IFByb21pc2U8YW55PjtcbiAgICBwcml2YXRlIGlzTXVsdGlCcm93c2VyOiBib29sZWFuO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgbG9jYWxCcm93c2Vyc0luZm86IERpY3Rpb25hcnk8TG9jYWxCcm93c2VySW5mbz47XG5cbiAgICBwdWJsaWMgY29uc3RydWN0b3IgKHBsdWdpbjogYW55KSB7XG4gICAgICAgIHRoaXMucGx1Z2luICAgICAgICAgPSBwbHVnaW47XG4gICAgICAgIHRoaXMuaW5pdFByb21pc2UgICAgPSBQcm9taXNlLnJlc29sdmUoZmFsc2UpO1xuICAgICAgICB0aGlzLmlzTXVsdGlCcm93c2VyID0gdGhpcy5wbHVnaW4uaXNNdWx0aUJyb3dzZXI7XG4gICAgICAgIC8vIEhBQ0s6IFRoZSBicm93c2VyIHdpbmRvdyBoYXMgZGlmZmVyZW50IGJvcmRlciBzaXplcyBpbiBub3JtYWwgYW5kIG1heGltaXplZCBtb2Rlcy4gU28sIHdlIG5lZWQgdG8gYmUgc3VyZSB0aGF0IHRoZSB3aW5kb3cgaXNcbiAgICAgICAgLy8gbm90IG1heGltaXplZCBiZWZvcmUgcmVzaXppbmcgaXQgaW4gb3JkZXIgdG8ga2VlcCB0aGUgbWVjaGFuaXNtIG9mIGNvcnJlY3RpbmcgdGhlIGNsaWVudCBhcmVhIHNpemUgd29ya2luZy4gV2hlbiBicm93c2VyIGlzIHN0YXJ0ZWQsXG4gICAgICAgIC8vIHdlIGFyZSByZXNpemluZyBpdCBmb3IgdGhlIGZpcnN0IHRpbWUgdG8gc3dpdGNoIHRoZSB3aW5kb3cgdG8gbm9ybWFsIG1vZGUsIGFuZCBmb3IgdGhlIHNlY29uZCB0aW1lIC0gdG8gcmVzdG9yZSB0aGUgY2xpZW50IGFyZWEgc2l6ZS5cbiAgICAgICAgdGhpcy5sb2NhbEJyb3dzZXJzSW5mbyA9IHt9O1xuICAgIH1cblxuICAgIHByaXZhdGUgX2Vuc3VyZUxvY2FsQnJvd3NlckluZm8gKGJyb3dzZXJJZDogc3RyaW5nKTogdm9pZCB7XG4gICAgICAgIGlmICh0aGlzLmxvY2FsQnJvd3NlcnNJbmZvW2Jyb3dzZXJJZF0pXG4gICAgICAgICAgICByZXR1cm47XG5cbiAgICAgICAgdGhpcy5sb2NhbEJyb3dzZXJzSW5mb1ticm93c2VySWRdID0ge1xuICAgICAgICAgICAgd2luZG93RGVzY3JpcHRvcjogIG51bGwsXG4gICAgICAgICAgICBtYXhTY3JlZW5TaXplOiAgICAgbnVsbCxcbiAgICAgICAgICAgIHJlc2l6ZUNvcnJlY3Rpb25zOiBudWxsLFxuICAgICAgICB9O1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgX2ZpbmRXaW5kb3cgKGJyb3dzZXJJZDogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICAgICAgY29uc3QgcGFnZVRpdGxlID0gdGhpcy5fZ2V0UGFnZVRpdGxlKGJyb3dzZXJJZCk7XG5cbiAgICAgICAgcmV0dXJuIGJyb3dzZXJUb29scy5maW5kV2luZG93KHBhZ2VUaXRsZSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfZ2V0UGFnZVRpdGxlIChicm93c2VySWQ6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgICAgIGlmICh0aGlzLnBsdWdpbi5nZXRQYWdlVGl0bGUpXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5wbHVnaW4uZ2V0UGFnZVRpdGxlKGJyb3dzZXJJZCk7XG5cbiAgICAgICAgcmV0dXJuIGJyb3dzZXJJZDtcbiAgICB9XG5cbiAgICBwcml2YXRlIF9nZXRXaW5kb3dEZXNjcmlwdG9yIChicm93c2VySWQ6IHN0cmluZyk6IHN0cmluZyB8IG51bGwge1xuICAgICAgICBpZiAodGhpcy5wbHVnaW4uZ2V0V2luZG93RGVzY3JpcHRvcilcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnBsdWdpbi5nZXRXaW5kb3dEZXNjcmlwdG9yKGJyb3dzZXJJZCk7XG5cbiAgICAgICAgcmV0dXJuIHRoaXMubG9jYWxCcm93c2Vyc0luZm9bYnJvd3NlcklkXSAmJiB0aGlzLmxvY2FsQnJvd3NlcnNJbmZvW2Jyb3dzZXJJZF0ud2luZG93RGVzY3JpcHRvcjtcbiAgICB9XG5cbiAgICBwcml2YXRlIF9zZXRXaW5kb3dEZXNjcmlwdG9yIChicm93c2VySWQ6IHN0cmluZywgd2luZG93RGVzY3JpcHRvcjogc3RyaW5nIHwgbnVsbCk6IHZvaWQge1xuICAgICAgICBpZiAodGhpcy5wbHVnaW4uc2V0V2luZG93RGVzY3JpcHRvcikge1xuICAgICAgICAgICAgdGhpcy5wbHVnaW4uc2V0V2luZG93RGVzY3JpcHRvcihicm93c2VySWQsIHdpbmRvd0Rlc2NyaXB0b3IpO1xuXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmxvY2FsQnJvd3NlcnNJbmZvW2Jyb3dzZXJJZF0ud2luZG93RGVzY3JpcHRvciA9IHdpbmRvd0Rlc2NyaXB0b3I7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfZ2V0TWF4U2NyZWVuU2l6ZSAoYnJvd3NlcklkOiBzdHJpbmcpOiBTaXplIHwgbnVsbCB7XG4gICAgICAgIHJldHVybiB0aGlzLmxvY2FsQnJvd3NlcnNJbmZvW2Jyb3dzZXJJZF0gJiYgdGhpcy5sb2NhbEJyb3dzZXJzSW5mb1ticm93c2VySWRdLm1heFNjcmVlblNpemU7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfZ2V0UmVzaXplQ29ycmVjdGlvbnMgKGJyb3dzZXJJZDogc3RyaW5nKTogU2l6ZSB8IG51bGwge1xuICAgICAgICByZXR1cm4gdGhpcy5sb2NhbEJyb3dzZXJzSW5mb1ticm93c2VySWRdICYmIHRoaXMubG9jYWxCcm93c2Vyc0luZm9bYnJvd3NlcklkXS5yZXNpemVDb3JyZWN0aW9ucztcbiAgICB9XG5cbiAgICBwcml2YXRlIF9pc0Jyb3dzZXJJZGxlIChicm93c2VySWQ6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgICAgICBjb25zdCBjb25uZWN0aW9uID0gQnJvd3NlckNvbm5lY3Rpb24uZ2V0QnlJZChicm93c2VySWQpIGFzIEJyb3dzZXJDb25uZWN0aW9uO1xuXG4gICAgICAgIHJldHVybiBjb25uZWN0aW9uLmlkbGU7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBfY2FsY3VsYXRlUmVzaXplQ29ycmVjdGlvbnMgKGJyb3dzZXJJZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGlmICghdGhpcy5faXNCcm93c2VySWRsZShicm93c2VySWQpKVxuICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgIGNvbnN0IHRpdGxlID0gYXdhaXQgdGhpcy5wbHVnaW4ucnVuSW5pdFNjcmlwdChicm93c2VySWQsIEdFVF9USVRMRV9TQ1JJUFQpO1xuXG4gICAgICAgIGlmICghYXdhaXQgYnJvd3NlclRvb2xzLmlzTWF4aW1pemVkKHRpdGxlKSlcbiAgICAgICAgICAgIHJldHVybjtcblxuICAgICAgICBjb25zdCBjdXJyZW50U2l6ZSA9IGF3YWl0IHRoaXMucGx1Z2luLnJ1bkluaXRTY3JpcHQoYnJvd3NlcklkLCBHRVRfV0lORE9XX0RJTUVOU0lPTlNfSU5GT19TQ1JJUFQpIGFzIFdpbmRvd0RpbWVudGlvbnNJbmZvO1xuICAgICAgICBjb25zdCBldGFsb25TaXplICA9IHN1YnRyYWN0U2l6ZXMoY3VycmVudFNpemUsIFJFU0laRV9ESUZGX1NJWkUpO1xuXG4gICAgICAgIGF3YWl0IGJyb3dzZXJUb29scy5yZXNpemUodGl0bGUsIGN1cnJlbnRTaXplLndpZHRoLCBjdXJyZW50U2l6ZS5oZWlnaHQsIGV0YWxvblNpemUud2lkdGgsIGV0YWxvblNpemUuaGVpZ2h0KTtcblxuICAgICAgICBsZXQgcmVzaXplZFNpemUgICAgPSBhd2FpdCB0aGlzLnBsdWdpbi5ydW5Jbml0U2NyaXB0KGJyb3dzZXJJZCwgR0VUX1dJTkRPV19ESU1FTlNJT05TX0lORk9fU0NSSVBUKSBhcyBXaW5kb3dEaW1lbnRpb25zSW5mbztcbiAgICAgICAgbGV0IGNvcnJlY3Rpb25TaXplID0gc3VidHJhY3RTaXplcyhyZXNpemVkU2l6ZSwgZXRhbG9uU2l6ZSk7XG5cbiAgICAgICAgYXdhaXQgYnJvd3NlclRvb2xzLnJlc2l6ZSh0aXRsZSwgcmVzaXplZFNpemUud2lkdGgsIHJlc2l6ZWRTaXplLmhlaWdodCwgZXRhbG9uU2l6ZS53aWR0aCwgZXRhbG9uU2l6ZS5oZWlnaHQpO1xuXG4gICAgICAgIHJlc2l6ZWRTaXplID0gYXdhaXQgdGhpcy5wbHVnaW4ucnVuSW5pdFNjcmlwdChicm93c2VySWQsIEdFVF9XSU5ET1dfRElNRU5TSU9OU19JTkZPX1NDUklQVCkgYXMgV2luZG93RGltZW50aW9uc0luZm87XG5cbiAgICAgICAgY29ycmVjdGlvblNpemUgPSBzdW1TaXplcyhjb3JyZWN0aW9uU2l6ZSwgc3VidHJhY3RTaXplcyhyZXNpemVkU2l6ZSwgZXRhbG9uU2l6ZSkpO1xuXG4gICAgICAgIGlmICh0aGlzLmxvY2FsQnJvd3NlcnNJbmZvW2Jyb3dzZXJJZF0pXG4gICAgICAgICAgICB0aGlzLmxvY2FsQnJvd3NlcnNJbmZvW2Jyb3dzZXJJZF0ucmVzaXplQ29ycmVjdGlvbnMgPSBjb3JyZWN0aW9uU2l6ZTtcblxuICAgICAgICBhd2FpdCBicm93c2VyVG9vbHMubWF4aW1pemUodGl0bGUpO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgX2NhbGN1bGF0ZU1hY1NpemVMaW1pdHMgKGJyb3dzZXJJZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGlmICghdGhpcy5faXNCcm93c2VySWRsZShicm93c2VySWQpKVxuICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgIGNvbnN0IHNpemVJbmZvID0gYXdhaXQgdGhpcy5wbHVnaW4ucnVuSW5pdFNjcmlwdChicm93c2VySWQsIEdFVF9XSU5ET1dfRElNRU5TSU9OU19JTkZPX1NDUklQVCkgYXMgV2luZG93RGltZW50aW9uc0luZm87XG5cbiAgICAgICAgaWYgKHRoaXMubG9jYWxCcm93c2Vyc0luZm9bYnJvd3NlcklkXSkge1xuICAgICAgICAgICAgdGhpcy5sb2NhbEJyb3dzZXJzSW5mb1ticm93c2VySWRdLm1heFNjcmVlblNpemUgPSB7XG4gICAgICAgICAgICAgICAgd2lkdGg6ICBzaXplSW5mby5hdmFpbGFibGVXaWR0aCAtIChzaXplSW5mby5vdXRlcldpZHRoIC0gc2l6ZUluZm8ud2lkdGgpLFxuICAgICAgICAgICAgICAgIGhlaWdodDogc2l6ZUluZm8uYXZhaWxhYmxlSGVpZ2h0IC0gKHNpemVJbmZvLm91dGVySGVpZ2h0IC0gc2l6ZUluZm8uaGVpZ2h0KSxcbiAgICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIF9lbnN1cmVCcm93c2VyV2luZG93RGVzY3JpcHRvciAoYnJvd3NlcklkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgaWYgKHRoaXMuX2dldFdpbmRvd0Rlc2NyaXB0b3IoYnJvd3NlcklkKSlcbiAgICAgICAgICAgIHJldHVybjtcblxuICAgICAgICBhd2FpdCB0aGlzLl9lbnN1cmVMb2NhbEJyb3dzZXJJbmZvKGJyb3dzZXJJZCk7XG5cbiAgICAgICAgLy8gTk9URTogZGVsYXkgdG8gZW5zdXJlIHRoZSB3aW5kb3cgZmluaXNoZWQgdGhlIG9wZW5pbmdcbiAgICAgICAgYXdhaXQgdGhpcy5wbHVnaW4ud2FpdEZvckNvbm5lY3Rpb25SZWFkeShicm93c2VySWQpO1xuICAgICAgICBhd2FpdCBkZWxheShCUk9XU0VSX09QRU5JTkdfREVMQVkpO1xuXG4gICAgICAgIGlmICh0aGlzLmxvY2FsQnJvd3NlcnNJbmZvW2Jyb3dzZXJJZF0pIHtcbiAgICAgICAgICAgIGNvbnN0IGNvbm5lY3Rpb24gICAgID0gQnJvd3NlckNvbm5lY3Rpb24uZ2V0QnlJZChicm93c2VySWQpIGFzIEJyb3dzZXJDb25uZWN0aW9uO1xuICAgICAgICAgICAgbGV0IHdpbmRvd0Rlc2NyaXB0b3IgPSBudWxsO1xuXG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIHdpbmRvd0Rlc2NyaXB0b3IgPSBhd2FpdCB0aGlzLl9maW5kV2luZG93KGJyb3dzZXJJZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjYXRjaCAoZXJyOiBhbnkpIHtcbiAgICAgICAgICAgICAgICAvLyBOT1RFOiBXZSBjYW4gc3VwcHJlc3MgdGhlIGVycm9yIGhlcmUgc2luY2Ugd2UgY2FuIGp1c3QgZGlzYWJsZSB3aW5kb3cgbWFuaXB1bGF0aW9uIGZ1bmN0aW9uc1xuICAgICAgICAgICAgICAgIC8vIHdoZW4gd2UgY2Fubm90IGZpbmQgYSBsb2NhbCB3aW5kb3cgZGVzY3JpcHRvclxuICAgICAgICAgICAgICAgIERFQlVHX0xPR0dFUihlcnIpO1xuICAgICAgICAgICAgICAgIGNvbm5lY3Rpb24uYWRkV2FybmluZyhcbiAgICAgICAgICAgICAgICAgICAgV0FSTklOR19NRVNTQUdFLmNhbm5vdEZpbmRXaW5kb3dEZXNjcmlwdG9yRXJyb3IsXG4gICAgICAgICAgICAgICAgICAgIGNvbm5lY3Rpb24uYnJvd3NlckluZm8uYWxpYXMsXG4gICAgICAgICAgICAgICAgICAgIGVyci5tZXNzYWdlXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdGhpcy5fc2V0V2luZG93RGVzY3JpcHRvcihicm93c2VySWQsIHdpbmRvd0Rlc2NyaXB0b3IpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBfZW5zdXJlQnJvd3NlcldpbmRvd1BhcmFtZXRlcnMgKGJyb3dzZXJJZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGF3YWl0IHRoaXMuX2Vuc3VyZUJyb3dzZXJXaW5kb3dEZXNjcmlwdG9yKGJyb3dzZXJJZCk7XG5cbiAgICAgICAgaWYgKE9TLndpbiAmJiAhdGhpcy5fZ2V0UmVzaXplQ29ycmVjdGlvbnMoYnJvd3NlcklkKSlcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuX2NhbGN1bGF0ZVJlc2l6ZUNvcnJlY3Rpb25zKGJyb3dzZXJJZCk7XG4gICAgICAgIGVsc2UgaWYgKE9TLm1hYyAmJiAhdGhpcy5fZ2V0TWF4U2NyZWVuU2l6ZShicm93c2VySWQpKVxuICAgICAgICAgICAgYXdhaXQgdGhpcy5fY2FsY3VsYXRlTWFjU2l6ZUxpbWl0cyhicm93c2VySWQpO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgX2Nsb3NlTG9jYWxCcm93c2VyIChicm93c2VySWQ6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAodGhpcy5wbHVnaW4ubmVlZENsZWFuVXBCcm93c2VySW5mbylcbiAgICAgICAgICAgIHRoaXMucGx1Z2luLmNsZWFuVXBCcm93c2VySW5mbyhicm93c2VySWQpO1xuXG4gICAgICAgIGNvbnN0IHdpbmRvd0Rlc2NyaXB0b3IgPSB0aGlzLl9nZXRXaW5kb3dEZXNjcmlwdG9yKGJyb3dzZXJJZCk7XG5cbiAgICAgICAgYXdhaXQgYnJvd3NlclRvb2xzLmNsb3NlKHdpbmRvd0Rlc2NyaXB0b3IpO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgX3Jlc2l6ZUxvY2FsQnJvd3NlcldpbmRvdyAoYnJvd3NlcklkOiBzdHJpbmcsIHdpZHRoOiBudW1iZXIsIGhlaWdodDogbnVtYmVyLCBjdXJyZW50V2lkdGg6IG51bWJlciwgY3VycmVudEhlaWdodDogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGF3YWl0IHRoaXMuX2Vuc3VyZUJyb3dzZXJXaW5kb3dEZXNjcmlwdG9yKGJyb3dzZXJJZCk7XG5cbiAgICAgICAgY29uc3QgcmVzaXplQ29ycmVjdGlvbnMgPSB0aGlzLl9nZXRSZXNpemVDb3JyZWN0aW9ucyhicm93c2VySWQpO1xuXG4gICAgICAgIGlmIChyZXNpemVDb3JyZWN0aW9ucyAmJiBhd2FpdCBicm93c2VyVG9vbHMuaXNNYXhpbWl6ZWQodGhpcy5fZ2V0V2luZG93RGVzY3JpcHRvcihicm93c2VySWQpKSkge1xuICAgICAgICAgICAgd2lkdGggLT0gcmVzaXplQ29ycmVjdGlvbnMud2lkdGg7XG4gICAgICAgICAgICBoZWlnaHQgLT0gcmVzaXplQ29ycmVjdGlvbnMuaGVpZ2h0O1xuICAgICAgICB9XG5cbiAgICAgICAgYXdhaXQgYnJvd3NlclRvb2xzLnJlc2l6ZSh0aGlzLl9nZXRXaW5kb3dEZXNjcmlwdG9yKGJyb3dzZXJJZCksIGN1cnJlbnRXaWR0aCwgY3VycmVudEhlaWdodCwgd2lkdGgsIGhlaWdodCk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBfdGFrZUxvY2FsQnJvd3NlclNjcmVlbnNob3QgKGJyb3dzZXJJZDogc3RyaW5nLCBzY3JlZW5zaG90UGF0aDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGF3YWl0IGJyb3dzZXJUb29scy5zY3JlZW5zaG90KHRoaXMuX2dldFdpbmRvd0Rlc2NyaXB0b3IoYnJvd3NlcklkKSwgc2NyZWVuc2hvdFBhdGgpO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgX2NhblJlc2l6ZUxvY2FsQnJvd3NlcldpbmRvd1RvRGltZW5zaW9ucyAoYnJvd3NlcklkOiBzdHJpbmcsIHdpZHRoOiBudW1iZXIsIGhlaWdodDogbnVtYmVyKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgICAgIGlmICghT1MubWFjKVxuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG5cbiAgICAgICAgY29uc3QgbWF4U2NyZWVuU2l6ZSA9IHRoaXMuX2dldE1heFNjcmVlblNpemUoYnJvd3NlcklkKSBhcyBTaXplO1xuXG4gICAgICAgIHJldHVybiB3aWR0aCA8PSBtYXhTY3JlZW5TaXplLndpZHRoICYmIGhlaWdodCA8PSBtYXhTY3JlZW5TaXplLmhlaWdodDtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIF9tYXhpbWl6ZUxvY2FsQnJvd3NlcldpbmRvdyAoYnJvd3NlcklkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgYXdhaXQgdGhpcy5fZW5zdXJlQnJvd3NlcldpbmRvd0Rlc2NyaXB0b3IoYnJvd3NlcklkKTtcblxuICAgICAgICBhd2FpdCBicm93c2VyVG9vbHMubWF4aW1pemUodGhpcy5fZ2V0V2luZG93RGVzY3JpcHRvcihicm93c2VySWQpKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIF9lbnN1cmVSZXRyeVRlc3RQYWdlc1dhcm5pbmcgKGJyb3dzZXJJZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IGNvbm5lY3Rpb24gPSBCcm93c2VyQ29ubmVjdGlvbi5nZXRCeUlkKGJyb3dzZXJJZCkgYXMgQnJvd3NlckNvbm5lY3Rpb247XG5cbiAgICAgICAgaWYgKGNvbm5lY3Rpb24/LnJldHJ5VGVzdFBhZ2VzKSB7XG4gICAgICAgICAgICBjb25zdCBpc1NlcnZpY2VXb3JrZXJFbmFibGVkID0gYXdhaXQgdGhpcy5wbHVnaW4ucnVuSW5pdFNjcmlwdChicm93c2VySWQsIEdFVF9JU19TRVJWSUNFX1dPUktFUl9FTkFCTEVEKTtcblxuICAgICAgICAgICAgaWYgKCFpc1NlcnZpY2VXb3JrZXJFbmFibGVkKVxuICAgICAgICAgICAgICAgIGNvbm5lY3Rpb24uYWRkV2FybmluZyhXQVJOSU5HX01FU1NBR0UucmV0cnlUZXN0UGFnZXNJc05vdFN1cHBvcnRlZCwgY29ubmVjdGlvbi5icm93c2VySW5mby5hbGlhcywgY29ubmVjdGlvbi5icm93c2VySW5mby5hbGlhcyk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgY2FuVXNlRGVmYXVsdFdpbmRvd0FjdGlvbnMgKGJyb3dzZXJJZDogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgICAgIGNvbnN0IGlzTG9jYWxCcm93c2VyICAgID0gYXdhaXQgdGhpcy5wbHVnaW4uaXNMb2NhbEJyb3dzZXIoYnJvd3NlcklkKTtcbiAgICAgICAgY29uc3QgaXNIZWFkbGVzc0Jyb3dzZXIgPSBhd2FpdCB0aGlzLnBsdWdpbi5pc0hlYWRsZXNzQnJvd3Nlcihicm93c2VySWQpO1xuXG4gICAgICAgIHJldHVybiBpc0xvY2FsQnJvd3NlciAmJiAhaXNIZWFkbGVzc0Jyb3dzZXI7XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIGluaXQgKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBpbml0aWFsaXplZCA9IGF3YWl0IHRoaXMuaW5pdFByb21pc2U7XG5cbiAgICAgICAgaWYgKGluaXRpYWxpemVkKVxuICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgIHRoaXMuaW5pdFByb21pc2UgPSB0aGlzLnBsdWdpblxuICAgICAgICAgICAgLmluaXQoKVxuICAgICAgICAgICAgLnRoZW4oKCkgPT4gdHJ1ZSk7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuaW5pdFByb21pc2U7XG4gICAgICAgIH1cbiAgICAgICAgY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICB0aGlzLmluaXRQcm9taXNlID0gUHJvbWlzZS5yZXNvbHZlKGZhbHNlKTtcblxuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICAgIH1cb