Skip to content

Commit

Permalink
fix: uninstall wda with '.xctrunner' suffix for real device (appium#1052
Browse files Browse the repository at this point in the history
)

* fix: Follow the new bundleid if running env on Xcode 11

* get bundleid via ios-device, revert some implementations

* move getBundleIdsByBundleName to iosdeploy

* call getUserInstalledBundleIdsByBundleName from device

* add unittests for uninstall
  • Loading branch information
KazuCocoa committed Sep 1, 2019
1 parent e3efa2d commit 3e8fadd
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 22 deletions.
2 changes: 1 addition & 1 deletion lib/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ class XCUITestDriver extends BaseDriver {
await this.wda.quitAndUninstall();
this.logEvent('wdaUninstalled');
} else if (!util.hasValue(this.wda.webDriverAgentUrl)) {
await this.wda.setupCaching(this.opts.updatedWDABundleId);
await this.wda.setupCaching();
}

// local helper for the two places we need to uninstall wda and re-start it
Expand Down
22 changes: 22 additions & 0 deletions lib/ios-deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import path from 'path';
import { services } from 'appium-ios-device';
import B from 'bluebird';
import log from './logger';
import _ from 'lodash';

const APPLICATION_INSTALLED_NOTIFICATION = 'com.apple.mobile.application_installed';
const INSTALLATION_STAGING_DIR = 'PublicStaging';
Expand Down Expand Up @@ -125,6 +126,27 @@ class IOSDeploy {
service.close();
}
}

/**
* @param {string} bundleName The name of CFBundleName in Info.plist
*
* @returns {Array<string>} A list of User level apps' bundle ids which has
* 'CFBundleName' attribute as 'bundleName'.
*/
async getUserInstalledBundleIdsByBundleName (bundleName) {
const service = await services.startInstallationProxyService(this.udid);
try {
const applications = await service.listApplications({applicationType: 'User'});
return _.reduce(applications, (acc, {CFBundleName}, key) => {
if (CFBundleName === bundleName) {
acc.push(key);
}
return acc;
}, []);
} finally {
service.close();
}
}
}

export default IOSDeploy;
42 changes: 31 additions & 11 deletions lib/wda/webdriveragent.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import { exec } from 'teen_process';
import AsyncLock from 'async-lock';
import { BOOTSTRAP_PATH, WDA_BUNDLE_ID, WDA_RUNNER_BUNDLE_ID, checkForDependencies } from 'appium-webdriveragent';


const WDA_LAUNCH_TIMEOUT = 60 * 1000;
const WDA_AGENT_PORT = 8100;
const WDA_BASE_URL = 'http://localhost';
const WDA_CF_BUNDLE_NAME = 'WebDriverAgentRunner-Runner';

const SHARED_RESOURCES_GUARD = new AsyncLock();

Expand All @@ -32,7 +32,7 @@ class WebDriverAgent {
this.platformName = args.platformName;
this.iosSdkVersion = args.iosSdkVersion;
this.host = args.host;
this.realDevice = !!args.realDevice;
this.isRealDevice = !!args.realDevice;

this.setWDAPaths(args.bootstrapPath, args.agentPath);

Expand All @@ -54,13 +54,15 @@ class WebDriverAgent {
this.usePrebuiltWDA = args.usePrebuiltWDA;
this.derivedDataPath = args.derivedDataPath;

this.updatedWDABundleId = args.updatedWDABundleId;

this.xcodebuild = new XcodeBuild(this.xcodeVersion, this.device, {
platformVersion: this.platformVersion,
platformName: this.platformName,
iosSdkVersion: this.iosSdkVersion,
agentPath: this.agentPath,
bootstrapPath: this.bootstrapPath,
realDevice: this.realDevice,
realDevice: this.isRealDevice,
showXcodeLog: args.showXcodeLog,
xcodeConfigFile: args.xcodeConfigFile,
xcodeOrgId: args.xcodeOrgId,
Expand All @@ -69,7 +71,7 @@ class WebDriverAgent {
keychainPassword: args.keychainPassword,
useSimpleBuildTest: args.useSimpleBuildTest,
usePrebuiltWDA: args.usePrebuiltWDA,
updatedWDABundleId: args.updatedWDABundleId,
updatedWDABundleId: this.updatedWDABundleId,
launchTimeout: args.wdaLaunchTimeout || WDA_LAUNCH_TIMEOUT,
wdaRemotePort: this.wdaRemotePort,
useXctestrunFile: this.useXctestrunFile,
Expand Down Expand Up @@ -156,10 +158,23 @@ class WebDriverAgent {
}
}

/**
* Uninstall WDAs from the test device.
* Over Xcode 11, multiple WDA can be in the device since Xcode 11 generates different WDA.
* Appium does not expect multiple WDAs are running on a device.
*/
async uninstall () {
log.debug(`Removing WDA application from device`);
try {
await this.device.removeApp(WDA_BUNDLE_ID);
const bundleIds = await this.device.getUserInstalledBundleIdsByBundleName(WDA_CF_BUNDLE_NAME);
if (_.isEmpty(bundleIds)) {
log.debug('No WDAs on the device.');
return;
}

log.debug(`Uninstalling WDAs: '${bundleIds}'`);
for (const bundleId of bundleIds) {
await this.device.removeApp(bundleId);
}
} catch (e) {
log.warn(`WebDriverAgent uninstall failed. Perhaps, it is already uninstalled? Original error: ${JSON.stringify(e)}`);
}
Expand Down Expand Up @@ -222,7 +237,7 @@ class WebDriverAgent {
});
}
// We need to provide WDA local port, because it might be occupied with
await resetXCTestProcesses(this.device.udid, !this.realDevice);
await resetXCTestProcesses(this.device.udid, !this.isRealDevice);

await this.ensureConnection();

Expand Down Expand Up @@ -279,7 +294,7 @@ class WebDriverAgent {
}

async ensureConnection () {
if (!this.realDevice || this.webDriverAgentUrl || this.iproxy) {
if (!this.isRealDevice || this.webDriverAgentUrl || this.iproxy) {
return;
}
if (isLocalHost(this.wdaBaseUrl)) {
Expand Down Expand Up @@ -350,7 +365,7 @@ class WebDriverAgent {
*
* @param {string} updatedWDABundleId BundleId you'd like to use
*/
async setupCaching (updatedWDABundleId) {
async setupCaching () {
const status = await this.getStatus();
if (!status || !status.build) {
log.debug('WDA is currently not running. There is nothing to cache');
Expand All @@ -361,11 +376,13 @@ class WebDriverAgent {
productBundleIdentifier,
upgradedAt,
} = status.build;
if (util.hasValue(productBundleIdentifier) && util.hasValue(updatedWDABundleId) && updatedWDABundleId !== productBundleIdentifier) {
// for real device
if (util.hasValue(productBundleIdentifier) && util.hasValue(this.updatedWDABundleId) && this.updatedWDABundleId !== productBundleIdentifier) {
log.info(`Will uninstall running WDA since it has different bundle id. The actual value is '${productBundleIdentifier}'.`);
return await this.uninstall();
}
if (util.hasValue(productBundleIdentifier) && !util.hasValue(updatedWDABundleId) && WDA_RUNNER_BUNDLE_ID !== productBundleIdentifier) {
// for simulator
if (util.hasValue(productBundleIdentifier) && !util.hasValue(this.updatedWDABundleId) && WDA_RUNNER_BUNDLE_ID !== productBundleIdentifier) {
log.info(`Will uninstall running WDA since its bundle id is not equal to the default value ${WDA_RUNNER_BUNDLE_ID}`);
return await this.uninstall();
}
Expand All @@ -386,6 +403,9 @@ class WebDriverAgent {
this.webDriverAgentUrl = this.url.href;
}

/**
* Quit and uninstall running WDA.
*/
async quitAndUninstall () {
await this.quit();
await this.uninstall();
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"appium-idb": "^0",
"appium-ios-device": "^0.10.2",
"appium-ios-driver": "^4.0.0",
"appium-ios-simulator": "^3.13.0",
"appium-ios-simulator": "^3.14.0",
"appium-remote-debugger": "^5.1.0",
"appium-support": "^2.32.0",
"appium-webdriveragent": "^1.1.0",
Expand Down
75 changes: 66 additions & 9 deletions test/unit/webdriveragent-specs.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,28 +166,30 @@ describe('setupCaching()', function () {
});

it('should call uninstall once since bundle id is different with updatedWDABundleId capability', async function () {
const updatedWDABundleId = 'com.example.WebDriverAgent';
wdaStub.callsFake(function () {
return {build: { time: 'Jun 24 2018 17:08:21', productBundleIdentifier: 'com.example.different.WebDriverAgent' }};
});

wdaStubUninstall.callsFake(_.noop);

await wda.setupCaching(updatedWDABundleId);
await wda.setupCaching();
wdaStub.calledOnce.should.be.true;
wdaStubUninstall.calledOnce.should.be.true;
_.isUndefined(wda.webDriverAgentUrl).should.be.true;
});

it('should not call uninstall since bundle id is equal to updatedWDABundleId capability', async function () {
const updatedWDABundleId = 'com.example.WebDriverAgent';
wda = new WebDriverAgent('1', { updatedWDABundleId: 'com.example.WebDriverAgent' });
wdaStub = sinon.stub(wda, 'getStatus');
wdaStubUninstall = sinon.stub(wda, 'uninstall');

wdaStub.callsFake(function () {
return {build: { time: 'Jun 24 2018 17:08:21', productBundleIdentifier: 'com.example.WebDriverAgent' }};
});

wdaStubUninstall.callsFake(_.noop);

await wda.setupCaching(updatedWDABundleId);
await wda.setupCaching();
wdaStub.calledOnce.should.be.true;
wdaStubUninstall.notCalled.should.be.true;
wda.webDriverAgentUrl.should.equal('http://localhost:8100/');
Expand All @@ -200,7 +202,7 @@ describe('setupCaching()', function () {
getTimestampStub.callsFake(() => '2');
wdaStubUninstall.callsFake(_.noop);

await wda.setupCaching('something');
await wda.setupCaching();
wdaStub.calledOnce.should.be.true;
wdaStubUninstall.calledOnce.should.be.true;
});
Expand All @@ -212,7 +214,7 @@ describe('setupCaching()', function () {
getTimestampStub.callsFake(() => '1');
wdaStubUninstall.callsFake(_.noop);

await wda.setupCaching('something');
await wda.setupCaching();
wdaStub.calledOnce.should.be.true;
wdaStubUninstall.notCalled.should.be.true;
});
Expand All @@ -224,7 +226,7 @@ describe('setupCaching()', function () {
getTimestampStub.callsFake(() => '1');
wdaStubUninstall.callsFake(_.noop);

await wda.setupCaching('something');
await wda.setupCaching();
wdaStub.calledOnce.should.be.true;
wdaStubUninstall.notCalled.should.be.true;
});
Expand All @@ -236,8 +238,63 @@ describe('setupCaching()', function () {
getTimestampStub.callsFake(() => null);
wdaStubUninstall.callsFake(_.noop);

await wda.setupCaching('something');
await wda.setupCaching();
wdaStub.calledOnce.should.be.true;
wdaStubUninstall.notCalled.should.be.true;
});
});

describe('uninstall', function () {
let device;
let wda;
let deviceGetBundleIdsStub;
let deviceRemoveAppStub;

beforeEach(function () {
device = {
getUserInstalledBundleIdsByBundleName: () => {},
removeApp: () => {}
};
wda = new WebDriverAgent('1', {device});
deviceGetBundleIdsStub = sinon.stub(device, 'getUserInstalledBundleIdsByBundleName');
deviceRemoveAppStub = sinon.stub(device, 'removeApp');
});

afterEach(function () {
for (const stub of [deviceGetBundleIdsStub, deviceRemoveAppStub]) {
if (stub) {
stub.reset();
}
}
});

it('should not call uninstall', async function () {
deviceGetBundleIdsStub.callsFake(() => []);

await wda.uninstall();
deviceGetBundleIdsStub.calledOnce.should.be.true;
deviceRemoveAppStub.notCalled.should.be.true;
});

it('should call uninstall once', async function () {
const uninstalledBundIds = [];
deviceGetBundleIdsStub.callsFake(() => ['com.appium.WDA1']);
deviceRemoveAppStub.callsFake((id) => uninstalledBundIds.push(id));

await wda.uninstall();
deviceGetBundleIdsStub.calledOnce.should.be.true;
deviceRemoveAppStub.calledOnce.should.be.true;
uninstalledBundIds.should.eql(['com.appium.WDA1']);
});

it('should call uninstall twice', async function () {
const uninstalledBundIds = [];
deviceGetBundleIdsStub.callsFake(() => ['com.appium.WDA1', 'com.appium.WDA2']);
deviceRemoveAppStub.callsFake((id) => uninstalledBundIds.push(id));

await wda.uninstall();
deviceGetBundleIdsStub.calledOnce.should.be.true;
deviceRemoveAppStub.calledTwice.should.be.true;
uninstalledBundIds.should.eql(['com.appium.WDA1', 'com.appium.WDA2']);
});
});
});

0 comments on commit 3e8fadd

Please sign in to comment.