detox 20.11.5-smoke.0 → 20.12.0-smoke.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. package/Detox-android/com/wix/detox/{20.11.5-smoke.0/detox-20.11.5-smoke.0-javadoc.jar → 20.12.0-smoke.1/detox-20.12.0-smoke.1-javadoc.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.12.0-smoke.1/detox-20.12.0-smoke.1-javadoc.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.12.0-smoke.1/detox-20.12.0-smoke.1-javadoc.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.12.0-smoke.1/detox-20.12.0-smoke.1-javadoc.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.12.0-smoke.1/detox-20.12.0-smoke.1-javadoc.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/{20.11.5-smoke.0/detox-20.11.5-smoke.0-sources.jar → 20.12.0-smoke.1/detox-20.12.0-smoke.1-sources.jar} +0 -0
  7. package/Detox-android/com/wix/detox/20.12.0-smoke.1/detox-20.12.0-smoke.1-sources.jar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.12.0-smoke.1/detox-20.12.0-smoke.1-sources.jar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.12.0-smoke.1/detox-20.12.0-smoke.1-sources.jar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.12.0-smoke.1/detox-20.12.0-smoke.1-sources.jar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/20.12.0-smoke.1/detox-20.12.0-smoke.1.aar +0 -0
  12. package/Detox-android/com/wix/detox/20.12.0-smoke.1/detox-20.12.0-smoke.1.aar.md5 +1 -0
  13. package/Detox-android/com/wix/detox/20.12.0-smoke.1/detox-20.12.0-smoke.1.aar.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/20.12.0-smoke.1/detox-20.12.0-smoke.1.aar.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/20.12.0-smoke.1/detox-20.12.0-smoke.1.aar.sha512 +1 -0
  16. package/Detox-android/com/wix/detox/{20.11.5-smoke.0/detox-20.11.5-smoke.0.pom → 20.12.0-smoke.1/detox-20.12.0-smoke.1.pom} +1 -1
  17. package/Detox-android/com/wix/detox/20.12.0-smoke.1/detox-20.12.0-smoke.1.pom.md5 +1 -0
  18. package/Detox-android/com/wix/detox/20.12.0-smoke.1/detox-20.12.0-smoke.1.pom.sha1 +1 -0
  19. package/Detox-android/com/wix/detox/20.12.0-smoke.1/detox-20.12.0-smoke.1.pom.sha256 +1 -0
  20. package/Detox-android/com/wix/detox/20.12.0-smoke.1/detox-20.12.0-smoke.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/src/full/java/com/wix/detox/espresso/DetoxAssertion.java +44 -25
  29. package/android/detox/src/full/java/com/wix/detox/espresso/EspressoDetox.java +6 -7
  30. package/android/detox/src/full/java/com/wix/detox/espresso/action/GetAttributesAction.kt +4 -4
  31. package/android/detox/src/full/java/com/wix/detox/espresso/performer/MultipleViewsActionPerformer.kt +43 -0
  32. package/android/detox/src/full/java/com/wix/detox/espresso/performer/SingleViewActionPerformer.kt +19 -0
  33. package/android/detox/src/full/java/com/wix/detox/espresso/performer/ViewActionPerformer.kt +24 -0
  34. package/android/detox/src/full/java/com/wix/invoke/types/Invocation.java +5 -4
  35. package/android/detox/src/main/java/com/wix/detox/espresso/MultipleViewsAction.kt +4 -0
  36. package/android/detox/src/main/java/com/wix/detox/espresso/UiControllerSpy.kt +0 -1
  37. package/android/detox/src/main/java/com/wix/detox/espresso/ViewActionWithResult.kt +2 -1
  38. package/android/detox/src/testFull/java/com/wix/detox/espresso/action/GetAttributesActionTest.kt +6 -5
  39. package/android/detox/src/testFull/java/com/wix/detox/espresso/performer/ViewActionPerformerSpec.kt +37 -0
  40. package/index.d.ts +47 -3
  41. package/local-cli/reset-lock-file.js +5 -9
  42. package/package.json +6 -6
  43. package/runners/jest/testEnvironment/index.js +1 -0
  44. package/src/DetoxWorker.js +5 -11
  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 +0 -17
  51. package/src/configuration/composeLoggerConfig.js +1 -0
  52. package/src/devices/allocation/DeviceAllocator.js +66 -20
  53. package/src/devices/allocation/DeviceList.js +44 -0
  54. package/src/devices/allocation/DeviceRegistry.js +189 -0
  55. package/src/devices/allocation/drivers/AllocationDriverBase.d.ts +15 -0
  56. package/src/devices/{common/drivers/android/tools → allocation/drivers/android}/FreeDeviceFinder.js +11 -10
  57. package/src/devices/allocation/drivers/android/attached/AttachedAndroidAllocDriver.js +22 -17
  58. package/src/devices/allocation/drivers/android/emulator/EmulatorAllocDriver.js +97 -38
  59. package/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.js +32 -45
  60. package/src/devices/allocation/drivers/android/emulator/FreeEmulatorFinder.js +1 -1
  61. package/src/devices/allocation/drivers/android/emulator/FreePortFinder.js +16 -0
  62. package/src/devices/allocation/drivers/android/emulator/launchEmulatorProcess.js +3 -3
  63. package/src/devices/allocation/drivers/android/genycloud/GenyAllocDriver.js +104 -32
  64. package/src/devices/allocation/drivers/android/genycloud/GenyInstanceLauncher.js +40 -31
  65. package/src/devices/allocation/drivers/android/genycloud/GenyRegistry.js +121 -0
  66. package/src/devices/allocation/drivers/android/genycloud/services/GenyInstanceLifecycleService.js +24 -0
  67. package/src/devices/{common → allocation}/drivers/android/genycloud/services/GenyRecipesService.js +1 -1
  68. package/src/devices/{common → allocation}/drivers/android/genycloud/services/dto/GenyInstance.js +6 -1
  69. package/src/devices/allocation/drivers/ios/SimulatorAllocDriver.js +94 -51
  70. package/src/devices/allocation/drivers/ios/SimulatorLauncher.js +11 -7
  71. package/src/devices/allocation/drivers/ios/SimulatorQuery.js +24 -0
  72. package/src/devices/allocation/factories/android.js +29 -35
  73. package/src/devices/allocation/factories/ios.js +7 -5
  74. package/src/devices/common/drivers/DeviceCookie.d.ts +12 -0
  75. package/src/devices/common/drivers/android/cookies.d.ts +11 -0
  76. package/src/devices/common/drivers/android/emulator/exec/EmulatorExec.js +17 -5
  77. package/src/devices/common/drivers/android/exec/ADB.js +1 -0
  78. package/src/devices/common/drivers/ios/cookies.d.ts +9 -0
  79. package/src/devices/common/drivers/ios/tools/AppleSimUtils.js +2 -0
  80. package/src/devices/cookies/index.js +0 -6
  81. package/src/devices/runtime/drivers/android/genycloud/GenyCloudDriver.js +7 -6
  82. package/src/devices/runtime/factories/android.js +3 -11
  83. package/src/devices/runtime/factories/ios.js +3 -2
  84. package/src/{servicelocator → devices/servicelocator}/android/emulatorServiceLocator.js +1 -1
  85. package/src/devices/servicelocator/android/genycloudServiceLocator.js +17 -0
  86. package/src/devices/servicelocator/android/index.js +23 -0
  87. package/src/{validation → devices/validation}/EnvironmentValidatorBase.js +1 -0
  88. package/src/{validation → devices/validation}/android/GenycloudEnvValidator.js +2 -2
  89. package/src/{validation → devices/validation}/factories/index.js +1 -1
  90. package/src/{validation → devices/validation}/ios/IosSimulatorEnvValidator.js +2 -2
  91. package/src/environmentFactory.js +1 -11
  92. package/src/ipc/IPCClient.js +17 -1
  93. package/src/ipc/IPCServer.js +27 -1
  94. package/src/logger/DetoxLogger.js +2 -2
  95. package/src/realms/DetoxContext.js +6 -0
  96. package/src/realms/DetoxPrimaryContext.js +42 -42
  97. package/src/realms/DetoxSecondaryContext.js +19 -0
  98. package/src/realms/symbols.js +4 -0
  99. package/src/utils/PIDService.js +27 -0
  100. package/src/utils/environment.js +8 -15
  101. package/src/utils/errorUtils.js +3 -3
  102. package/tsconfig.json +5 -3
  103. package/Detox-android/com/wix/detox/20.11.5-smoke.0/detox-20.11.5-smoke.0-javadoc.jar.md5 +0 -1
  104. package/Detox-android/com/wix/detox/20.11.5-smoke.0/detox-20.11.5-smoke.0-javadoc.jar.sha1 +0 -1
  105. package/Detox-android/com/wix/detox/20.11.5-smoke.0/detox-20.11.5-smoke.0-javadoc.jar.sha256 +0 -1
  106. package/Detox-android/com/wix/detox/20.11.5-smoke.0/detox-20.11.5-smoke.0-javadoc.jar.sha512 +0 -1
  107. package/Detox-android/com/wix/detox/20.11.5-smoke.0/detox-20.11.5-smoke.0-sources.jar.md5 +0 -1
  108. package/Detox-android/com/wix/detox/20.11.5-smoke.0/detox-20.11.5-smoke.0-sources.jar.sha1 +0 -1
  109. package/Detox-android/com/wix/detox/20.11.5-smoke.0/detox-20.11.5-smoke.0-sources.jar.sha256 +0 -1
  110. package/Detox-android/com/wix/detox/20.11.5-smoke.0/detox-20.11.5-smoke.0-sources.jar.sha512 +0 -1
  111. package/Detox-android/com/wix/detox/20.11.5-smoke.0/detox-20.11.5-smoke.0.aar +0 -0
  112. package/Detox-android/com/wix/detox/20.11.5-smoke.0/detox-20.11.5-smoke.0.aar.md5 +0 -1
  113. package/Detox-android/com/wix/detox/20.11.5-smoke.0/detox-20.11.5-smoke.0.aar.sha1 +0 -1
  114. package/Detox-android/com/wix/detox/20.11.5-smoke.0/detox-20.11.5-smoke.0.aar.sha256 +0 -1
  115. package/Detox-android/com/wix/detox/20.11.5-smoke.0/detox-20.11.5-smoke.0.aar.sha512 +0 -1
  116. package/Detox-android/com/wix/detox/20.11.5-smoke.0/detox-20.11.5-smoke.0.pom.md5 +0 -1
  117. package/Detox-android/com/wix/detox/20.11.5-smoke.0/detox-20.11.5-smoke.0.pom.sha1 +0 -1
  118. package/Detox-android/com/wix/detox/20.11.5-smoke.0/detox-20.11.5-smoke.0.pom.sha256 +0 -1
  119. package/Detox-android/com/wix/detox/20.11.5-smoke.0/detox-20.11.5-smoke.0.pom.sha512 +0 -1
  120. package/src/devices/DeviceRegistry.js +0 -176
  121. package/src/devices/allocation/drivers/AllocationDriverBase.js +0 -30
  122. package/src/devices/allocation/drivers/android/attached/AttachedAndroidLauncher.js +0 -13
  123. package/src/devices/allocation/drivers/android/emulator/EmulatorAllocationHelper.js +0 -72
  124. package/src/devices/allocation/drivers/android/genycloud/GenyDeviceRegistryFactory.js +0 -16
  125. package/src/devices/allocation/drivers/android/genycloud/GenyInstanceAllocationHelper.js +0 -65
  126. package/src/devices/common/drivers/DeviceAllocationHelper.js +0 -20
  127. package/src/devices/common/drivers/DeviceLauncher.js +0 -19
  128. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceLifecycleService.js +0 -25
  129. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceLookupService.js +0 -38
  130. package/src/devices/common/drivers/android/genycloud/services/GenyInstanceNaming.js +0 -14
  131. package/src/devices/cookies/AndroidDeviceCookie.js +0 -13
  132. package/src/devices/cookies/AndroidEmulatorCookie.js +0 -6
  133. package/src/devices/cookies/AttachedAndroidDeviceCookie.js +0 -12
  134. package/src/devices/cookies/DeviceCookie.js +0 -4
  135. package/src/devices/cookies/GenycloudEmulatorCookie.js +0 -20
  136. package/src/devices/cookies/IosCookie.js +0 -6
  137. package/src/devices/cookies/IosSimulatorCookie.js +0 -10
  138. package/src/devices/lifecycle/GenyGlobalLifecycleHandler.js +0 -71
  139. package/src/devices/lifecycle/factories/GenyGlobalLifecycleHandlerFactory.js +0 -18
  140. package/src/servicelocator/android/genycloudServiceLocator.js +0 -21
  141. package/src/servicelocator/android/index.js +0 -25
  142. package/src/servicelocator/ios.js +0 -7
  143. /package/src/devices/{common → allocation}/drivers/android/genycloud/exec/GenyCloudExec.js +0 -0
  144. /package/src/devices/{common → allocation}/drivers/android/genycloud/services/GenyAuthService.js +0 -0
  145. /package/src/devices/{common → allocation}/drivers/android/genycloud/services/dto/GenyRecipe.js +0 -0
@@ -1,25 +1,43 @@
1
+ /**
2
+ * @typedef {import('../../AllocationDriverBase').AllocationDriverBase} AllocationDriverBase
3
+ * @typedef {import('../../../../common/drivers/android/cookies').GenycloudEmulatorCookie} GenycloudEmulatorCookie
4
+ */
5
+
1
6
  const { DetoxRuntimeError } = require('../../../../../errors');
2
7
  const Timer = require('../../../../../utils/Timer');
3
- const GenycloudEmulatorCookie = require('../../../../cookies/GenycloudEmulatorCookie');
4
- const AllocationDriverBase = require('../../AllocationDriverBase');
8
+ const log = require('../../../../../utils/logger').child({ cat: 'device' });
9
+
10
+ const GenyRegistry = require('./GenyRegistry');
5
11
 
6
- class GenyAllocDriver extends AllocationDriverBase {
12
+ const events = {
13
+ GENYCLOUD_TEARDOWN: { event: 'GENYCLOUD_TEARDOWN' },
14
+ };
7
15
 
16
+ /**
17
+ * @implements {AllocationDriverBase}
18
+ */
19
+ class GenyAllocDriver {
8
20
  /**
9
21
  * @param {object} options
10
22
  * @param {import('../../../../common/drivers/android/exec/ADB')} options.adb
11
- * @param {import('./GenyRecipeQuerying')} options.recipeQuerying
12
- * @param {import('./GenyInstanceAllocationHelper')} options.allocationHelper
23
+ * @param {DetoxInternals.SessionState} options.detoxSession
24
+ * @param {import('./GenyRegistry')} options.genyRegistry
13
25
  * @param {import('./GenyInstanceLauncher')} options.instanceLauncher
26
+ * @param {import('./GenyRecipeQuerying')} options.recipeQuerying
14
27
  */
15
- constructor({ adb, recipeQuerying, allocationHelper, instanceLauncher }) {
16
- super();
17
-
28
+ constructor({
29
+ adb,
30
+ detoxSession,
31
+ genyRegistry = new GenyRegistry(),
32
+ instanceLauncher,
33
+ recipeQuerying,
34
+ }) {
18
35
  this._adb = adb;
19
- this._recipeQuerying = recipeQuerying;
36
+ this._detoxSessionId = detoxSession.id;
37
+ this._genyRegistry = genyRegistry;
20
38
  this._instanceLauncher = instanceLauncher;
21
- this._instanceAllocationHelper = allocationHelper;
22
- this._launchInfo = {};
39
+ this._recipeQuerying = recipeQuerying;
40
+ this._instanceCounter = 0;
23
41
  }
24
42
 
25
43
  /**
@@ -27,47 +45,83 @@ class GenyAllocDriver extends AllocationDriverBase {
27
45
  * @return {Promise<GenycloudEmulatorCookie>}
28
46
  */
29
47
  async allocate(deviceConfig) {
48
+ await new Promise((resolve) => setTimeout(resolve, 10000));
30
49
  const deviceQuery = deviceConfig.device;
31
50
  const recipe = await this._recipeQuerying.getRecipeFromQuery(deviceQuery);
32
51
  this._assertRecipe(deviceQuery, recipe);
33
52
 
34
- const { instance, isNew } = await this._instanceAllocationHelper.allocateDevice(recipe);
35
- this._launchInfo[instance.uuid] = { isNew };
36
- return new GenycloudEmulatorCookie(instance);
53
+ let instance = this._genyRegistry.findFreeInstance(recipe);
54
+ if (!instance) {
55
+ const instanceName = `Detox.${this._detoxSessionId}.${this._instanceCounter++}`;
56
+ instance = await this._instanceLauncher.launch(recipe, instanceName);
57
+ this._genyRegistry.addInstance(instance, recipe);
58
+ }
59
+
60
+ return {
61
+ id: instance.uuid,
62
+ adbName: instance.adbName,
63
+ name: instance.name,
64
+ instance,
65
+ };
37
66
  }
38
67
 
39
68
  /**
40
69
  * @param {GenycloudEmulatorCookie} cookie
41
- * @returns {Promise<void>}
42
70
  */
43
71
  async postAllocate(cookie) {
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
- });
72
+ const instance = await this._instanceLauncher.connect(cookie.instance);
73
+ this._genyRegistry.updateInstance(instance);
74
+
75
+ if (this._genyRegistry.pollNewInstance(instance.uuid)) {
76
+ const { adbName } = instance;
77
+
78
+ await Timer.run(20000, 'waiting for device to respond', async () => {
79
+ await this._adb.disableAndroidAnimations(adbName);
80
+ await this._adb.setWiFiToggle(adbName, true);
81
+ await this._adb.apiLevel(adbName);
82
+ });
83
+ }
84
+
85
+ return {
86
+ ...cookie,
87
+ adbName: instance.adbName,
88
+ };
54
89
  }
55
90
 
56
91
  /**
57
- * @param cookie { GenycloudEmulatorCookie }
58
- * @param options { DeallocOptions }
92
+ * @param cookie {Omit<GenycloudEmulatorCookie, 'instance'>}
93
+ * @param options {Partial<import('../../AllocationDriverBase').DeallocOptions>}
59
94
  * @return {Promise<void>}
60
95
  */
61
96
  async free(cookie, options = {}) {
62
- const { instance } = cookie;
63
-
64
- await this._instanceAllocationHelper.deallocateDevice(instance.uuid);
65
-
97
+ // Known issue: cookie won't have a proper 'instance' field due to (de)serialization
66
98
  if (options.shutdown) {
67
- await this._instanceLauncher.shutdown(instance);
99
+ this._genyRegistry.removeInstance(cookie.id);
100
+ await this._instanceLauncher.shutdown(cookie.id);
101
+ } else {
102
+ this._genyRegistry.markAsFree(cookie.id);
68
103
  }
69
104
  }
70
105
 
106
+ async cleanup() {
107
+ log.info(events.GENYCLOUD_TEARDOWN, 'Initiating Genymotion SaaS instances teardown...');
108
+
109
+ const killPromises = this._genyRegistry.getInstances().map((instance) => {
110
+ this._genyRegistry.markAsBusy(instance.uuid);
111
+ const onSuccess = () => this._genyRegistry.removeInstance(instance.uuid);
112
+ const onError = (error) => ({ ...instance, error });
113
+ return this._instanceLauncher.shutdown(instance.uuid).then(onSuccess, onError);
114
+ });
115
+
116
+ const deletionLeaks = (await Promise.all(killPromises)).filter(Boolean);
117
+ this._reportGlobalCleanupSummary(deletionLeaks);
118
+ }
119
+
120
+ emergencyCleanup() {
121
+ const instances = this._genyRegistry.getInstances();
122
+ this._reportGlobalCleanupSummary(instances);
123
+ }
124
+
71
125
  _assertRecipe(deviceQuery, recipe) {
72
126
  if (!recipe) {
73
127
  throw new DetoxRuntimeError({
@@ -76,6 +130,24 @@ class GenyAllocDriver extends AllocationDriverBase {
76
130
  });
77
131
  }
78
132
  }
133
+
134
+ _reportGlobalCleanupSummary(deletionLeaks) {
135
+ if (deletionLeaks.length) {
136
+ log.warn(events.GENYCLOUD_TEARDOWN, 'WARNING! Detected a Genymotion SaaS instance leakage, for the following instances:');
137
+
138
+ deletionLeaks.forEach(({ uuid, name, error }) => {
139
+ log.warn(events.GENYCLOUD_TEARDOWN, [
140
+ `Instance ${name} (${uuid})${error ? `: ${error}` : ''}`,
141
+ ` Kill it by visiting https://cloud.geny.io/instance/${uuid}, or by running:`,
142
+ ` gmsaas instances stop ${uuid}`,
143
+ ].join('\n'));
144
+ });
145
+
146
+ log.info(events.GENYCLOUD_TEARDOWN, 'Instances teardown completed with warnings');
147
+ } else {
148
+ log.info(events.GENYCLOUD_TEARDOWN, 'Instances teardown completed successfully');
149
+ }
150
+ }
79
151
  }
80
152
 
81
153
  module.exports = GenyAllocDriver;
@@ -1,44 +1,49 @@
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
- class GenyInstanceLauncher extends DeviceLauncher {
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
- * 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 }
18
+ * @param {import('./services/dto/GenyRecipe')} recipe
19
+ * @param {string} instanceName
23
20
  * @returns {Promise<GenyInstance>}
24
21
  */
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);
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
 
35
- async shutdown(instance) {
36
- const { uuid } = instance;
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
+ }
37
41
 
38
- await this._notifyPreShutdown(uuid);
39
- await this._instanceLifecycleService.deleteInstance(uuid);
40
- await this._deviceCleanupRegistry.disposeDevice(uuid);
41
- await this._notifyShutdownCompleted(uuid);
42
+ /**
43
+ * @param {string} instanceId
44
+ */
45
+ async shutdown(instanceId) {
46
+ await this._instanceLifecycleService.deleteInstance(instanceId);
42
47
  }
43
48
 
44
49
  async _waitForInstanceBoot(instance) {
@@ -48,17 +53,21 @@ class GenyInstanceLauncher extends DeviceLauncher {
48
53
 
49
54
  const options = {
50
55
  backoff: 'none',
51
- retries: 25,
56
+ retries: 20,
52
57
  interval: 5000,
53
58
  initialSleep: 45000,
59
+ shouldUnref: true,
54
60
  };
55
61
 
56
62
  return await retry(options, async () => {
57
- const _instance = await this._instanceLookupService.getInstance(instance.uuid);
58
- if (!_instance.isOnline()) {
63
+ const { instance: _instance } = await this._genyCloudExec.getInstance(instance.uuid);
64
+ const anInstance = new GenyInstance(_instance);
65
+
66
+ if (!anInstance.isOnline()) {
59
67
  throw new DetoxRuntimeError(`Timeout waiting for instance ${instance.uuid} to be ready`);
60
68
  }
61
- return _instance;
69
+
70
+ return anInstance;
62
71
  });
63
72
  }
64
73
 
@@ -0,0 +1,121 @@
1
+ const { DetoxInternalError } = require('../../../../../errors');
2
+
3
+ class GenyRegistry {
4
+ constructor() {
5
+ /** @type {Map<string, import('./services/dto/GenyRecipe')>} */
6
+ this._recipes = new Map();
7
+ /** @type {Map<string, import('./services/dto/GenyInstance')>} */
8
+ this._freeInstances = new Map();
9
+ /** @type {Map<string, import('./services/dto/GenyInstance')>} */
10
+ this._busyInstances = new Map();
11
+ /** @type {Set<string>} */
12
+ this._newInstances = new Set();
13
+ }
14
+
15
+ /**
16
+ * @returns {import('./services/dto/GenyInstance')[]}
17
+ */
18
+ getInstances() {
19
+ return [
20
+ ...this._freeInstances.values(),
21
+ ...this._busyInstances.values(),
22
+ ];
23
+ }
24
+
25
+ /**
26
+ * @param {import('./services/dto/GenyInstance')} instance
27
+ * @param {import('./services/dto/GenyRecipe')} recipe
28
+ */
29
+ addInstance(instance, recipe) {
30
+ this._recipes.set(instance.uuid, recipe);
31
+ this._busyInstances.set(instance.uuid, instance);
32
+ this._newInstances.add(instance.uuid);
33
+ }
34
+
35
+ /**
36
+ * @param {string} instanceId
37
+ * @returns {boolean}
38
+ */
39
+ pollNewInstance(instanceId) {
40
+ const result = this._newInstances.has(instanceId);
41
+ this._newInstances.delete(instanceId);
42
+ return result;
43
+ }
44
+
45
+ /** @param {import('./services/dto/GenyInstance')} instance */
46
+ updateInstance(instance) {
47
+ let found = false;
48
+
49
+ if (this._freeInstances.has(instance.uuid)) {
50
+ this._freeInstances.set(instance.uuid, instance);
51
+ found = true;
52
+ }
53
+
54
+ if (this._busyInstances.has(instance.uuid)) {
55
+ this._busyInstances.set(instance.uuid, instance);
56
+ found = true;
57
+ }
58
+
59
+ if (!found) {
60
+ throw new DetoxInternalError(`Cannot update an unknown instance ${instance.uuid}`);
61
+ }
62
+ }
63
+
64
+ /** @param {string} instanceId */
65
+ removeInstance(instanceId) {
66
+ this._freeInstances.delete(instanceId);
67
+ this._busyInstances.delete(instanceId);
68
+ this._newInstances.delete(instanceId);
69
+ this._recipes.delete(instanceId);
70
+ }
71
+
72
+ /**
73
+ * @param {string} instanceId
74
+ * @returns {import('./services/dto/GenyInstance')}
75
+ */
76
+ markAsBusy(instanceId) {
77
+ if (this._busyInstances.has(instanceId)) {
78
+ return this._busyInstances.get(instanceId);
79
+ }
80
+
81
+ const instance = this._freeInstances.get(instanceId);
82
+ if (!instance) {
83
+ throw new DetoxInternalError(`Cannot mark an unknown instance ${instanceId} as busy`);
84
+ }
85
+
86
+ this._busyInstances.set(instanceId, instance);
87
+ this._freeInstances.delete(instanceId);
88
+ return instance;
89
+ }
90
+
91
+ /**
92
+ * @param {string} instanceId
93
+ * @returns {import('./services/dto/GenyInstance')}
94
+ */
95
+ markAsFree(instanceId) {
96
+ if (this._freeInstances.has(instanceId)) {
97
+ return this._freeInstances.get(instanceId);
98
+ }
99
+
100
+ const instance = this._busyInstances.get(instanceId);
101
+ if (!instance) {
102
+ throw new DetoxInternalError(`Cannot mark an unknown instance ${instanceId} as free`);
103
+ }
104
+
105
+ this._busyInstances.delete(instanceId);
106
+ this._freeInstances.set(instanceId, instance);
107
+ return instance;
108
+ }
109
+
110
+ /** @returns {import('./services/dto/GenyInstance') | undefined} */
111
+ findFreeInstance(recipe) {
112
+ for (const instance of this._freeInstances.values()) {
113
+ const aRecipe = this._recipes.get(instance.uuid);
114
+ if (recipe.uuid === aRecipe.uuid) {
115
+ return this.markAsBusy(instance.uuid);
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ module.exports = GenyRegistry;
@@ -0,0 +1,24 @@
1
+ const Instance = require('./dto/GenyInstance');
2
+
3
+ class GenyInstanceLifecycleService {
4
+ constructor(genyCloudExec) {
5
+ this._genyCloudExec = genyCloudExec;
6
+ }
7
+
8
+ async createInstance(recipeUUID, instanceName) {
9
+ const result = await this._genyCloudExec.startInstance(recipeUUID, instanceName);
10
+ return new Instance(result.instance);
11
+ }
12
+
13
+ async adbConnectInstance(instanceUUID) {
14
+ const result = (await this._genyCloudExec.adbConnect(instanceUUID));
15
+ return new Instance(result.instance);
16
+ }
17
+
18
+ async deleteInstance(instanceUUID) {
19
+ const result = await this._genyCloudExec.stopInstance(instanceUUID);
20
+ return new Instance(result.instance);
21
+ }
22
+ }
23
+
24
+ module.exports = GenyInstanceLifecycleService;
@@ -1,6 +1,6 @@
1
1
  const logger = require('../../../../../../utils/logger').child({ cat: 'device' });
2
2
 
3
- const Recipe = require('./dto/GenyRecipe');
3
+ const Recipe = require('./dto/GenyRecipe');
4
4
 
5
5
  class GenyRecipesService {
6
6
  constructor(genyCloudExec) {
@@ -59,7 +59,12 @@ class GenyInstance {
59
59
  }
60
60
 
61
61
  toString() {
62
- return `GenyCloud:${this.name} (${this.uuid} ${this.adbName})`;
62
+ const description = [
63
+ this.uuid,
64
+ this.adbName
65
+ ].filter(Boolean).join('/');
66
+
67
+ return `${this.name} (${description})`;
63
68
  }
64
69
  }
65
70