detox 20.12.0-smoke.0 → 20.12.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. package/Detox-android/com/wix/detox/{20.12.0-smoke.0/detox-20.12.0-smoke.0-javadoc.jar → 20.12.1/detox-20.12.1-javadoc.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1-javadoc.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1-javadoc.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1-javadoc.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1-javadoc.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/{20.12.0-smoke.0/detox-20.12.0-smoke.0-sources.jar → 20.12.1/detox-20.12.1-sources.jar} +0 -0
  7. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1-sources.jar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1-sources.jar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1-sources.jar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1-sources.jar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.aar +0 -0
  12. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.aar.md5 +1 -0
  13. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.aar.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.aar.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.aar.sha512 +1 -0
  16. package/Detox-android/com/wix/detox/{20.12.0-smoke.0/detox-20.12.0-smoke.0.pom → 20.12.1/detox-20.12.1.pom} +1 -1
  17. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.pom.md5 +1 -0
  18. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.pom.sha1 +1 -0
  19. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.pom.sha256 +1 -0
  20. package/Detox-android/com/wix/detox/20.12.1/detox-20.12.1.pom.sha512 +1 -0
  21. package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
  22. package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
  23. package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
  24. package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
  25. package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
  26. package/Detox-ios-src.tbz +0 -0
  27. package/Detox-ios.tbz +0 -0
  28. package/android/detox/build.gradle +1 -1
  29. package/android/detox/src/full/java/com/wix/detox/espresso/DetoxAssertion.java +44 -25
  30. package/android/detox/src/full/java/com/wix/detox/espresso/EspressoDetox.java +6 -7
  31. package/android/detox/src/full/java/com/wix/detox/espresso/action/GetAttributesAction.kt +4 -4
  32. package/android/detox/src/full/java/com/wix/detox/espresso/performer/MultipleViewsActionPerformer.kt +43 -0
  33. package/android/detox/src/full/java/com/wix/detox/espresso/performer/SingleViewActionPerformer.kt +19 -0
  34. package/android/detox/src/full/java/com/wix/detox/espresso/performer/ViewActionPerformer.kt +24 -0
  35. package/android/detox/src/full/java/com/wix/invoke/types/Invocation.java +5 -4
  36. package/android/detox/src/main/java/com/wix/detox/espresso/MultipleViewsAction.kt +4 -0
  37. package/android/detox/src/main/java/com/wix/detox/espresso/UiControllerSpy.kt +0 -1
  38. package/android/detox/src/main/java/com/wix/detox/espresso/ViewActionWithResult.kt +2 -1
  39. package/android/detox/src/testFull/java/com/wix/detox/espresso/action/GetAttributesActionTest.kt +6 -5
  40. package/android/detox/src/testFull/java/com/wix/detox/espresso/performer/ViewActionPerformerSpec.kt +37 -0
  41. package/index.d.ts +47 -3
  42. package/local-cli/reset-lock-file.js +9 -5
  43. package/package.json +6 -6
  44. package/src/DetoxWorker.js +9 -5
  45. package/src/android/core/NativeElement.js +26 -29
  46. package/src/android/espressoapi/DetoxAssertion.js +16 -14
  47. package/src/android/espressoapi/EspressoDetox.js +9 -2
  48. package/src/android/interactions/native.js +2 -3
  49. package/src/artifacts/providers/index.js +3 -3
  50. package/src/artifacts/screenshot/SimulatorScreenshotPlugin.js +17 -0
  51. package/src/devices/DeviceRegistry.js +176 -0
  52. package/src/devices/allocation/DeviceAllocator.js +15 -50
  53. package/src/devices/allocation/drivers/AllocationDriverBase.js +4 -10
  54. package/src/devices/allocation/drivers/android/attached/AttachedAndroidAllocDriver.js +6 -7
  55. package/src/devices/allocation/drivers/android/attached/AttachedAndroidLauncher.js +13 -0
  56. package/src/devices/allocation/drivers/android/emulator/EmulatorAllocDriver.js +26 -108
  57. package/src/devices/allocation/drivers/android/emulator/EmulatorAllocationHelper.js +72 -0
  58. package/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.js +43 -33
  59. package/src/devices/allocation/drivers/android/emulator/FreeEmulatorFinder.js +1 -1
  60. package/src/devices/allocation/drivers/android/emulator/launchEmulatorProcess.js +3 -3
  61. package/src/devices/allocation/drivers/android/genycloud/GenyAllocDriver.js +27 -87
  62. package/src/devices/allocation/drivers/android/genycloud/GenyDeviceRegistryFactory.js +16 -0
  63. package/src/devices/allocation/drivers/android/genycloud/GenyInstanceAllocationHelper.js +65 -0
  64. package/src/devices/allocation/drivers/android/genycloud/GenyInstanceLauncher.js +28 -39
  65. package/src/devices/allocation/drivers/ios/SimulatorAllocDriver.js +40 -70
  66. package/src/devices/allocation/drivers/ios/SimulatorLauncher.js +7 -11
  67. package/src/devices/allocation/factories/android.js +35 -29
  68. package/src/devices/allocation/factories/ios.js +5 -7
  69. package/src/devices/common/drivers/DeviceAllocationHelper.js +20 -0
  70. package/src/devices/common/drivers/DeviceLauncher.js +19 -0
  71. package/src/devices/common/drivers/android/emulator/exec/EmulatorExec.js +5 -17
  72. package/src/devices/common/drivers/android/exec/ADB.js +0 -1
  73. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceLifecycleService.js +25 -0
  74. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceLookupService.js +38 -0
  75. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceNaming.js +14 -0
  76. package/src/devices/{allocation → common}/drivers/android/genycloud/services/GenyRecipesService.js +1 -1
  77. package/src/devices/{allocation → common}/drivers/android/genycloud/services/dto/GenyInstance.js +1 -6
  78. package/src/devices/{allocation/drivers/android → common/drivers/android/tools}/FreeDeviceFinder.js +10 -11
  79. package/src/devices/common/drivers/ios/tools/AppleSimUtils.js +3 -1
  80. package/src/devices/cookies/AndroidDeviceCookie.js +0 -4
  81. package/src/devices/cookies/GenycloudEmulatorCookie.js +5 -3
  82. package/src/devices/cookies/IosSimulatorCookie.js +0 -4
  83. package/src/devices/lifecycle/GenyGlobalLifecycleHandler.js +71 -0
  84. package/src/devices/lifecycle/factories/GenyGlobalLifecycleHandlerFactory.js +18 -0
  85. package/src/devices/runtime/drivers/android/genycloud/GenyCloudDriver.js +2 -2
  86. package/src/devices/runtime/factories/android.js +1 -1
  87. package/src/devices/runtime/factories/ios.js +2 -3
  88. package/src/environmentFactory.js +11 -1
  89. package/src/ipc/IPCClient.js +1 -17
  90. package/src/ipc/IPCServer.js +1 -25
  91. package/src/realms/DetoxContext.js +0 -6
  92. package/src/realms/DetoxPrimaryContext.js +42 -42
  93. package/src/realms/DetoxSecondaryContext.js +0 -19
  94. package/src/realms/symbols.js +0 -4
  95. package/src/{devices/servicelocator → servicelocator}/android/emulatorServiceLocator.js +1 -1
  96. package/src/servicelocator/android/genycloudServiceLocator.js +21 -0
  97. package/src/servicelocator/android/index.js +25 -0
  98. package/src/servicelocator/ios.js +7 -0
  99. package/src/utils/environment.js +15 -8
  100. package/src/utils/errorUtils.js +2 -2
  101. package/src/{devices/validation → validation}/android/GenycloudEnvValidator.js +2 -2
  102. package/src/{devices/validation → validation}/factories/index.js +1 -1
  103. package/src/{devices/validation → validation}/ios/IosSimulatorEnvValidator.js +2 -2
  104. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-javadoc.jar.md5 +0 -1
  105. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-javadoc.jar.sha1 +0 -1
  106. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-javadoc.jar.sha256 +0 -1
  107. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-javadoc.jar.sha512 +0 -1
  108. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-sources.jar.md5 +0 -1
  109. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-sources.jar.sha1 +0 -1
  110. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-sources.jar.sha256 +0 -1
  111. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0-sources.jar.sha512 +0 -1
  112. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0.aar +0 -0
  113. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0.aar.md5 +0 -1
  114. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0.aar.sha1 +0 -1
  115. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0.aar.sha256 +0 -1
  116. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0.aar.sha512 +0 -1
  117. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0.pom.md5 +0 -1
  118. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0.pom.sha1 +0 -1
  119. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0.pom.sha256 +0 -1
  120. package/Detox-android/com/wix/detox/20.12.0-smoke.0/detox-20.12.0-smoke.0.pom.sha512 +0 -1
  121. package/src/devices/allocation/DeviceList.js +0 -44
  122. package/src/devices/allocation/DeviceRegistry.js +0 -186
  123. package/src/devices/allocation/drivers/android/emulator/FreePortFinder.js +0 -16
  124. package/src/devices/allocation/drivers/android/genycloud/GenyRegistry.js +0 -93
  125. package/src/devices/allocation/drivers/android/genycloud/services/GenyInstanceLifecycleService.js +0 -24
  126. package/src/devices/allocation/drivers/ios/SimulatorQuery.js +0 -24
  127. package/src/devices/servicelocator/android/genycloudServiceLocator.js +0 -17
  128. package/src/devices/servicelocator/android/index.js +0 -23
  129. package/src/utils/PIDService.js +0 -27
  130. /package/src/devices/{allocation → common}/drivers/android/genycloud/exec/GenyCloudExec.js +0 -0
  131. /package/src/devices/{allocation → common}/drivers/android/genycloud/services/GenyAuthService.js +0 -0
  132. /package/src/devices/{allocation → common}/drivers/android/genycloud/services/dto/GenyRecipe.js +0 -0
  133. /package/src/{devices/validation → validation}/EnvironmentValidatorBase.js +0 -0
@@ -1,39 +1,25 @@
1
1
  const { DetoxRuntimeError } = require('../../../../../errors');
2
2
  const Timer = require('../../../../../utils/Timer');
3
- const log = require('../../../../../utils/logger').child({ cat: 'device' });
4
3
  const GenycloudEmulatorCookie = require('../../../../cookies/GenycloudEmulatorCookie');
5
4
  const AllocationDriverBase = require('../../AllocationDriverBase');
6
5
 
7
- const GenyRegistry = require('./GenyRegistry');
8
-
9
- const events = {
10
- GENYCLOUD_TEARDOWN: { event: 'GENYCLOUD_TEARDOWN' },
11
- };
12
-
13
6
  class GenyAllocDriver extends AllocationDriverBase {
7
+
14
8
  /**
15
9
  * @param {object} options
16
10
  * @param {import('../../../../common/drivers/android/exec/ADB')} options.adb
17
- * @param {DetoxInternals.SessionState} options.detoxSession
18
- * @param {import('./GenyRegistry')} options.genyRegistry
19
- * @param {import('./GenyInstanceLauncher')} options.instanceLauncher
20
11
  * @param {import('./GenyRecipeQuerying')} options.recipeQuerying
12
+ * @param {import('./GenyInstanceAllocationHelper')} options.allocationHelper
13
+ * @param {import('./GenyInstanceLauncher')} options.instanceLauncher
21
14
  */
22
- constructor({
23
- adb,
24
- detoxSession,
25
- genyRegistry = new GenyRegistry(),
26
- instanceLauncher,
27
- recipeQuerying,
28
- }) {
15
+ constructor({ adb, recipeQuerying, allocationHelper, instanceLauncher }) {
29
16
  super();
30
17
 
31
18
  this._adb = adb;
32
- this._detoxSessionId = detoxSession.id;
33
- this._genyRegistry = genyRegistry;
34
- this._instanceLauncher = instanceLauncher;
35
19
  this._recipeQuerying = recipeQuerying;
36
- this._instanceCounter = 0;
20
+ this._instanceLauncher = instanceLauncher;
21
+ this._instanceAllocationHelper = allocationHelper;
22
+ this._launchInfo = {};
37
23
  }
38
24
 
39
25
  /**
@@ -41,72 +27,45 @@ class GenyAllocDriver extends AllocationDriverBase {
41
27
  * @return {Promise<GenycloudEmulatorCookie>}
42
28
  */
43
29
  async allocate(deviceConfig) {
44
- await new Promise((resolve) => setTimeout(resolve, 10000));
45
30
  const deviceQuery = deviceConfig.device;
46
31
  const recipe = await this._recipeQuerying.getRecipeFromQuery(deviceQuery);
47
32
  this._assertRecipe(deviceQuery, recipe);
48
33
 
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
-
34
+ const { instance, isNew } = await this._instanceAllocationHelper.allocateDevice(recipe);
35
+ this._launchInfo[instance.uuid] = { isNew };
56
36
  return new GenycloudEmulatorCookie(instance);
57
37
  }
58
38
 
59
39
  /**
60
40
  * @param {GenycloudEmulatorCookie} cookie
41
+ * @returns {Promise<void>}
61
42
  */
62
43
  async postAllocate(cookie) {
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);
44
+ const { instance } = cookie;
45
+ const { isNew } = this._launchInfo[instance.uuid];
46
+ const readyInstance = cookie.instance = await this._instanceLauncher.launch(instance, isNew);
47
+
48
+ const { adbName } = readyInstance;
49
+ await Timer.run(20000, 'waiting for device to respond', async () => {
50
+ await this._adb.disableAndroidAnimations(adbName);
51
+ await this._adb.setWiFiToggle(adbName, true);
52
+ await this._adb.apiLevel(adbName);
53
+ });
77
54
  }
78
55
 
79
56
  /**
80
- * @param cookie {GenycloudEmulatorCookie}
81
- * @param options {Partial<import('../../AllocationDriverBase').DeallocOptions>}
57
+ * @param cookie { GenycloudEmulatorCookie }
58
+ * @param options { DeallocOptions }
82
59
  * @return {Promise<void>}
83
60
  */
84
61
  async free(cookie, options = {}) {
85
- if (options.shutdown) {
86
- this._genyRegistry.removeInstance(cookie.instance);
87
- await this._instanceLauncher.shutdown(cookie.instance);
88
- } else {
89
- this._genyRegistry.markAsFree(cookie.instance);
90
- }
91
- }
92
-
93
- async cleanup() {
94
- log.info(events.GENYCLOUD_TEARDOWN, 'Initiating Genymotion SaaS instances teardown...');
62
+ const { instance } = cookie;
95
63
 
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
- }
64
+ await this._instanceAllocationHelper.deallocateDevice(instance.uuid);
106
65
 
107
- emergencyCleanup() {
108
- const instances = this._genyRegistry.getInstances();
109
- this._reportGlobalCleanupSummary(instances);
66
+ if (options.shutdown) {
67
+ await this._instanceLauncher.shutdown(instance);
68
+ }
110
69
  }
111
70
 
112
71
  _assertRecipe(deviceQuery, recipe) {
@@ -117,25 +76,6 @@ class GenyAllocDriver extends AllocationDriverBase {
117
76
  });
118
77
  }
119
78
  }
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
- }
138
79
  }
139
80
 
140
-
141
81
  module.exports = GenyAllocDriver;
@@ -0,0 +1,16 @@
1
+ const environment = require('../../../../../utils/environment');
2
+ const DeviceRegistry = require('../../../../DeviceRegistry');
3
+
4
+ class GenyDeviceRegistryFactory {
5
+ forRuntime() {
6
+ return DeviceRegistry.forAndroid();
7
+ }
8
+
9
+ forGlobalShutdown() {
10
+ return new DeviceRegistry({
11
+ lockfilePath: environment.getGenyCloudGlobalCleanupFilePath(),
12
+ });
13
+ }
14
+ }
15
+
16
+ module.exports = new GenyDeviceRegistryFactory();
@@ -0,0 +1,65 @@
1
+ // @ts-nocheck
2
+ const logger = require('../../../../../utils/logger').child({ cat: 'device' });
3
+ const DeviceAllocationHelper = require('../../../../common/drivers/DeviceAllocationHelper');
4
+
5
+ const { ALLOCATE_DEVICE_LOG_EVT } = DeviceAllocationHelper;
6
+
7
+ class AllocationResult {
8
+ constructor(instance, isNew) {
9
+ this.instance = instance;
10
+ this.isNew = isNew;
11
+ }
12
+ }
13
+
14
+ class GenyInstanceAllocationHelper extends DeviceAllocationHelper {
15
+ constructor({ deviceRegistry, instanceLookupService, instanceLifecycleService }) {
16
+ super(deviceRegistry, logger);
17
+
18
+ this._instanceLookupService = instanceLookupService;
19
+ this._instanceLifecycleService = instanceLifecycleService;
20
+ }
21
+
22
+ /**
23
+ * @param recipe { GenyRecipe }
24
+ * @return { Promise<AllocationResult> }
25
+ */
26
+ async allocateDevice(recipe) {
27
+ this._logAllocationAttempt(recipe);
28
+
29
+ const allocationResult = await this._doSynchronizedAllocation(recipe);
30
+ this._logAllocationResult(recipe, allocationResult.instance);
31
+
32
+ return allocationResult;
33
+ }
34
+
35
+ async deallocateDevice(instanceUUID) {
36
+ await this._deviceRegistry.disposeDevice(instanceUUID);
37
+ }
38
+
39
+ /**
40
+ * @param recipe { GenyRecipe }
41
+ * @return {Promise<{AllocationResult}>}
42
+ * @private
43
+ */
44
+ async _doSynchronizedAllocation(recipe) {
45
+ let instance = null;
46
+ let isNew = false;
47
+
48
+ await this._deviceRegistry.allocateDevice(async () => {
49
+ instance = await this._instanceLookupService.findFreeInstance();
50
+ if (!instance) {
51
+ instance = await this._instanceLifecycleService.createInstance(recipe.uuid);
52
+ isNew = true;
53
+ }
54
+ return instance.uuid;
55
+ });
56
+
57
+ return new AllocationResult(instance, isNew);
58
+ }
59
+
60
+ _logAllocationResult(deviceQuery, deviceHandle) {
61
+ logger.info({ event: ALLOCATE_DEVICE_LOG_EVT }, `Allocating Genymotion-Cloud instance ${deviceHandle.name} for testing. To access it via a browser, go to: https://cloud.geny.io/instance/${deviceHandle.uuid}`);
62
+ }
63
+ }
64
+
65
+ module.exports = GenyInstanceAllocationHelper;
@@ -1,51 +1,44 @@
1
+ // @ts-nocheck
1
2
  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');
4
5
 
5
- const GenyInstance = require('./services/dto/GenyInstance');
6
+ class GenyInstanceLauncher extends DeviceLauncher {
7
+ constructor({ instanceLifecycleService, instanceLookupService, deviceCleanupRegistry, eventEmitter }) {
8
+ super(eventEmitter);
6
9
 
7
- const events = {
8
- CREATE_DEVICE: { event: 'CREATE_DEVICE' },
9
- };
10
-
11
- class GenyInstanceLauncher {
12
- constructor({ genyCloudExec, instanceLifecycleService }) {
13
- this._genyCloudExec = genyCloudExec;
14
10
  this._instanceLifecycleService = instanceLifecycleService;
11
+ this._instanceLookupService = instanceLookupService;
12
+ this._deviceCleanupRegistry = deviceCleanupRegistry;
15
13
  }
16
14
 
17
15
  /**
18
- * @param {import('./services/dto/GenyRecipe')} recipe
19
- * @param {string} instanceName
16
+ * Note:
17
+ * In the context of Genymotion-cloud (as opposed to local emulators), emulators are
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 }
20
23
  * @returns {Promise<GenyInstance>}
21
24
  */
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
-
25
+ async launch(instance, isNew = true) {
26
+ if (isNew) {
27
+ await this._deviceCleanupRegistry.allocateDevice(instance.uuid, { name: instance.name });
28
+ }
29
+ instance = await this._waitForInstanceBoot(instance);
30
+ instance = await this._adbConnectIfNeeded(instance);
31
+ await this._notifyBootEvent(instance.adbName, instance.recipeName, isNew);
28
32
  return instance;
29
33
  }
30
34
 
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
- */
45
35
  async shutdown(instance) {
46
36
  const { uuid } = instance;
47
37
 
38
+ await this._notifyPreShutdown(uuid);
48
39
  await this._instanceLifecycleService.deleteInstance(uuid);
40
+ await this._deviceCleanupRegistry.disposeDevice(uuid);
41
+ await this._notifyShutdownCompleted(uuid);
49
42
  }
50
43
 
51
44
  async _waitForInstanceBoot(instance) {
@@ -55,21 +48,17 @@ class GenyInstanceLauncher {
55
48
 
56
49
  const options = {
57
50
  backoff: 'none',
58
- retries: 20,
51
+ retries: 25,
59
52
  interval: 5000,
60
53
  initialSleep: 45000,
61
- shouldUnref: true,
62
54
  };
63
55
 
64
56
  return await retry(options, async () => {
65
- const { instance: _instance } = await this._genyCloudExec.getInstance(instance.uuid);
66
- const anInstance = new GenyInstance(_instance);
67
-
68
- if (!anInstance.isOnline()) {
57
+ const _instance = await this._instanceLookupService.getInstance(instance.uuid);
58
+ if (!_instance.isOnline()) {
69
59
  throw new DetoxRuntimeError(`Timeout waiting for instance ${instance.uuid} to be ready`);
70
60
  }
71
-
72
- return anInstance;
61
+ return _instance;
73
62
  });
74
63
  }
75
64
 
@@ -2,31 +2,21 @@
2
2
  const _ = require('lodash');
3
3
 
4
4
  const DetoxRuntimeError = require('../../../../errors/DetoxRuntimeError');
5
- const log = require('../../../../utils/logger').child({ cat: 'device,device-allocation' });
6
5
  const IosSimulatorCookie = require('../../../cookies/IosSimulatorCookie');
7
6
  const AllocationDriverBase = require('../AllocationDriverBase');
8
7
 
9
- const SimulatorQuery = require('./SimulatorQuery');
10
-
11
8
  class SimulatorAllocDriver extends AllocationDriverBase {
12
9
  /**
13
- * @param deviceRegistry { DeviceRegistry }
14
- * @param detoxConfig { DetoxInternals.RuntimeConfig }
15
10
  * @param deviceRegistry { DeviceRegistry }
16
11
  * @param applesimutils { AppleSimUtils }
17
12
  * @param simulatorLauncher { SimulatorLauncher }
18
13
  */
19
- constructor({ detoxConfig, deviceRegistry, applesimutils, simulatorLauncher }) {
14
+ constructor({ deviceRegistry, applesimutils, simulatorLauncher }) {
20
15
  super();
21
16
  this._deviceRegistry = deviceRegistry;
22
17
  this._applesimutils = applesimutils;
23
18
  this._simulatorLauncher = simulatorLauncher;
24
19
  this._launchInfo = {};
25
- this._shouldShutdown = detoxConfig.behavior.cleanup.shutdownDevice;
26
- }
27
-
28
- async init() {
29
- await this._deviceRegistry.unregisterZombieDevices();
30
20
  }
31
21
 
32
22
  /**
@@ -34,15 +24,16 @@ class SimulatorAllocDriver extends AllocationDriverBase {
34
24
  * @return {Promise<IosSimulatorCookie>}
35
25
  */
36
26
  async allocate(deviceConfig) {
37
- const deviceQuery = new SimulatorQuery(deviceConfig.device);
27
+ const deviceQuery = this._adaptQuery(deviceConfig.device);
38
28
 
39
29
  // TODO Delegate this onto a well tested allocator class
40
- const udid = await this._deviceRegistry.registerDevice(async () => {
30
+ const udid = await this._deviceRegistry.allocateDevice(async () => {
41
31
  return await this._findOrCreateDevice(deviceQuery);
42
32
  });
43
33
 
34
+ const deviceComment = this._commentDevice(deviceQuery);
44
35
  if (!udid) {
45
- throw new DetoxRuntimeError(`Failed to find device matching ${deviceQuery.getDeviceComment()}`);
36
+ throw new DetoxRuntimeError(`Failed to find device matching ${deviceComment}`);
46
37
  }
47
38
 
48
39
  this._launchInfo[udid] = { deviceConfig };
@@ -67,40 +58,21 @@ class SimulatorAllocDriver extends AllocationDriverBase {
67
58
  async free(cookie, options = {}) {
68
59
  const { udid } = cookie;
69
60
 
70
- if (options.shutdown) {
71
- await this._doShutdown(udid);
72
- await this._deviceRegistry.unregisterDevice(udid);
73
- } else {
74
- await this._deviceRegistry.releaseDevice(udid);
75
- }
76
- }
61
+ await this._deviceRegistry.disposeDevice(udid);
77
62
 
78
- async cleanup() {
79
- if (this._shouldShutdown) {
80
- const sessionDevices = await this._deviceRegistry.readSessionDevices();
81
- const shutdownPromises = sessionDevices.getIds().map((udid) => this._doShutdown(udid));
82
- await Promise.all(shutdownPromises);
83
- }
84
-
85
- await this._deviceRegistry.unregisterSessionDevices();
86
- }
87
-
88
- /**
89
- * @param {string} udid
90
- * @returns {Promise<void>}
91
- * @private
92
- */
93
- async _doShutdown(udid) {
94
- try {
63
+ if (options.shutdown) {
95
64
  await this._simulatorLauncher.shutdown(udid);
96
- } catch (err) {
97
- log.warn({ err }, `Failed to shutdown simulator ${udid}`);
98
65
  }
99
66
  }
100
67
 
101
68
  /***
102
69
  * @private
103
- * @param {SimulatorQuery} deviceQuery
70
+ * @param deviceQuery {{
71
+ * byId?: string;
72
+ * byName?: string;
73
+ * byType?: string;
74
+ * byOS?: string;
75
+ * }}
104
76
  * @returns {Promise<String>}
105
77
  */
106
78
  async _findOrCreateDevice(deviceQuery) {
@@ -111,7 +83,6 @@ class SimulatorAllocDriver extends AllocationDriverBase {
111
83
  if (_.isEmpty(free)) {
112
84
  const prototypeDevice = taken[0];
113
85
  udid = this._applesimutils.create(prototypeDevice);
114
- await this._runScreenshotWorkaround(udid);
115
86
  } else {
116
87
  udid = free[0].udid;
117
88
  }
@@ -119,30 +90,11 @@ class SimulatorAllocDriver extends AllocationDriverBase {
119
90
  return udid;
120
91
  }
121
92
 
122
- async _runScreenshotWorkaround(udid) {
123
- await this._applesimutils.takeScreenshot(udid, '/dev/null').catch(() => {
124
- log.debug({}, `
125
- NOTE: For an unknown yet reason, taking the first screenshot is apt
126
- to fail when booting iOS Simulator in a hidden window mode (or on CI).
127
- Detox applies a workaround by taking a dummy screenshot to ensure
128
- that the future ones are going to work fine. This screenshot is not
129
- saved anywhere, and the error above is suppressed for all log levels
130
- except for "debug" and "trace."
131
- `.trim());
132
- });
133
- }
134
-
135
- /**
136
- * @private
137
- * @param {SimulatorQuery} deviceQuery
138
- */
139
93
  async _groupDevicesByStatus(deviceQuery) {
140
94
  const searchResults = await this._queryDevices(deviceQuery);
141
- const takenDevices = this._deviceRegistry.getTakenDevicesSync();
142
-
143
- const { taken, free } = _.groupBy(searchResults, ({ udid }) => {
144
- return takenDevices.includes(udid) ? 'taken' : 'free';
145
- });
95
+ const { rawDevices: takenDevices } = this._deviceRegistry.getRegisteredDevices();
96
+ const takenUDIDs = new Set(_.map(takenDevices, 'id'));
97
+ const { taken, free } = _.groupBy(searchResults, ({ udid }) => takenUDIDs.has(udid) ? 'taken' : 'free');
146
98
 
147
99
  const targetOS = _.get(taken, '0.os.identifier');
148
100
  const isMatching = targetOS && { os: { identifier: targetOS } };
@@ -153,25 +105,43 @@ class SimulatorAllocDriver extends AllocationDriverBase {
153
105
  };
154
106
  }
155
107
 
156
- /**
157
- * @private
158
- * @param {SimulatorQuery} deviceQuery
159
- */
160
108
  async _queryDevices(deviceQuery) {
161
109
  const result = await this._applesimutils.list(
162
110
  deviceQuery,
163
- `Searching for device ${deviceQuery} ...`
111
+ `Searching for device ${this._commentQuery(deviceQuery)} ...`
164
112
  );
165
113
 
166
114
  if (_.isEmpty(result)) {
167
115
  throw new DetoxRuntimeError({
168
- message: `Failed to find a device ${deviceQuery}`,
116
+ message: `Failed to find a device ${this._commentQuery(deviceQuery)}`,
169
117
  hint: `Run 'applesimutils --list' to list your supported devices. ` +
170
118
  `It is advised only to specify a device type, e.g., "iPhone Xʀ" and avoid explicit search by OS version.`
171
119
  });
172
120
  }
173
121
  return result;
174
122
  }
123
+
124
+ _adaptQuery({ id, name, os, type }) {
125
+ return _.omitBy({
126
+ byId: id,
127
+ byName: name,
128
+ byOS: os,
129
+ byType: type,
130
+ }, _.isUndefined);
131
+ }
132
+
133
+ _commentQuery({ byId, byName, byOS, byType }) {
134
+ return _.compact([
135
+ byId && `by UDID = ${JSON.stringify(byId)}`,
136
+ byName && `by name = ${JSON.stringify(byName)}`,
137
+ byType && `by type = ${JSON.stringify(byType)}`,
138
+ byOS && `by OS = ${JSON.stringify(byOS)}`,
139
+ ]).join(' and ');
140
+ }
141
+
142
+ _commentDevice({ byId, byName, byOS, byType }) {
143
+ return byId || _.compact([byName, byType, byOS]).join(', ');
144
+ }
175
145
  }
176
146
 
177
147
  module.exports = SimulatorAllocDriver;
@@ -1,24 +1,20 @@
1
- class SimulatorLauncher {
1
+ const DeviceLauncher = require('../../../common/drivers/DeviceLauncher');
2
+
3
+ class SimulatorLauncher extends DeviceLauncher {
2
4
  constructor({ applesimutils, eventEmitter }) {
5
+ super(eventEmitter);
3
6
  this._applesimutils = applesimutils;
4
- this._eventEmitter = eventEmitter;
5
7
  }
6
8
 
7
9
  async launch(udid, type, bootArgs, headless) {
8
10
  const coldBoot = await this._applesimutils.boot(udid, bootArgs, headless);
9
- return coldBoot;
11
+ await this._notifyBootEvent(udid, type, coldBoot, headless);
10
12
  }
11
13
 
12
14
  async shutdown(udid) {
13
- if (this._eventEmitter) {
14
- await this._eventEmitter.emit('beforeShutdownDevice', { deviceId: udid });
15
- }
16
-
15
+ await this._notifyPreShutdown(udid);
17
16
  await this._applesimutils.shutdown(udid);
18
-
19
- if (this._eventEmitter) {
20
- await this._eventEmitter.emit('shutdownDevice', { deviceId: udid });
21
- }
17
+ await this._notifyShutdownCompleted(udid);
22
18
  }
23
19
  }
24
20