Repository: wix/Detox
Pull request #3672 (2022-11-07 08:10:52)

Fix(Android): Emulator boot not detected >= api 31
## Description
Detect when emulator boots >= api 31

Fixes: https://github.com/wix/Detox/issues/3071
Fixes: https://github.com/wix/Detox/issues/3342


Patch:
diff --git a/detox/package.json b/detox/package.json
index 36e14e1f9..e6a6ad64e 100644
--- a/detox/package.json
+++ b/detox/package.json
@@ -82,7 +82,6 @@
     "signal-exit": "^3.0.3",
     "stream-json": "^1.7.4",
     "strip-ansi": "^6.0.1",
-    "tail": "^2.0.0",
     "telnet-client": "1.2.8",
     "tempfile": "^2.0.0",
     "trace-event-lib": "^1.3.1",
diff --git a/detox/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.js b/detox/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.js
index 0c5edde97..d47cbe40e 100644
--- a/detox/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.js
+++ b/detox/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.js
@@ -36,7 +36,7 @@ class EmulatorLauncher extends DeviceLauncher {
         retries: 2,
         interval: 100,
         conditionFn: isUnknownEmulatorError,
-      }, () => this._launchEmulator(avdName, launchCommand));
+      }, () => this._launchEmulator(avdName, launchCommand, adbName));
     }
     await this._awaitEmulatorBoot(adbName);
     await this._notifyBootEvent(adbName, avdName, !isRunning);
@@ -60,8 +60,8 @@ class EmulatorLauncher extends DeviceLauncher {
     await this._notifyShutdownCompleted(adbName);
   }
 
-  _launchEmulator(emulatorName, launchCommand) {
-    return launchEmulatorProcess(emulatorName, this._emulatorExec, launchCommand);
+  _launchEmulator(emulatorName, launchCommand, adbName) {
+    return launchEmulatorProcess(emulatorName, this._emulatorExec, launchCommand, this._adb, adbName);
   }
 
   async _awaitEmulatorBoot(adbName) {
diff --git a/detox/src/devices/allocation/drivers/android/emulator/launchEmulatorProcess.js b/detox/src/devices/allocation/drivers/android/emulator/launchEmulatorProcess.js
index 0f2ea2756..f5bf7b006 100644
--- a/detox/src/devices/allocation/drivers/android/emulator/launchEmulatorProcess.js
+++ b/detox/src/devices/allocation/drivers/android/emulator/launchEmulatorProcess.js
@@ -1,29 +1,15 @@
 const fs = require('fs');
 
 const _ = require('lodash');
-const { Tail } = require('tail');
 
 const unitLogger = require('../../../../../utils/logger').child({ cat: 'device' });
 
-function launchEmulatorProcess(emulatorName, emulatorExec, emulatorLaunchCommand) {
+function launchEmulatorProcess(emulatorName, emulatorExec, emulatorLaunchCommand, adb, adbName) {
   let childProcessOutput;
-
   const portName = emulatorLaunchCommand.port ? `-${emulatorLaunchCommand.port}` : '';
   const tempLog = `./${emulatorName}${portName}.log`;
   const stdout = fs.openSync(tempLog, 'a');
   const stderr = fs.openSync(tempLog, 'a');
-  const tailOptions = {
-    useWatchFile: true,
-    fsWatchOptions: {
-      interval: 1500,
-    },
-  };
-  const tail = new Tail(tempLog, tailOptions)
-    .on('line', (line) => {
-      if (line.includes('Adb connected, start proxing data')) {
-        childProcessPromise._cpResolve();
-      }
-    });
 
   function detach() {
     if (childProcessOutput) {
@@ -32,7 +18,6 @@ function launchEmulatorProcess(emulatorName, emulatorExec, emulatorLaunchCommand
 
     childProcessOutput = fs.readFileSync(tempLog, 'utf8');
 
-    tail.unwatch();
     fs.closeSync(stdout);
     fs.closeSync(stderr);
     fs.unlink(tempLog, _.noop);
@@ -46,6 +31,8 @@ function launchEmulatorProcess(emulatorName, emulatorExec, emulatorLaunchCommand
 
   log = log.child({ child_pid: childProcessPromise.childProcess.pid });
 
+  adb.waitForDevice(adbName).then(() => childProcessPromise._cpResolve());
+
   return childProcessPromise.then(() => true).catch((err) => {
     detach();
 
diff --git a/detox/src/devices/common/drivers/android/exec/ADB.js b/detox/src/devices/common/drivers/android/exec/ADB.js
index 9b0b346a5..cd92a6c01 100644
--- a/detox/src/devices/common/drivers/android/exec/ADB.js
+++ b/detox/src/devices/common/drivers/android/exec/ADB.js
@@ -189,6 +189,10 @@ class ADB {
     }
   }
 
+  async waitForDevice(deviceId) {
+    return await this.adbCmd(deviceId, 'wait-for-device');
+  }
+
   async apiLevel(deviceId) {
     if (this._cachedApiLevels.has(deviceId)) {
       return this._cachedApiLevels.get(deviceId);


Test patch:
diff --git a/detox/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.test.js b/detox/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.test.js
index a9896a6ea..929e8e003 100644
--- a/detox/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.test.js
+++ b/detox/src/devices/allocation/drivers/android/emulator/EmulatorLauncher.test.js
@@ -55,7 +55,7 @@ describe('Emulator launcher', () => {
 
       it('should launch the specified emulator in a separate process', async () => {
         await uut.launch(avdName, adbName, isRunning);
-        expect(launchEmulatorProcess).toHaveBeenCalledWith(avdName, emulatorExec, expect.anything());
+        expect(launchEmulatorProcess).toHaveBeenCalledWith(avdName, emulatorExec, expect.anything(), adb, adbName);
       });
 
       it('should launch using a specific emulator port, if provided', async () => {
diff --git a/detox/src/devices/common/drivers/android/exec/ADB.test.js b/detox/src/devices/common/drivers/android/exec/ADB.test.js
index 805480028..96363e5b5 100644
--- a/detox/src/devices/common/drivers/android/exec/ADB.test.js
+++ b/detox/src/devices/common/drivers/android/exec/ADB.test.js
@@ -83,6 +83,14 @@ describe('ADB', () => {
     });
   });
 
+  it('should await device boot', async () => {
+    await adb.waitForDevice(deviceId);
+
+    expect(execWithRetriesAndLogs).toHaveBeenCalledWith(
+      expect.stringContaining(`"${adbBinPath}" -s ${deviceId} wait-for-device`),
+      expect.any(Object));
+  });
+
   it('should install an APK (api≤22)', async () => {
     jest.spyOn(adb, 'apiLevel').mockImplementation(async () => 22);
     await adb.install(deviceId, 'path/to/bin.apk');