UNPKG

gst-atom-xcuitest-driver

Version:

ATOM driver for iOS using XCUITest for backend

546 lines (426 loc) 75.7 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.getAvailableBundleIds = getAvailableBundleIds; exports.parseContainerPath = parseContainerPath; exports.default = exports.commands = void 0; require("source-map-support/register"); var _lodash = _interopRequireDefault(require("lodash")); var _appiumSupport = require("appium-support"); var _path = _interopRequireDefault(require("path")); var _gstAtomIosDriver = require("gst-atom-ios-driver"); var _logger = _interopRequireDefault(require("../logger")); var _teen_process = require("teen_process"); var _bluebird = _interopRequireDefault(require("bluebird")); var _gstAtomIosDevice = require("gst-atom-ios-device"); const CONTAINER_PATH_MARKER = '@'; const CONTAINER_PATH_PATTERN = new RegExp(`^${CONTAINER_PATH_MARKER}([^/]+)/(.*)`); const CONTAINER_TYPE_SEPARATOR = ':'; const IFUSE_CONTAINER_DOCUMENTS = 'documents'; const CONTAINER_DOCUMENTS_PATH = 'Documents'; const IO_TIMEOUT = 60000; const OBJECT_NOT_FOUND_ERROR_MESSAGE = 'OBJECT_NOT_FOUND'; const MAX_PULL_CHUNK_SIZE = 5; let commands = _gstAtomIosDriver.iosCommands.file; exports.commands = commands; function verifyIsSubPath(originalPath, root) { const normalizedRoot = _path.default.normalize(root); const normalizedPath = _path.default.normalize(_path.default.dirname(originalPath)); if (normalizedRoot !== originalPath && !normalizedPath.startsWith(normalizedRoot)) { _logger.default.errorAndThrow(`'${normalizedPath}' is expected to be a subpath of '${normalizedRoot}'`); } } async function createAfcClient(udid, bundleId, containerType, opts) { opts.udid = udid; if (!bundleId) { return await _gstAtomIosDevice.services.startAfcService(opts); } const service = await _gstAtomIosDevice.services.startHouseArrestService(opts); if (isDocuments(containerType)) { return await service.vendDocuments(bundleId); } else { return await service.vendContainer(bundleId); } } function isDocuments(containerType) { return _lodash.default.toLower(containerType) === IFUSE_CONTAINER_DOCUMENTS; } async function mkdirpDevice(service, dir) { if (dir === '.' || dir === '/') { return; } try { await service.listDirectory(dir); return; } catch (e) { await mkdirpDevice(service, _path.default.dirname(dir)); } await service.createDirectory(dir); } async function createService(udid, remotePath, opts) { if (CONTAINER_PATH_PATTERN.test(remotePath)) { const { bundleId, pathInContainer, containerType } = await parseContainerPath(remotePath); const service = await createAfcClient(udid, bundleId, containerType, opts); const relativePath = isDocuments(containerType) ? _path.default.join(CONTAINER_DOCUMENTS_PATH, pathInContainer) : pathInContainer; return { service, relativePath }; } else { const service = await createAfcClient(udid, null, null, opts); const relativePath = remotePath; return { service, relativePath }; } } async function pullFileFromRealDevice(service, relativePath) { const stream = await service.createReadStream(relativePath, { autoDestroy: true }); const pullPromise = new _bluebird.default((resolve, reject) => { stream.on('close', resolve); stream.on('error', reject); }); const buffers = []; stream.on('data', data => buffers.push(data)); try { await pullPromise.timeout(IO_TIMEOUT); } catch (e) { throw new Error(`Couldn't pull the file '${relativePath}' ` + `within the given timeout ${IO_TIMEOUT}ms. Original error: ${e.message}`); } return Buffer.concat(buffers).toString('base64'); } async function pullFolderFromRealDevice(service, relativePath) { const tmpFolder = await _appiumSupport.tempDir.openDir(); try { const folderPath = _path.default.join(tmpFolder, relativePath); await (0, _appiumSupport.mkdirp)(folderPath); const pullPromises = []; const waitForPullChunks = async () => { if (_lodash.default.isEmpty(pullPromises)) { return; } try { await _bluebird.default.all(pullPromises).timeout(IO_TIMEOUT); } catch (e) { throw new Error(`Couldn't pull all items in the folder '${relativePath}' ` + `within the given timeout ${IO_TIMEOUT}ms. Original error: ${e.message}`); } }; await service.walkDir(relativePath, true, async (itemPath, isDir) => { const pathOnServer = _path.default.join(tmpFolder, itemPath); if (isDir) { await _appiumSupport.fs.mkdir(pathOnServer); return; } const readStream = await service.createReadStream(itemPath, { autoDestroy: true }); const writeStream = _appiumSupport.fs.createWriteStream(pathOnServer, { autoClose: true }); pullPromises.push(new _bluebird.default((resolve, reject) => { writeStream.on('close', resolve); const onStreamingError = e => { readStream.unpipe(writeStream); reject(e); }; writeStream.on('error', onStreamingError); readStream.on('error', onStreamingError); })); readStream.pipe(writeStream); if (pullPromises.length % MAX_PULL_CHUNK_SIZE === 0) { await waitForPullChunks(); } }); await waitForPullChunks(); return (await _appiumSupport.zip.toInMemoryZip(folderPath, { encodeToBase64: true })).toString(); } finally { await _appiumSupport.fs.rimraf(tmpFolder); } } async function parseContainerPath(remotePath, containerRootSupplier) { const match = CONTAINER_PATH_PATTERN.exec(remotePath); if (!match) { _logger.default.errorAndThrow(`It is expected that package identifier ` + `starts with '${CONTAINER_PATH_MARKER}' and is separated from the ` + `relative path with a single slash. '${remotePath}' is given instead`); } let [, bundleId, relativePath] = match; let containerType = null; const typeSeparatorPos = bundleId.indexOf(CONTAINER_TYPE_SEPARATOR); if (typeSeparatorPos > 0 && typeSeparatorPos < bundleId.length - 1) { containerType = bundleId.substring(typeSeparatorPos + 1); _logger.default.debug(`Parsed container type: ${containerType}`); bundleId = bundleId.substring(0, typeSeparatorPos); } if (_lodash.default.isNil(containerRootSupplier)) { const pathInContainer = relativePath; return { bundleId, pathInContainer, containerType }; } const containerRoot = _lodash.default.isFunction(containerRootSupplier) ? await containerRootSupplier(bundleId, containerType) : containerRootSupplier; const pathInContainer = _path.default.posix.resolve(containerRoot, relativePath); verifyIsSubPath(pathInContainer, containerRoot); return { bundleId, pathInContainer, containerType }; } async function pushFileToSimulator(device, remotePath, base64Data) { const buffer = Buffer.from(base64Data, 'base64'); if (CONTAINER_PATH_PATTERN.test(remotePath)) { const { bundleId, pathInContainer: dstPath } = await parseContainerPath(remotePath, async (appBundle, containerType) => await device.simctl.getAppContainer(appBundle, containerType)); _logger.default.info(`Parsed bundle identifier '${bundleId}' from '${remotePath}'. ` + `Will put the data into '${dstPath}'`); if (!(await _appiumSupport.fs.exists(_path.default.dirname(dstPath)))) { _logger.default.debug(`The destination folder '${_path.default.dirname(dstPath)}' does not exist. Creating...`); await (0, _appiumSupport.mkdirp)(_path.default.dirname(dstPath)); } await _appiumSupport.fs.writeFile(dstPath, buffer); return; } const dstFolder = await _appiumSupport.tempDir.openDir(); const dstPath = _path.default.resolve(dstFolder, _path.default.basename(remotePath)); try { await _appiumSupport.fs.writeFile(dstPath, buffer); await device.simctl.addMedia(dstPath); } finally { await _appiumSupport.fs.rimraf(dstFolder); } } async function pushFileToRealDevice(device, remotePath, base64Data, opts) { const { service, relativePath } = await createService(device.udid, remotePath, opts); try { await mkdirpDevice(service, _path.default.dirname(relativePath)); const stream = await service.createWriteStream(relativePath, { autoDestroy: true }); let pushError = null; const pushPromise = new _bluebird.default((resolve, reject) => { stream.on('error', e => { pushError = e; }); stream.on('close', () => { if (pushError) { reject(pushError); } else { resolve(); } }); }); stream.write(Buffer.from(base64Data, 'base64')); stream.end(); try { await pushPromise.timeout(IO_TIMEOUT); } catch (e) { throw new Error(`Could not push the file within the given timeout ${IO_TIMEOUT}ms. ` + `Original error: ${e.message}`); } } finally { service.close(); } } async function pullFromSimulator(device, remotePath, isFile) { let pathOnServer; if (CONTAINER_PATH_PATTERN.test(remotePath)) { const { bundleId, pathInContainer: dstPath } = await parseContainerPath(remotePath, async (appBundle, containerType) => await device.simctl.getAppContainer(appBundle, containerType)); _logger.default.info(`Parsed bundle identifier '${bundleId}' from '${remotePath}'. ` + `Will get the data from '${dstPath}'`); pathOnServer = dstPath; } else { const simRoot = device.getDir(); pathOnServer = _path.default.posix.join(simRoot, remotePath); verifyIsSubPath(pathOnServer, simRoot); _logger.default.info(`Got the full item path: ${pathOnServer}`); } if (!(await _appiumSupport.fs.exists(pathOnServer))) { _logger.default.errorAndThrow(`The remote ${isFile ? 'file' : 'folder'} at '${pathOnServer}' does not exist`); } const buffer = isFile ? await _appiumSupport.util.toInMemoryBase64(pathOnServer) : await _appiumSupport.zip.toInMemoryZip(pathOnServer, { encodeToBase64: true }); return buffer.toString(); } async function pullFromRealDevice(device, remotePath, isFile, opts) { const { service, relativePath } = await createService(device.udid, remotePath, opts); try { const fileInfo = await service.getFileInfo(relativePath); if (isFile && fileInfo.isDirectory()) { throw new Error(`The requested path is not a file. Path: '${remotePath}'`); } if (!isFile && !fileInfo.isDirectory()) { throw new Error(`The requested path is not a folder. Path: '${remotePath}'`); } if (fileInfo.isFile()) { return await pullFileFromRealDevice(service, relativePath); } else { return await pullFolderFromRealDevice(service, relativePath); } } catch (e) { if (e.message.includes(OBJECT_NOT_FOUND_ERROR_MESSAGE)) { throw new Error(`Path '${remotePath}' does not exist on the device`); } throw e; } finally { service.close(); } } async function deleteFromSimulator(device, remotePath) { let pathOnServer; if (CONTAINER_PATH_PATTERN.test(remotePath)) { const { bundleId, pathInContainer: dstPath } = await parseContainerPath(remotePath, async (appBundle, containerType) => await device.simctl.getAppContainer(appBundle, containerType)); _logger.default.info(`Parsed bundle identifier '${bundleId}' from '${remotePath}'. ` + `'${dstPath}' will be deleted`); pathOnServer = dstPath; } else { const simRoot = device.getDir(); pathOnServer = _path.default.posix.join(simRoot, remotePath); verifyIsSubPath(pathOnServer, simRoot); _logger.default.info(`Got the full path: ${pathOnServer}`); } if (!(await _appiumSupport.fs.exists(pathOnServer))) { _logger.default.errorAndThrow(`The remote path at '${pathOnServer}' does not exist`); } await _appiumSupport.fs.rimraf(pathOnServer); } async function deleteFromRealDevice(device, remotePath, opts) { const { service, relativePath } = await createService(device.udid, remotePath, opts); try { await service.deleteDirectory(relativePath); } catch (e) { if (e.message.includes(OBJECT_NOT_FOUND_ERROR_MESSAGE)) { throw new Error(`Path '${remotePath}' does not exist on the device`); } throw e; } finally { service.close(); } } async function getAvailableBundleIds(udid, opts) { opts.udid = udid; const service = await _gstAtomIosDevice.services.startInstallationProxyService(opts); try { const applications = await service.listApplications({ applicationType: 'User' }); const bundleIds = []; for (const [key, value] of Object.entries(applications)) { if (!value.UIFileSharingEnabled) { continue; } bundleIds.push(key); } return bundleIds; } finally { service.close(); } } commands.pushFile = async function pushFile(remotePath, base64Data, opts) { if (remotePath.endsWith('/')) { _logger.default.errorAndThrow(`It is expected that remote path points to a file and not to a folder. ` + `'${remotePath}' is given instead`); } if (_lodash.default.isArray(base64Data)) { base64Data = Buffer.from(base64Data).toString('utf8'); } return this.isSimulator() ? await pushFileToSimulator(this.opts.device, remotePath, base64Data) : await pushFileToRealDevice(this.opts.device, remotePath, base64Data, opts); }; commands.pullFile = async function pullFile(remotePath) { if (remotePath.endsWith('/')) { _logger.default.errorAndThrow(`It is expected that remote path points to a file and not to a folder. ` + `'${remotePath}' is given instead`); } return this.isSimulator() ? await pullFromSimulator(this.opts.device, remotePath, true) : await pullFromRealDevice(this.opts.device, remotePath, true, opts); }; async function deleteFileOrFolder(device, remotePath, isSimulator) { return isSimulator ? await deleteFromSimulator(device, remotePath) : await deleteFromRealDevice(device, remotePath, opts); } commands.mobileDeleteFolder = async function mobileDeleteFolder(opts = {}) { let { remotePath } = opts; if (!remotePath.endsWith('/')) { remotePath = `${remotePath}/`; } return await deleteFileOrFolder(this.opts.device, remotePath, this.isSimulator()); }; commands.mobileDeleteFile = async function mobileDeleteFile(opts = {}) { const { remotePath } = opts; if (remotePath.endsWith('/')) { _logger.default.errorAndThrow(`It is expected that remote path points to a file and not to a folder. ` + `'${remotePath}' is given instead`); } return await deleteFileOrFolder(this.opts.device, remotePath, this.isSimulator()); }; commands.getSimFileFullPath = async function getSimFileFullPath(remotePath) { let basePath = this.opts.device.getDir(); let appName = null; if (this.opts.app) { let appNameRegex = new RegExp(`\\${_path.default.sep}([\\w-]+\\.app)`); let appNameMatches = appNameRegex.exec(this.opts.app); if (appNameMatches) { appName = appNameMatches[1]; } } if (_appiumSupport.system.isWindows()) { if (remotePath.indexof('://') === 1) { remotePath = remotePath.slice(4); } } else { if (remotePath.indexOf('/') === 0) { remotePath = remotePath.slice(1); } } if (remotePath.startsWith(appName)) { let findPath = basePath; if (!this.opts.platformVersion || _appiumSupport.util.compareVersions(this.opts.platformVersion, '>=', '8.0')) { findPath = _path.default.resolve(basePath, 'Containers', 'Bundle'); } findPath = findPath.replace(/\s/g, '\\ '); let { stdout } = await (0, _teen_process.exec)('find', [findPath, '-name', appName]); let appRoot = stdout.replace(/\n$/, ''); let subPath = remotePath.substring(appName.length + 1); let fullPath = _path.default.resolve(appRoot, subPath); _logger.default.debug(`Finding app-relative file: '${fullPath}'`); return fullPath; } let fullPath = _path.default.resolve(basePath, remotePath); _logger.default.debug(`Finding sim-relative file: ${fullPath}`); return fullPath; }; commands.pullFolder = async function pullFolder(remotePath) { if (!remotePath.endsWith('/')) { remotePath = `${remotePath}/`; } return this.isSimulator() ? await pullFromSimulator(this.opts.device, remotePath, false) : await pullFromRealDevice(this.opts.device, remotePath, false, opts); }; var _default = commands; exports.default = _default;require('source-map-support').install(); //# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxpYi9jb21tYW5kcy9maWxlLW1vdmVtZW50LmpzIl0sIm5hbWVzIjpbIkNPTlRBSU5FUl9QQVRIX01BUktFUiIsIkNPTlRBSU5FUl9QQVRIX1BBVFRFUk4iLCJSZWdFeHAiLCJDT05UQUlORVJfVFlQRV9TRVBBUkFUT1IiLCJJRlVTRV9DT05UQUlORVJfRE9DVU1FTlRTIiwiQ09OVEFJTkVSX0RPQ1VNRU5UU19QQVRIIiwiSU9fVElNRU9VVCIsIk9CSkVDVF9OT1RfRk9VTkRfRVJST1JfTUVTU0FHRSIsIk1BWF9QVUxMX0NIVU5LX1NJWkUiLCJjb21tYW5kcyIsImlvc0NvbW1hbmRzIiwiZmlsZSIsInZlcmlmeUlzU3ViUGF0aCIsIm9yaWdpbmFsUGF0aCIsInJvb3QiLCJub3JtYWxpemVkUm9vdCIsInBhdGgiLCJub3JtYWxpemUiLCJub3JtYWxpemVkUGF0aCIsImRpcm5hbWUiLCJzdGFydHNXaXRoIiwibG9nIiwiZXJyb3JBbmRUaHJvdyIsImNyZWF0ZUFmY0NsaWVudCIsInVkaWQiLCJidW5kbGVJZCIsImNvbnRhaW5lclR5cGUiLCJvcHRzIiwic2VydmljZXMiLCJzdGFydEFmY1NlcnZpY2UiLCJzZXJ2aWNlIiwic3RhcnRIb3VzZUFycmVzdFNlcnZpY2UiLCJpc0RvY3VtZW50cyIsInZlbmREb2N1bWVudHMiLCJ2ZW5kQ29udGFpbmVyIiwiXyIsInRvTG93ZXIiLCJta2RpcnBEZXZpY2UiLCJkaXIiLCJsaXN0RGlyZWN0b3J5IiwiZSIsImNyZWF0ZURpcmVjdG9yeSIsImNyZWF0ZVNlcnZpY2UiLCJyZW1vdGVQYXRoIiwidGVzdCIsInBhdGhJbkNvbnRhaW5lciIsInBhcnNlQ29udGFpbmVyUGF0aCIsInJlbGF0aXZlUGF0aCIsImpvaW4iLCJwdWxsRmlsZUZyb21SZWFsRGV2aWNlIiwic3RyZWFtIiwiY3JlYXRlUmVhZFN0cmVhbSIsImF1dG9EZXN0cm95IiwicHVsbFByb21pc2UiLCJCIiwicmVzb2x2ZSIsInJlamVjdCIsIm9uIiwiYnVmZmVycyIsImRhdGEiLCJwdXNoIiwidGltZW91dCIsIkVycm9yIiwibWVzc2FnZSIsIkJ1ZmZlciIsImNvbmNhdCIsInRvU3RyaW5nIiwicHVsbEZvbGRlckZyb21SZWFsRGV2aWNlIiwidG1wRm9sZGVyIiwidGVtcERpciIsIm9wZW5EaXIiLCJmb2xkZXJQYXRoIiwicHVsbFByb21pc2VzIiwid2FpdEZvclB1bGxDaHVua3MiLCJpc0VtcHR5IiwiYWxsIiwid2Fsa0RpciIsIml0ZW1QYXRoIiwiaXNEaXIiLCJwYXRoT25TZXJ2ZXIiLCJmcyIsIm1rZGlyIiwicmVhZFN0cmVhbSIsIndyaXRlU3RyZWFtIiwiY3JlYXRlV3JpdGVTdHJlYW0iLCJhdXRvQ2xvc2UiLCJvblN0cmVhbWluZ0Vycm9yIiwidW5waXBlIiwicGlwZSIsImxlbmd0aCIsInppcCIsInRvSW5NZW1vcnlaaXAiLCJlbmNvZGVUb0Jhc2U2NCIsInJpbXJhZiIsImNvbnRhaW5lclJvb3RTdXBwbGllciIsIm1hdGNoIiwiZXhlYyIsInR5cGVTZXBhcmF0b3JQb3MiLCJpbmRleE9mIiwic3Vic3RyaW5nIiwiZGVidWciLCJpc05pbCIsImNvbnRhaW5lclJvb3QiLCJpc0Z1bmN0aW9uIiwicG9zaXgiLCJwdXNoRmlsZVRvU2ltdWxhdG9yIiwiZGV2aWNlIiwiYmFzZTY0RGF0YSIsImJ1ZmZlciIsImZyb20iLCJkc3RQYXRoIiwiYXBwQnVuZGxlIiwic2ltY3RsIiwiZ2V0QXBwQ29udGFpbmVyIiwiaW5mbyIsImV4aXN0cyIsIndyaXRlRmlsZSIsImRzdEZvbGRlciIsImJhc2VuYW1lIiwiYWRkTWVkaWEiLCJwdXNoRmlsZVRvUmVhbERldmljZSIsInB1c2hFcnJvciIsInB1c2hQcm9taXNlIiwid3JpdGUiLCJlbmQiLCJjbG9zZSIsInB1bGxGcm9tU2ltdWxhdG9yIiwiaXNGaWxlIiwic2ltUm9vdCIsImdldERpciIsInV0aWwiLCJ0b0luTWVtb3J5QmFzZTY0IiwicHVsbEZyb21SZWFsRGV2aWNlIiwiZmlsZUluZm8iLCJnZXRGaWxlSW5mbyIsImlzRGlyZWN0b3J5IiwiaW5jbHVkZXMiLCJkZWxldGVGcm9tU2ltdWxhdG9yIiwiZGVsZXRlRnJvbVJlYWxEZXZpY2UiLCJkZWxldGVEaXJlY3RvcnkiLCJnZXRBdmFpbGFibGVCdW5kbGVJZHMiLCJzdGFydEluc3RhbGxhdGlvblByb3h5U2VydmljZSIsImFwcGxpY2F0aW9ucyIsImxpc3RBcHBsaWNhdGlvbnMiLCJhcHBsaWNhdGlvblR5cGUiLCJidW5kbGVJZHMiLCJrZXkiLCJ2YWx1ZSIsIk9iamVjdCIsImVudHJpZXMiLCJVSUZpbGVTaGFyaW5nRW5hYmxlZCIsInB1c2hGaWxlIiwiZW5kc1dpdGgiLCJpc0FycmF5IiwiaXNTaW11bGF0b3IiLCJwdWxsRmlsZSIsImRlbGV0ZUZpbGVPckZvbGRlciIsIm1vYmlsZURlbGV0ZUZvbGRlciIsIm1vYmlsZURlbGV0ZUZpbGUiLCJnZXRTaW1GaWxlRnVsbFBhdGgiLCJiYXNlUGF0aCIsImFwcE5hbWUiLCJhcHAiLCJhcHBOYW1lUmVnZXgiLCJzZXAiLCJhcHBOYW1lTWF0Y2hlcyIsInN5c3RlbSIsImlzV2luZG93cyIsImluZGV4b2YiLCJzbGljZSIsImZpbmRQYXRoIiwicGxhdGZvcm1WZXJzaW9uIiwiY29tcGFyZVZlcnNpb25zIiwicmVwbGFjZSIsInN0ZG91dCIsImFwcFJvb3QiLCJzdWJQYXRoIiwiZnVsbFBhdGgiLCJwdWxsRm9sZGVyIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7O0FBQUE7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBRUEsTUFBTUEscUJBQXFCLEdBQUcsR0FBOUI7QUFFQSxNQUFNQyxzQkFBc0IsR0FBRyxJQUFJQyxNQUFKLENBQVksSUFBR0YscUJBQXNCLGNBQXJDLENBQS9CO0FBQ0EsTUFBTUcsd0JBQXdCLEdBQUcsR0FBakM7QUFDQSxNQUFNQyx5QkFBeUIsR0FBRyxXQUFsQztBQUNBLE1BQU1DLHdCQUF3QixHQUFHLFdBQWpDO0FBQ0EsTUFBTUMsVUFBVSxHQUFHLEtBQW5CO0FB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b2Rhc2gnO1xuaW1wb3J0IHsgc3lzdGVtLCBmcywgdGVtcERpciwgbWtkaXJwLCB6aXAsIHV0aWwgfSBmcm9tICdhcHBpdW0tc3VwcG9ydCc7XG5pbXBvcnQgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCB7IGlvc0NvbW1hbmRzIH0gZnJvbSAnZ3N0LWF0b20taW9zLWRyaXZlcic7XG5pbXBvcnQgbG9nIGZyb20gJy4uL2xvZ2dlcic7XG5pbXBvcnQgeyBleGVjIH0gZnJvbSAndGVlbl9wcm9jZXNzJztcbmltcG9ydCBCIGZyb20gJ2JsdWViaXJkJztcbmltcG9ydCB7IHNlcnZpY2VzIH0gZnJvbSAnZ3N0LWF0b20taW9zLWRldmljZSc7XG5cbmNvbnN0IENPTlRBSU5FUl9QQVRIX01BUktFUiA9ICdAJztcbi8vIGh0dHBzOi8vcmVnZXgxMDEuY29tL3IvUExkQjBHLzJcbmNvbnN0IENPTlRBSU5FUl9QQVRIX1BBVFRFUk4gPSBuZXcgUmVnRXhwKGBeJHtDT05UQUlORVJfUEFUSF9NQVJLRVJ9KFteL10rKS8oLiopYCk7XG5jb25zdCBDT05UQUlORVJfVFlQRV9TRVBBUkFUT1IgPSAnOic7XG5jb25zdCBJRlVTRV9DT05UQUlORVJfRE9DVU1FTlRTID0gJ2RvY3VtZW50cyc7XG5jb25zdCBDT05UQUlORVJfRE9DVU1FTlRTX1BBVEggPSAnRG9jdW1lbnRzJztcbmNvbnN0IElPX1RJTUVPVVQgPSA2MDAwMDtcbmNvbnN0IE9CSkVDVF9OT1RfRk9VTkRfRVJST1JfTUVTU0FHRSA9ICdPQkpFQ1RfTk9UX0ZPVU5EJztcbmNvbnN0IE1BWF9QVUxMX0NIVU5LX1NJWkUgPSA1O1xuXG5sZXQgY29tbWFuZHMgPSBpb3NDb21tYW5kcy5maWxlO1xuXG5mdW5jdGlvbiB2ZXJpZnlJc1N1YlBhdGggKG9yaWdpbmFsUGF0aCwgcm9vdCkge1xuICBjb25zdCBub3JtYWxpemVkUm9vdCA9IHBhdGgubm9ybWFsaXplKHJvb3QpO1xuICBjb25zdCBub3JtYWxpemVkUGF0aCA9IHBhdGgubm9ybWFsaXplKHBhdGguZGlybmFtZShvcmlnaW5hbFBhdGgpKTtcbiAgLy8gSWYgb3JpZ2luYWxQYXRoIGlzIHJvb3QsIGAvYCwgb3JpZ2luYWxQYXRoIHNob3VsZCBlcXVhbCB0byBub3JtYWxpemVkUm9vdFxuICBpZiAobm9ybWFsaXplZFJvb3QgIT09IG9yaWdpbmFsUGF0aCAmJiAhbm9ybWFsaXplZFBhdGguc3RhcnRzV2l0aChub3JtYWxpemVkUm9vdCkpIHtcbiAgICBsb2cuZXJyb3JBbmRUaHJvdyhgJyR7bm9ybWFsaXplZFBhdGh9JyBpcyBleHBlY3RlZCB0byBiZSBhIHN1YnBhdGggb2YgJyR7bm9ybWFsaXplZFJvb3R9J2ApO1xuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGNyZWF0ZUFmY0NsaWVudCAodWRpZCwgYnVuZGxlSWQsIGNvbnRhaW5lclR5cGUsIG9wdHMpIHtcbiAgb3B0cy51ZGlkID0gdWRpZDtcbiAgaWYgKCFidW5kbGVJZCkge1xuICAgIHJldHVybiBhd2FpdCBzZXJ2aWNlcy5zdGFydEFmY1NlcnZpY2Uob3B0cyk7XG4gIH1cbiAgY29uc3Qgc2VydmljZSA9IGF3YWl0IHNlcnZpY2VzLnN0YXJ0SG91c2VBcnJlc3RTZXJ2aWNlKG9wdHMpO1xuICBpZiAoaXNEb2N1bWVudHMoY29udGFpbmVyVHlwZSkpIHtcbiAgICByZXR1cm4gYXdhaXQgc2VydmljZS52ZW5kRG9jdW1lbnRzKGJ1bmRsZUlkKTtcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gYXdhaXQgc2VydmljZS52ZW5kQ29udGFpbmVyKGJ1bmRsZUlkKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBpc0RvY3VtZW50cyAoY29udGFpbmVyVHlwZSkge1xuICByZXR1cm4gXy50b0xvd2VyKGNvbnRhaW5lclR5cGUpID09PSBJRlVTRV9DT05UQUlORVJfRE9DVU1FTlRTO1xufVxuXG5hc3luYyBmdW5jdGlvbiBta2RpcnBEZXZpY2UgKHNlcnZpY2UsIGRpcikge1xuICBpZiAoZGlyID09PSAnLicgfHwgZGlyID09PSAnLycpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgdHJ5IHtcbiAgICBhd2FpdCBzZXJ2aWNlLmxpc3REaXJlY3RvcnkoZGlyKTtcbiAgICByZXR1cm47XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICAvLyBUaGlzIG1lYW5zIHRoYXQgdGhlIGRpcmVjdG9yeSBpcyBtaXNzaW5nIGFuZCB3ZSBnb3QgYW4gb2JqZWN0IG5vdCBmb3VuZCBlcnJvci4gVGhlcmVmb3JlLCB3ZSBhcmUgZ29pbmcgdG8gdGhlIHBhcmVudFxuICAgIGF3YWl0IG1rZGlycERldmljZShzZXJ2aWNlLCBwYXRoLmRpcm5hbWUoZGlyKSk7XG4gIH1cbiAgYXdhaXQgc2VydmljZS5jcmVhdGVEaXJlY3RvcnkoZGlyKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gY3JlYXRlU2VydmljZSAodWRpZCwgcmVtb3RlUGF0aCwgb3B0cykge1xuICBpZiAoQ09OVEFJTkVSX1BBVEhfUEFUVEVSTi50ZXN0KHJlbW90ZVBhdGgpKSB7XG4gICAgY29uc3Qge2J1bmRsZUlkLCBwYXRoSW5Db250YWluZXIsIGNvbnRhaW5lclR5cGV9ID0gYXdhaXQgcGFyc2VDb250YWluZXJQYXRoKHJlbW90ZVBhdGgpO1xuICAgIGNvbnN0IHNlcnZpY2UgPSBhd2FpdCBjcmVhdGVBZmNDbGllbnQodWRpZCwgYnVuZGxlSWQsIGNvbnRhaW5lclR5cGUsIG9wdHMpO1xuICAgIGNvbnN0IHJlbGF0aXZlUGF0aCA9IGlzRG9jdW1lbnRzKGNvbnRhaW5lclR5cGUpID8gcGF0aC5qb2luKENPTlRBSU5FUl9ET0NVTUVOVFNfUEFUSCwgcGF0aEluQ29udGFpbmVyKSA6IHBhdGhJbkNvbnRhaW5lcjtcbiAgICByZXR1cm4ge3NlcnZpY2UsIHJlbGF0aXZlUGF0aH07XG4gIH0gZWxzZSB7XG4gICAgY29uc3Qgc2VydmljZSA9IGF3YWl0IGNyZWF0ZUFmY0NsaWVudCh1ZGlkLCBudWxsLCBudWxsLCBvcHRzKTtcbiAgICBjb25zdCByZWxhdGl2ZVBhdGggPSByZW1vdGVQYXRoO1xuICAgIHJldHVybiB7c2VydmljZSwgcmVsYXRpdmVQYXRofTtcbiAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiBwdWxsRmlsZUZyb21SZWFsRGV2aWNlIChzZXJ2aWNlLCByZWxhdGl2ZVBhdGgpIHtcbiAgY29uc3Qgc3RyZWFtID0gYXdhaXQgc2VydmljZS5jcmVhdGVSZWFkU3RyZWFtKHJlbGF0aXZlUGF0aCwgeyBhdXRvRGVzdHJveTogdHJ1ZSB9KTtcbiAgY29uc3QgcHVsbFByb21pc2UgPSBuZXcgQigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgc3RyZWFtLm9uKCdjbG9zZScsIHJlc29sdmUpO1xuICAgIHN0cmVhbS5vbignZXJyb3InLCByZWplY3QpO1xuICB9KTtcbiAgY29uc3QgYnVmZmVycyA9IFtdO1xuICBzdHJlYW0ub24oJ2RhdGEnLCAoZGF0YSkgPT4gYnVmZmVycy5wdXNoKGRhdGEpKTtcbiAgdHJ5IHtcbiAgICBhd2FpdCBwdWxsUHJvbWlzZS50aW1lb3V0KElPX1RJTUVPVVQpO1xuICB9IGNhdGNoIChlKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBDb3VsZG4ndCBwdWxsIHRoZSBmaWxlICcke3JlbGF0aXZlUGF0aH0nIGAgK1xuICAgICAgYHdpdGhpbiB0aGUgZ2l2ZW4gdGltZW91dCAke0lPX1RJTUVPVVR9bXMuIE9yaWdpbmFsIGVycm9yOiAke2UubWVzc2FnZX1gKTtcbiAgfVxuICByZXR1cm4gQnVmZmVyLmNvbmNhdChidWZmZXJzKS50b1N0cmluZygnYmFzZTY0Jyk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHB1bGxGb2xkZXJGcm9tUmVhbERldmljZSAoc2VydmljZSwgcmVsYXRpdmVQYXRoKSB7XG4gIGNvbnN0IHRtcEZvbGRlciA9IGF3YWl0IHRlbXBEaXIub3BlbkRpcigpO1xuICB0cnkge1xuICAgIGNvbnN0IGZvbGRlclBhdGggPSBwYXRoLmpvaW4odG1wRm9sZGVyLCByZWxhdGl2ZVBhdGgpO1xuICAgIGF3YWl0IG1rZGlycChmb2xkZXJQYXRoKTtcbiAgICBjb25zdCBwdWxsUHJvbWlzZXMgPSBbXTtcbiAgICBjb25zdCB3YWl0Rm9yUHVsbENodW5rcyA9IGFzeW5jICgpID0+IHtcbiAgICAgIGlmIChfLmlzRW1wdHkocHVsbFByb21pc2VzKSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IEIuYWxsKHB1bGxQcm9taXNlcykudGltZW91dChJT19USU1FT1VUKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBDb3VsZG4ndCBwdWxsIGFsbCBpdGVtcyBpbiB0aGUgZm9sZGVyICcke3JlbGF0aXZlUGF0aH0nIGAgK1xuICAgICAgICAgIGB3aXRoaW4gdGhlIGdpdmVuIHRpbWVvdXQgJHtJT19USU1FT1VUfW1zLiBPcmlnaW5hbCBlcnJvcjogJHtlLm1lc3NhZ2V9YCk7XG4gICAgICB9XG4gICAgfTtcbiAgICBhd2FpdCBzZXJ2aWNlLndhbGtEaXIocmVsYXRpdmVQYXRoLCB0cnVlLCBhc3luYyAoaXRlbVBhdGgsIGlzRGlyKSA9PiB7XG4gICAgICBjb25zdCBwYXRoT25TZXJ2ZXIgPSBwYXRoLmpvaW4odG1wRm9sZGVyLCBpdGVtUGF0aCk7XG4gICAgICBpZiAoaXNEaXIpIHtcbiAgICAgICAgYXdhaXQgZnMubWtkaXIocGF0aE9uU2VydmVyKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBjb25zdCByZWFkU3RyZWFtID0gYXdhaXQgc2VydmljZS5jcmVhdGVSZWFkU3RyZWFtKGl0ZW1QYXRoLCB7YXV0b0Rlc3Ryb3k6IHRydWV9KTtcbiAgICAgIGNvbnN0IHdyaXRlU3RyZWFtID0gZnMuY3JlYXRlV3JpdGVTdHJlYW0ocGF0aE9uU2VydmVyLCB7YXV0b0Nsb3NlOiB0cnVlfSk7XG4gICAgICBwdWxsUHJvbWlzZXMucHVzaChuZXcgQigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgIHdyaXRlU3RyZWFtLm9uKCdjbG9zZScsIHJlc29sdmUpO1xuICAgICAgICBjb25zdCBvblN0cmVhbWluZ0Vycm9yID0gKGUpID0+IHtcbiAgICAgICAgICByZWFkU3RyZWFtLnVucGlwZSh3cml0ZVN0cmVhbSk7XG4gICAgICAgICAgcmVqZWN0KGUpO1xuICAgICAgICB9O1xuICAgICAgICB3cml0ZVN0cmVhbS5vbignZXJyb3InLCBvblN0cmVhbWluZ0Vycm9yKTtcbiAgICAgICAgcmVhZFN0cmVhbS5vbignZXJyb3InLCBvblN0cmVhbWluZ0Vycm9yKTtcbiAgICAgIH0pKTtcbiAgICAgIHJlYWRTdHJlYW0ucGlwZSh3cml0ZVN0cmVhbSk7XG4gICAgICBpZiAocHVsbFByb21pc2VzLmxlbmd0aCAlIE1BWF9QVUxMX0NIVU5LX1NJWkUgPT09IDApIHtcbiAgICAgICAgYXdhaXQgd2FpdEZvclB1bGxDaHVua3MoKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICAvLyBXYWl0IGZvciB0aGUgcmVzdCBvZiB0aGUgY2h1bmtzXG4gICAgYXdhaXQgd2FpdEZvclB1bGxDaHVua3MoKTtcbiAgICByZXR1cm4gKGF3YWl0IHppcC50b0luTWVtb3J5WmlwKGZvbGRlclBhdGgsIHtcbiAgICAgIGVuY29kZVRvQmFzZTY0OiB0cnVlLFxuICAgIH0pKS50b1N0cmluZygpO1xuICB9IGZpbmFsbHkge1xuICAgIGF3YWl0IGZzLnJpbXJhZih0bXBGb2xkZXIpO1xuICB9XG59XG5cbi8qKlxuICogQHR5cGVkZWYge09iamVjdH0gQ29udGFpbmVyT2JqZWN0XG4gKlxuICogQHByb3BlcnR5IHtzdHJpbmd9IGJ1bmRsZUlkIC0gVGhlIHBhcnNlZCBidW5kbGUgaWRlbnRpZmllclxuICogQHByb3BlcnR5IHtzdHJpbmd9IHBhdGhJbkNvbnRhaW5lciAtIFRoZSBhYnNvbHV0ZSBmdWxsIHBhdGggb2YgdGhlIGl0ZW0gb24gdGhlIGxvY2FsIGZpbGUgc3lzdGVtXG4gKiBAcHJvcGVydHkgez9zdHJpbmd9IGNvbnRhaW5lclR5cGUgLSBUaGUgY29udGFpbmVyIHR5cGVcbiAqL1xuXG4vKipcbiAqIFBhcnNlcyB0aGUgYWN0dWFsIHBhdGggYW5kIHRoZSBidW5kbGUgaWRlbnRpZmllciBmcm9tIHRoZSBnaXZlbiBwYXRoIHN0cmluZ1xuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSByZW1vdGVQYXRoIC0gVGhlIGdpdmVuIHBhdGggc3RyaW5nLiBUaGUgc3RyaW5nIHNob3VsZFxuICogbWF0Y2ggYENPTlRBSU5FUl9QQVRIX1BBVFRFUk5gIHJlZ2V4cCwgb3RoZXJ3aXNlIGFuIGVycm9yIGlzIGdvaW5nXG4gKiB0byBiZSB0aHJvd24uIEEgdmFsaWQgc3RyaW5nIGV4YW1wbGU6IGBAYnVuZGxlLmlkZW50aWZpZXI6Y29udGFpbmVyX3R5cGUvcmVsYXRpdmVfcGF0aF9pbl9jb250YWluZXJgXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufHN0cmluZ30gY29udGFpbmVyUm9vdFN1cHBsaWVyIC0gRWl0aGVyIGEgc3RyaW5nLCB0aGF0IGNvbnRhaW5zXG4gKiBmdWxsIHBhdGggdG8gdGhlIG1vdW50IHJvb3QgZm9yIHJlYWwgZGV2aWNlcyBvciBhIGZ1bmN0aW9uLCB3aGljaCBhY2NlcHRzIHR3byBwYXJhbWV0ZXJzXG4gKiAoYnVuZGxlIGlkZW50aWZpZXIgYW5kIG9wdGlvbmFsIGNvbnRhaW5lciB0eXBlKSBhbmQgcmV0dXJucyBmdWxsIHBhdGggdG8gY29udGFpbmVyXG4gKiByb290IGZvbGRlciBvbiB0aGUgbG9jYWwgZmlsZSBzeXN0ZW0sIGZvciBTaW11bGF0b3JcbiAqIEByZXR1cm5zIHtDb250YWluZXJPYmplY3R9XG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHBhcnNlQ29udGFpbmVyUGF0aCAocmVtb3RlUGF0aCwgY29udGFpbmVyUm9vdFN1cHBsaWVyKSB7XG4gIGNvbnN0IG1hdGNoID0gQ09OVEFJTkVSX1BBVEhfUEFUVEVSTi5leGVjKHJlbW90ZVBhdGgpO1xuICBpZiAoIW1hdGNoKSB7XG4gICAgbG9nLmVycm9yQW5kVGhyb3coYEl0IGlzIGV4cGVjdGVkIHRoYXQgcGFja2FnZSBpZGVudGlmaWVyIGAgK1xuICAgICAgYHN0YXJ0cyB3aXRoICcke0NPTlRBSU5FUl9QQVRIX01BUktFUn0nIGFuZCBpcyBzZXBhcmF0ZWQgZnJvbSB0aGUgYCArXG4gICAgICBgcmVsYXRpdmUgcGF0aCB3aXRoIGEgc2luZ2xlIHNsYXNoLiAnJHtyZW1vdGVQYXRofScgaXMgZ2l2ZW4gaW5zdGVhZGApO1xuICB9XG4gIGxldCBbLCBidW5kbGVJZCwgcmVsYXRpdmVQYXRoXSA9IG1hdGNoO1xuICBsZXQgY29udGFpbmVyVHlwZSA9IG51bGw7XG4gIGNvbnN0IHR5cGVTZXBhcmF0b3JQb3MgPSBidW5kbGVJZC5pbmRleE9mKENPTlRBSU5FUl9UWVBFX1NFUEFSQVRPUik7XG4gIC8vIFdlIG9ubHkgY29uc2lkZXIgY29udGFpbmVyIHR5cGUgZXhpc3RzIGlmIGl0cyBsZW5ndGggaXMgZ3JlYXRlciB0aGFuIHplcm9cbiAgLy8gbm90IGNvdW50aW5nIHRoZSBjb2xvblxuICBpZiAodHlwZVNlcGFyYXRvclBvcyA+IDAgJiYgdHlwZVNlcGFyYXRvclBvcyA8IGJ1bmRsZUlkLmxlbmd0aCAtIDEpIHtcbiAgICBjb250YWluZXJUeXBlID0gYnVuZGxlSWQuc3Vic3RyaW5nKHR5cGVTZXBhcmF0b3JQb3MgKyAxKTtcbiAgICBsb2cuZGVidWcoYFBhcnNlZCBjb250YWluZXIgdHlwZTogJHtjb250YWluZXJUeXBlfWApO1xuICAgIGJ1bmRsZUlkID0gYnVuZGxlSWQuc3Vic3RyaW5nKDAsIHR5cGVTZXBhcmF0b3JQb3MpO1xuICB9XG4gIGlmIChfLmlzTmlsKGNvbnRh