diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9577b018fc..e8022d2dd3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -232,7 +232,7 @@ jobs: name: video-${{ matrix.browser }}-linux-test-results path: test-results test_android: - name: Android on macOS + name: Android Emulator runs-on: macos-10.15 steps: - uses: actions/checkout@v2 @@ -250,6 +250,7 @@ jobs: env: FOLIO_JSON_OUTPUT_NAME: "test-results/report.json" PW_ANDROID_TESTS: 1 + DEBUG: pw:api - run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json if: always() && github.ref == 'refs/heads/master' - uses: actions/upload-artifact@v1 diff --git a/src/server/android/android.ts b/src/server/android/android.ts index 20bbc78ac5..dcedd34b38 100644 --- a/src/server/android/android.ts +++ b/src/server/android/android.ts @@ -145,7 +145,7 @@ export class AndroidDevice extends EventEmitter { } async open(command: string): Promise { - return await this._backend.open(`shell:${command}`); + return await this._backend.open(`${command}`); } private async _driver(): Promise { @@ -167,18 +167,7 @@ export class AndroidDevice extends EventEmitter { debug('pw:android')('Starting the new driver'); this.shell(`am instrument -w com.microsoft.playwright.androiddriver.test/androidx.test.runner.AndroidJUnitRunner`); - - debug('pw:android')('Polling the socket'); - let socket; - while (!socket) { - try { - socket = await this._backend.open(`localabstract:playwright_android_driver_socket`); - } catch (e) { - await new Promise(f => setTimeout(f, 100)); - } - } - - debug('pw:android')('Connected to driver'); + const socket = await this._waitForLocalAbstract('playwright_android_driver_socket'); const transport = new Transport(socket, socket, socket, 'be'); transport.onmessage = message => { const response = JSON.parse(message); @@ -197,6 +186,20 @@ export class AndroidDevice extends EventEmitter { return this._driverPromise; } + private async _waitForLocalAbstract(socketName: string): Promise { + let socket: SocketBackend | undefined; + debug('pw:android')(`Polling the socket localabstract:${socketName}`); + while (!socket) { + try { + socket = await this._backend.open(`localabstract:${socketName}`); + } catch (e) { + await new Promise(f => setTimeout(f, 250)); + } + } + debug('pw:android')(`Connected to localabstract:${socketName}`); + return socket; + } + async send(method: string, params: any): Promise { const driver = await this._driver(); const id = ++this._lastId; @@ -224,20 +227,11 @@ export class AndroidDevice extends EventEmitter { debug('pw:android')('Force-stopping', pkg); await this._backend.runCommand(`shell:am force-stop ${pkg}`); - const socketName = createGuid(); + const socketName = 'playwright-' + createGuid(); const commandLine = `_ --disable-fre --no-default-browser-check --no-first-run --remote-debugging-socket-name=${socketName}`; debug('pw:android')('Starting', pkg, commandLine); await this._backend.runCommand(`shell:echo "${commandLine}" > /data/local/tmp/chrome-command-line`); await this._backend.runCommand(`shell:am start -n ${pkg}/com.google.android.apps.chrome.Main about:blank`); - - debug('pw:android')('Polling for socket', socketName); - while (true) { - const net = await this._backend.runCommand(`shell:cat /proc/net/unix | grep ${socketName}$`); - if (net) - break; - await new Promise(f => setTimeout(f, 100)); - } - debug('pw:android')('Got the socket, connecting'); return await this._connectToBrowser(socketName, options); } @@ -249,8 +243,9 @@ export class AndroidDevice extends EventEmitter { } private async _connectToBrowser(socketName: string, options: types.BrowserContextOptions = {}): Promise { - const androidBrowser = new AndroidBrowser(this, socketName); - await androidBrowser._open(); + const socket = await this._waitForLocalAbstract(socketName); + const androidBrowser = new AndroidBrowser(this, socket); + await androidBrowser._init(); this._browserConnections.add(androidBrowser); const browserOptions: BrowserOptions = { @@ -357,17 +352,22 @@ export class AndroidDevice extends EventEmitter { class AndroidBrowser extends EventEmitter { readonly device: AndroidDevice; - readonly socketName: string; - private _socket: SocketBackend | undefined; + private _socket: SocketBackend; private _receiver: stream.Writable; private _waitForNextTask = makeWaitForNextTask(); onmessage?: (message: any) => void; onclose?: () => void; - constructor(device: AndroidDevice, socketName: string) { + constructor(device: AndroidDevice, socket: SocketBackend) { super(); this.device = device; - this.socketName = socketName; + this._socket = socket; + this._socket.on('close', () => { + this._waitForNextTask(() => { + if (this.onclose) + this.onclose(); + }); + }); this._receiver = new (ws as any).Receiver() as stream.Writable; this._receiver.on('message', message => { this._waitForNextTask(() => { @@ -377,14 +377,7 @@ class AndroidBrowser extends EventEmitter { }); } - async _open() { - this._socket = await this.device._backend.open(`localabstract:${this.socketName}`); - this._socket.on('close', () => { - this._waitForNextTask(() => { - if (this.onclose) - this.onclose(); - }); - }); + async _init() { await this._socket.write(Buffer.from(`GET /devtools/browser HTTP/1.1\r Upgrade: WebSocket\r Connection: Upgrade\r diff --git a/test/android/device.spec.ts b/test/android/device.spec.ts index 344717efe8..9e72f5628e 100644 --- a/test/android/device.spec.ts +++ b/test/android/device.spec.ts @@ -24,7 +24,7 @@ if (process.env.PW_ANDROID_TESTS) { }); it('should open a ADB socket', async function({ device }) { - const socket = await device.open('/bin/cat'); + const socket = await device.open('shell:/bin/cat'); await socket.write(Buffer.from('321\n')); const output = await new Promise(resolve => socket.on('data', resolve)); expect(output.toString()).toBe('321\n'); diff --git a/utils/avd_recreate.sh b/utils/avd_recreate.sh index 2a8e836e0d..2538ad25ea 100755 --- a/utils/avd_recreate.sh +++ b/utils/avd_recreate.sh @@ -10,5 +10,5 @@ fi ${ANDROID_HOME}/tools/bin/avdmanager delete avd --name android30 || true echo "y" | ${ANDROID_HOME}/tools/bin/sdkmanager --install "system-images;android-30;google_apis;x86" -echo "no" | ${ANDROID_HOME}/tools/bin/avdmanager create avd --force --name android30 --device "Nexus 5X" -k 'system-images;android-30;google_apis;x86' +echo "no" | ${ANDROID_HOME}/tools/bin/avdmanager create avd --force --name android30 --device pixel_4 --package "system-images;android-30;google_apis;x86" ${ANDROID_HOME}/emulator/emulator -list-avds diff --git a/utils/avd_start.sh b/utils/avd_start.sh index 63efcf2aff..cc629d8d57 100755 --- a/utils/avd_start.sh +++ b/utils/avd_start.sh @@ -9,7 +9,7 @@ if [[ -z "${ANDROID_HOME}" ]]; then fi echo "Killing previous emulators" -adb devices | grep emulator | cut -f1 | while read line; do adb -s $line emu kill; done +${ANDROID_HOME}/platform-tools/adb devices | grep emulator | cut -f1 | while read line; do adb -s $line emu kill; done echo "Starting emulator" nohup ${ANDROID_HOME}/emulator/emulator -avd android30 -no-audio -no-snapshot -no-window -gpu swiftshader_indirect &