appium-ios-device 3.1.9 → 3.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/build/lib/afc/index.d.ts +1 -1
- package/build/lib/afc/index.d.ts.map +1 -1
- package/build/lib/afc/index.js +9 -9
- package/build/lib/afc/index.js.map +1 -1
- package/build/lib/afc/protocol.d.ts.map +1 -1
- package/build/lib/afc/protocol.js +16 -16
- package/build/lib/afc/protocol.js.map +1 -1
- package/build/lib/afc/streams.d.ts.map +1 -1
- package/build/lib/afc/streams.js.map +1 -1
- package/build/lib/afc/transformer/afcdecoder.d.ts.map +1 -1
- package/build/lib/afc/transformer/afcdecoder.js.map +1 -1
- package/build/lib/afc/transformer/afcencoder.d.ts.map +1 -1
- package/build/lib/afc/transformer/afcencoder.js.map +1 -1
- package/build/lib/base-service.d.ts.map +1 -1
- package/build/lib/base-service.js.map +1 -1
- package/build/lib/house-arrest/index.d.ts.map +1 -1
- package/build/lib/house-arrest/index.js.map +1 -1
- package/build/lib/imagemounter/index.d.ts.map +1 -1
- package/build/lib/imagemounter/index.js +4 -4
- package/build/lib/imagemounter/index.js.map +1 -1
- package/build/lib/imagemounter/utils/list_developer_image.d.ts.map +1 -1
- package/build/lib/imagemounter/utils/list_developer_image.js +3 -3
- package/build/lib/imagemounter/utils/list_developer_image.js.map +1 -1
- package/build/lib/installation-proxy/index.d.ts +11 -11
- package/build/lib/installation-proxy/index.d.ts.map +1 -1
- package/build/lib/installation-proxy/index.js +19 -17
- package/build/lib/installation-proxy/index.js.map +1 -1
- package/build/lib/instrument/headers.d.ts.map +1 -1
- package/build/lib/instrument/headers.js +7 -7
- package/build/lib/instrument/headers.js.map +1 -1
- package/build/lib/instrument/index.d.ts +1 -1
- package/build/lib/instrument/index.d.ts.map +1 -1
- package/build/lib/instrument/index.js +13 -7
- package/build/lib/instrument/index.js.map +1 -1
- package/build/lib/instrument/transformer/dtx-decode.d.ts.map +1 -1
- package/build/lib/instrument/transformer/dtx-decode.js +9 -3
- package/build/lib/instrument/transformer/dtx-decode.js.map +1 -1
- package/build/lib/instrument/transformer/dtx-encode.d.ts.map +1 -1
- package/build/lib/instrument/transformer/dtx-encode.js +1 -1
- package/build/lib/instrument/transformer/dtx-encode.js.map +1 -1
- package/build/lib/instrument/transformer/nskeyed.d.ts.map +1 -1
- package/build/lib/instrument/transformer/nskeyed.js +16 -9
- package/build/lib/instrument/transformer/nskeyed.js.map +1 -1
- package/build/lib/lockdown/index.d.ts +5 -5
- package/build/lib/lockdown/index.d.ts.map +1 -1
- package/build/lib/lockdown/index.js +9 -10
- package/build/lib/lockdown/index.js.map +1 -1
- package/build/lib/logger.js.map +1 -1
- package/build/lib/mcinstall/index.d.ts.map +1 -1
- package/build/lib/mcinstall/index.js +14 -6
- package/build/lib/mcinstall/index.js.map +1 -1
- package/build/lib/notification-proxy/index.d.ts.map +1 -1
- package/build/lib/notification-proxy/index.js +2 -2
- package/build/lib/notification-proxy/index.js.map +1 -1
- package/build/lib/plist-service/index.d.ts.map +1 -1
- package/build/lib/plist-service/index.js.map +1 -1
- package/build/lib/plist-service/transformer/plist-service-decoder.d.ts.map +1 -1
- package/build/lib/plist-service/transformer/plist-service-decoder.js.map +1 -1
- package/build/lib/plist-service/transformer/plist-service-encoder.d.ts.map +1 -1
- package/build/lib/plist-service/transformer/plist-service-encoder.js.map +1 -1
- package/build/lib/services.d.ts.map +1 -1
- package/build/lib/services.js +3 -3
- package/build/lib/services.js.map +1 -1
- package/build/lib/simulatelocation/index.d.ts.map +1 -1
- package/build/lib/simulatelocation/index.js.map +1 -1
- package/build/lib/ssl-helper.js +3 -3
- package/build/lib/ssl-helper.js.map +1 -1
- package/build/lib/syslog/index.d.ts +1 -1
- package/build/lib/syslog/index.d.ts.map +1 -1
- package/build/lib/syslog/index.js +1 -1
- package/build/lib/syslog/index.js.map +1 -1
- package/build/lib/syslog/transformer/syslog-decoder.d.ts.map +1 -1
- package/build/lib/syslog/transformer/syslog-decoder.js +4 -2
- package/build/lib/syslog/transformer/syslog-decoder.js.map +1 -1
- package/build/lib/testmanagerd/index.d.ts.map +1 -1
- package/build/lib/testmanagerd/index.js.map +1 -1
- package/build/lib/usbmux/index.d.ts.map +1 -1
- package/build/lib/usbmux/index.js +16 -13
- package/build/lib/usbmux/index.js.map +1 -1
- package/build/lib/usbmux/transformer/usbmux-decoder.d.ts.map +1 -1
- package/build/lib/usbmux/transformer/usbmux-decoder.js +1 -1
- package/build/lib/usbmux/transformer/usbmux-decoder.js.map +1 -1
- package/build/lib/usbmux/transformer/usbmux-encoder.d.ts.map +1 -1
- package/build/lib/usbmux/transformer/usbmux-encoder.js +1 -1
- package/build/lib/usbmux/transformer/usbmux-encoder.js.map +1 -1
- package/build/lib/util/transformer/length-based-splitter.d.ts.map +1 -1
- package/build/lib/util/transformer/length-based-splitter.js +9 -4
- package/build/lib/util/transformer/length-based-splitter.js.map +1 -1
- package/build/lib/util/transformer/stream-logger.d.ts.map +1 -1
- package/build/lib/util/transformer/stream-logger.js +4 -3
- package/build/lib/util/transformer/stream-logger.js.map +1 -1
- package/build/lib/util/uuid/parse.js.map +1 -1
- package/build/lib/util/uuid/stringify.js.map +1 -1
- package/build/lib/util/uuid/validate.d.ts.map +1 -1
- package/build/lib/util/uuid/validate.js.map +1 -1
- package/build/lib/utilities.d.ts.map +1 -1
- package/build/lib/utilities.js +12 -12
- package/build/lib/utilities.js.map +1 -1
- package/build/lib/webinspector/index.d.ts.map +1 -1
- package/build/lib/webinspector/index.js +11 -9
- package/build/lib/webinspector/index.js.map +1 -1
- package/build/lib/webinspector/transformer/webinspector-decoder.d.ts.map +1 -1
- package/build/lib/webinspector/transformer/webinspector-decoder.js.map +1 -1
- package/build/lib/webinspector/transformer/webinspector-encoder.d.ts.map +1 -1
- package/build/lib/webinspector/transformer/webinspector-encoder.js.map +1 -1
- package/build/lib/xctest.d.ts.map +1 -1
- package/build/lib/xctest.js +3 -6
- package/build/lib/xctest.js.map +1 -1
- package/lib/afc/index.js +49 -43
- package/lib/afc/protocol.js +53 -50
- package/lib/afc/streams.js +7 -9
- package/lib/afc/transformer/afcdecoder.js +8 -10
- package/lib/afc/transformer/afcencoder.js +7 -10
- package/lib/base-service.js +2 -4
- package/lib/constants.js +1 -1
- package/lib/house-arrest/index.js +16 -14
- package/lib/imagemounter/index.js +107 -104
- package/lib/imagemounter/utils/list_developer_image.js +115 -100
- package/lib/installation-proxy/index.js +31 -27
- package/lib/instrument/headers.js +51 -40
- package/lib/instrument/index.js +32 -21
- package/lib/instrument/transformer/dtx-decode.js +30 -16
- package/lib/instrument/transformer/dtx-encode.js +6 -8
- package/lib/instrument/transformer/nskeyed.js +40 -21
- package/lib/lockdown/index.js +44 -35
- package/lib/logger.js +1 -1
- package/lib/mcinstall/index.js +20 -13
- package/lib/notification-proxy/index.js +18 -17
- package/lib/plist-service/index.js +13 -14
- package/lib/plist-service/transformer/plist-service-decoder.js +6 -7
- package/lib/plist-service/transformer/plist-service-encoder.js +6 -7
- package/lib/services.js +45 -29
- package/lib/simulatelocation/index.js +4 -5
- package/lib/ssl-helper.js +6 -7
- package/lib/syslog/index.js +7 -8
- package/lib/syslog/transformer/syslog-decoder.js +11 -10
- package/lib/testmanagerd/index.js +10 -7
- package/lib/usbmux/index.js +52 -41
- package/lib/usbmux/transformer/usbmux-decoder.js +8 -10
- package/lib/usbmux/transformer/usbmux-encoder.js +10 -8
- package/lib/util/transformer/length-based-splitter.js +62 -24
- package/lib/util/transformer/stream-logger.js +14 -11
- package/lib/util/uuid/parse.ts +2 -2
- package/lib/util/uuid/stringify.ts +1 -1
- package/lib/util/uuid/validate.ts +2 -1
- package/lib/utilities.js +39 -28
- package/lib/webinspector/index.js +59 -46
- package/lib/webinspector/transformer/webinspector-decoder.js +22 -11
- package/lib/webinspector/transformer/webinspector-encoder.js +6 -8
- package/lib/xctest.js +284 -245
- package/package.json +4 -2
package/lib/xctest.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
startHouseArrestService,
|
|
3
|
+
startTestmanagerdService,
|
|
4
|
+
startInstallationProxyService,
|
|
5
|
+
startInstrumentService,
|
|
6
|
+
} from './services';
|
|
7
|
+
import {INSTRUMENT_CHANNEL} from './instrument';
|
|
8
|
+
import {DTXMessageAuxBuffer} from './instrument/headers';
|
|
9
|
+
import {NSUUID, XCTestConfiguration} from './instrument/transformer/nskeyed';
|
|
10
|
+
import {TESTMANAGERD_CHANNEL} from './testmanagerd';
|
|
11
|
+
import {util} from '@appium/support';
|
|
12
|
+
import {getOSVersion} from './utilities';
|
|
13
|
+
import {log} from './logger';
|
|
9
14
|
import B from 'bluebird';
|
|
10
15
|
|
|
11
|
-
const {
|
|
16
|
+
const {DAEMON_CONNECTION_INTERFACE} = TESTMANAGERD_CHANNEL;
|
|
12
17
|
const XCTEST_CONFIGURATION_EXTENSION = '.xctestconfiguration';
|
|
13
18
|
const TMP_FOLDER_PREFIX = '/tmp';
|
|
14
19
|
const XCTEST_EXECUTABLE_SUFFIX = '-Runner';
|
|
@@ -18,7 +23,7 @@ const MAJOR_IOS_VERSION_12 = 12;
|
|
|
18
23
|
//This is not related with which xcode user installed but only a marker to use inside device
|
|
19
24
|
const XCODE_BUILD_PATH = '/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild';
|
|
20
25
|
const XCODE_VERSION = 29;
|
|
21
|
-
const MAGIC_CHANNEL =
|
|
26
|
+
const MAGIC_CHANNEL = 0xffffffff;
|
|
22
27
|
/**
|
|
23
28
|
* @typedef {Object} XCTestConfigurationProperties
|
|
24
29
|
* @property {string?} productModuleName
|
|
@@ -38,259 +43,293 @@ const MAGIC_CHANNEL = 0xFFFFFFFF;
|
|
|
38
43
|
* This class simulates the procedure which xcode uses to invoke xctests.
|
|
39
44
|
*/
|
|
40
45
|
class Xctest {
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
/** @type {import('./testmanagerd').TestmanagerdService|undefined} */
|
|
47
|
+
_initialControlSession;
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
/** @type {import('./testmanagerd').TestmanagerdService|undefined} */
|
|
50
|
+
_execTestPlanSession;
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
52
|
+
/**
|
|
53
|
+
* @param {string} udid Device udid.
|
|
54
|
+
* @param {string} xctestBundleId Bundle Id of xctest app on device. The app must be installed on device.
|
|
55
|
+
* @param {string?} targetBundleId test target bundle id.
|
|
56
|
+
* @param {Partial<XctestOption>} opts addition options to specific XCTestConfiguration and app launch env
|
|
57
|
+
*/
|
|
58
|
+
constructor(udid, xctestBundleId, targetBundleId = null, opts = {}) {
|
|
59
|
+
this.udid = udid;
|
|
60
|
+
this.running = false;
|
|
61
|
+
this._executing = false;
|
|
62
|
+
this.xctestBundleId = xctestBundleId;
|
|
63
|
+
this.targetBundleId = targetBundleId;
|
|
64
|
+
this._conf = opts?.conf || {};
|
|
65
|
+
this._env = opts?.env || {};
|
|
66
|
+
}
|
|
62
67
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
const appInfo = lookupResult[this.xctestBundleId];
|
|
77
|
-
if (!appInfo) {
|
|
78
|
-
throw new Error(`${this.xctestBundleId} not found on device ${this.udid}`);
|
|
79
|
-
}
|
|
80
|
-
const signerIdentifier = appInfo.SignerIdentity;
|
|
81
|
-
log.info(`SignerIdentifier: ${signerIdentifier}`);
|
|
82
|
-
const appContainer = appInfo.Container;
|
|
83
|
-
const execName = appInfo.CFBundleExecutable;
|
|
84
|
-
if (!execName.endsWith(XCTEST_EXECUTABLE_SUFFIX)) {
|
|
85
|
-
throw new Error(`Invalid CFBundleExecutable ${execName} from ${this.xctestBundleId}, is this bundle a valid xctest app?`);
|
|
86
|
-
}
|
|
87
|
-
const targetName = execName.substr(0, execName.indexOf(XCTEST_EXECUTABLE_SUFFIX));
|
|
88
|
-
const xctestPath = `${TMP_FOLDER_PREFIX}/${targetName}-${sessionIdentifier.toUpperCase()}${XCTEST_CONFIGURATION_EXTENSION}`;
|
|
89
|
-
// @ts-ignore This works
|
|
90
|
-
const xctestConfiguration = new XCTestConfiguration({
|
|
91
|
-
...this._conf,
|
|
92
|
-
// properties below should not be override
|
|
93
|
-
testBundleURL: `file://${appInfo.Path}/PlugIns/${targetName}.xctest`,
|
|
94
|
-
sessionIdentifier,
|
|
95
|
-
targetApplicationBundleID: this.targetBundleId,
|
|
96
|
-
});
|
|
97
|
-
await this._writeConfigurationToDevice(xctestConfiguration, xctestPath);
|
|
98
|
-
this._instrumentService = await startInstrumentService(this.udid);
|
|
99
|
-
this._instrumentService.registerLifecycleCallback('close', this.stop.bind(this));
|
|
100
|
-
await this._instrumentService.callChannel(PROCESS_CONTROL, 'processIdentifierForBundleIdentifier:', this.xctestBundleId);
|
|
101
|
-
const appPath = appInfo.Path;
|
|
102
|
-
const xctestConfigurationPath = appContainer + xctestPath;
|
|
103
|
-
const appEnv = {
|
|
104
|
-
CA_ASSERT_MAIN_THREAD_TRANSACTIONS: '0',
|
|
105
|
-
CA_DEBUG_TRANSACTIONS: '0',
|
|
106
|
-
DYLD_FRAMEWORK_PATH: `${appPath}/Frameworks:`,
|
|
107
|
-
DYLD_LIBRARY_PATH: `${appPath}/Frameworks`,
|
|
108
|
-
NSUnbufferedIO: 'YES',
|
|
109
|
-
SQLITE_ENABLE_THREAD_ASSERTIONS: '1',
|
|
110
|
-
WDA_PRODUCT_BUNDLE_IDENTIFIER: '',
|
|
111
|
-
XCTestConfigurationFilePath: xctestConfigurationPath,
|
|
112
|
-
XCODE_DBG_XPC_EXCLUSIONS: 'com.apple.dt.xctestSymbolicator',
|
|
113
|
-
MJPEG_SERVER_PORT: '',
|
|
114
|
-
USE_PORT: '',
|
|
115
|
-
// %p means pid
|
|
116
|
-
LLVM_PROFILE_FILE: `${appContainer}/tmp/%p.profraw`,
|
|
117
|
-
...this._env
|
|
118
|
-
};
|
|
119
|
-
if (majorVersion >= MAJOR_IOS_VERSION_11) {
|
|
120
|
-
appEnv.DYLD_INSERT_LIBRARIES = '/Developer/usr/lib/libMainThreadChecker.dylib';
|
|
121
|
-
appEnv.OS_ACTIVITY_DT_MODE = 'YES';
|
|
122
|
-
}
|
|
123
|
-
const appArgs = [
|
|
124
|
-
'-NSTreatUnknownArgumentsAsOpen', 'NO',
|
|
125
|
-
'-ApplePersistenceIgnoreState', 'YES'
|
|
126
|
-
];
|
|
127
|
-
const appOptions = { StartSuspendedKey: false };
|
|
128
|
-
if (majorVersion >= MAJOR_IOS_VERSION_12) {
|
|
129
|
-
appOptions.ActivateSuspended = true;
|
|
130
|
-
appOptions.__ActivateSuspended = true;
|
|
131
|
-
}
|
|
132
|
-
const launchResult = await this._instrumentService.callChannel(PROCESS_CONTROL,
|
|
133
|
-
'launchSuspendedProcessWithDevicePath:bundleIdentifier:environment:arguments:options:',
|
|
134
|
-
appPath, this.xctestBundleId, appEnv, appArgs, appOptions);
|
|
135
|
-
const pid = launchResult.selector;
|
|
136
|
-
if (typeof pid !== 'number') {
|
|
137
|
-
throw new Error(`Failed on launching ${this.xctestBundleId}: ${launchResult}`);
|
|
138
|
-
}
|
|
139
|
-
log.info(`Pid of launched ${this.xctestBundleId}: ${pid}`);
|
|
140
|
-
const msg = new DTXMessageAuxBuffer();
|
|
141
|
-
msg.appendObject(pid);
|
|
142
|
-
await this._instrumentService.callChannel(PROCESS_CONTROL, 'startObservingPid:', msg);
|
|
143
|
-
return pid;
|
|
68
|
+
/**
|
|
69
|
+
* @param {number} majorVersion first part of iOS version 9/10/11/12/13/14/15/16
|
|
70
|
+
* @param {string} sessionIdentifier uuid with format: 00000000-0000-0000-0000-000000000000
|
|
71
|
+
*/
|
|
72
|
+
async _launchAppRunner(majorVersion, sessionIdentifier) {
|
|
73
|
+
const {PROCESS_CONTROL} = INSTRUMENT_CHANNEL;
|
|
74
|
+
const installation = await startInstallationProxyService(this.udid);
|
|
75
|
+
let lookupResult;
|
|
76
|
+
try {
|
|
77
|
+
lookupResult = await installation.lookupApplications({bundleIds: [this.xctestBundleId]});
|
|
78
|
+
} finally {
|
|
79
|
+
installation.close();
|
|
144
80
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
81
|
+
const appInfo = lookupResult[this.xctestBundleId];
|
|
82
|
+
if (!appInfo) {
|
|
83
|
+
throw new Error(`${this.xctestBundleId} not found on device ${this.udid}`);
|
|
84
|
+
}
|
|
85
|
+
const signerIdentifier = appInfo.SignerIdentity;
|
|
86
|
+
log.info(`SignerIdentifier: ${signerIdentifier}`);
|
|
87
|
+
const appContainer = appInfo.Container;
|
|
88
|
+
const execName = appInfo.CFBundleExecutable;
|
|
89
|
+
if (!execName.endsWith(XCTEST_EXECUTABLE_SUFFIX)) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Invalid CFBundleExecutable ${execName} from ${this.xctestBundleId}, is this bundle a valid xctest app?`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
const targetName = execName.substr(0, execName.indexOf(XCTEST_EXECUTABLE_SUFFIX));
|
|
95
|
+
const xctestPath = `${TMP_FOLDER_PREFIX}/${targetName}-${sessionIdentifier.toUpperCase()}${XCTEST_CONFIGURATION_EXTENSION}`;
|
|
96
|
+
// @ts-ignore This works
|
|
97
|
+
const xctestConfiguration = new XCTestConfiguration({
|
|
98
|
+
...this._conf,
|
|
99
|
+
// properties below should not be override
|
|
100
|
+
testBundleURL: `file://${appInfo.Path}/PlugIns/${targetName}.xctest`,
|
|
101
|
+
sessionIdentifier,
|
|
102
|
+
targetApplicationBundleID: this.targetBundleId,
|
|
103
|
+
});
|
|
104
|
+
await this._writeConfigurationToDevice(xctestConfiguration, xctestPath);
|
|
105
|
+
this._instrumentService = await startInstrumentService(this.udid);
|
|
106
|
+
this._instrumentService.registerLifecycleCallback('close', this.stop.bind(this));
|
|
107
|
+
await this._instrumentService.callChannel(
|
|
108
|
+
PROCESS_CONTROL,
|
|
109
|
+
'processIdentifierForBundleIdentifier:',
|
|
110
|
+
this.xctestBundleId,
|
|
111
|
+
);
|
|
112
|
+
const appPath = appInfo.Path;
|
|
113
|
+
const xctestConfigurationPath = appContainer + xctestPath;
|
|
114
|
+
const appEnv = {
|
|
115
|
+
CA_ASSERT_MAIN_THREAD_TRANSACTIONS: '0',
|
|
116
|
+
CA_DEBUG_TRANSACTIONS: '0',
|
|
117
|
+
DYLD_FRAMEWORK_PATH: `${appPath}/Frameworks:`,
|
|
118
|
+
DYLD_LIBRARY_PATH: `${appPath}/Frameworks`,
|
|
119
|
+
NSUnbufferedIO: 'YES',
|
|
120
|
+
SQLITE_ENABLE_THREAD_ASSERTIONS: '1',
|
|
121
|
+
WDA_PRODUCT_BUNDLE_IDENTIFIER: '',
|
|
122
|
+
XCTestConfigurationFilePath: xctestConfigurationPath,
|
|
123
|
+
XCODE_DBG_XPC_EXCLUSIONS: 'com.apple.dt.xctestSymbolicator',
|
|
124
|
+
MJPEG_SERVER_PORT: '',
|
|
125
|
+
USE_PORT: '',
|
|
126
|
+
// %p means pid
|
|
127
|
+
LLVM_PROFILE_FILE: `${appContainer}/tmp/%p.profraw`,
|
|
128
|
+
...this._env,
|
|
129
|
+
};
|
|
130
|
+
if (majorVersion >= MAJOR_IOS_VERSION_11) {
|
|
131
|
+
appEnv.DYLD_INSERT_LIBRARIES = '/Developer/usr/lib/libMainThreadChecker.dylib';
|
|
132
|
+
appEnv.OS_ACTIVITY_DT_MODE = 'YES';
|
|
175
133
|
}
|
|
134
|
+
const appArgs = ['-NSTreatUnknownArgumentsAsOpen', 'NO', '-ApplePersistenceIgnoreState', 'YES'];
|
|
135
|
+
const appOptions = {StartSuspendedKey: false};
|
|
136
|
+
if (majorVersion >= MAJOR_IOS_VERSION_12) {
|
|
137
|
+
appOptions.ActivateSuspended = true;
|
|
138
|
+
appOptions.__ActivateSuspended = true;
|
|
139
|
+
}
|
|
140
|
+
const launchResult = await this._instrumentService.callChannel(
|
|
141
|
+
PROCESS_CONTROL,
|
|
142
|
+
'launchSuspendedProcessWithDevicePath:bundleIdentifier:environment:arguments:options:',
|
|
143
|
+
appPath,
|
|
144
|
+
this.xctestBundleId,
|
|
145
|
+
appEnv,
|
|
146
|
+
appArgs,
|
|
147
|
+
appOptions,
|
|
148
|
+
);
|
|
149
|
+
const pid = launchResult.selector;
|
|
150
|
+
if (typeof pid !== 'number') {
|
|
151
|
+
throw new Error(`Failed on launching ${this.xctestBundleId}: ${launchResult}`);
|
|
152
|
+
}
|
|
153
|
+
log.info(`Pid of launched ${this.xctestBundleId}: ${pid}`);
|
|
154
|
+
const msg = new DTXMessageAuxBuffer();
|
|
155
|
+
msg.appendObject(pid);
|
|
156
|
+
await this._instrumentService.callChannel(PROCESS_CONTROL, 'startObservingPid:', msg);
|
|
157
|
+
return pid;
|
|
158
|
+
}
|
|
176
159
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
160
|
+
/**
|
|
161
|
+
* @param {XCTestConfiguration} xctestConfiguration plist contains
|
|
162
|
+
* @param {string} xctestPath where xctestConfiguration should be place in app sandbox.
|
|
163
|
+
*/
|
|
164
|
+
async _writeConfigurationToDevice(xctestConfiguration, xctestPath) {
|
|
165
|
+
const xctestContent = xctestConfiguration.getBytes();
|
|
166
|
+
const houseArrestService = await startHouseArrestService(this.udid);
|
|
167
|
+
let vendContainer;
|
|
168
|
+
let stream;
|
|
169
|
+
try {
|
|
170
|
+
vendContainer = await houseArrestService.vendContainer(this.xctestBundleId);
|
|
171
|
+
const list = await vendContainer.listDirectory(TMP_FOLDER_PREFIX);
|
|
172
|
+
for (const file of list) {
|
|
173
|
+
if (file.endsWith(XCTEST_CONFIGURATION_EXTENSION)) {
|
|
174
|
+
const fullPath = `${TMP_FOLDER_PREFIX}/${file}`;
|
|
175
|
+
log.debug(`removing ${fullPath}`);
|
|
176
|
+
await vendContainer.deleteDirectory(fullPath);
|
|
182
177
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
178
|
+
}
|
|
179
|
+
stream = await vendContainer.createWriteStream(xctestPath, {});
|
|
180
|
+
await new B((resolve, reject) => {
|
|
181
|
+
stream.write(xctestContent, resolve);
|
|
182
|
+
stream.on('error', reject);
|
|
183
|
+
});
|
|
184
|
+
} finally {
|
|
185
|
+
stream?.end();
|
|
186
|
+
vendContainer?.close();
|
|
187
|
+
houseArrestService.close();
|
|
187
188
|
}
|
|
189
|
+
}
|
|
188
190
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
this._executing = true;
|
|
197
|
-
const msg = new DTXMessageAuxBuffer();
|
|
198
|
-
msg.appendObject(XCODE_VERSION);
|
|
199
|
-
// @ts-ignore _execTestPlanSession must be defined here
|
|
200
|
-
this._execTestPlanSession._callChannel(false, MAGIC_CHANNEL, '_IDE_startExecutingTestPlanWithProtocolVersion:', msg);
|
|
201
|
-
};
|
|
202
|
-
const showLogMessage = (message) => {
|
|
203
|
-
if (message.auxiliaries.join('').includes('Received test runner ready reply with error: (null')) {
|
|
204
|
-
log.info('Test runner ready');
|
|
205
|
-
//A magic thing is that if not using a delay this would fail on iPhone7 iOS 13.6.1
|
|
206
|
-
setTimeout(() => startExecuting(), 1000);
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
this._execTestPlanSession.registerSelectorCallback('_XCT_testBundleReadyWithProtocolVersion:minimumVersion:', startExecuting);
|
|
210
|
-
this._execTestPlanSession.registerSelectorCallback('_XCT_logDebugMessage:', showLogMessage);
|
|
211
|
-
const msg = new DTXMessageAuxBuffer();
|
|
212
|
-
msg.appendObject(new NSUUID(sessionIdentifier));
|
|
213
|
-
msg.appendObject(`${sessionIdentifier}-746F-006D726964646C79`);
|
|
214
|
-
msg.appendObject(XCODE_BUILD_PATH);
|
|
215
|
-
msg.appendObject(XCODE_VERSION);
|
|
216
|
-
await this._execTestPlanSession.callChannel(DAEMON_CONNECTION_INTERFACE,
|
|
217
|
-
'_IDE_initiateSessionWithIdentifier:forClient:atPath:protocolVersion:', msg);
|
|
191
|
+
async _startInitialSession(majorVersion) {
|
|
192
|
+
this._initialControlSession = await startTestmanagerdService(this.udid);
|
|
193
|
+
this._initialControlSession.registerLifecycleCallback('close', this.stop.bind(this));
|
|
194
|
+
if (majorVersion < MAJOR_IOS_VERSION_11) {
|
|
195
|
+
return;
|
|
218
196
|
}
|
|
197
|
+
const msg = new DTXMessageAuxBuffer();
|
|
198
|
+
msg.appendObject(XCODE_VERSION);
|
|
199
|
+
await this._initialControlSession.callChannel(
|
|
200
|
+
DAEMON_CONNECTION_INTERFACE,
|
|
201
|
+
'_IDE_initiateControlSessionWithProtocolVersion:',
|
|
202
|
+
msg,
|
|
203
|
+
);
|
|
204
|
+
}
|
|
219
205
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
206
|
+
async _startExecSession(sessionIdentifier) {
|
|
207
|
+
this._execTestPlanSession = await startTestmanagerdService(this.udid);
|
|
208
|
+
this._execTestPlanSession.registerLifecycleCallback('close', this.stop.bind(this));
|
|
209
|
+
const startExecuting = () => {
|
|
210
|
+
if (this._executing) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
this._executing = true;
|
|
214
|
+
const msg = new DTXMessageAuxBuffer();
|
|
215
|
+
msg.appendObject(XCODE_VERSION);
|
|
216
|
+
// @ts-ignore _execTestPlanSession must be defined here
|
|
217
|
+
this._execTestPlanSession._callChannel(
|
|
218
|
+
false,
|
|
219
|
+
MAGIC_CHANNEL,
|
|
220
|
+
'_IDE_startExecutingTestPlanWithProtocolVersion:',
|
|
221
|
+
msg,
|
|
222
|
+
);
|
|
223
|
+
};
|
|
224
|
+
const showLogMessage = (message) => {
|
|
225
|
+
if (
|
|
226
|
+
message.auxiliaries.join('').includes('Received test runner ready reply with error: (null')
|
|
227
|
+
) {
|
|
228
|
+
log.info('Test runner ready');
|
|
229
|
+
//A magic thing is that if not using a delay this would fail on iPhone7 iOS 13.6.1
|
|
230
|
+
setTimeout(() => startExecuting(), 1000);
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
this._execTestPlanSession.registerSelectorCallback(
|
|
234
|
+
'_XCT_testBundleReadyWithProtocolVersion:minimumVersion:',
|
|
235
|
+
startExecuting,
|
|
236
|
+
);
|
|
237
|
+
this._execTestPlanSession.registerSelectorCallback('_XCT_logDebugMessage:', showLogMessage);
|
|
238
|
+
const msg = new DTXMessageAuxBuffer();
|
|
239
|
+
msg.appendObject(new NSUUID(sessionIdentifier));
|
|
240
|
+
msg.appendObject(`${sessionIdentifier}-746F-006D726964646C79`);
|
|
241
|
+
msg.appendObject(XCODE_BUILD_PATH);
|
|
242
|
+
msg.appendObject(XCODE_VERSION);
|
|
243
|
+
await this._execTestPlanSession.callChannel(
|
|
244
|
+
DAEMON_CONNECTION_INTERFACE,
|
|
245
|
+
'_IDE_initiateSessionWithIdentifier:forClient:atPath:protocolVersion:',
|
|
246
|
+
msg,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async _notifyTestProcessId(pid, majorVersion) {
|
|
251
|
+
const msg = new DTXMessageAuxBuffer();
|
|
252
|
+
msg.appendObject(pid);
|
|
253
|
+
if (majorVersion >= MAJOR_IOS_VERSION_12) {
|
|
254
|
+
// @ts-ignore _initialControlSession must be defined here
|
|
255
|
+
return await this._initialControlSession.callChannel(
|
|
256
|
+
DAEMON_CONNECTION_INTERFACE,
|
|
257
|
+
'_IDE_authorizeTestSessionWithProcessID:',
|
|
258
|
+
msg,
|
|
259
|
+
);
|
|
237
260
|
}
|
|
261
|
+
if (majorVersion <= MAJOR_IOS_VERSION_9) {
|
|
262
|
+
// @ts-ignore _initialControlSession must be defined here
|
|
263
|
+
return await this._initialControlSession.callChannel(
|
|
264
|
+
DAEMON_CONNECTION_INTERFACE,
|
|
265
|
+
'_IDE_initiateControlSessionForTestProcessID:',
|
|
266
|
+
msg,
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
msg.appendObject(XCODE_VERSION);
|
|
270
|
+
// @ts-ignore _initialControlSession must be defined here
|
|
271
|
+
return await this._initialControlSession.callChannel(
|
|
272
|
+
DAEMON_CONNECTION_INTERFACE,
|
|
273
|
+
'_IDE_initiateControlSessionForTestProcessID:protocolVersion:',
|
|
274
|
+
msg,
|
|
275
|
+
);
|
|
276
|
+
}
|
|
238
277
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
278
|
+
/**
|
|
279
|
+
* Start xctest process. If this method has been called before and the `stop()` method has not been called,
|
|
280
|
+
* calling this again would return directly.
|
|
281
|
+
* @throws If xctest bundle id invalid or not installed.
|
|
282
|
+
*/
|
|
283
|
+
async start() {
|
|
284
|
+
if (this.running) {
|
|
285
|
+
const targetMessage = this.targetBundleId ? `(targeting ${this.targetBundleId})` : '';
|
|
286
|
+
const message = `Xctest for ${this.xctestBundleId}${targetMessage} on device ${this.udid} is already running!`;
|
|
287
|
+
log.info(`${message} Doing nothing here`);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
this.running = true;
|
|
291
|
+
try {
|
|
292
|
+
const productVersion = await getOSVersion(this.udid);
|
|
293
|
+
const majorVersion = parseInt(productVersion.split('.')[0], 10);
|
|
294
|
+
const sessionIdentifier = util.uuidV4();
|
|
295
|
+
//first connection
|
|
296
|
+
await this._startInitialSession(majorVersion);
|
|
258
297
|
|
|
259
|
-
|
|
260
|
-
|
|
298
|
+
//second connection
|
|
299
|
+
await this._startExecSession(sessionIdentifier);
|
|
261
300
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
301
|
+
const pid = await this._launchAppRunner(majorVersion, sessionIdentifier);
|
|
302
|
+
await this._notifyTestProcessId(pid, majorVersion);
|
|
303
|
+
} catch (e) {
|
|
304
|
+
this.stop();
|
|
305
|
+
throw e;
|
|
268
306
|
}
|
|
307
|
+
}
|
|
269
308
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
this.running = false;
|
|
279
|
-
this._instrumentService?.close();
|
|
280
|
-
this._instrumentService?.dispose();
|
|
281
|
-
this._instrumentService = undefined;
|
|
282
|
-
this._execTestPlanSession?.close();
|
|
283
|
-
this._execTestPlanSession?.dispose();
|
|
284
|
-
this._execTestPlanSession = undefined;
|
|
285
|
-
this._initialControlSession?.close();
|
|
286
|
-
this._initialControlSession?.dispose();
|
|
287
|
-
this._initialControlSession = undefined;
|
|
288
|
-
this._executing = false;
|
|
289
|
-
const targetMessage = this.targetBundleId ? `(targeting ${this.targetBundleId})` : '';
|
|
290
|
-
const message = `Xctest for ${this.xctestBundleId}${targetMessage} on device ${this.udid} has stopped!`;
|
|
291
|
-
log.debug(message);
|
|
309
|
+
/**
|
|
310
|
+
* Stop xctest process.
|
|
311
|
+
*/
|
|
312
|
+
stop() {
|
|
313
|
+
if (!this.running) {
|
|
314
|
+
// not started or already called `stop()`
|
|
315
|
+
return;
|
|
292
316
|
}
|
|
317
|
+
this.running = false;
|
|
318
|
+
this._instrumentService?.close();
|
|
319
|
+
this._instrumentService?.dispose();
|
|
320
|
+
this._instrumentService = undefined;
|
|
321
|
+
this._execTestPlanSession?.close();
|
|
322
|
+
this._execTestPlanSession?.dispose();
|
|
323
|
+
this._execTestPlanSession = undefined;
|
|
324
|
+
this._initialControlSession?.close();
|
|
325
|
+
this._initialControlSession?.dispose();
|
|
326
|
+
this._initialControlSession = undefined;
|
|
327
|
+
this._executing = false;
|
|
328
|
+
const targetMessage = this.targetBundleId ? `(targeting ${this.targetBundleId})` : '';
|
|
329
|
+
const message = `Xctest for ${this.xctestBundleId}${targetMessage} on device ${this.udid} has stopped!`;
|
|
330
|
+
log.debug(message);
|
|
331
|
+
}
|
|
293
332
|
}
|
|
294
333
|
|
|
295
|
-
export {
|
|
334
|
+
export {Xctest};
|
|
296
335
|
export default Xctest;
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"keywords": [
|
|
5
5
|
"appium"
|
|
6
6
|
],
|
|
7
|
-
"version": "3.1.
|
|
7
|
+
"version": "3.1.11",
|
|
8
8
|
"author": "Appium Contributors",
|
|
9
9
|
"license": "Apache-2.0",
|
|
10
10
|
"repository": {
|
|
@@ -46,6 +46,8 @@
|
|
|
46
46
|
"clean": "npm run build -- --clean",
|
|
47
47
|
"lint": "eslint .",
|
|
48
48
|
"lint:fix": "npm run lint -- --fix",
|
|
49
|
+
"format": "prettier -w ./lib ./test",
|
|
50
|
+
"format:check": "prettier --check ./lib ./test",
|
|
49
51
|
"prepare": "npm run build",
|
|
50
52
|
"test": "mocha --exit --timeout 1m \"./test/**/*-specs.js\""
|
|
51
53
|
},
|
|
@@ -71,6 +73,6 @@
|
|
|
71
73
|
"prettier": "^3.0.0",
|
|
72
74
|
"semantic-release": "^25.0.2",
|
|
73
75
|
"ts-node": "^10.9.1",
|
|
74
|
-
"typescript": "^
|
|
76
|
+
"typescript": "^6.0.3"
|
|
75
77
|
}
|
|
76
78
|
}
|