/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; /* import-globals-from ../../../shared/test/shared-head.js */ const MOCKS_ROOT = CHROME_URL_ROOT + "mocks/"; /* import-globals-from mocks/helper-adb-mock.js */ Services.scriptloader.loadSubScript(MOCKS_ROOT + "helper-adb-mock.js", this); /* import-globals-from mocks/helper-client-wrapper-mock.js */ Services.scriptloader.loadSubScript( MOCKS_ROOT + "helper-client-wrapper-mock.js", this ); /* import-globals-from mocks/helper-runtime-client-factory-mock.js */ Services.scriptloader.loadSubScript( MOCKS_ROOT + "helper-runtime-client-factory-mock.js", this ); const { RUNTIMES, } = require("resource://devtools/client/aboutdebugging/src/constants.js"); /** * This wrapper around the mocks used in about:debugging tests provides helpers to * quickly setup mocks for runtime tests involving USB, network or wifi runtimes that can * are difficult to setup in a test environment. */ class Mocks { constructor() { // Setup the adb mock to rely on internal arrays. this.adbMock = createAdbMock(); this.adbProcessMock = createAdbProcessMock(); this._usbRuntimes = []; this._usbDevices = []; this.adbMock.adb.getRuntimes = () => { return this._usbRuntimes; }; this.adbMock.adb.getDevices = () => { const runtimeDevices = this._usbRuntimes.map(r => { return { id: r.deviceId, name: r.deviceName }; }); return runtimeDevices.concat(this._usbDevices); }; // adb.updateRuntimes should ultimately fire the "runtime-list-updated" event. this.adbMock.adb.updateRuntimes = () => { this.emitUSBUpdate(); }; this.adbMock.adb.isProcessStarted = () => true; // Prepare a fake observer to be able to emit events from this mock. this._observerMock = addObserverMock(this.adbMock.adb); // Setup the runtime-client-factory mock to rely on the internal _clients map. this.runtimeClientFactoryMock = createRuntimeClientFactoryMock(); this._clients = { [RUNTIMES.NETWORK]: {}, [RUNTIMES.THIS_FIREFOX]: {}, [RUNTIMES.USB]: {}, }; this.runtimeClientFactoryMock.createClientForRuntime = runtime => { return this._clients[runtime.type][runtime.id]; }; // Add a client for THIS_FIREFOX, since about:debugging will start on the This Firefox // page. this._thisFirefoxClient = createThisFirefoxClientMock(); this._clients[RUNTIMES.THIS_FIREFOX][RUNTIMES.THIS_FIREFOX] = this._thisFirefoxClient; // Enable mocks and remove them after the test. this.enableMocks(); registerCleanupFunction(() => this.disableMocks()); } get thisFirefoxClient() { return this._thisFirefoxClient; } enableMocks() { enableAdbMock(this.adbMock); enableAdbProcessMock(this.adbProcessMock); enableRuntimeClientFactoryMock(this.runtimeClientFactoryMock); } disableMocks() { disableAdbMock(); disableAdbProcessMock(); disableRuntimeClientFactoryMock(); for (const host of Object.keys(this._clients[RUNTIMES.NETWORK])) { this.removeNetworkRuntime(host); } } createNetworkRuntime(host, runtimeInfo) { const { addNetworkLocation, } = require("resource://devtools/client/aboutdebugging/src/modules/network-locations.js"); addNetworkLocation(host); // Add a valid client that can be returned for this particular runtime id. const mockNetworkClient = createClientMock(); mockNetworkClient.getDeviceDescription = () => { return { name: runtimeInfo.name || "TestBrand", channel: runtimeInfo.channel || "release", version: runtimeInfo.version || "1.0", }; }; this._clients[RUNTIMES.NETWORK][host] = mockNetworkClient; return mockNetworkClient; } removeNetworkRuntime(host) { const { removeNetworkLocation, } = require("resource://devtools/client/aboutdebugging/src/modules/network-locations.js"); removeNetworkLocation(host); delete this._clients[RUNTIMES.NETWORK][host]; } emitUSBUpdate() { this._observerMock.emit("runtime-list-updated"); } /** * Creates a USB runtime for which a client conenction can be established. * @param {String} id * The id of the runtime. * @param {Object} optional object used to create the fake runtime & device * - channel: {String} Release channel, for instance "release", "nightly" * - clientWrapper: {ClientWrapper} optional ClientWrapper for this runtime * - deviceId: {String} Device id * - deviceName: {String} Device name * - isFenix: {Boolean} set by ADB if the package name matches a Fenix package * - name: {String} Application name, for instance "Firefox" * - shortName: {String} Short name for the device * - socketPath: {String} (should only be used for connecting, so not here) * - version: {String} Version, for instance "63.0a" * - versionName: {String} Version return by ADB "63.0a" * @return {Object} Returns the mock client created for this runtime so that methods * can be overridden on it. */ createUSBRuntime(id, runtimeInfo = {}) { // Add a new runtime to the list of scanned runtimes. this._usbRuntimes.push({ deviceId: runtimeInfo.deviceId || "test device id", deviceName: runtimeInfo.deviceName || "test device name", id, isFenix: runtimeInfo.isFenix, shortName: runtimeInfo.shortName || "testshort", socketPath: runtimeInfo.socketPath || "test/path", versionName: runtimeInfo.versionName || "1.0", }); // Add a valid client that can be returned for this particular runtime id. let mockUsbClient = runtimeInfo.clientWrapper; if (mockUsbClient) { const originalGetDeviceDescription = mockUsbClient.getDeviceDescription.bind(mockUsbClient); mockUsbClient.getDeviceDescription = async () => { const deviceDescription = await originalGetDeviceDescription(); return { channel: runtimeInfo.channel || deviceDescription.channel, name: runtimeInfo.name || deviceDescription.name, version: runtimeInfo.version || deviceDescription.version, }; }; } else { // If no clientWrapper was provided, create a mock client here. mockUsbClient = createClientMock(); mockUsbClient.getDeviceDescription = () => { return { channel: runtimeInfo.channel || "release", name: runtimeInfo.name || "TestBrand", version: runtimeInfo.version || "1.0", }; }; } this._clients[RUNTIMES.USB][id] = mockUsbClient; return mockUsbClient; } removeUSBRuntime(id) { this._usbRuntimes = this._usbRuntimes.filter(runtime => runtime.id !== id); delete this._clients[RUNTIMES.USB][id]; } addDevice(deviceId, deviceName) { this._usbDevices.push({ id: deviceId, name: deviceName, }); } removeDevice(deviceId) { this._usbDevices = this._usbDevices.filter(d => { return d.id !== deviceId; }); } removeRuntime(id) { if (this._clients[RUNTIMES.USB][id]) { this.removeUSBRuntime(id); } else if (this._clients[RUNTIMES.NETWORK][id]) { this.removeNetworkRuntime(id); } } } /* exported Mocks */ const silenceWorkerUpdates = function () { const { removeMockedModule, setMockedModule, } = require("resource://devtools/shared/loader/browser-loader-mocks.js"); const mock = { WorkersListener: () => { return { addListener: () => {}, removeListener: () => {}, }; }, }; setMockedModule(mock, "devtools/client/shared/workers-listener"); registerCleanupFunction(() => { removeMockedModule("devtools/client/shared/workers-listener"); }); }; /* exported silenceWorkerUpdates */ async function createLocalClientWrapper() { info("Create a local DevToolsClient"); // First, instantiate a DevToolsServer, the same way it is being done when running // firefox --start-debugger-server const { useDistinctSystemPrincipalLoader, releaseDistinctSystemPrincipalLoader, } = ChromeUtils.importESModule( "resource://devtools/shared/loader/DistinctSystemPrincipalLoader.sys.mjs" ); const requester = {}; const serverLoader = useDistinctSystemPrincipalLoader(requester); registerCleanupFunction(() => { releaseDistinctSystemPrincipalLoader(requester); }); const { DevToolsServer } = serverLoader.require( "resource://devtools/server/devtools-server.js" ); DevToolsServer.init(); DevToolsServer.registerAllActors(); DevToolsServer.allowChromeProcess = true; // Then spawn a DevToolsClient connected to this new DevToolsServer const { DevToolsClient, } = require("resource://devtools/client/devtools-client.js"); const { ClientWrapper, } = require("resource://devtools/client/aboutdebugging/src/modules/client-wrapper.js"); const client = new DevToolsClient(DevToolsServer.connectPipe()); await client.connect(); return new ClientWrapper(client); } /* exported createLocalClientWrapper */ // Create a basic mock for this-firefox client, and setup a runtime-client-factory mock // to return our mock client when needed. function setupThisFirefoxMock() { const runtimeClientFactoryMock = createRuntimeClientFactoryMock(); const thisFirefoxClient = createThisFirefoxClientMock(); runtimeClientFactoryMock.createClientForRuntime = runtime => { if (runtime.id === RUNTIMES.THIS_FIREFOX) { return thisFirefoxClient; } throw new Error("Unexpected runtime id " + runtime.id); }; info("Enable mocks"); enableRuntimeClientFactoryMock(runtimeClientFactoryMock); registerCleanupFunction(() => { disableRuntimeClientFactoryMock(); }); return thisFirefoxClient; } /* exported setupThisFirefoxMock */