detox 20.11.4 → 20.12.0-smoke.0
Sign up to get free protection for your applications and to get access to all the features.
- package/Detox-android/com/wix/detox/{20.11.4/detox-20.11.4-javadoc.jar → 20.12.0-smoke.0/detox-20.12.0-smoke.0-javadoc.jar} +0 -0
- package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-javadoc.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-javadoc.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-javadoc.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-javadoc.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.11.4/detox-20.11.4-sources.jar → 20.12.0-smoke.0/detox-20.12.0-smoke.0-sources.jar} +0 -0
- package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-sources.jar.md5 +1 -0
- package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-sources.jar.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-sources.jar.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-sources.jar.sha512 +1 -0
- package/Detox-android/com/wix/detox/{20.11.4/detox-20.11.4.pom → 20.12.0-smoke.0/detox-20.12.0-smoke.0.pom} +1 -1
- package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0.pom.md5 +1 -0
- package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0.pom.sha1 +1 -0
- package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0.pom.sha256 +1 -0
- package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0.pom.sha512 +1 -0
- package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
- package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
- package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
- package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
- package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
- package/Detox-ios-src.tbz +0 -0
- package/Detox-ios.tbz +0 -0
- package/local-cli/reset-lock-file.js +5 -9
- package/package.json +6 -6
- package/src/DetoxWorker.js +5 -9
- package/src/artifacts/providers/index.js +3 -3
- package/src/artifacts/screenshot/SimulatorScreenshotPlugin.js +0 -17
- package/src/devices/allocation/DeviceAllocator.js +50 -15
- package/src/devices/allocation/DeviceList.js +44 -0
- package/src/devices/allocation/DeviceRegistry.js +186 -0
- package/src/devices/allocation/drivers/AllocationDriverBase.js +10 -4
- package/src/devices/{common/drivers/android/tools → allocation/drivers/android}/FreeDeviceFinder.js +11 -10
- package/src/devices/allocation/drivers/android/attached/AttachedAndroidAllocDriver.js +7 -6
- package/src/devices/allocation/drivers/android/emulator/EmulatorAllocDriver.js +108 -26
- package/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.js +33 -43
- package/src/devices/allocation/drivers/android/emulator/FreeEmulatorFinder.js +1 -1
- package/src/devices/allocation/drivers/android/emulator/FreePortFinder.js +16 -0
- package/src/devices/allocation/drivers/android/emulator/launchEmulatorProcess.js +3 -3
- package/src/devices/allocation/drivers/android/genycloud/GenyAllocDriver.js +87 -27
- package/src/devices/allocation/drivers/android/genycloud/GenyInstanceLauncher.js +39 -28
- package/src/devices/allocation/drivers/android/genycloud/GenyRegistry.js +93 -0
- package/src/devices/allocation/drivers/android/genycloud/services/GenyInstanceLifecycleService.js +24 -0
- package/src/devices/{common → allocation}/drivers/android/genycloud/services/GenyRecipesService.js +1 -1
- package/src/devices/{common → allocation}/drivers/android/genycloud/services/dto/GenyInstance.js +6 -1
- package/src/devices/allocation/drivers/ios/SimulatorAllocDriver.js +70 -40
- package/src/devices/allocation/drivers/ios/SimulatorLauncher.js +11 -7
- package/src/devices/allocation/drivers/ios/SimulatorQuery.js +24 -0
- package/src/devices/allocation/factories/android.js +29 -35
- package/src/devices/allocation/factories/ios.js +7 -5
- package/src/devices/common/drivers/android/emulator/exec/EmulatorExec.js +17 -5
- package/src/devices/common/drivers/android/exec/ADB.js +1 -0
- package/src/devices/common/drivers/ios/tools/AppleSimUtils.js +1 -1
- package/src/devices/cookies/AndroidDeviceCookie.js +4 -0
- package/src/devices/cookies/GenycloudEmulatorCookie.js +3 -5
- package/src/devices/cookies/IosSimulatorCookie.js +4 -0
- package/src/devices/runtime/drivers/android/genycloud/GenyCloudDriver.js +2 -2
- package/src/devices/runtime/factories/android.js +1 -1
- package/src/devices/runtime/factories/ios.js +3 -2
- package/src/{servicelocator → devices/servicelocator}/android/emulatorServiceLocator.js +1 -1
- package/src/devices/servicelocator/android/genycloudServiceLocator.js +17 -0
- package/src/devices/servicelocator/android/index.js +23 -0
- package/src/{validation → devices/validation}/android/GenycloudEnvValidator.js +2 -2
- package/src/{validation → devices/validation}/factories/index.js +1 -1
- package/src/{validation → devices/validation}/ios/IosSimulatorEnvValidator.js +2 -2
- package/src/environmentFactory.js +1 -11
- package/src/ipc/IPCClient.js +17 -1
- package/src/ipc/IPCServer.js +25 -1
- package/src/realms/DetoxContext.js +6 -0
- package/src/realms/DetoxPrimaryContext.js +42 -42
- package/src/realms/DetoxSecondaryContext.js +19 -0
- package/src/realms/symbols.js +4 -0
- package/src/utils/PIDService.js +27 -0
- package/src/utils/environment.js +8 -15
- package/src/utils/errorUtils.js +2 -2
- package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4-javadoc.jar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4-javadoc.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4-javadoc.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4-javadoc.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4-sources.jar.md5 +0 -1
- package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4-sources.jar.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4-sources.jar.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4-sources.jar.sha512 +0 -1
- package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4.pom.md5 +0 -1
- package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4.pom.sha1 +0 -1
- package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4.pom.sha256 +0 -1
- package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4.pom.sha512 +0 -1
- package/src/devices/DeviceRegistry.js +0 -176
- package/src/devices/allocation/drivers/android/attached/AttachedAndroidLauncher.js +0 -13
- package/src/devices/allocation/drivers/android/emulator/EmulatorAllocationHelper.js +0 -72
- package/src/devices/allocation/drivers/android/genycloud/GenyDeviceRegistryFactory.js +0 -16
- package/src/devices/allocation/drivers/android/genycloud/GenyInstanceAllocationHelper.js +0 -65
- package/src/devices/common/drivers/DeviceAllocationHelper.js +0 -20
- package/src/devices/common/drivers/DeviceLauncher.js +0 -19
- package/src/devices/common/drivers/android/genycloud/services/GenyInstanceLifecycleService.js +0 -25
- package/src/devices/common/drivers/android/genycloud/services/GenyInstanceLookupService.js +0 -38
- package/src/devices/common/drivers/android/genycloud/services/GenyInstanceNaming.js +0 -14
- package/src/devices/lifecycle/GenyGlobalLifecycleHandler.js +0 -71
- package/src/devices/lifecycle/factories/GenyGlobalLifecycleHandlerFactory.js +0 -18
- package/src/servicelocator/android/genycloudServiceLocator.js +0 -21
- package/src/servicelocator/android/index.js +0 -25
- package/src/servicelocator/ios.js +0 -7
- /package/Detox-android/com/wix/detox/{20.11.4/detox-20.11.4.aar → 20.12.0-smoke.0/detox-20.12.0-smoke.0.aar} +0 -0
- /package/Detox-android/com/wix/detox/{20.11.4/detox-20.11.4.aar.md5 → 20.12.0-smoke.0/detox-20.12.0-smoke.0.aar.md5} +0 -0
- /package/Detox-android/com/wix/detox/{20.11.4/detox-20.11.4.aar.sha1 → 20.12.0-smoke.0/detox-20.12.0-smoke.0.aar.sha1} +0 -0
- /package/Detox-android/com/wix/detox/{20.11.4/detox-20.11.4.aar.sha256 → 20.12.0-smoke.0/detox-20.12.0-smoke.0.aar.sha256} +0 -0
- /package/Detox-android/com/wix/detox/{20.11.4/detox-20.11.4.aar.sha512 → 20.12.0-smoke.0/detox-20.12.0-smoke.0.aar.sha512} +0 -0
- /package/src/devices/{common → allocation}/drivers/android/genycloud/exec/GenyCloudExec.js +0 -0
- /package/src/devices/{common → allocation}/drivers/android/genycloud/services/GenyAuthService.js +0 -0
- /package/src/devices/{common → allocation}/drivers/android/genycloud/services/dto/GenyRecipe.js +0 -0
- /package/src/{validation → devices/validation}/EnvironmentValidatorBase.js +0 -0
@@ -7,14 +7,16 @@ class AttachedAndroidAllocDriver extends AllocationDriverBase {
|
|
7
7
|
* @param adb { ADB }
|
8
8
|
* @param deviceRegistry { DeviceRegistry }
|
9
9
|
* @param freeDeviceFinder { FreeDeviceFinder }
|
10
|
-
* @param attachedAndroidLauncher { AttachedAndroidLauncher }
|
11
10
|
*/
|
12
|
-
constructor({ adb, deviceRegistry, freeDeviceFinder
|
11
|
+
constructor({ adb, deviceRegistry, freeDeviceFinder }) {
|
13
12
|
super();
|
14
13
|
this._adb = adb;
|
15
14
|
this._deviceRegistry = deviceRegistry;
|
16
15
|
this._freeDeviceFinder = freeDeviceFinder;
|
17
|
-
|
16
|
+
}
|
17
|
+
|
18
|
+
async init() {
|
19
|
+
await this._deviceRegistry.unregisterZombieDevices();
|
18
20
|
}
|
19
21
|
|
20
22
|
/**
|
@@ -23,7 +25,7 @@ class AttachedAndroidAllocDriver extends AllocationDriverBase {
|
|
23
25
|
*/
|
24
26
|
async allocate(deviceConfig) {
|
25
27
|
const adbNamePattern = deviceConfig.device.adbName;
|
26
|
-
const adbName = await this._deviceRegistry.
|
28
|
+
const adbName = await this._deviceRegistry.registerDevice(() => this._freeDeviceFinder.findFreeDevice(adbNamePattern));
|
27
29
|
|
28
30
|
return new AttachedAndroidDeviceCookie(adbName);
|
29
31
|
}
|
@@ -38,7 +40,6 @@ class AttachedAndroidAllocDriver extends AllocationDriverBase {
|
|
38
40
|
// TODO Also disable native animations?
|
39
41
|
await this._adb.apiLevel(adbName);
|
40
42
|
await this._adb.unlockScreen(adbName);
|
41
|
-
await this._attachedAndroidLauncher.notifyLaunchCompleted(adbName);
|
42
43
|
}
|
43
44
|
|
44
45
|
/**
|
@@ -47,7 +48,7 @@ class AttachedAndroidAllocDriver extends AllocationDriverBase {
|
|
47
48
|
*/
|
48
49
|
async free(cookie) {
|
49
50
|
const { adbName } = cookie;
|
50
|
-
await this._deviceRegistry.
|
51
|
+
await this._deviceRegistry.unregisterDevice(adbName);
|
51
52
|
}
|
52
53
|
}
|
53
54
|
|
@@ -1,6 +1,7 @@
|
|
1
|
-
// @ts-nocheck
|
2
1
|
const _ = require('lodash');
|
3
2
|
|
3
|
+
const Deferred = require('../../../../../utils/Deferred');
|
4
|
+
const log = require('../../../../../utils/logger').child({ cat: 'device,device-allocation' });
|
4
5
|
const AndroidEmulatorCookie = require('../../../../cookies/AndroidEmulatorCookie');
|
5
6
|
const AllocationDriverBase = require('../../AllocationDriverBase');
|
6
7
|
|
@@ -8,20 +9,45 @@ const { patchAvdSkinConfig } = require('./patchAvdSkinConfig');
|
|
8
9
|
|
9
10
|
class EmulatorAllocDriver extends AllocationDriverBase {
|
10
11
|
/**
|
11
|
-
* @param
|
12
|
-
* @param
|
13
|
-
* @param
|
14
|
-
* @param
|
15
|
-
* @param
|
12
|
+
* @param {object} options
|
13
|
+
* @param {import('../../../../common/drivers/android/exec/ADB')} options.adb
|
14
|
+
* @param {import('./AVDValidator')} options.avdValidator
|
15
|
+
* @param {DetoxInternals.RuntimeConfig} options.detoxConfig
|
16
|
+
* @param {import('../../../DeviceRegistry')} options.deviceRegistry
|
17
|
+
* @param {import('./FreeEmulatorFinder')} options.freeDeviceFinder
|
18
|
+
* @param {import('./FreePortFinder')} options.freePortFinder
|
19
|
+
* @param {import('./EmulatorLauncher')} options.emulatorLauncher
|
20
|
+
* @param {import('./EmulatorVersionResolver')} options.emulatorVersionResolver
|
16
21
|
*/
|
17
|
-
constructor({
|
22
|
+
constructor({
|
23
|
+
adb,
|
24
|
+
avdValidator,
|
25
|
+
detoxConfig,
|
26
|
+
deviceRegistry,
|
27
|
+
freeDeviceFinder,
|
28
|
+
freePortFinder,
|
29
|
+
emulatorVersionResolver,
|
30
|
+
emulatorLauncher
|
31
|
+
}) {
|
18
32
|
super();
|
33
|
+
|
34
|
+
/** @type {Deferred} */
|
35
|
+
this._deferredAllocation = Deferred.resolved(null);
|
36
|
+
/** @type {Promise<unknown>} */
|
37
|
+
this._pendingAllocation = Promise.resolve();
|
38
|
+
|
19
39
|
this._adb = adb;
|
20
40
|
this._avdValidator = avdValidator;
|
41
|
+
this._deviceRegistry = deviceRegistry;
|
21
42
|
this._emulatorVersionResolver = emulatorVersionResolver;
|
22
43
|
this._emulatorLauncher = emulatorLauncher;
|
23
|
-
this.
|
24
|
-
this.
|
44
|
+
this._freeDeviceFinder = freeDeviceFinder;
|
45
|
+
this._freePortFinder = freePortFinder;
|
46
|
+
this._shouldShutdown = detoxConfig.behavior.cleanup.shutdownDevice;
|
47
|
+
}
|
48
|
+
|
49
|
+
async init() {
|
50
|
+
await this._deviceRegistry.unregisterZombieDevices();
|
25
51
|
}
|
26
52
|
|
27
53
|
/**
|
@@ -29,55 +55,111 @@ class EmulatorAllocDriver extends AllocationDriverBase {
|
|
29
55
|
* @returns {Promise<AndroidEmulatorCookie>}
|
30
56
|
*/
|
31
57
|
async allocate(deviceConfig) {
|
58
|
+
await this._pendingAllocation.catch(() => { /* ignore previous errors */ });
|
59
|
+
this._deferredAllocation = new Deferred();
|
60
|
+
this._pendingAllocation = this._deviceRegistry.registerDevice(() => this._deferredAllocation.promise);
|
61
|
+
|
62
|
+
try {
|
63
|
+
return await this._doAllocate(deviceConfig);
|
64
|
+
} catch (e) {
|
65
|
+
this._deferredAllocation.reject(e);
|
66
|
+
throw e;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
/**
|
71
|
+
* @param deviceConfig
|
72
|
+
* @returns {Promise<AndroidEmulatorCookie>}
|
73
|
+
*/
|
74
|
+
async _doAllocate(deviceConfig) {
|
32
75
|
const avdName = deviceConfig.device.avdName;
|
33
76
|
|
34
77
|
await this._avdValidator.validate(avdName, deviceConfig.headless);
|
35
78
|
await this._fixAvdConfigIniSkinNameIfNeeded(avdName, deviceConfig.headless);
|
36
79
|
|
37
|
-
|
38
|
-
|
80
|
+
let adbName = await this._freeDeviceFinder.findFreeDevice(avdName);
|
81
|
+
if (!adbName) {
|
82
|
+
const port = await this._freePortFinder.findFreePort();
|
83
|
+
adbName = `emulator-${port}`;
|
39
84
|
|
40
|
-
|
41
|
-
avdName,
|
42
|
-
isRunning: allocResult.isRunning,
|
43
|
-
launchOptions: {
|
85
|
+
await this._emulatorLauncher.launch({
|
44
86
|
bootArgs: deviceConfig.bootArgs,
|
45
87
|
gpuMode: deviceConfig.gpuMode,
|
46
88
|
headless: deviceConfig.headless,
|
47
89
|
readonly: deviceConfig.readonly,
|
48
|
-
|
49
|
-
|
50
|
-
|
90
|
+
avdName,
|
91
|
+
adbName,
|
92
|
+
port,
|
93
|
+
});
|
94
|
+
}
|
51
95
|
|
52
96
|
return new AndroidEmulatorCookie(adbName);
|
53
97
|
}
|
54
98
|
|
99
|
+
/**
|
100
|
+
* @param {AndroidEmulatorCookie} deviceCookie
|
101
|
+
*/
|
102
|
+
async postAllocate(deviceCookie) {
|
103
|
+
try {
|
104
|
+
await this._doPostAllocate(deviceCookie);
|
105
|
+
this._deferredAllocation.resolve(deviceCookie.adbName);
|
106
|
+
} catch (e) {
|
107
|
+
this._deferredAllocation.reject(e);
|
108
|
+
throw e;
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
55
112
|
/**
|
56
113
|
* @param {AndroidEmulatorCookie} deviceCookie
|
57
114
|
* @returns {Promise<void>}
|
58
115
|
*/
|
59
|
-
async
|
116
|
+
async _doPostAllocate(deviceCookie) {
|
60
117
|
const { adbName } = deviceCookie;
|
61
|
-
const { avdName, isRunning, launchOptions } = this._launchInfo[adbName];
|
62
118
|
|
63
|
-
await this._emulatorLauncher.
|
119
|
+
await this._emulatorLauncher.awaitEmulatorBoot(adbName);
|
64
120
|
await this._adb.apiLevel(adbName);
|
65
121
|
await this._adb.disableAndroidAnimations(adbName);
|
66
122
|
await this._adb.unlockScreen(adbName);
|
67
123
|
}
|
68
124
|
|
69
125
|
/**
|
70
|
-
* @param cookie {
|
71
|
-
* @param options {
|
72
|
-
* @return {
|
126
|
+
* @param cookie {AndroidEmulatorCookie}
|
127
|
+
* @param options {Partial<import('../../AllocationDriverBase').DeallocOptions>}
|
128
|
+
* @return {Promise<void>}
|
73
129
|
*/
|
74
130
|
async free(cookie, options = {}) {
|
75
131
|
const { adbName } = cookie;
|
76
132
|
|
77
|
-
await this._allocationHelper.deallocateDevice(adbName);
|
78
|
-
|
79
133
|
if (options.shutdown) {
|
134
|
+
await this._doShutdown(adbName);
|
135
|
+
await this._deviceRegistry.unregisterDevice(adbName);
|
136
|
+
} else {
|
137
|
+
await this._deviceRegistry.releaseDevice(adbName);
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
async cleanup() {
|
142
|
+
if (this._shouldShutdown) {
|
143
|
+
const { devices } = await this._adb.devices();
|
144
|
+
const actualEmulators = devices.map((device) => device.adbName);
|
145
|
+
const sessionDevices = await this._deviceRegistry.readSessionDevices();
|
146
|
+
const emulatorsToShutdown = _.intersection(sessionDevices.getIds(), actualEmulators);
|
147
|
+
const shutdownPromises = emulatorsToShutdown.map((adbName) => this._doShutdown(adbName));
|
148
|
+
await Promise.all(shutdownPromises);
|
149
|
+
}
|
150
|
+
|
151
|
+
await this._deviceRegistry.unregisterSessionDevices();
|
152
|
+
}
|
153
|
+
|
154
|
+
/**
|
155
|
+
* @param {string} adbName
|
156
|
+
* @return {Promise<void>}
|
157
|
+
*/
|
158
|
+
async _doShutdown(adbName) {
|
159
|
+
try {
|
80
160
|
await this._emulatorLauncher.shutdown(adbName);
|
161
|
+
} catch (err) {
|
162
|
+
log.warn({ err }, `Failed to shutdown emulator ${adbName}`);
|
81
163
|
}
|
82
164
|
}
|
83
165
|
|
@@ -3,47 +3,54 @@ const { DetoxRuntimeError } = require('../../../../../errors');
|
|
3
3
|
const log = require('../../../../../utils/logger').child({ cat: 'device' });
|
4
4
|
const retry = require('../../../../../utils/retry');
|
5
5
|
const traceMethods = require('../../../../../utils/traceMethods');
|
6
|
-
const DeviceLauncher = require('../../../../common/drivers/DeviceLauncher');
|
7
6
|
const { LaunchCommand } = require('../../../../common/drivers/android/emulator/exec/EmulatorExec');
|
8
7
|
|
9
8
|
const { launchEmulatorProcess } = require('./launchEmulatorProcess');
|
10
9
|
|
11
10
|
const isUnknownEmulatorError = (err) => (err.message || '').includes('failed with code null');
|
12
11
|
|
13
|
-
class EmulatorLauncher
|
14
|
-
constructor({ adb, emulatorExec
|
15
|
-
super(eventEmitter);
|
12
|
+
class EmulatorLauncher {
|
13
|
+
constructor({ adb, emulatorExec }) {
|
16
14
|
this._adb = adb;
|
17
15
|
this._emulatorExec = emulatorExec;
|
18
|
-
traceMethods(log, this, ['
|
16
|
+
traceMethods(log, this, ['awaitEmulatorBoot']);
|
19
17
|
}
|
20
18
|
|
21
19
|
/**
|
22
|
-
* @param
|
23
|
-
* @param
|
24
|
-
* @param
|
25
|
-
* @param
|
26
|
-
* @param
|
27
|
-
* @param
|
28
|
-
* @param options.
|
29
|
-
* @param options.
|
30
|
-
* @param options.readonly { Boolean }
|
20
|
+
* @param {object} options
|
21
|
+
* @param {string} options.avdName
|
22
|
+
* @param {string} options.adbName
|
23
|
+
* @param {number} options.port
|
24
|
+
* @param {string | undefined} options.bootArgs
|
25
|
+
* @param {string | undefined} options.gpuMode
|
26
|
+
* @param {boolean} options.headless
|
27
|
+
* @param {boolean} options.readonly
|
31
28
|
*/
|
32
|
-
async launch(
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
29
|
+
async launch(options) {
|
30
|
+
const launchCommand = new LaunchCommand(options);
|
31
|
+
await retry({
|
32
|
+
retries: 2,
|
33
|
+
interval: 100,
|
34
|
+
conditionFn: isUnknownEmulatorError,
|
35
|
+
}, () => launchEmulatorProcess(this._emulatorExec, this._adb, launchCommand));
|
36
|
+
}
|
37
|
+
|
38
|
+
/**
|
39
|
+
* @param {string} adbName
|
40
|
+
*/
|
41
|
+
async awaitEmulatorBoot(adbName) {
|
42
|
+
await retry({ retries: 240, interval: 2500, shouldUnref: true }, async () => {
|
43
|
+
const isBootComplete = await this._adb.isBootComplete(adbName);
|
44
|
+
|
45
|
+
if (!isBootComplete) {
|
46
|
+
throw new DetoxRuntimeError({
|
47
|
+
message: `Waited for ${adbName} to complete booting for too long!`,
|
48
|
+
});
|
49
|
+
}
|
50
|
+
});
|
43
51
|
}
|
44
52
|
|
45
53
|
async shutdown(adbName) {
|
46
|
-
await this._notifyPreShutdown(adbName);
|
47
54
|
await this._adb.emuKill(adbName);
|
48
55
|
await retry({
|
49
56
|
retries: 5,
|
@@ -57,23 +64,6 @@ class EmulatorLauncher extends DeviceLauncher {
|
|
57
64
|
});
|
58
65
|
}
|
59
66
|
});
|
60
|
-
await this._notifyShutdownCompleted(adbName);
|
61
|
-
}
|
62
|
-
|
63
|
-
_launchEmulator(emulatorName, launchCommand, adbName) {
|
64
|
-
return launchEmulatorProcess(emulatorName, this._emulatorExec, launchCommand, this._adb, adbName);
|
65
|
-
}
|
66
|
-
|
67
|
-
async _awaitEmulatorBoot(adbName) {
|
68
|
-
await retry({ retries: 240, interval: 2500, shouldUnref: true }, async () => {
|
69
|
-
const isBootComplete = await this._adb.isBootComplete(adbName);
|
70
|
-
|
71
|
-
if (!isBootComplete) {
|
72
|
-
throw new DetoxRuntimeError({
|
73
|
-
message: `Waited for ${adbName} to complete booting for too long!`,
|
74
|
-
});
|
75
|
-
}
|
76
|
-
});
|
77
67
|
}
|
78
68
|
}
|
79
69
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class FreePortFinder {
|
2
|
+
constructor({ min = 10000, max = 20000 } = {}) {
|
3
|
+
this._min = min;
|
4
|
+
this._max = max;
|
5
|
+
}
|
6
|
+
|
7
|
+
async findFreePort() {
|
8
|
+
const min = this._min;
|
9
|
+
const max = this._max;
|
10
|
+
let port = Math.random() * (max - min) + min;
|
11
|
+
port = port & 0xFFFFFFFE; // Should always be even
|
12
|
+
return port;
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
module.exports = FreePortFinder;
|
@@ -4,10 +4,10 @@ const _ = require('lodash');
|
|
4
4
|
|
5
5
|
const unitLogger = require('../../../../../utils/logger').child({ cat: 'device' });
|
6
6
|
|
7
|
-
function launchEmulatorProcess(
|
7
|
+
function launchEmulatorProcess(emulatorExec, adb, emulatorLaunchCommand) {
|
8
8
|
let childProcessOutput;
|
9
9
|
const portName = emulatorLaunchCommand.port ? `-${emulatorLaunchCommand.port}` : '';
|
10
|
-
const tempLog = `./${
|
10
|
+
const tempLog = `./${emulatorLaunchCommand.avdName}${portName}.log`;
|
11
11
|
const stdout = fs.openSync(tempLog, 'a');
|
12
12
|
const stderr = fs.openSync(tempLog, 'a');
|
13
13
|
|
@@ -31,7 +31,7 @@ function launchEmulatorProcess(emulatorName, emulatorExec, emulatorLaunchCommand
|
|
31
31
|
|
32
32
|
log = log.child({ child_pid: childProcessPromise.childProcess.pid });
|
33
33
|
|
34
|
-
adb.waitForDevice(adbName).then(() => childProcessPromise._cpResolve());
|
34
|
+
adb.waitForDevice(emulatorLaunchCommand.adbName).then(() => childProcessPromise._cpResolve());
|
35
35
|
|
36
36
|
return childProcessPromise.then(() => true).catch((err) => {
|
37
37
|
detach();
|
@@ -1,25 +1,39 @@
|
|
1
1
|
const { DetoxRuntimeError } = require('../../../../../errors');
|
2
2
|
const Timer = require('../../../../../utils/Timer');
|
3
|
+
const log = require('../../../../../utils/logger').child({ cat: 'device' });
|
3
4
|
const GenycloudEmulatorCookie = require('../../../../cookies/GenycloudEmulatorCookie');
|
4
5
|
const AllocationDriverBase = require('../../AllocationDriverBase');
|
5
6
|
|
6
|
-
|
7
|
+
const GenyRegistry = require('./GenyRegistry');
|
8
|
+
|
9
|
+
const events = {
|
10
|
+
GENYCLOUD_TEARDOWN: { event: 'GENYCLOUD_TEARDOWN' },
|
11
|
+
};
|
7
12
|
|
13
|
+
class GenyAllocDriver extends AllocationDriverBase {
|
8
14
|
/**
|
9
15
|
* @param {object} options
|
10
16
|
* @param {import('../../../../common/drivers/android/exec/ADB')} options.adb
|
11
|
-
* @param {
|
12
|
-
* @param {import('./
|
17
|
+
* @param {DetoxInternals.SessionState} options.detoxSession
|
18
|
+
* @param {import('./GenyRegistry')} options.genyRegistry
|
13
19
|
* @param {import('./GenyInstanceLauncher')} options.instanceLauncher
|
20
|
+
* @param {import('./GenyRecipeQuerying')} options.recipeQuerying
|
14
21
|
*/
|
15
|
-
constructor({
|
22
|
+
constructor({
|
23
|
+
adb,
|
24
|
+
detoxSession,
|
25
|
+
genyRegistry = new GenyRegistry(),
|
26
|
+
instanceLauncher,
|
27
|
+
recipeQuerying,
|
28
|
+
}) {
|
16
29
|
super();
|
17
30
|
|
18
31
|
this._adb = adb;
|
19
|
-
this.
|
32
|
+
this._detoxSessionId = detoxSession.id;
|
33
|
+
this._genyRegistry = genyRegistry;
|
20
34
|
this._instanceLauncher = instanceLauncher;
|
21
|
-
this.
|
22
|
-
this.
|
35
|
+
this._recipeQuerying = recipeQuerying;
|
36
|
+
this._instanceCounter = 0;
|
23
37
|
}
|
24
38
|
|
25
39
|
/**
|
@@ -27,47 +41,74 @@ class GenyAllocDriver extends AllocationDriverBase {
|
|
27
41
|
* @return {Promise<GenycloudEmulatorCookie>}
|
28
42
|
*/
|
29
43
|
async allocate(deviceConfig) {
|
44
|
+
await new Promise((resolve) => setTimeout(resolve, 10000));
|
30
45
|
const deviceQuery = deviceConfig.device;
|
31
46
|
const recipe = await this._recipeQuerying.getRecipeFromQuery(deviceQuery);
|
32
47
|
this._assertRecipe(deviceQuery, recipe);
|
33
48
|
|
34
|
-
|
35
|
-
|
49
|
+
let instance = this._genyRegistry.findFreeInstance(recipe);
|
50
|
+
if (!instance) {
|
51
|
+
const instanceName = `Detox.${this._detoxSessionId}.${this._instanceCounter++}`;
|
52
|
+
instance = await this._instanceLauncher.launch(recipe, instanceName);
|
53
|
+
this._genyRegistry.addInstance(instance, recipe);
|
54
|
+
}
|
55
|
+
|
36
56
|
return new GenycloudEmulatorCookie(instance);
|
37
57
|
}
|
38
58
|
|
39
59
|
/**
|
40
60
|
* @param {GenycloudEmulatorCookie} cookie
|
41
|
-
* @returns {Promise<void>}
|
42
61
|
*/
|
43
62
|
async postAllocate(cookie) {
|
44
|
-
const
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
await
|
51
|
-
|
52
|
-
|
53
|
-
|
63
|
+
const instance = await this._instanceLauncher.connect(cookie.instance);
|
64
|
+
this._genyRegistry.updateInstance(instance);
|
65
|
+
|
66
|
+
if (this._genyRegistry.pollNewInstance(instance)) {
|
67
|
+
const { adbName } = instance;
|
68
|
+
|
69
|
+
await Timer.run(20000, 'waiting for device to respond', async () => {
|
70
|
+
await this._adb.disableAndroidAnimations(adbName);
|
71
|
+
await this._adb.setWiFiToggle(adbName, true);
|
72
|
+
await this._adb.apiLevel(adbName);
|
73
|
+
});
|
74
|
+
}
|
75
|
+
|
76
|
+
return new GenycloudEmulatorCookie(instance);
|
54
77
|
}
|
55
78
|
|
56
79
|
/**
|
57
|
-
* @param cookie {
|
58
|
-
* @param options {
|
80
|
+
* @param cookie {GenycloudEmulatorCookie}
|
81
|
+
* @param options {Partial<import('../../AllocationDriverBase').DeallocOptions>}
|
59
82
|
* @return {Promise<void>}
|
60
83
|
*/
|
61
84
|
async free(cookie, options = {}) {
|
62
|
-
const { instance } = cookie;
|
63
|
-
|
64
|
-
await this._instanceAllocationHelper.deallocateDevice(instance.uuid);
|
65
|
-
|
66
85
|
if (options.shutdown) {
|
67
|
-
|
86
|
+
this._genyRegistry.removeInstance(cookie.instance);
|
87
|
+
await this._instanceLauncher.shutdown(cookie.instance);
|
88
|
+
} else {
|
89
|
+
this._genyRegistry.markAsFree(cookie.instance);
|
68
90
|
}
|
69
91
|
}
|
70
92
|
|
93
|
+
async cleanup() {
|
94
|
+
log.info(events.GENYCLOUD_TEARDOWN, 'Initiating Genymotion SaaS instances teardown...');
|
95
|
+
|
96
|
+
const killPromises = this._genyRegistry.getInstances().map((instance) => {
|
97
|
+
this._genyRegistry.markAsBusy(instance);
|
98
|
+
const onSuccess = () => this._genyRegistry.removeInstance(instance);
|
99
|
+
const onError = (error) => ({ ...instance, error });
|
100
|
+
return this._instanceLauncher.shutdown(instance).then(onSuccess, onError);
|
101
|
+
});
|
102
|
+
|
103
|
+
const deletionLeaks = (await Promise.all(killPromises)).filter(Boolean);
|
104
|
+
this._reportGlobalCleanupSummary(deletionLeaks);
|
105
|
+
}
|
106
|
+
|
107
|
+
emergencyCleanup() {
|
108
|
+
const instances = this._genyRegistry.getInstances();
|
109
|
+
this._reportGlobalCleanupSummary(instances);
|
110
|
+
}
|
111
|
+
|
71
112
|
_assertRecipe(deviceQuery, recipe) {
|
72
113
|
if (!recipe) {
|
73
114
|
throw new DetoxRuntimeError({
|
@@ -76,6 +117,25 @@ class GenyAllocDriver extends AllocationDriverBase {
|
|
76
117
|
});
|
77
118
|
}
|
78
119
|
}
|
120
|
+
|
121
|
+
_reportGlobalCleanupSummary(deletionLeaks) {
|
122
|
+
if (deletionLeaks.length) {
|
123
|
+
log.warn(events.GENYCLOUD_TEARDOWN, 'WARNING! Detected a Genymotion SaaS instance leakage, for the following instances:');
|
124
|
+
|
125
|
+
deletionLeaks.forEach(({ uuid, name, error }) => {
|
126
|
+
log.warn(events.GENYCLOUD_TEARDOWN, [
|
127
|
+
`Instance ${name} (${uuid})${error ? `: ${error}` : ''}`,
|
128
|
+
` Kill it by visiting https://cloud.geny.io/instance/${uuid}, or by running:`,
|
129
|
+
` gmsaas instances stop ${uuid}`,
|
130
|
+
].join('\n'));
|
131
|
+
});
|
132
|
+
|
133
|
+
log.info(events.GENYCLOUD_TEARDOWN, 'Instances teardown completed with warnings');
|
134
|
+
} else {
|
135
|
+
log.info(events.GENYCLOUD_TEARDOWN, 'Instances teardown completed successfully');
|
136
|
+
}
|
137
|
+
}
|
79
138
|
}
|
80
139
|
|
140
|
+
|
81
141
|
module.exports = GenyAllocDriver;
|
@@ -1,44 +1,51 @@
|
|
1
|
-
// @ts-nocheck
|
2
1
|
const DetoxRuntimeError = require('../../../../../errors/DetoxRuntimeError');
|
2
|
+
const logger = require('../../../../../utils/logger').child({ cat: 'device' });
|
3
3
|
const retry = require('../../../../../utils/retry');
|
4
|
-
const DeviceLauncher = require('../../../../common/drivers/DeviceLauncher');
|
5
4
|
|
6
|
-
|
7
|
-
constructor({ instanceLifecycleService, instanceLookupService, deviceCleanupRegistry, eventEmitter }) {
|
8
|
-
super(eventEmitter);
|
5
|
+
const GenyInstance = require('./services/dto/GenyInstance');
|
9
6
|
|
7
|
+
const events = {
|
8
|
+
CREATE_DEVICE: { event: 'CREATE_DEVICE' },
|
9
|
+
};
|
10
|
+
|
11
|
+
class GenyInstanceLauncher {
|
12
|
+
constructor({ genyCloudExec, instanceLifecycleService }) {
|
13
|
+
this._genyCloudExec = genyCloudExec;
|
10
14
|
this._instanceLifecycleService = instanceLifecycleService;
|
11
|
-
this._instanceLookupService = instanceLookupService;
|
12
|
-
this._deviceCleanupRegistry = deviceCleanupRegistry;
|
13
15
|
}
|
14
16
|
|
15
17
|
/**
|
16
|
-
*
|
17
|
-
*
|
18
|
-
* not launched per-se, as with local emulators. Rather, we just need to sync-up with
|
19
|
-
* them and connect, if needed.
|
20
|
-
*
|
21
|
-
* @param instance {GenyInstance} The freshly allocated cloud-instance.
|
22
|
-
* @param isNew { boolean }
|
18
|
+
* @param {import('./services/dto/GenyRecipe')} recipe
|
19
|
+
* @param {string} instanceName
|
23
20
|
* @returns {Promise<GenyInstance>}
|
24
21
|
*/
|
25
|
-
async launch(
|
26
|
-
|
27
|
-
|
28
|
-
}
|
29
|
-
instance
|
30
|
-
|
31
|
-
await this._notifyBootEvent(instance.adbName, instance.recipeName, isNew);
|
22
|
+
async launch(recipe, instanceName) {
|
23
|
+
logger.debug(events.CREATE_DEVICE, `Trying to create a device based on "${recipe}"`);
|
24
|
+
const instance = await this._instanceLifecycleService.createInstance(recipe.uuid, instanceName);
|
25
|
+
const { name, uuid } = instance;
|
26
|
+
logger.info(events.CREATE_DEVICE, `Allocating Genymotion Cloud instance ${name} for testing. To access it via a browser, go to: https://cloud.geny.io/instance/${uuid}`);
|
27
|
+
|
32
28
|
return instance;
|
33
29
|
}
|
34
30
|
|
31
|
+
/**
|
32
|
+
* @param {GenyInstance} instance The freshly allocated cloud-instance.
|
33
|
+
* @returns {Promise<GenyInstance>}
|
34
|
+
*/
|
35
|
+
async connect(instance) {
|
36
|
+
const bootedInstance = await this._waitForInstanceBoot(instance);
|
37
|
+
const connectedInstance = await this._adbConnectIfNeeded(bootedInstance);
|
38
|
+
|
39
|
+
return connectedInstance;
|
40
|
+
}
|
41
|
+
|
42
|
+
/**
|
43
|
+
* @param {import('./services/dto/GenyInstance')} instance The freshly allocated cloud-instance.
|
44
|
+
*/
|
35
45
|
async shutdown(instance) {
|
36
46
|
const { uuid } = instance;
|
37
47
|
|
38
|
-
await this._notifyPreShutdown(uuid);
|
39
48
|
await this._instanceLifecycleService.deleteInstance(uuid);
|
40
|
-
await this._deviceCleanupRegistry.disposeDevice(uuid);
|
41
|
-
await this._notifyShutdownCompleted(uuid);
|
42
49
|
}
|
43
50
|
|
44
51
|
async _waitForInstanceBoot(instance) {
|
@@ -48,17 +55,21 @@ class GenyInstanceLauncher extends DeviceLauncher {
|
|
48
55
|
|
49
56
|
const options = {
|
50
57
|
backoff: 'none',
|
51
|
-
retries:
|
58
|
+
retries: 20,
|
52
59
|
interval: 5000,
|
53
60
|
initialSleep: 45000,
|
61
|
+
shouldUnref: true,
|
54
62
|
};
|
55
63
|
|
56
64
|
return await retry(options, async () => {
|
57
|
-
const _instance = await this.
|
58
|
-
|
65
|
+
const { instance: _instance } = await this._genyCloudExec.getInstance(instance.uuid);
|
66
|
+
const anInstance = new GenyInstance(_instance);
|
67
|
+
|
68
|
+
if (!anInstance.isOnline()) {
|
59
69
|
throw new DetoxRuntimeError(`Timeout waiting for instance ${instance.uuid} to be ready`);
|
60
70
|
}
|
61
|
-
|
71
|
+
|
72
|
+
return anInstance;
|
62
73
|
});
|
63
74
|
}
|
64
75
|
|