Skip to content
This repository has been archived by the owner on Sep 2, 2020. It is now read-only.

Commit

Permalink
[Android] Enable usage of custom instrumentation test runners (wix#675)
Browse files Browse the repository at this point in the history
* query pm and regex through results

* PR comments

* mock android SDK path

* More modularity and better test readability

(cherry picked from commit a9bc3b8)
  • Loading branch information
wiyarmir committed Apr 24, 2018
1 parent c81b9c2 commit 9df4c39
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 25 deletions.
8 changes: 5 additions & 3 deletions detox/src/devices/AndroidDriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ class AndroidDriver extends DeviceDriverBase {
return this.instrumentationProcess.pid;
}

const testRunner = await this.adb.getInstrumentationRunner(deviceId, bundleId);

this.instrumentationProcess = spawn(this.adb.adbBin, [`-s`, `${deviceId}`, `shell`, `am`, `instrument`, `-w`, `-r`, `${args.join(' ')}`, `-e`, `debug`,
`false`, `${bundleId}.test/android.support.test.runner.AndroidJUnitRunner`]);
`false`, testRunner]);
log.verbose(this.instrumentationProcess.spawnargs.join(" "));
log.verbose('Instrumentation spawned, childProcess.pid: ', this.instrumentationProcess.pid);
this.instrumentationProcess.stdout.on('data', function(data) {
Expand All @@ -96,7 +98,7 @@ class AndroidDriver extends DeviceDriverBase {
const call = invoke.call(invoke.Android.Class("com.wix.detox.Detox"), 'startActivityFromUrl', invoke.Android.String(params.url));
await this.invocationManager.execute(call);
}

//The other types are not yet supported.
}

Expand Down Expand Up @@ -171,7 +173,7 @@ class AndroidDriver extends DeviceDriverBase {
landscape: 1, // top at left side landscape
portrait: 0 // non-reversed portrait.
};

const call = invoke.call(invoke.Android.Class(EspressoDetox), 'changeOrientation', invoke.Android.Integer(orientationMapping[orientation]));
await this.invocationManager.execute(call);
}
Expand Down
18 changes: 17 additions & 1 deletion detox/src/devices/android/ADB.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class ADB {
await this.adbCmd(deviceId, `install -r -g ${apkPath}`);
} else {
await this.adbCmd(deviceId, `install -rg ${apkPath}`);
}
}
}

async uninstall(deviceId, appId) {
Expand Down Expand Up @@ -102,6 +102,22 @@ class ADB {
async sleep(ms = 0) {
return new Promise((resolve, reject) => setTimeout(resolve, ms));
}

async listInstrumentation(deviceId) {
return await this.shell(deviceId, 'pm list instrumentation');
}

instrumentationRunnerForBundleId(instrumentationRunners, bundleId) {
const runnerForBundleRegEx = new RegExp(`^instrumentation:(.*) \\(target=${bundleId.replace(new RegExp('\\.', 'g'), "\\.")}\\)$`, 'gm');
return _.get(runnerForBundleRegEx.exec(instrumentationRunners), [1], 'undefined');
}

async getInstrumentationRunner(deviceId, bundleId) {
const instrumentationRunners = await this.listInstrumentation(deviceId);
const instrumentationRunner = this.instrumentationRunnerForBundleId(instrumentationRunners, bundleId);
if (instrumentationRunner === 'undefined') throw new Error(`No instrumentation runner found on device ${deviceId} for package ${bundleId}`);
return instrumentationRunner;
}
}

module.exports = ADB;
92 changes: 71 additions & 21 deletions detox/src/devices/android/ADB.test.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,39 @@
//Disabled until we can create a build environment for Android in CI
xdescribe('ADB', () => {
describe('ADB', () => {
let ADB;
let adb;
let EmulatorTelnet;
let exec;

beforeEach(() => {
jest.mock('npmlog');
jest.mock('../../utils/environment', () => ({
getAndroidSDKPath: () => '/dev/null',
}));

ADB = require('./ADB');

jest.mock('./EmulatorTelnet');
EmulatorTelnet = require('./EmulatorTelnet');

jest.mock('../../utils/exec');

jest.mock('../../utils/exec', () => {
const exec = jest.fn();
exec.mockReturnValue({ stdout: '' });
return { execWithRetriesAndLogs: exec };
});
exec = require('../../utils/exec').execWithRetriesAndLogs;

adb = new ADB();
});

it(`Parse 'adb device' output`, async () => {
const adbDevicesConsoleOutput = "List of devices attached\n"
+ "192.168.60.101:5555\tdevice\n"
+ "emulator-5556\tdevice\n"
+ "emulator-5554\tdevice\n"
+ "sx432wsds\tdevice\n"
+ "\n";
exec.mockReturnValue(Promise.resolve({stdout: adbDevicesConsoleOutput}));

const parsedDevices = [
{"adbName": "192.168.60.101:5555", "name": "192.168.60.101:5555", "type": "genymotion"},
{"adbName": "emulator-5556", "name": undefined, "port": "5556", "type": "emulator"},
{"adbName": "emulator-5554", "name": undefined, "port": "5554", "type": "emulator"},
{"adbName": "sx432wsds", "name": "sx432wsds", "type": "device"}];

const devices = await adb.devices();
expect(devices).toEqual(parsedDevices);
it(`devices`, async () => {
await adb.devices();
expect(exec).toHaveBeenCalledTimes(1);
});

it(`install`, async () => {
await adb.install('path/to/app');
expect(exec).toHaveBeenCalledTimes(1);
expect(exec).toHaveBeenCalledTimes(2);
});

it(`uninstall`, async () => {
Expand All @@ -55,5 +50,60 @@ xdescribe('ADB', () => {
await adb.unlockScreen('deviceId');
expect(exec).toHaveBeenCalledTimes(1);
});

it(`listInstrumentation passes the right deviceId`, async () => {
const deviceId = 'aDeviceId';
const spyShell = jest.spyOn(adb, 'shell');

await adb.listInstrumentation(deviceId);

expect(spyShell).toBeCalledWith(deviceId, expect.any(String));
});

it(`Parse 'adb device' output`, async () => {
const adbDevicesConsoleOutput = "List of devices attached\n"
+ "192.168.60.101:5555\tdevice\n"
+ "emulator-5556\tdevice\n"
+ "emulator-5554\tdevice\n"
+ "sx432wsds\tdevice\n"
+ "\n";

const spyDevices = jest.spyOn(adb, 'devices');
spyDevices.mockReturnValue(Promise.resolve(adbDevicesConsoleOutput));

const parsedDevices = [
{ "adbName": "192.168.60.101:5555", "name": "192.168.60.101:5555", "type": "genymotion" },
{ "adbName": "emulator-5556", "name": undefined, "port": "5556", "type": "emulator" },
{ "adbName": "emulator-5554", "name": undefined, "port": "5554", "type": "emulator" },
{ "adbName": "sx432wsds", "name": "sx432wsds", "type": "device" }];

const actual = await adb.parseAdbDevicesConsoleOutput(adbDevicesConsoleOutput);
expect(actual).toEqual(parsedDevices);
});

it(`getInstrumentationRunner passes the right deviceId`, async () => {
const deviceId = 'aDeviceId';
const spyRunnerForBundle = jest.spyOn(adb, 'instrumentationRunnerForBundleId');
spyRunnerForBundle.mockReturnValue('');
const spyShell = jest.spyOn(adb, 'shell');

await adb.getInstrumentationRunner(deviceId, 'com.whatever.package');

expect(spyShell).toBeCalledWith(deviceId, expect.any(String));
});

it(`instrumentationRunnerForBundleId parses the correct runner for the package`, async () => {
const expectedRunner = "com.example.android.apis/.app.LocalSampleInstrumentation";
const expectedPackage = "com.example.android.apis";
const instrumentationRunnersShellOutput =
"instrumentation:com.android.emulator.smoketests/android.support.test.runner.AndroidJUnitRunner (target=com.android.emulator.smoketests)\n" +
"instrumentation:com.android.smoketest.tests/com.android.smoketest.SmokeTestRunner (target=com.android.smoketest)\n" +
`instrumentation:${expectedRunner} (target=${expectedPackage})\n` +
"instrumentation:org.chromium.webview_shell/.WebViewLayoutTestRunner (target=org.chromium.webview_shell)\n";

const result = await adb.instrumentationRunnerForBundleId(instrumentationRunnersShellOutput, expectedPackage);

expect(result).toEqual(expectedRunner);
});
});

0 comments on commit 9df4c39

Please sign in to comment.