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.
Files changed (110) hide show
  1. 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
  2. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-javadoc.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-javadoc.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-javadoc.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-javadoc.jar.sha512 +1 -0
  6. 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
  7. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-sources.jar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-sources.jar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-sources.jar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-sources.jar.sha512 +1 -0
  11. 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
  12. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0.pom.md5 +1 -0
  13. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0.pom.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0.pom.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0.pom.sha512 +1 -0
  16. package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
  17. package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
  18. package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
  19. package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
  20. package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
  21. package/Detox-ios-src.tbz +0 -0
  22. package/Detox-ios.tbz +0 -0
  23. package/local-cli/reset-lock-file.js +5 -9
  24. package/package.json +6 -6
  25. package/src/DetoxWorker.js +5 -9
  26. package/src/artifacts/providers/index.js +3 -3
  27. package/src/artifacts/screenshot/SimulatorScreenshotPlugin.js +0 -17
  28. package/src/devices/allocation/DeviceAllocator.js +50 -15
  29. package/src/devices/allocation/DeviceList.js +44 -0
  30. package/src/devices/allocation/DeviceRegistry.js +186 -0
  31. package/src/devices/allocation/drivers/AllocationDriverBase.js +10 -4
  32. package/src/devices/{common/drivers/android/tools → allocation/drivers/android}/FreeDeviceFinder.js +11 -10
  33. package/src/devices/allocation/drivers/android/attached/AttachedAndroidAllocDriver.js +7 -6
  34. package/src/devices/allocation/drivers/android/emulator/EmulatorAllocDriver.js +108 -26
  35. package/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.js +33 -43
  36. package/src/devices/allocation/drivers/android/emulator/FreeEmulatorFinder.js +1 -1
  37. package/src/devices/allocation/drivers/android/emulator/FreePortFinder.js +16 -0
  38. package/src/devices/allocation/drivers/android/emulator/launchEmulatorProcess.js +3 -3
  39. package/src/devices/allocation/drivers/android/genycloud/GenyAllocDriver.js +87 -27
  40. package/src/devices/allocation/drivers/android/genycloud/GenyInstanceLauncher.js +39 -28
  41. package/src/devices/allocation/drivers/android/genycloud/GenyRegistry.js +93 -0
  42. package/src/devices/allocation/drivers/android/genycloud/services/GenyInstanceLifecycleService.js +24 -0
  43. package/src/devices/{common → allocation}/drivers/android/genycloud/services/GenyRecipesService.js +1 -1
  44. package/src/devices/{common → allocation}/drivers/android/genycloud/services/dto/GenyInstance.js +6 -1
  45. package/src/devices/allocation/drivers/ios/SimulatorAllocDriver.js +70 -40
  46. package/src/devices/allocation/drivers/ios/SimulatorLauncher.js +11 -7
  47. package/src/devices/allocation/drivers/ios/SimulatorQuery.js +24 -0
  48. package/src/devices/allocation/factories/android.js +29 -35
  49. package/src/devices/allocation/factories/ios.js +7 -5
  50. package/src/devices/common/drivers/android/emulator/exec/EmulatorExec.js +17 -5
  51. package/src/devices/common/drivers/android/exec/ADB.js +1 -0
  52. package/src/devices/common/drivers/ios/tools/AppleSimUtils.js +1 -1
  53. package/src/devices/cookies/AndroidDeviceCookie.js +4 -0
  54. package/src/devices/cookies/GenycloudEmulatorCookie.js +3 -5
  55. package/src/devices/cookies/IosSimulatorCookie.js +4 -0
  56. package/src/devices/runtime/drivers/android/genycloud/GenyCloudDriver.js +2 -2
  57. package/src/devices/runtime/factories/android.js +1 -1
  58. package/src/devices/runtime/factories/ios.js +3 -2
  59. package/src/{servicelocator → devices/servicelocator}/android/emulatorServiceLocator.js +1 -1
  60. package/src/devices/servicelocator/android/genycloudServiceLocator.js +17 -0
  61. package/src/devices/servicelocator/android/index.js +23 -0
  62. package/src/{validation → devices/validation}/android/GenycloudEnvValidator.js +2 -2
  63. package/src/{validation → devices/validation}/factories/index.js +1 -1
  64. package/src/{validation → devices/validation}/ios/IosSimulatorEnvValidator.js +2 -2
  65. package/src/environmentFactory.js +1 -11
  66. package/src/ipc/IPCClient.js +17 -1
  67. package/src/ipc/IPCServer.js +25 -1
  68. package/src/realms/DetoxContext.js +6 -0
  69. package/src/realms/DetoxPrimaryContext.js +42 -42
  70. package/src/realms/DetoxSecondaryContext.js +19 -0
  71. package/src/realms/symbols.js +4 -0
  72. package/src/utils/PIDService.js +27 -0
  73. package/src/utils/environment.js +8 -15
  74. package/src/utils/errorUtils.js +2 -2
  75. package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4-javadoc.jar.md5 +0 -1
  76. package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4-javadoc.jar.sha1 +0 -1
  77. package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4-javadoc.jar.sha256 +0 -1
  78. package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4-javadoc.jar.sha512 +0 -1
  79. package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4-sources.jar.md5 +0 -1
  80. package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4-sources.jar.sha1 +0 -1
  81. package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4-sources.jar.sha256 +0 -1
  82. package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4-sources.jar.sha512 +0 -1
  83. package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4.pom.md5 +0 -1
  84. package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4.pom.sha1 +0 -1
  85. package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4.pom.sha256 +0 -1
  86. package/Detox-android/com/wix/detox/20.11.4/detox-20.11.4.pom.sha512 +0 -1
  87. package/src/devices/DeviceRegistry.js +0 -176
  88. package/src/devices/allocation/drivers/android/attached/AttachedAndroidLauncher.js +0 -13
  89. package/src/devices/allocation/drivers/android/emulator/EmulatorAllocationHelper.js +0 -72
  90. package/src/devices/allocation/drivers/android/genycloud/GenyDeviceRegistryFactory.js +0 -16
  91. package/src/devices/allocation/drivers/android/genycloud/GenyInstanceAllocationHelper.js +0 -65
  92. package/src/devices/common/drivers/DeviceAllocationHelper.js +0 -20
  93. package/src/devices/common/drivers/DeviceLauncher.js +0 -19
  94. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceLifecycleService.js +0 -25
  95. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceLookupService.js +0 -38
  96. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceNaming.js +0 -14
  97. package/src/devices/lifecycle/GenyGlobalLifecycleHandler.js +0 -71
  98. package/src/devices/lifecycle/factories/GenyGlobalLifecycleHandlerFactory.js +0 -18
  99. package/src/servicelocator/android/genycloudServiceLocator.js +0 -21
  100. package/src/servicelocator/android/index.js +0 -25
  101. package/src/servicelocator/ios.js +0 -7
  102. /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
  103. /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
  104. /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
  105. /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
  106. /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
  107. /package/src/devices/{common → allocation}/drivers/android/genycloud/exec/GenyCloudExec.js +0 -0
  108. /package/src/devices/{common → allocation}/drivers/android/genycloud/services/GenyAuthService.js +0 -0
  109. /package/src/devices/{common → allocation}/drivers/android/genycloud/services/dto/GenyRecipe.js +0 -0
  110. /package/src/{validation → devices/validation}/EnvironmentValidatorBase.js +0 -0
@@ -0,0 +1 @@
1
+ 9e1ed6595db1eb79fd10b72e768b9561
@@ -0,0 +1 @@
1
+ d94beb57b34924c9ff07315304976d12682bcb64
@@ -0,0 +1 @@
1
+ 282729592fa58c2559eaeae9eb7524552ef025e306c20b6619b2ab79e0187f08
@@ -0,0 +1 @@
1
+ bea0140ed5de7a5b9143c3cfbe6a65e5a11d5d7fa4699c0ed81f1d50a4978a72b3cd60a2908d88be78a908260bdb081258edcfd5ee34a5ec3a72a73d1fae7161
@@ -0,0 +1 @@
1
+ 2cd5617bb26a597c18bd4a2fcc3e47a5
@@ -0,0 +1 @@
1
+ e3addaba91ad374e5e74a90b848ce1691e089f9c
@@ -0,0 +1 @@
1
+ ea34470f9caff1392d20c486c997cbcee75f7aad8dfa4cf9268d6984ae620d3c
@@ -0,0 +1 @@
1
+ f66e64dc2055ed7bb0ef4ce4434308e607f7f5cfa27bc5b447b94a355aacb731664e1ab8a86816dc64f5b8a949ab7bd76753413a84aa9ad610a11cc2d0c91534
@@ -3,7 +3,7 @@
3
3
  <modelVersion>4.0.0</modelVersion>
4
4
  <groupId>com.wix</groupId>
5
5
  <artifactId>detox</artifactId>
6
- <version>20.11.4</version>
6
+ <version>20.12.0-smoke.0</version>
7
7
  <packaging>aar</packaging>
8
8
  <name>Detox</name>
9
9
  <description>Gray box end-to-end testing and automation library for mobile apps</description>
@@ -0,0 +1 @@
1
+ b657a85f93b0087e4bd3d2ef359cdcca
@@ -0,0 +1 @@
1
+ abbf983b4b77e1b748513273b9fb853feea6cfc9
@@ -0,0 +1 @@
1
+ 8532ca9a4f54fcbdedcea406b715c7d7150d031e9478996ab505e453dca5d9bc
@@ -0,0 +1 @@
1
+ 20dc62ea32120332c031cc66c5b90e9917c7bd5ebe0bfff2c30014a05b7e613572fdd6a45b08260fb3b5f8feafc916034c6015a8a3627e67db6205b57cd4697f
@@ -3,11 +3,11 @@
3
3
  <groupId>com.wix</groupId>
4
4
  <artifactId>detox</artifactId>
5
5
  <versioning>
6
- <latest>20.11.4</latest>
7
- <release>20.11.4</release>
6
+ <latest>20.12.0-smoke.0</latest>
7
+ <release>20.12.0-smoke.0</release>
8
8
  <versions>
9
- <version>20.11.4</version>
9
+ <version>20.12.0-smoke.0</version>
10
10
  </versions>
11
- <lastUpdated>20230824160037</lastUpdated>
11
+ <lastUpdated>20230905081618</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- 1fae4b4769a24aabf21136e0157420ca
1
+ c0ebd655b45d26da7f4503b42682e6ac
@@ -1 +1 @@
1
- b19a09221ce9a7e60f6cd29822839a8f24729b21
1
+ b28e7e113a3adf4f4ac956666bef4767712332c5
@@ -1 +1 @@
1
- 65c7ae9291289f20b2cfcf022b0f72553d062eb4b5cc4a751c821b5f272a6392
1
+ 428cdbf9560b42fa11a47e4ad09d61f067e46c0222b046489e8cfdb914d43d3e
@@ -1 +1 @@
1
- 1cde86cbf9a88db4bdb189f901a27e2d8fb495b689ee17d3a81492ead7f7da92b9a679c379010dfcb97c5bc14f658a7b38d5cca616bc041c78ee78fb29b85448
1
+ c7042d22a9567df5b06587e91169041fb202a4881b61f76a2b8a3db7b31c571cd8a1b51e4a44fe54ac80499bc249e0b195f67854408cfa47f2388c2c2d0f76ca
package/Detox-ios-src.tbz CHANGED
Binary file
package/Detox-ios.tbz CHANGED
Binary file
@@ -1,16 +1,12 @@
1
1
  const { log } = require('../internals');
2
- const DeviceRegistry = require('../src/devices/DeviceRegistry');
3
- const { getDetoxLibraryRootPath } = require('../src/utils/environment');
4
-
2
+ const DeviceRegistry = require('../src/devices/allocation/DeviceRegistry');
5
3
 
6
4
  module.exports.command = 'reset-lock-file';
7
- module.exports.desc = 'Resets all Detox lock files. Useful when you need to run multiple `detox test` commands in parallel with --keepLockFile.';
5
+ module.exports.desc = 'Resets all Detox lock files. Useful when you need to clean up leftover locks from a crashed Detox instance.';
8
6
 
9
7
  module.exports.handler = async function resetLockFile() {
10
- await Promise.all([
11
- DeviceRegistry.forIOS().reset(),
12
- DeviceRegistry.forAndroid().reset(),
13
- ]);
8
+ const registry = new DeviceRegistry();
9
+ await registry.reset();
14
10
 
15
- log.info(`Cleaned lock files from: ${getDetoxLibraryRootPath()}`);
11
+ log.info(`Cleaned lock file at: ${registry.lockFilePath}`);
16
12
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "detox",
3
3
  "description": "E2E tests and automation for mobile",
4
- "version": "20.11.4",
4
+ "version": "20.12.0-smoke.0",
5
5
  "bin": {
6
6
  "detox": "local-cli/cli.js"
7
7
  },
@@ -196,15 +196,15 @@
196
196
  ],
197
197
  "coverageThreshold": {
198
198
  "global": {
199
- "statements": 100,
200
- "branches": 100,
201
- "functions": 100,
202
- "lines": 100
199
+ "statements": 50,
200
+ "branches": 50,
201
+ "functions": 50,
202
+ "lines": 50
203
203
  }
204
204
  }
205
205
  },
206
206
  "browserslist": [
207
207
  "node 14"
208
208
  ],
209
- "gitHead": "1b48840c8d68721ee3968ccaa2783f9f2cfd3339"
209
+ "gitHead": "6a0b45ddbd1eaae9158e6e27f7d6b85b1411f553"
210
210
  }
@@ -33,7 +33,6 @@ class DetoxWorker {
33
33
  onError: this._onEmitError.bind(this),
34
34
  });
35
35
 
36
-
37
36
  /** @type {DetoxInternals.RuntimeConfig['apps']} */
38
37
  this._appsConfig = null;
39
38
  /** @type {DetoxInternals.RuntimeConfig['artifacts']} */
@@ -115,14 +114,13 @@ class DetoxWorker {
115
114
  const {
116
115
  // @ts-ignore
117
116
  envValidatorFactory,
118
- deviceAllocatorFactory,
119
117
  // @ts-ignore
120
118
  artifactsManagerFactory,
121
119
  // @ts-ignore
122
120
  matchersFactory,
123
121
  // @ts-ignore
124
122
  runtimeDeviceFactory,
125
- } = environmentFactory.createFactories(this._deviceConfig);
123
+ } = environmentFactory.createFactories(deviceConfig);
126
124
 
127
125
  const envValidator = envValidatorFactory.createValidator();
128
126
  yield envValidator.validate();
@@ -135,10 +133,7 @@ class DetoxWorker {
135
133
  };
136
134
 
137
135
  this._artifactsManager = artifactsManagerFactory.createArtifactsManager(this._artifactsConfig, commonDeps);
138
- this._deviceAllocator = deviceAllocatorFactory.createDeviceAllocator(commonDeps);
139
- this._deviceCookie = yield this._deviceAllocator.allocate(this._deviceConfig);
140
-
141
- yield this._deviceAllocator.postAllocate(this._deviceCookie);
136
+ this._deviceCookie = yield this._context[symbols.allocateDevice]();
142
137
 
143
138
  this.device = runtimeDeviceFactory.createRuntimeDevice(
144
139
  this._deviceCookie,
@@ -157,6 +152,8 @@ class DetoxWorker {
157
152
  });
158
153
  Object.assign(this, matchers);
159
154
 
155
+ yield this._eventEmitter.emit('bootDevice', { deviceId: this.device.id });
156
+
160
157
  if (behaviorConfig.init.exposeGlobals) {
161
158
  const injectedGlobals = {
162
159
  ...matchers,
@@ -207,8 +204,7 @@ class DetoxWorker {
207
204
  }
208
205
 
209
206
  if (this._deviceCookie) {
210
- const shutdown = this._behaviorConfig ? this._behaviorConfig.cleanup.shutdownDevice : false;
211
- await this._deviceAllocator.free(this._deviceCookie, { shutdown });
207
+ await this._context[symbols.deallocateDevice](this._deviceCookie);
212
208
  }
213
209
 
214
210
  this._deviceAllocator = null;
@@ -4,7 +4,7 @@ class ArtifactPluginsProvider {
4
4
 
5
5
  class AndroidArtifactPluginsProvider extends ArtifactPluginsProvider {
6
6
  declareArtifactPlugins({ client }) {
7
- const serviceLocator = require('../../servicelocator/android');
7
+ const serviceLocator = require('../../devices/servicelocator/android');
8
8
  const adb = serviceLocator.adb;
9
9
  const devicePathBuilder = serviceLocator.devicePathBuilder;
10
10
 
@@ -34,8 +34,8 @@ class IosArtifactPluginsProvider extends ArtifactPluginsProvider {
34
34
 
35
35
  class IosSimulatorArtifactPluginsProvider extends IosArtifactPluginsProvider {
36
36
  declareArtifactPlugins({ client }) {
37
- const serviceLocator = require('../../servicelocator/ios');
38
- const appleSimUtils = serviceLocator.appleSimUtils;
37
+ const AppleSimUtils = require('../../devices/common/drivers/ios/tools/AppleSimUtils');
38
+ const appleSimUtils = new AppleSimUtils();
39
39
 
40
40
  const SimulatorInstrumentsPlugin = require('../instruments/ios/SimulatorInstrumentsPlugin');
41
41
  const SimulatorLogPlugin = require('../log/ios/SimulatorLogPlugin');
@@ -22,23 +22,6 @@ class SimulatorScreenshotPlugin extends ScreenshotArtifactPlugin {
22
22
  }
23
23
  }
24
24
 
25
- async onBootDevice(event) {
26
- await super.onBootDevice(event);
27
-
28
- if (this.enabled && event.coldBoot) {
29
- await this.appleSimUtils.takeScreenshot(event.deviceId, '/dev/null').catch(() => {
30
- log.debug({}, `
31
- NOTE: For an unknown yet reason, taking the first screenshot is apt
32
- to fail when booting iOS Simulator in a hidden window mode (or on CI).
33
- Detox applies a workaround by taking a dummy screenshot to ensure
34
- that the future ones are going to work fine. This screenshot is not
35
- saved anywhere, and the error above is suppressed for all log levels
36
- except for "debug" and "trace."
37
- `.trim());
38
- });
39
- }
40
- }
41
-
42
25
  async onBeforeUninstallApp(event) {
43
26
  await this.api.requestIdleCallback(async () => {
44
27
  const snapshots = [
@@ -1,5 +1,5 @@
1
1
  // @ts-nocheck
2
- const log = require('../../utils/logger').child({ cat: 'device' });
2
+ const log = require('../../utils/logger').child({ cat: 'device,device-allocation' });
3
3
  const traceMethods = require('../../utils/traceMethods');
4
4
 
5
5
  class DeviceAllocator {
@@ -8,36 +8,71 @@ class DeviceAllocator {
8
8
  */
9
9
  constructor(allocationDriver) {
10
10
  this._driver = allocationDriver;
11
- traceMethods(log, this, ['allocate', 'postAllocate', 'free']);
11
+ traceMethods(log, this, ['init', 'cleanup', 'emergencyCleanup']);
12
+ }
13
+
14
+ /**
15
+ * @returns {Promise<void>}
16
+ */
17
+ async init() {
18
+ if (typeof this._driver.init === 'function') {
19
+ await this._driver.init();
20
+ }
12
21
  }
13
22
 
14
23
  /**
15
24
  * @param deviceConfig { Object }
16
- * @return {Promise<DeviceCookie>}
25
+ * @returns {Promise<DeviceCookie>}
17
26
  */
18
- allocate(deviceConfig) {
19
- return this._driver.allocate(deviceConfig);
27
+ async allocate(deviceConfig) {
28
+ return await log.trace.complete({ data: deviceConfig, id: Math.random() }, 'allocate', async () => {
29
+ const cookie = await this._driver.allocate(deviceConfig);
30
+ log.debug({ event: undefined }, `settled on ${cookie}`);
31
+ return cookie;
32
+ });
20
33
  }
21
34
 
22
35
  /**
23
36
  * @param {DeviceCookie} deviceCookie
24
- * @return {Promise<unknown>}
37
+ * @returns {Promise<DeviceCookie>}
25
38
  */
26
- postAllocate(deviceCookie) {
27
- if (typeof this._driver.postAllocate !== 'function') {
28
- return Promise.resolve();
29
- }
39
+ async postAllocate(deviceCookie) {
40
+ return await log.trace.complete({ data: deviceCookie, id: Math.random() }, `post-allocate: ${deviceCookie}`, async () => {
41
+ const updatedCookie = typeof this._driver.postAllocate === 'function'
42
+ ? await this._driver.postAllocate(deviceCookie)
43
+ : undefined;
30
44
 
31
- return this._driver.postAllocate(deviceCookie);
45
+ return updatedCookie || deviceCookie;
46
+ });
32
47
  }
33
48
 
34
49
  /**
35
50
  * @param cookie { DeviceCookie }
36
- * @param options { DeallocOptions }
37
- * @return {Promise<void>}
51
+ * @param options { Partial<DeallocOptions> }
52
+ * @returns {Promise<void>}
53
+ */
54
+ async free(cookie, options = {}) {
55
+ await log.trace.complete({ data: options, id: Math.random() }, `free: ${cookie}`, async () => {
56
+ await this._driver.free(cookie, options);
57
+ });
58
+ }
59
+
60
+ /**
61
+ * @returns {Promise<void>}
62
+ */
63
+ async cleanup() {
64
+ if (typeof this._driver.cleanup === 'function') {
65
+ await this._driver.cleanup();
66
+ }
67
+ }
68
+
69
+ /**
70
+ * @returns {void}
38
71
  */
39
- free(cookie, options) {
40
- return this._driver.free(cookie, options);
72
+ emergencyCleanup() {
73
+ if (typeof this._driver.emergencyCleanup === 'function') {
74
+ this._driver.emergencyCleanup();
75
+ }
41
76
  }
42
77
  }
43
78
 
@@ -0,0 +1,44 @@
1
+ class DeviceListReadonly {
2
+ constructor(devices = []) {
3
+ this._devices = new Map(devices.map(device => [device.id, device]));
4
+ }
5
+
6
+ concat(other) {
7
+ return new DeviceListReadonly([...this, ...other]);
8
+ }
9
+
10
+ getIds() {
11
+ return [...this._devices.keys()];
12
+ }
13
+
14
+ [Symbol.iterator]() {
15
+ return this._devices.values();
16
+ }
17
+
18
+ /**
19
+ * @param {string} deviceId
20
+ * @returns {boolean}
21
+ */
22
+ includes(deviceId) {
23
+ return this._devices.has(deviceId);
24
+ }
25
+ }
26
+
27
+ class DeviceList extends DeviceListReadonly {
28
+ filter(predicate) {
29
+ return new DeviceListReadonly([...this].filter(predicate));
30
+ }
31
+
32
+ add(deviceId, data) {
33
+ this._devices.set(deviceId, {
34
+ id: deviceId,
35
+ ...data,
36
+ });
37
+ }
38
+
39
+ delete(deviceId) {
40
+ this._devices.delete(deviceId);
41
+ }
42
+ }
43
+
44
+ module.exports = DeviceList;
@@ -0,0 +1,186 @@
1
+ const ExclusiveLockfile = require('../../utils/ExclusiveLockfile');
2
+ const PIDService = require('../../utils/PIDService');
3
+ const { getDeviceRegistryPath } = require('../../utils/environment');
4
+ const safeAsync = require('../../utils/safeAsync');
5
+
6
+ const DeviceList = require('./DeviceList');
7
+
8
+ const readOptions = {
9
+ encoding: 'utf8',
10
+ };
11
+
12
+ class DeviceRegistry {
13
+ constructor({
14
+ lockfilePath = getDeviceRegistryPath(),
15
+ sessionId = '',
16
+ pidService = new PIDService(),
17
+ } = {}) {
18
+ /***
19
+ * @private
20
+ * @type {string}
21
+ */
22
+ this._lockfilePath = lockfilePath;
23
+ /***
24
+ * @private
25
+ * @type {string}
26
+ */
27
+ this._sessionId = sessionId;
28
+ /***
29
+ * @private
30
+ */
31
+ this._pidService = pidService;
32
+ /***
33
+ * @protected
34
+ * @type {ExclusiveLockfile}
35
+ */
36
+ this._lockfile = new ExclusiveLockfile(this._lockfilePath, {
37
+ getInitialState: this._getInitialLockFileState.bind(this),
38
+ readOptions,
39
+ });
40
+ }
41
+
42
+ get lockFilePath() {
43
+ return this._lockfilePath;
44
+ }
45
+
46
+ /**
47
+ * Safety method to ensure that there are no remains of previously crashed Detox sessions.
48
+ */
49
+ async reset() {
50
+ await this._lockfile.exclusively(() => {
51
+ const empty = this._getInitialLockFileState();
52
+ this._lockfile.write(empty);
53
+ });
54
+ }
55
+
56
+ /***
57
+ * @param {string|Function} getDeviceId
58
+ * @returns {Promise<string>}
59
+ */
60
+ async registerDevice(getDeviceId) {
61
+ return this._lockfile.exclusively(async () => {
62
+ const deviceId = await safeAsync(getDeviceId);
63
+ if (deviceId) {
64
+ this._upsertDevice(deviceId, {
65
+ busy: true,
66
+ sessionId: this._sessionId,
67
+ pid: this._pidService.getPid(),
68
+ });
69
+ }
70
+ return deviceId;
71
+ });
72
+ }
73
+
74
+ /***
75
+ * @param {string|Function} getDeviceId
76
+ * @returns {Promise<string>}
77
+ */
78
+ async releaseDevice(getDeviceId) {
79
+ return this._lockfile.exclusively(async () => {
80
+ const deviceId = await safeAsync(getDeviceId);
81
+ if (deviceId) {
82
+ this._upsertDevice(deviceId, {
83
+ busy: false,
84
+ sessionId: this._sessionId,
85
+ pid: this._pidService.getPid(),
86
+ });
87
+ }
88
+ return deviceId;
89
+ });
90
+ }
91
+
92
+ /***
93
+ * @param {string|Function} getDeviceId
94
+ * @returns {Promise<void>}
95
+ */
96
+ async unregisterDevice(getDeviceId) {
97
+ await this._lockfile.exclusively(async () => {
98
+ const deviceId = await safeAsync(getDeviceId);
99
+ if (deviceId) {
100
+ this._deleteDevice(deviceId);
101
+ }
102
+ });
103
+ }
104
+
105
+ async unregisterSessionDevices() {
106
+ await this._lockfile.exclusively(async () => {
107
+ const allDevices = this._getRegisteredDevices();
108
+ const sessionDevices = allDevices.filter(device => device.sessionId === this._sessionId);
109
+ for (const id of sessionDevices.getIds()) {
110
+ allDevices.delete(id);
111
+ }
112
+ this._lockfile.write([...allDevices]);
113
+ });
114
+ }
115
+
116
+ async unregisterZombieDevices() {
117
+ await this._lockfile.exclusively(async () => {
118
+ const allDevices = this._getRegisteredDevices();
119
+ const zombieDevices = allDevices.filter(device => {
120
+ return device.sessionId !== this._sessionId && !this._pidService.isAlive(device.pid);
121
+ });
122
+
123
+ for (const id of zombieDevices.getIds()) {
124
+ allDevices.delete(id);
125
+ }
126
+ this._lockfile.write([...allDevices]);
127
+ });
128
+ }
129
+
130
+ async readSessionDevices() {
131
+ let devices;
132
+ await this._lockfile.exclusively(() => {
133
+ devices = this._getSessionDevicesSync();
134
+ });
135
+ return devices;
136
+ }
137
+
138
+ getTakenDevicesSync() {
139
+ const allDevices = this._getRegisteredDevices();
140
+ const busyDevices = allDevices.filter(device => device.busy);
141
+ const externalDevices = allDevices.filter(device => device.sessionId !== this._sessionId);
142
+ return busyDevices.concat(externalDevices);
143
+ }
144
+
145
+ /***
146
+ * @private
147
+ */
148
+ _getInitialLockFileState() {
149
+ return [];
150
+ }
151
+
152
+ /**
153
+ * @private
154
+ */
155
+ _upsertDevice(deviceId, data) {
156
+ const devices = this._getRegisteredDevices();
157
+ devices.add(deviceId, data);
158
+ this._lockfile.write([...devices]);
159
+ }
160
+
161
+ /**
162
+ * @private
163
+ */
164
+ _deleteDevice(deviceId) {
165
+ const devices = this._getRegisteredDevices();
166
+ devices.delete(deviceId);
167
+ this._lockfile.write([...devices]);
168
+ }
169
+
170
+ /***
171
+ * @private
172
+ * @returns {DeviceList}
173
+ */
174
+ _getRegisteredDevices() {
175
+ const devices = this._lockfile.read();
176
+ return new DeviceList(devices);
177
+ }
178
+
179
+ _getSessionDevicesSync() {
180
+ const devices = this._getRegisteredDevices();
181
+ const sessionDevices = devices.filter(device => device.sessionId === this._sessionId);
182
+ return sessionDevices;
183
+ }
184
+ }
185
+
186
+ module.exports = DeviceRegistry;
@@ -7,24 +7,30 @@
7
7
  */
8
8
 
9
9
  class AllocationDriverBase {
10
+ async init() {}
11
+
10
12
  /**
11
13
  * @param deviceConfig { Object }
12
- * @return {Promise<DeviceCookie>}
14
+ * @return {Promise<import('../../cookies/DeviceCookie')>}
13
15
  */
14
16
  async allocate(deviceConfig) {}
15
17
 
16
18
  /**
17
- * @param {DeviceCookie} deviceCookie
18
- * @return {Promise<void>}
19
+ * @param {import('../../cookies/DeviceCookie')} deviceCookie
20
+ * @return {Promise<import('../../cookies/DeviceCookie') | void>}
19
21
  */
20
22
  async postAllocate(deviceCookie) {}
21
23
 
22
24
  /**
23
- * @param cookie { DeviceCookie }
25
+ * @param cookie { import('../../cookies/DeviceCookie') }
24
26
  * @param options { DeallocOptions }
25
27
  * @return {Promise<void>}
26
28
  */
27
29
  async free(cookie, options) {}
30
+
31
+ async cleanup() {}
32
+
33
+ emergencyCleanup() {}
28
34
  }
29
35
 
30
36
  module.exports = AllocationDriverBase;
@@ -1,6 +1,6 @@
1
- const log = require('../../../../../utils/logger').child({ cat: 'device' });
1
+ const log = require('../../../../utils/logger').child({ cat: 'device' });
2
2
 
3
- const DEVICE_LOOKUP_LOG_EVT = 'DEVICE_LOOKUP';
3
+ const DEVICE_LOOKUP = { event: 'DEVICE_LOOKUP' };
4
4
 
5
5
  class FreeDeviceFinder {
6
6
  constructor(adb, deviceRegistry) {
@@ -10,8 +10,9 @@ class FreeDeviceFinder {
10
10
 
11
11
  async findFreeDevice(deviceQuery) {
12
12
  const { devices } = await this.adb.devices();
13
+ const takenDevices = this.deviceRegistry.getTakenDevicesSync();
13
14
  for (const candidate of devices) {
14
- if (await this._isDeviceFreeAndMatching(candidate, deviceQuery)) {
15
+ if (await this._isDeviceFreeAndMatching(takenDevices, candidate, deviceQuery)) {
15
16
  return candidate.adbName;
16
17
  }
17
18
  }
@@ -19,30 +20,30 @@ class FreeDeviceFinder {
19
20
  }
20
21
 
21
22
  /**
22
- * @protected
23
+ * @private
23
24
  */
24
- async _isDeviceFreeAndMatching(candidate, deviceQuery) {
25
+ async _isDeviceFreeAndMatching(takenDevices, candidate, deviceQuery) {
25
26
  const { adbName } = candidate;
26
27
 
27
- const isTaken = this.deviceRegistry.includes(adbName);
28
+ const isTaken = takenDevices.includes(adbName);
28
29
  if (isTaken) {
29
- log.debug({ event: DEVICE_LOOKUP_LOG_EVT }, `Device ${adbName} is already taken, skipping...`);
30
+ log.debug(DEVICE_LOOKUP, `Device ${adbName} is already taken, skipping...`);
30
31
  return false;
31
32
  }
32
33
 
33
34
  const isOffline = candidate.status === 'offline';
34
35
  if (isOffline) {
35
- log.debug({ event: DEVICE_LOOKUP_LOG_EVT }, `Device ${adbName} is offline, skipping...`);
36
+ log.debug(DEVICE_LOOKUP, `Device ${adbName} is offline, skipping...`);
36
37
  return false;
37
38
  }
38
39
 
39
40
  const isMatching = await this._isDeviceMatching(candidate, deviceQuery);
40
41
  if (!isMatching) {
41
- log.debug({ event: DEVICE_LOOKUP_LOG_EVT }, `Device ${adbName} does not match "${deviceQuery}"`);
42
+ log.debug(DEVICE_LOOKUP, `Device ${adbName} does not match "${deviceQuery}"`);
42
43
  return false;
43
44
  }
44
45
 
45
- log.debug({ event: DEVICE_LOOKUP_LOG_EVT }, `Found a matching & free device ${candidate.adbName}`);
46
+ log.debug(DEVICE_LOOKUP, `Found a matching & free device ${candidate.adbName}`);
46
47
  return true;
47
48
  }
48
49