Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add webDriverAgentMacUrl #41

Merged
merged 33 commits into from
Jan 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
73e5b0d
feat: add webDriverAgentUrl
KazuCocoa Jan 2, 2021
d6c524a
add tests
KazuCocoa Jan 2, 2021
f4b06ec
fix
KazuCocoa Jan 2, 2021
4b2f2d8
add docstring
KazuCocoa Jan 2, 2021
d979188
tweak
KazuCocoa Jan 2, 2021
799ac19
add e.g. in readme
KazuCocoa Jan 3, 2021
85919ab
simplify opts.webDriverAgentMacUrl check
KazuCocoa Jan 3, 2021
b188c8d
return early
KazuCocoa Jan 3, 2021
5712287
Merge branch 'master' into add-usewdaurl
KazuCocoa Jan 3, 2021
07276ce
update test
KazuCocoa Jan 3, 2021
38a2925
simplify condition
KazuCocoa Jan 3, 2021
dd66d78
tmp
KazuCocoa Jan 3, 2021
083f45f
add isProxying
KazuCocoa Jan 4, 2021
d5516a0
tweak
KazuCocoa Jan 4, 2021
5c3cd14
tweak
KazuCocoa Jan 4, 2021
537c6cb
add isProxyingRemoteWDAMac
KazuCocoa Jan 4, 2021
438e57d
tune some lines
KazuCocoa Jan 4, 2021
7fbad39
separate should prepare proxy
KazuCocoa Jan 5, 2021
a7a31c8
add return
KazuCocoa Jan 5, 2021
a8119d3
simplify
KazuCocoa Jan 5, 2021
5b2d81a
customize error message
KazuCocoa Jan 5, 2021
59b45fb
fix process
KazuCocoa Jan 5, 2021
391e260
tweak docstring
KazuCocoa Jan 5, 2021
721ca47
tune
KazuCocoa Jan 5, 2021
9640b44
replace isRunning
KazuCocoa Jan 5, 2021
2130a05
remove redundant shouldPrepareProxy
KazuCocoa Jan 5, 2021
a846d80
test more
KazuCocoa Jan 5, 2021
548eb51
exist previous process
KazuCocoa Jan 5, 2021
49c4f95
add todo
KazuCocoa Jan 5, 2021
4c7573d
use URL()
KazuCocoa Jan 6, 2021
ca9dda0
fix to kill
KazuCocoa Jan 6, 2021
ab16e4b
Merge branch 'master' into add-usewdaurl
KazuCocoa Jan 6, 2021
7b6c390
replace stop to kill
KazuCocoa Jan 6, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ platformName | Should be set to `mac`
automationName | Must always be set to `mac2`. Values of automationName are compared case-insensitively.
appium:systemPort | The number of the port for the internal server to listen on. If not provided then Mac2Driver will use the default port `10100`.
appium:systemHost | The name of the host for the internal server to listen on. If not provided then Mac2Driver will use the default host address `127.0.0.1`. You could set it to `0.0.0.0` to make the server listening on all available network interfaces. It is also possible to set the particular interface name, for example `en1`.
appium:webDriverAgentMacUrl | Appium will connect to an existing WebDriverAgentMac instance at this URL instead of starting a new one. e.g. `http://192.168.10.1:10101`
appium:showServerLogs | Set it to `true` in order to include xcodebuild output to the Appium server log. `false` by default.
appium:bootstrapRoot | The full path to `WebDriverAgentMac` root folder where Xcode project of the server sources lives. By default this project is located in the same folder where the corresponding driver Node.js module lives.
appium:serverStartupTimeout | The number of milliseconds to wait util the WebDriverAgentMac project is built and started. `120000` by default
Expand Down
3 changes: 3 additions & 0 deletions lib/desired-caps.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ const desiredCapConstraints = {
},
postrun: {
isObject: true
},
webDriverAgentMacUrl: {
isString: true
}
};

Expand Down
119 changes: 98 additions & 21 deletions lib/wda-mac.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import _ from 'lodash';
import path from 'path';
import url from 'url';
import { JWProxy, errors } from 'appium-base-driver';
import { fs, logger, util, timing, mkdirp } from 'appium-support';
import { SubProcess, exec } from 'teen_process';
Expand Down Expand Up @@ -279,13 +280,12 @@ class WDAMacProcess {

class WDAMacServer {
constructor () {
this.process = new WDAMacProcess();
this.process = null;
this.serverStartupTimeoutMs = STARTUP_TIMEOUT_MS;
this.proxy = null;
}

get isRunning () {
return !!(this.process?.isRunning);
// To handle if the WDAMac server is proxying requests to a remote WDAMac app instance
this.isProxyingToRemoteServer = false;
}

async isProxyReady (throwOnExit = true) {
Expand All @@ -304,21 +304,91 @@ class WDAMacServer {
}
}


/**
* @typedef {Object} ProxyProperties
*
* @property {string} scheme - The scheme proxy to.
* @property {string} host - The host name proxy to.
* @property {number} port - The port number proxy to.
* @property {string} path - The path proxy to.
*/

/**
* Returns proxy information where WDAMacServer proxy to.
*
* @param {Object} caps - The capabilities in the session.
* @return {ProxyProperties}
* @throws Error if 'webDriverAgentMacUrl' had invalid URL
*/
parseProxyProperties (caps) {
let scheme = 'http';
if (!caps.webDriverAgentMacUrl) {
return {
scheme,
server: (this.process?.host ?? caps.systemHost) ?? DEFAULT_SYSTEM_HOST,
port: (this.process?.port ?? caps.systemPort) ?? DEFAULT_SYSTEM_PORT,
path: ''
};
}

let parsed_url;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parsedUrl

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops...

try {
parsed_url = new url.URL(caps.webDriverAgentMacUrl);
} catch (e) {
throw new Error(`webDriverAgentMacUrl, '${caps.webDriverAgentMacUrl}', ` +
`in the capabilities is invalid. ${e.message}`);
}

const { protocol, hostname, port, pathname } = parsed_url;
if (_.isString(protocol)) {
scheme = protocol.split(':')[0];
}
return {
scheme,
server: hostname ?? DEFAULT_SYSTEM_HOST,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say we should throw an exception if hostname cannot be parsed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same about protocol

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do this later

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

port: _.isEmpty(port) ? DEFAULT_SYSTEM_PORT : _.parseInt(port),
path: pathname === '/' ? '' : pathname
};
}

async startSession (caps) {
this.serverStartupTimeoutMs = caps.serverStartupTimeout ?? this.serverStartupTimeoutMs;
const wasProcessInitNecessary = await this.process.init(caps);

if (wasProcessInitNecessary || !(await this.isProxyReady(false))) {
this.isProxyingToRemoteServer = !!caps.webDriverAgentMacUrl;

let wasProcessInitNecessary;
if (this.isProxyingToRemoteServer) {
KazuCocoa marked this conversation as resolved.
Show resolved Hide resolved
if (this.process) {
await this.process.kill();
await cleanupObsoleteProcesses();
this.process = null;
}
KazuCocoa marked this conversation as resolved.
Show resolved Hide resolved

wasProcessInitNecessary = false;
} else {
if (!this.process) {
this.process = new WDAMacProcess();
}
wasProcessInitNecessary = await this.process.init(caps);
}

if (wasProcessInitNecessary || this.isProxyingToRemoteServer || !this.proxy) {
const {scheme, server, port, path} = this.parseProxyProperties(caps);
this.proxy = new WDAMacProxy({
mykola-mokhnach marked this conversation as resolved.
Show resolved Hide resolved
server: DEFAULT_SYSTEM_HOST,
port: this.process.port,
base: '',
scheme,
server,
port,
base: path,
keepAlive: true,
});
this.proxy.didProcessExit = false;
this.process.proc.on('exit', () => {
this.proxy.didProcessExit = true;
});

if (this.process) {
this.process.proc.on('exit', () => {
this.proxy.didProcessExit = true;
});
}

const timer = new timing.Timer().start();
try {
Expand All @@ -327,22 +397,29 @@ class WDAMacServer {
intervalMs: 1000,
});
} catch (e) {
if (this.process.isRunning) {
if (this.process?.isRunning) {
// avoid "frozen" processes,
await this.process.kill();
}
if (/Condition unmet/.test(e.message)) {
throw new Error(`Mac2Driver server is not listening within ${this.serverStartupTimeoutMs}ms timeout. ` +
const msg = this.isProxyingToRemoteServer
? `No response from '${scheme}://${server}:${port}${path}' within ${this.serverStartupTimeoutMs}ms timeout.` +
`Please make sure the remote server is running and accessible by Appium`
: `Mac2Driver server is not listening within ${this.serverStartupTimeoutMs}ms timeout. ` +
`Try to increase the value of 'serverStartupTimeout' capability, check the server logs ` +
`and make sure the ${XCODEBUILD} host process could be started manually from a terminal`);
`and make sure the ${XCODEBUILD} host process could be started manually from a terminal`;
throw new Error(msg);
}
mykola-mokhnach marked this conversation as resolved.
Show resolved Hide resolved
throw e;
}
const pid = this.process.pid;
const childrenPids = await this.process.listChildrenPids();
RUNNING_PROCESS_IDS.push(...childrenPids, pid);
this.process.proc.on('exit', () => void _.pull(RUNNING_PROCESS_IDS, pid));
log.info(`The host process is ready within ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);

if (this.process) {
const pid = this.process.pid;
const childrenPids = await this.process.listChildrenPids();
RUNNING_PROCESS_IDS.push(...childrenPids, pid);
this.process.proc.on('exit', () => void _.pull(RUNNING_PROCESS_IDS, pid));
log.info(`The host process is ready within ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
}
} else {
log.info('The host process has already been listening. Proceeding with session creation');
}
Expand All @@ -356,7 +433,7 @@ class WDAMacServer {
}

async stopSession () {
if (!this.isRunning) {
if (!this.isProxyingToRemoteServer && !(this.process?.isRunning)) {
KazuCocoa marked this conversation as resolved.
Show resolved Hide resolved
log.info(`Mac2Driver session cannot be stopped, because the server is not running`);
return;
}
Expand Down
35 changes: 35 additions & 0 deletions test/unit/wda-mac-specs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import WDA_MAC_SERVER from '../../lib/wda-mac';

chai.use(chaiAsPromised);

describe('WDAMacServer', function () {
describe('parseProxyProperties', function () {
it('should default', function () {
WDA_MAC_SERVER.parseProxyProperties({}).should.eql(
{scheme: 'http', server: '127.0.0.1', port: 10100, path: ''}
);
});

it('should follow WebDriverAgentMacUrl', function () {
WDA_MAC_SERVER.parseProxyProperties({ webDriverAgentMacUrl: 'http://customhost:9999' }).should.eql(
{scheme: 'http', server: 'customhost', port: 9999, path: ''}
);
});

it('should follow WebDriverAgentMacUrl with custom path', function () {
WDA_MAC_SERVER.parseProxyProperties({ webDriverAgentMacUrl: 'https://customhost/path' }).should.eql(
{scheme: 'https', server: 'customhost', port: 10100, path: '/path'}
);
});

it('should follow WebDriverAgentMacUrl with invalid url', function () {
try {
WDA_MAC_SERVER.parseProxyProperties({ webDriverAgentMacUrl: 'invalid url' });
} catch (e) {
e.message.should.contain('is invalid');
}
});
});
});