| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| import assert from 'assert'; // not esm |
| import * as vscode from 'vscode'; |
| import { beforeEach, describe, it } from 'mocha'; |
| import { createSandbox } from 'sinon'; |
| |
| import { Ffx, FfxEventType, FuchsiaDevice } from '../../ffx'; |
| import * as logger from '../../logger'; |
| |
| import { StubbedSpawn } from './utils'; |
| |
| describe('FuchsiaDevice', function () { |
| describe('#constructor()', function () { |
| it('creates an instance of FuchsiaDevice from the json returned from ffx target list', |
| function () { |
| const data = { |
| 'nodename': 'test-device', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'rcs_state': 'Y', |
| 'serial': '<unknown>', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'target_type': 'workstation.qemu-x64', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'target_state': 'Product', |
| 'addresses': ['fe80::3bee:1d4:e205:777e%brqemu', '172.1.1.1'] |
| }; |
| |
| let device = new FuchsiaDevice(data); |
| assert.strictEqual(device.nodeName, data['nodename']); |
| assert.strictEqual(device.rcsState, data['rcs_state']); |
| assert.strictEqual(device.serial, data['serial']); |
| assert.strictEqual(device.targetType, data['target_type']); |
| assert.strictEqual(device.targetState, data['target_state']); |
| let testAddresses = data['addresses']; |
| let actualAddresses = device.addresses; |
| for (let i in testAddresses) { |
| assert.strictEqual(testAddresses[i], actualAddresses[i]); |
| } |
| }); |
| }); |
| }); |
| |
| describe('Ffx', function () { |
| const sandbox = createSandbox(); |
| const TEST_FFX = '/path/to/ffx'; |
| const TEST_CWD = '/path/to/workspace'; |
| const ANALYTICS_FLAG = ['--config', 'fuchsia.analytics.ffx_invoker=vscode-fuchsia']; |
| var stubbedSpawn: StubbedSpawn; |
| |
| this.beforeEach(function () { |
| stubbedSpawn = new StubbedSpawn(sandbox); |
| const log = vscode.window.createOutputChannel('tool_finder.test'); |
| logger.initLogger(log); |
| }); |
| |
| this.afterEach(function () { |
| sandbox.restore(); |
| }); |
| describe('#constructor', function () { |
| it('creates an instance of ffx', function () { |
| new Ffx(TEST_CWD, TEST_FFX); |
| }); |
| }); |
| |
| describe('#rebootTarget', function () { |
| it('calls ffx target reboot for the default device successfully', async () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| |
| let promise = ffx.rebootTarget(); |
| stubbedSpawn.spawnEvent.emit('exit', 0); |
| stubbedSpawn.spawnEvent.emit('close', 0); |
| let output = await promise; |
| assert.strictEqual(output, ''); |
| assert.deepStrictEqual( |
| stubbedSpawn.spawnStubInfo.getCall(0).args, |
| [TEST_FFX, [...ANALYTICS_FLAG, 'target', 'reboot'], { cwd: TEST_CWD, detached: true }]); |
| }); |
| |
| it('calls ffx target reboot for the default device and fails', async () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| |
| const errorMessage = 'Cannot reboot device'; |
| |
| await assert.rejects(async () => { |
| let promise = ffx.rebootTarget(); |
| stubbedSpawn.spawnEvent.stderr?.emit('data', errorMessage); |
| stubbedSpawn.spawnEvent.emit('exit', 1); |
| stubbedSpawn.spawnEvent.emit('close', 1); |
| await promise; |
| }, new Error(`ffx returned with non-zero exit code 1: ${errorMessage}`)); |
| }); |
| |
| it('calls ffx target reboot the specified device', async () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| |
| let device = new FuchsiaDevice({ 'nodename': 'test-device' }); |
| let promise = ffx.rebootTarget(device); |
| stubbedSpawn.spawnEvent.emit('exit', 0); |
| stubbedSpawn.spawnEvent.emit('close', 0); |
| let output = await promise; |
| assert.strictEqual(output, ''); |
| assert.deepStrictEqual(stubbedSpawn.spawnStubInfo.getCall(0).args, [ |
| TEST_FFX, [...ANALYTICS_FLAG, '--target', 'test-device', 'target', 'reboot'], |
| { cwd: TEST_CWD, detached: true } |
| ]); |
| }); |
| }); |
| |
| describe('#PoweroffTarget', function () { |
| it('calls ffx target off for the default device successfully', async () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| |
| let promise = ffx.poweroffTarget(); |
| stubbedSpawn.spawnEvent.emit('exit', 0); |
| stubbedSpawn.spawnEvent.emit('close', 0); |
| let output = await promise; |
| assert.strictEqual(output, ''); |
| assert.deepStrictEqual( |
| stubbedSpawn.spawnStubInfo.getCall(0).args, |
| [TEST_FFX, [...ANALYTICS_FLAG, 'target', 'off'], { cwd: TEST_CWD, detached: true }]); |
| }); |
| |
| it('calls ffx target off for the default device and fails', async () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| |
| const errorMessage = 'Cannot power off the device'; |
| |
| await assert.rejects(async () => { |
| let promise = ffx.poweroffTarget(); |
| stubbedSpawn.spawnEvent.stderr?.emit('data', errorMessage); |
| stubbedSpawn.spawnEvent.emit('exit', 1); |
| stubbedSpawn.spawnEvent.emit('close', 1); |
| await promise; |
| }, new Error(`ffx returned with non-zero exit code 1: ${errorMessage}`)); |
| }); |
| |
| it('calls ffx target off the specified device', async () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| |
| let device = new FuchsiaDevice({ 'nodename': 'test-device' }); |
| let promise = ffx.poweroffTarget(device); |
| stubbedSpawn.spawnEvent.emit('exit', 0); |
| stubbedSpawn.spawnEvent.emit('close', 0); |
| let output = await promise; |
| assert.strictEqual(output, ''); |
| assert.deepStrictEqual(stubbedSpawn.spawnStubInfo.getCall(0).args, [ |
| TEST_FFX, [...ANALYTICS_FLAG, '--target', 'test-device', 'target', 'off'], |
| { cwd: TEST_CWD, detached: true } |
| ]); |
| }); |
| }); |
| |
| |
| describe('#showTarget', function () { |
| it('calls ffx target show for the default device successfully', async () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| |
| let promise = ffx.showTarget(); |
| // normal exit, no output. |
| stubbedSpawn.spawnEvent.emit('exit', 0); |
| stubbedSpawn.spawnEvent.emit('close', 0); |
| let output = await promise; |
| |
| assert.strictEqual(output, ''); |
| assert.deepStrictEqual( |
| stubbedSpawn.spawnStubInfo.getCall(0).args, |
| [TEST_FFX, [...ANALYTICS_FLAG, 'target', 'show'], { cwd: TEST_CWD, detached: true }]); |
| }); |
| |
| it('calls ffx target show for the default device and fails', async () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| |
| const errorMessage = 'Cannot reboot device'; |
| |
| await assert.rejects(async () => { |
| let promise = ffx.showTarget(); |
| stubbedSpawn.spawnEvent.stderr?.emit('data', errorMessage); |
| stubbedSpawn.spawnEvent.emit('exit', 1); |
| stubbedSpawn.spawnEvent.emit('close', 1); |
| await promise; |
| }, new Error(`ffx returned with non-zero exit code 1: ${errorMessage}`)); |
| }); |
| |
| it('calls ffx target show for the specified device', async () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| |
| let device = new FuchsiaDevice({ 'nodename': 'test-device' }); |
| let promise = ffx.showTarget(device); |
| stubbedSpawn.spawnEvent.emit('exit', 0); |
| stubbedSpawn.spawnEvent.emit('close', 0); |
| let output = await promise; |
| assert.strictEqual(output, ''); |
| assert.deepStrictEqual(stubbedSpawn.spawnStubInfo.getCall(0).args, [ |
| TEST_FFX, [...ANALYTICS_FLAG, '--target', 'test-device', 'target', 'show'], |
| { cwd: TEST_CWD, detached: true } |
| ]); |
| }); |
| }); |
| |
| describe('#ExportSnapshot', function () { |
| it('calls ffx target snapshot for the default device successfully', async () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| |
| let promise = ffx.exportSnapshotToCWD(); |
| // normal exit, no output. |
| stubbedSpawn.spawnEvent.emit('exit', 0); |
| stubbedSpawn.spawnEvent.emit('close', 0); |
| let output = await promise; |
| |
| assert.strictEqual(output, TEST_CWD); |
| assert.deepStrictEqual( |
| stubbedSpawn.spawnStubInfo.getCall(0).args, |
| [TEST_FFX, [...ANALYTICS_FLAG, 'target', 'snapshot', '-d', TEST_CWD], |
| { cwd: TEST_CWD, detached: true }]); |
| }); |
| |
| it('calls ffx target snapshot for the default device and fails', async () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| |
| const errorMessage = 'Cannot reboot device'; |
| |
| await assert.rejects(async () => { |
| let promise = ffx.exportSnapshotToCWD(); |
| stubbedSpawn.spawnEvent.stderr?.emit('data', errorMessage); |
| stubbedSpawn.spawnEvent.emit('exit', 1); |
| stubbedSpawn.spawnEvent.emit('close', 1); |
| await promise; |
| }, new Error(`ffx returned with non-zero exit code 1: ${errorMessage}`)); |
| }); |
| |
| it('calls ffx target snapshot for the specified device', async () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| |
| let device = new FuchsiaDevice({ 'nodename': 'test-device' }); |
| let promise = ffx.exportSnapshotToCWD(device); |
| stubbedSpawn.spawnEvent.emit('exit', 0); |
| stubbedSpawn.spawnEvent.emit('close', 0); |
| let output = await promise; |
| assert.strictEqual(output, TEST_CWD); |
| assert.deepStrictEqual(stubbedSpawn.spawnStubInfo.getCall(0).args, [ |
| TEST_FFX, |
| [...ANALYTICS_FLAG, '--target', 'test-device', 'target', 'snapshot', '-d', TEST_CWD], |
| { cwd: TEST_CWD, detached: true } |
| ]); |
| }); |
| }); |
| |
| describe('#defaultTarget', function () { |
| it('sets the specified device to be the default target', async () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| let device = new FuchsiaDevice({ 'nodename': 'test-device' }); |
| let promise = ffx.defaultTarget(device); |
| stubbedSpawn.spawnEvent.emit('exit', 0); |
| stubbedSpawn.spawnEvent.emit('close', 0); |
| let output = await promise; |
| assert.strictEqual(output, ''); |
| assert.deepStrictEqual(stubbedSpawn.spawnStubInfo.getCall(0).args, [ |
| TEST_FFX, |
| [...ANALYTICS_FLAG, 'target', 'default', 'set', 'test-device'], |
| { cwd: TEST_CWD, detached: true } |
| ]); |
| }); |
| }); |
| |
| describe('#events', function () { |
| it('Verify set path events', () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| let lastEvent: FfxEventType | undefined; |
| ffx.onDidChangeConfiguration(event => { |
| lastEvent = event; |
| }); |
| ffx.path = TEST_FFX; |
| assert.strictEqual(lastEvent, FfxEventType.ffxPathSet); |
| ffx.path = undefined; |
| assert.strictEqual(lastEvent, FfxEventType.ffxPathReset); |
| ffx.path = TEST_FFX; |
| assert.strictEqual(lastEvent, FfxEventType.ffxPathSet); |
| }); |
| }); |
| |
| describe('#getTargetList', function () { |
| // targetListData: Targets = 2, DefaultTargetCount = 1, ConnectedTargetCount = 2 |
| const targetListData = [ |
| { |
| 'nodename': 'test-device', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'rcs_state': 'Y', |
| 'serial': 'serial-11111', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'target_type': 'test-product', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'target_state': 'Product', |
| 'addresses': ['fe80::41e7:ace8:59b7:3cb7%en11'], |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'is_default': false |
| }, |
| { |
| 'nodename': 'another-device', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'rcs_state': 'Y', |
| 'serial': 'serial-222222', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'target_type': 'test-product', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'target_state': 'Product', |
| 'addresses': ['fe80::1010:1010:1010:1010%en11'], |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'is_default': true |
| } |
| ]; |
| |
| // newTargetList: Targets = 3, DefaultTargetCount = 1, ConnectedTargetCount = 2 |
| const newTargetList = [ |
| { |
| 'nodename': 'dev1', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'rcs_state': 'Y', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'serial': 'na', 'target_type': 'na', 'target_state': 'na', 'addresses': ['127.0.0.1'], |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'is_default': true |
| }, |
| { |
| 'nodename': 'dev2', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'rcs_state': 'Y', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'serial': 'na', 'target_type': 'na', 'target_state': 'na', 'addresses': ['127.0.0.1'], |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'is_default': false |
| }, |
| { |
| 'nodename': 'dev3', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'rcs_state': 'N', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'serial': 'na', 'target_type': 'na', 'target_state': 'na', 'addresses': ['127.0.0.1'], |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'is_default': false |
| } |
| ]; |
| |
| // newTargetListError: Targets = 2, DefaultTargetCount = 0, ConnectedTargetCount = 0 |
| const newTargetListError = [ |
| { |
| 'nodename': '<unknown1>', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'rcs_state': 'N', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'serial': 'na', 'target_type': 'na', 'target_state': 'na', 'addresses': ['127.0.0.1'], |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'is_default': false |
| }, |
| { |
| 'nodename': '<unknown2>', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'rcs_state': 'N', |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'serial': 'na', 'target_type': 'na', 'target_state': 'na', 'addresses': ['127.0.0.1'], |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| 'is_default': false |
| } |
| ]; |
| |
| /** |
| * Get first default target name |
| * @param targetList |
| * @returns target name or undefine |
| */ |
| function defaultTarget(targetList: { [key: string]: FuchsiaDevice; }) { |
| return Object.values(targetList).map(e => e.isDefault ? e.nodeName : undefined) |
| .reduce<string | undefined>((acc, cur) => acc ?? cur, undefined); |
| } |
| |
| /** |
| * Returns the number of devices that are set to default, should be 0 or 1. |
| * @param targetList |
| * @returns default device count |
| */ |
| function defaultTargetCount(targetList: { [key: string]: FuchsiaDevice; }) { |
| return Object.values(targetList).map(e => e.isDefault ? 1 : 0) |
| .reduce<number>((acc, cur) => acc + cur, 0); |
| } |
| |
| /** |
| * Return the number of devices that we can connect to |
| * @param targetList |
| * @returns number of available devices |
| */ |
| function connectedTargetCount(targetList: { [key: string]: FuchsiaDevice; }) { |
| return Object.values(targetList).map(e => e.rcsState === 'Y' ? 1 : 0) |
| .reduce<number>((acc, cur) => acc + cur, 0); |
| } |
| |
| const TARGET_LIST_ARGS = ['--machine', 'json', 'target', 'list']; |
| |
| it('gets a map of device name to FuchsiaDevice.', async () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| |
| stubbedSpawn.spawnStubInfo.callsFake(function () { |
| setTimeout(() => { |
| stubbedSpawn.spawnEvent.stdout?.emit('data', JSON.stringify(targetListData)); |
| stubbedSpawn.spawnEvent.emit('exit', 0); |
| stubbedSpawn.spawnEvent.emit('close', 0); |
| }, 1); |
| return stubbedSpawn.spawnEvent; |
| }); |
| |
| let deviceList = await ffx.getTargetList(); |
| |
| assert.deepStrictEqual( |
| stubbedSpawn.spawnStubInfo.getCall(0).args, |
| [TEST_FFX, [...ANALYTICS_FLAG, ...TARGET_LIST_ARGS], { cwd: TEST_CWD, detached: true }]); |
| |
| // targetListData: Targets = 2, DefaultTargetCount = 1, ConnectedTargetCount = 2 |
| assert.strictEqual(Object.keys(deviceList).length, 2); |
| assert.strictEqual(defaultTargetCount(deviceList), 1); |
| assert.strictEqual(connectedTargetCount(deviceList), 2); |
| assert.strictEqual(defaultTarget(deviceList), 'another-device'); |
| }); |
| |
| it('Timeout while calling FFX Target List', async () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| |
| stubbedSpawn.spawnStubInfo.callsFake(function () { |
| return stubbedSpawn.spawnEvent; |
| }); |
| |
| await vscode.workspace.getConfiguration('fuchsia').update('connectionTimeout', 10); |
| try { |
| await ffx.getTargetList(); |
| } catch (e) { |
| assert.deepStrictEqual(true, e instanceof Error); |
| if (e instanceof Error) { |
| assert.strictEqual(true, e.toString().includes('timeout'), `"timeout" is not in "${e}"`); |
| return; |
| } |
| } |
| throw Error('Target list does not timeout!'); |
| }); |
| |
| /** |
| * Send two FFX target lists sequentially and get the final IDE device list |
| * @param testDataOne first target list returned by FFX target list |
| * @param testDataTwo second target list returned by FFX target list |
| * @returns a promise to the final IDE device list |
| */ |
| async function setupTwoDeviceListTest(testDataOne: string, testDataTwo: string) { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| |
| let useTestDataTwo = false; |
| stubbedSpawn.spawnStubInfo.callsFake(function () { |
| setTimeout(() => { |
| let data = useTestDataTwo ? testDataTwo : testDataOne; |
| stubbedSpawn.spawnEvent.stdout?.emit('data', data); |
| stubbedSpawn.spawnEvent.emit('exit', 0); |
| stubbedSpawn.spawnEvent.emit('close', 0); |
| }, 1); |
| return stubbedSpawn.spawnEvent; |
| }); |
| |
| let deviceList = await ffx.getTargetList(); |
| |
| assert.deepStrictEqual( |
| stubbedSpawn.spawnStubInfo.getCall(0).args, |
| [TEST_FFX, [...ANALYTICS_FLAG, ...TARGET_LIST_ARGS], { cwd: TEST_CWD, detached: true }]); |
| |
| // check number of devices |
| assert.strictEqual(Object.keys(deviceList).length, targetListData.length); |
| |
| //Use test data 2 on second call |
| useTestDataTwo = true; |
| deviceList = await ffx.getTargetList(); |
| |
| assert.deepStrictEqual( |
| stubbedSpawn.spawnStubInfo.getCall(1).args, |
| [TEST_FFX, [...ANALYTICS_FLAG, ...TARGET_LIST_ARGS], { cwd: TEST_CWD, detached: true }]); |
| |
| return deviceList; |
| } |
| |
| it('Verify device list after targetListData and then empty', async () => { |
| let deviceList = await setupTwoDeviceListTest(JSON.stringify(targetListData), ''); |
| |
| // Empty device list |
| assert.strictEqual(Object.keys(deviceList).length, 0); |
| }); |
| |
| it('Verify device list after targetListData and then newTargetList', async () => { |
| let deviceList = await setupTwoDeviceListTest( |
| JSON.stringify(targetListData), |
| JSON.stringify(newTargetList) |
| ); |
| |
| // newTargetList: Targets = 3, DefaultTargetCount = 1, ConnectedTargetCount = 2 |
| assert.strictEqual(Object.keys(deviceList).length, 3); |
| assert.strictEqual(defaultTargetCount(deviceList), 1); |
| assert.strictEqual(connectedTargetCount(deviceList), 2); |
| assert.strictEqual(defaultTarget(deviceList), 'dev1'); |
| }); |
| |
| it('Verify device list after targetListData and then newTargetListError', async () => { |
| let deviceList = await setupTwoDeviceListTest( |
| JSON.stringify(targetListData), |
| JSON.stringify(newTargetListError) |
| ); |
| |
| // newTargetListError: Targets = 2, DefaultTargetCount = 0, ConnectedTargetCount = 0 |
| assert.strictEqual(Object.keys(deviceList).length, 2); |
| assert.strictEqual(defaultTargetCount(deviceList), 0); |
| assert.strictEqual(connectedTargetCount(deviceList), 0); |
| assert.strictEqual(defaultTarget(deviceList), undefined); |
| }); |
| |
| it('gets a map of device name to FuchsiaDevice and gets the default.', async () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| |
| stubbedSpawn.spawnStubInfo.callsFake(function () { |
| setTimeout(() => { |
| stubbedSpawn.spawnEvent.stdout?.emit('data', JSON.stringify(targetListData)); |
| stubbedSpawn.spawnEvent.emit('exit', 0); |
| stubbedSpawn.spawnEvent.emit('close', 0); |
| }, 1); |
| return stubbedSpawn.spawnEvent; |
| }); |
| |
| let deviceList = await ffx.getTargetList(); |
| assert.deepStrictEqual( |
| stubbedSpawn.spawnStubInfo.getCall(0).args, |
| [TEST_FFX, [...ANALYTICS_FLAG, ...TARGET_LIST_ARGS], { cwd: TEST_CWD, detached: true }]); |
| |
| // check number of devices |
| assert.strictEqual(Object.keys(deviceList).length, targetListData.length); |
| |
| // check there is no default. |
| for (let name in deviceList) { |
| assert.strictEqual( |
| deviceList[name].isDefault, deviceList[name].nodeName === 'another-device'); |
| } |
| }); |
| |
| it('Verify set target event', async () => { |
| const ffx = new Ffx(TEST_CWD, TEST_FFX); |
| |
| stubbedSpawn.spawnStubInfo.callsFake(function () { |
| setTimeout(() => { |
| stubbedSpawn.spawnEvent.stdout?.emit('data', JSON.stringify(targetListData)); |
| stubbedSpawn.spawnEvent.emit('exit', 0); |
| stubbedSpawn.spawnEvent.emit('close', 0); |
| }, 1); |
| return stubbedSpawn.spawnEvent; |
| }); |
| |
| let count = 0; |
| ffx.onSetTarget(target => { |
| count += 1; |
| }); |
| |
| assert.strictEqual(count, 0); |
| ffx.path = TEST_FFX; |
| await new Promise(resolve => setTimeout(resolve, 50)); |
| assert.strictEqual(count, 1); |
| }); |
| }); |
| |
| describe('#runLog', () => { |
| let ffx: Ffx; |
| |
| const LOG_ARGS = ['--target', 'foo', '--machine', 'json', 'log']; |
| beforeEach(() => { |
| ffx = new Ffx(TEST_CWD, TEST_FFX); |
| }); |
| |
| it('parses stdout and sends it to the callback', () => { |
| let received; |
| const TEST_DATA: Object = { 'foo': 'bar' }; |
| stubbedSpawn.spawnStubInfo.callsFake(() => stubbedSpawn.spawnEvent); |
| ffx.runLog('foo', [], (data) => { |
| received = data; |
| }); |
| assert.deepStrictEqual( |
| stubbedSpawn.spawnStubInfo.getCall(0).args, |
| [TEST_FFX, [...ANALYTICS_FLAG, ...LOG_ARGS], { cwd: TEST_CWD, detached: true }]); |
| stubbedSpawn.spawnEvent.stdout?.emit('data', JSON.stringify(TEST_DATA)); |
| assert.deepStrictEqual(received, TEST_DATA); |
| }); |
| |
| it('sends stderr to the log', () => { |
| stubbedSpawn.spawnStubInfo.callsFake(() => stubbedSpawn.spawnEvent); |
| // spy on the singleton instead of the module method, since we can't |
| // modify singletons and such with ESM |
| let spiedWarn = sandbox.spy(logger.logger, 'warn'); |
| ffx.runLog('foo', [], (_) => { }); |
| stubbedSpawn.spawnEvent.stderr?.emit('data', 'oh no'); |
| assert(spiedWarn.calledOnce); |
| assert( |
| spiedWarn.getCall(0).args[0].endsWith('oh no')); |
| }); |
| }); |
| }); |