diff --git a/.env.sample b/.env.sample new file mode 100644 index 00000000..99af890f --- /dev/null +++ b/.env.sample @@ -0,0 +1,17 @@ +# this is a sample .env file. Copy it to .env, and edit the .env file to match your needs. + +export ANDROID_APK=/home/yougotthis/Downloads/session-android-universal.apk +export SDK_MANAGER_FULL_PATH=/home/yougotthis/Android/Sdk/cmdline-tools/latest/bin/sdkmanager +export AVD_MANAGER_FULL_PATH=/home/yougotthis/Android/Sdk/cmdline-tools/latest/bin/avdmanager +export EMULATOR_FULL_PATH=/home/yougotthis/Android/Sdk/emulator/emulator +export ANDROID_SYSTEM_IMAGE="system-images;android-35;google_atd;x86_64" +export EMULATOR_FULL_PATH=/home/yougotthis/Android/Sdk/cmdline-tools/latest/bin/avdmanager +export IOS_APP_PATH_PREFIX=just_not_empty +export IOS_FIRST_SIMULATOR=just_not_empty +export IOS_SECOND_SIMULATOR=just_not_empty +export IOS_THIRD_SIMULATOR=just_not_empty +export IOS_FOURTH_SIMULATOR=just_not_empty +export IOS_FIFTH_SIMULATOR=just_not_empty +export IOS_SIXTH_SIMULATOR=just_not_empty +export IOS_SEVENTH_SIMULATOR=just_not_empty +export IOS_EIGHTH_SIMULATOR=just_not_empty diff --git a/.github/workflows/android-regression.yml b/.github/workflows/android-regression.yml index e297dfe0..fe7ca9c6 100644 --- a/.github/workflows/android-regression.yml +++ b/.github/workflows/android-regression.yml @@ -1,18 +1,168 @@ -name: Run android regression tests +name: Android regression tests on: workflow_dispatch: inputs: - tags: + APK_URL: description: 'APK.tar.gz url to test' required: true type: string - # push: - # branches: - # - test-docker-container + default: https://oxen.rocks/session-foundation/session-android/release/1.20.3/session-android-20241101T052421Z-be16d3bf9-universal.tar.xz + + APPIUM_REPO: + description: 'appium repo to checkout' + required: true + type: choice + options: + - session-foundation/session-appium + - burtonemily/session-appium + - bilb/session-appium + default: session-foundation/session-appium + + BRANCH_TO_CHECKOUT: + description: 'branch to checkout' + required: true + type: string + default: test-ci-regression + + SHARD_NUMBER: + description: 'shard number' + required: true + type: choice + options: + - '1' + - '2' + - '3' + - '4' + default: '1' + + SHARD_COUNT: + description: 'shard count' + required: true + type: choice + options: + - '1' + - '2' + - '3' + - '4' + default: '1' + + PRINT_FAILED_TEST_LOGS: + description: 'print failed test logs (1 to enable)' + required: true + type: choice + options: + - '0' + - '1' + default: '0' + + PRINT_ONGOING_TEST_LOGS: + description: 'print ongoing test logs (1 to enable)' + required: true + type: choice + options: + - '0' + - '1' + default: '0' + +# concurrency: +# group: ${{ github.workflow }} +# cancel-in-progress: true jobs: android-regression: - runs-on: [self-hosted, linux, x64, qa-android] + runs-on: [self-hosted, linux, X64, qa-android] + env: + ANDROID_APK: '../extracted/session-android.apk' + APPIUM_ADB_FULL_PATH: '/opt/android/platform-tools/adb' + ANDROID_SDK_ROOT: '/opt/android' + PLAYWRIGHT_WORKERS_COUNT: 1 + PLAYWRIGHT_RETRIES_COUNT: 0 + PRINT_FAILED_TEST_LOGS: ${{ github.event.inputs.PRINT_FAILED_TEST_LOGS }} + PRINT_ONGOING_TEST_LOGS: ${{ github.event.inputs.PRINT_ONGOING_TEST_LOGS }} + IOS_APP_PATH_PREFIX: 'just_not_empty' + IOS_FIRST_SIMULATOR: 'just_not_empty' + IOS_SECOND_SIMULATOR: 'just_not_empty' + IOS_THIRD_SIMULATOR: 'just_not_empty' + IOS_FOURTH_SIMULATOR: 'just_not_empty' + IOS_FIFTH_SIMULATOR: 'just_not_empty' + IOS_SIXTH_SIMULATOR: 'just_not_empty' + IOS_SEVENTH_SIMULATOR: 'just_not_empty' + IOS_EIGHTH_SIMULATOR: 'just_not_empty' + steps: - - name: run tests in already running container - run: docker exec $(docker ps -q) sh -c "dl_and_test" + - uses: actions/checkout@v4 + - name: Runner Details + run: | + echo "BRANCH_TO_CHECKOUT ${{ github.event.inputs.BRANCH_TO_CHECKOUT }}" + echo "APPIUM_REPO ${{ github.event.inputs.APPIUM_REPO }}" + echo "APK_URL ${{ github.event.inputs.APK_URL }}" + echo "SHARD_NUMBER ${{ github.event.inputs.SHARD_NUMBER }}" + echo "SHARD_COUNT ${{ github.event.inputs.SHARD_COUNT }}" + - name: Download APK + run: | + wget -q -O session-android.apk.tar.xz ${{ github.event.inputs.APK_URL }} + ls + pwd + - name: Extract APK + run: | + tar xf session-android.apk.tar.xz + ls + pwd + - name: Rename extracted folder + run: | + mv session-android-*universal extracted + ls extracted + mv extracted/*.apk extracted/session-android.apk + ls extracted + pwd + - uses: actions/checkout@v4 + with: + repository: ${{ github.event.inputs.APPIUM_REPO }} + ref: ${{ github.event.inputs.BRANCH_TO_CHECKOUT }} + path: 'forked-session-appium' + + - uses: actions/setup-node@v4 + with: + # node-version-file: 'forked-session-appium/.nvmrc' + node-version: 18.15.0 + + - name: Install yarn + run: | + npm install -g yarn + corepack enable + yarn set version 4.1.1 + - name: Install test dependencies + run: | + cd forked-session-appium + ls + git status + touch yarn.lock + yarn install --immutable + - name: Build the Android tests + run: | + cd forked-session-appium + yarn tsc + - name: Restart adb server + shell: bash + continue-on-error: true # just so we don't fail if adb wasn't alreadu running + run: | + source ./scripts/ci.sh + adb kill-server; + adb start-server; + - name: Start 4 android emulators with snapshots + shell: bash + run: | + source ./scripts/ci.sh + start_with_snapshots + - name: Run the Android tests + run: | + cd forked-session-appium + pwd + yarn test-android-shard ${{ github.event.inputs.SHARD_NUMBER }}​​/${{ github.event.inputs.SHARD_COUNT }}​​ + - name: Killall running emulators + if: always() + continue-on-error: true # just so we don't fail + shell: bash + run: | + source ./scripts/ci.sh + killall_emulators diff --git a/.gitignore b/.gitignore index 508198ea..047c1fa8 100644 --- a/.gitignore +++ b/.gitignore @@ -21,12 +21,13 @@ chromeDriver run/**/*.js *.js.map *.js -run/test/specs/to do +run/test/specs/to do to do .vscode/ .yarn/ -.env playwright.config.js test-results/ output/ -dist/ \ No newline at end of file +dist/ +avd/* +.eslintcache diff --git a/.prettierignore b/.prettierignore index 22e4be35..d9410a5a 100644 --- a/.prettierignore +++ b/.prettierignore @@ -21,11 +21,13 @@ chromeDriver run/**/*.js *.js.map *.js -run/test/specs/to do +run/test/specs/to do to do .vscode/ .yarn/ -.env playwright.config.js test-results/ -.prettierrc \ No newline at end of file +.prettierrc +README.md +package.json +/avd/ \ No newline at end of file diff --git a/README.md b/README.md index 75b47c00..5db87c8b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Once done, you should be able to start each emulators and have them running at t ## Environment variables needed -Before you can start the tests, you need to setup some environment variables: +Before you can start the tests, you need to setup some environment variables. See the file .env.sample for an example. #### ANDROID_SDK_ROOT @@ -54,7 +54,7 @@ Somehow, Appium asks for the sdk tools but do not force the adb binary to come f ## Running tests on iOS Emulators -First you need to get correct branch of Session that you want to test from Github. See [(https://github.com/oxen-io/session-ios/releases/)] and download the latest **ipa** under **Assets** +First you need to get correct branch of Session that you want to test from Github. See [(https://github.com/session-foundation/session-ios/releases/)] and download the latest **ipa** under **Assets** Then to access the **.app** file that Appium needs for testing you need to build in Xcode and then find .app in your **Derived Data** folder for Xcode. diff --git a/adbkey b/adbkey new file mode 100644 index 00000000..2159b95e --- /dev/null +++ b/adbkey @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDGvv6TARgHpm86 +ROYUGk7YJ0AT7A61Sa74YaNz2IiE8nqECpEQ25NJyg0jZluXSb99XEAFjVDOGswh +nJtsrFmG0IEQh9kiQuLOVcyW5pxLiwoRaQgdcUlrocwe/HPkdQ6FbFqm+m9v8wJ3 +6zqLp6dm3xPpIdfp11ZTvPb1uRTEXF7ciRzq1I25h7spDXVJcA4yECooD4gvWUrd +FqoxL88pn/9MVjqKYQYUeC+pUbwkdm5fn4jWBcP5zFVB2fvzup9R3C0D1vuN2V5Z +Ef9P+XjJw9TUfKTQxzAHfQsHhkau5SGut+UCwpdSn9wDcgFpQhby4rAcPN1IFshD +/BYxwfM/AgMBAAECggEAHX+3ysX8yms6UyHALKJYVYjt39xyX8IXWv0oWkd8wTmO +/w46czrED8289CYl2Ly01BHfMXPE10fu8zDgI4R+p/NGqizKXmV4beZvlBjtKzab +QJ5K+rkX4VvbL4uKVWcdHVH0hULioMZ8SqRQGkPA0GoH9dsfcFvqj59FNeT0tM2Q +oCPg4wuEhQdSSmBGHyDH1B7vPMJJ5i9Rqe2+hT8WMhIvlAdDVpA3AY9xpg2PsBYb +JSMhjbeQz5OoHSsfw6+KsQcKoA2xqnwFDUz5VtnWhnUvarVbBfPZ+LJ30NB8oz3M +i/vt7qs2BKr1Eo36cQuVtskDnq7jHbXFOXnYiUCuIQKBgQDvai3CSftMW/GEMh91 +FCY9+2TtrFQHz92bqY4EEo11mCGOXiU06U15KXuONz4ntTCoPjk/+e0+iQ0G05dT +P+zHTwiAcMkVE7NpaReArowD+xV4Bax0pLq6QYSE9/uzy4OJlrTVsGrklfAUMvsk +Sg06zgBUBPY9fIR8P/qqkiNguQKBgQDUg5cjWsfTtkx9oGHxanYSZ6b0YZXQnJP8 +/5S1RSNtEMX2yyq9b5Nyn7BrDTaSTa3fGXY//qXMOOOZArYvrDmu8MwsiW5jUrca +C6n2qTRCHKQ1DbWhy2etx7+zAzL8V1e0S18Rf8ESw63SgxkYIlQs6WwhZKLhpMYH +wAZ9mIjHtwKBgQDpmGfdUeHnty4HFgCrc2V8xHTNkX7LwH0xDFaMkq2DN1RGXIVh +AuGcfmUXdb8nbxpyjYO+cFSS15XmOz1C9PRJ40sgRutrrTU4fFl6LNRgbfOcj6yc +gr3OTW7Hbk4gN0m4TVtTxDYHomQE0VlJJxeRcL3RPKYliACcWrBxDPWHwQKBgGPZ +5ITHHccbC7unRP3SwOGK9EY1J9haCarBR9UYRDn1vJ5l/ZH+v/AGmjTig3agqwZ9 +hzMwaiSXwlDdlk62BHBvUe/HLyfZ8JB7VIPCIvzBSmJnK6V21iMwDKGG6kjonRvo +oiWtukEf0idsU7kkQBrOHnFeQK325tkzX5lZt0LnAoGBAN+ZocRrObAYtaZ9AB5h +imN+fX2tm07UbkHiIlADlVINI9X26PkzkZHUzuAoniWh+ScxBlLzA80IR8BfbK5Y +Lq6oiVq7P627938L4hQV6z0OyijV+xkz5zJG61KClbd6dUJXS0fx4ZxuD5EP8y0D +M/SZ1KBz7Vg/LLeSpdVbqDnM +-----END PRIVATE KEY----- diff --git a/adbkey.pub b/adbkey.pub new file mode 100644 index 00000000..ac71beaa --- /dev/null +++ b/adbkey.pub @@ -0,0 +1,2 @@ +QAAAAEGDuHc/88ExFvxDyBZI3TwcsOLyFkJpAXID3J9Sl8IC5beuIeWuRoYHC30HMMfQpHzU1MPJePlP/xFZXtmN+9YDLdxRn7rz+9lBVcz5wwXWiJ9fbnYkvFGpL3gUBmGKOlZM/58pzy8xqhbdSlkviA8oKhAyDnBJdQ0pu4e5jdTqHIncXlzEFLn19rxTVtfp1yHpE99mp6eLOut3AvNvb/qmWmyFDnXkc/wezKFrSXEdCGkRCotLnOaWzFXO4kIi2YcQgdCGWaxsm5whzBrOUI0FQFx9v0mXW2YjDcpJk9sQkQqEevKEiNhzo2H4rkm1DuwTQCfYThoU5kQ6b6YHGAGT/r7G4qMNYnPnzAoCHKvL7n1oNNGqrbRETN3blNtQfjzzoCAZMIE+1gAK9BMaJu2gdsn8py1Ri8wejmcwL6/1eXtjrEVXByMQh8Tv4WcgMkQQzdjkafuwOxZaLVvlCqZvSgPpCpBKPgnvRSTlQJt185/k0D+lBocKUZNQ84QDLfQhV+0+uBssaUGc9lNcXDtHC1DvhmR9gThAAJqvazHMoskprLoLqySWqAgVJs3x/bsWJhy8Cnslfx5nGk3EG3wpVrjuc9lwauFEHef/5x1M4+Ofag+iVIqoscjMKNq8TIXl0APfgm6c4CF41poUhQccfAJ2tOaSm7txfnpRiUBh4wzoHAEAAQA= @buildkitsandbox + diff --git a/eslint.config.mjs b/eslint.config.mjs index 634ad36b..215595f8 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -7,7 +7,7 @@ export default tseslint.config( files: ['**/*.{ts,tsx,cts,mts,js,cjs,mjs}'], }, { - ignores: ['**/node_modules/**', '.yarn/', 'eslint.config.mjs', 'run/**/*.js'], + ignores: ['**/node_modules/**', '.yarn/', 'eslint.config.mjs', 'run/**/*.js', 'avd/'], }, eslint.configs.recommended, ...tseslint.configs.recommendedTypeChecked, // see https://typescript-eslint.io/getting-started/typed-linting/ diff --git a/playwright.config.ts b/playwright.config.ts index 4d292248..e9565dfe 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -2,7 +2,11 @@ import dotenv from 'dotenv'; dotenv.config(); import { defineConfig } from '@playwright/test'; -import { toNumber } from 'lodash'; +import { + getRepeatEachCount, + getRetriesCount, + getWorkersCount, +} from './run/test/specs/utils/binaries'; const useSessionReporter = true; // NOTE: without this, the wrong source map is loaded and the stacktraces are all wrong @@ -16,15 +20,9 @@ export default defineConfig({ testDir: './run/test/specs', testIgnore: '*.js', // outputDir: './tests/automation/test-results', - retries: process.env.PLAYWRIGHT_RETRIES_COUNT - ? toNumber(process.env.PLAYWRIGHT_RETRIES_COUNT) - : 0, - repeatEach: process.env.PLAYWRIGHT_REPEAT_COUNT - ? toNumber(process.env.PLAYWRIGHT_REPEAT_COUNT) - : 0, - workers: process.env.PLAYWRIGHT_WORKERS_COUNT - ? toNumber(process.env.PLAYWRIGHT_WORKERS_COUNT) - : 1, + retries: getRetriesCount(), + repeatEach: getRepeatEachCount(), + workers: getWorkersCount(), reportSlowTests: null, fullyParallel: true, // otherwise, tests in the same file are not run in parallel }); diff --git a/run/test/specs/test.spec.ts b/run/test/specs/test.spec.ts index cad5df61..83737974 100644 --- a/run/test/specs/test.spec.ts +++ b/run/test/specs/test.spec.ts @@ -6,7 +6,7 @@ import { newUser } from './utils/create_account'; import { SupportedPlatformsType, closeApp, openAppOnPlatformSingleDevice } from './utils/open_app'; iosIt('Tiny test', undefined, tinyTest, true); -androidIt('Tiny test', undefined, tinyTest, true); +androidIt('Tiny test', undefined, tinyTest, false); async function tinyTest(platform: SupportedPlatformsType) { const { device } = await openAppOnPlatformSingleDevice(platform); diff --git a/run/test/specs/utils/binaries.ts b/run/test/specs/utils/binaries.ts index bbec9d34..49d326f1 100644 --- a/run/test/specs/utils/binaries.ts +++ b/run/test/specs/utils/binaries.ts @@ -1,12 +1,20 @@ import { existsSync } from 'fs'; -import { join } from 'path'; +import { isNumber, toNumber } from 'lodash'; -export const getAndroidBinariesRoot = () => { - if (process.env.APPIUM_ANDROID_BINARIES_ROOT) { - return process.env.APPIUM_ANDROID_BINARIES_ROOT; +function existsOrThrow(path: string, id: string) { + if (!existsSync(path)) { + throw new Error(`"${id}" does not exist at: ${path}`); } - throw new Error('env variable `APPIUM_ANDROID_BINARIES_ROOT` needs to be set'); -}; +} + +export function getAndroidApk() { + const fromEnv = process.env.ANDROID_APK; + if (!fromEnv) { + throw new Error('env variable `ANDROID_APK` needs to be set'); + } + + return fromEnv; +} export const getAdbFullPath = () => { const fromEnv = process.env.APPIUM_ADB_FULL_PATH; @@ -14,33 +22,57 @@ export const getAdbFullPath = () => { throw new Error('env variable `APPIUM_ADB_FULL_PATH` needs to be set'); } - if (!existsSync(fromEnv)) { - throw new Error('adb does not exist at: ' + fromEnv); - } + existsOrThrow(fromEnv, 'adb'); return fromEnv; }; export const getEmulatorFullPath = () => { - if (!process.env.APPIUM_ADB_FULL_PATH) { - throw new Error('env variable `APPIUM_ADB_FULL_PATH` needs to be set'); + if (!process.env.EMULATOR_FULL_PATH) { + throw new Error('env variable `EMULATOR_FULL_PATH` needs to be set'); } - return join(process.env.APPIUM_ADB_FULL_PATH, '..', '..', 'emulator', 'emulator'); + return process.env.EMULATOR_FULL_PATH; }; export const getAvdManagerFullPath = () => { - if (!process.env.APPIUM_ADB_FULL_PATH) { - throw new Error('env variable `APPIUM_ADB_FULL_PATH` needs to be set'); + if (!process.env.AVD_MANAGER_FULL_PATH) { + throw new Error('env variable `AVD_MANAGER_FULL_PATH` needs to be set'); } - return join( - process.env.APPIUM_ADB_FULL_PATH, - '..', - '..', - 'cmdline-tools', - 'latest', - 'bin', - 'avdmanager' - ); + return process.env.AVD_MANAGER_FULL_PATH; +}; + +export const getSdkManagerFullPath = () => { + if (!process.env.SDK_MANAGER_FULL_PATH) { + throw new Error('env variable `SDK_MANAGER_FULL_PATH` needs to be set'); + } + + return process.env.SDK_MANAGER_FULL_PATH; +}; + +export const getAndroidSystemImageToUse = () => { + if (!process.env.ANDROID_SYSTEM_IMAGE) { + throw new Error('env variable `ANDROID_SYSTEM_IMAGE` needs to be set'); + } + + return process.env.ANDROID_SYSTEM_IMAGE; +}; + +export const getRetriesCount = () => { + return isNumber(process.env.PLAYWRIGHT_RETRIES_COUNT) + ? toNumber(process.env.PLAYWRIGHT_RETRIES_COUNT) + : 0; +}; + +export const getRepeatEachCount = () => { + return isNumber(process.env.PLAYWRIGHT_REPEAT_COUNT) + ? toNumber(process.env.PLAYWRIGHT_REPEAT_COUNT) + : 0; +}; + +export const getWorkersCount = () => { + return isNumber(process.env.PLAYWRIGHT_WORKERS_COUNT) + ? toNumber(process.env.PLAYWRIGHT_WORKERS_COUNT) + : 1; }; diff --git a/run/test/specs/utils/capabilities_android.ts b/run/test/specs/utils/capabilities_android.ts index b4a012c7..cbbbc9d4 100644 --- a/run/test/specs/utils/capabilities_android.ts +++ b/run/test/specs/utils/capabilities_android.ts @@ -6,16 +6,12 @@ import { import dotenv from 'dotenv'; import { isString } from 'lodash'; import { CapabilitiesIndexType } from './capabilities_ios'; +import { getAndroidApk } from './binaries'; dotenv.config(); // Access the environment variable -const androidPathPrefix = process.env.ANDROID_APP_PATH_PREFIX; - -if (!androidPathPrefix) { - throw new Error('ANDROID_APP_PATH_PREFIX environment variable is not set'); -} // Concatenate the environment variable with the fixed part of the path -const androidAppFullPath = `${androidPathPrefix}`; +const androidAppFullPath = getAndroidApk(); console.log(`Android app full path: ${androidAppFullPath}`); diff --git a/run/test/specs/utils/open_app.ts b/run/test/specs/utils/open_app.ts index b419f329..c00d018f 100644 --- a/run/test/specs/utils/open_app.ts +++ b/run/test/specs/utils/open_app.ts @@ -1,6 +1,6 @@ import { getAndroidCapabilities, getAndroidUdid } from './capabilities_android'; import { CapabilitiesIndexType, capabilityIsValid, getIosCapabilities } from './capabilities_ios'; -import { runScriptAndLog } from './utilities'; +import { isCI, runScriptAndLog } from './utilities'; import { XCUITestDriverOpts } from 'appium-xcuitest-driver/build/lib/driver'; import AndroidUiautomator2Driver from 'appium-uiautomator2-driver'; @@ -10,14 +10,18 @@ import { compact } from 'lodash'; import { DeviceWrapper } from '../../../types/DeviceWrapper'; import { User, USERNAME } from '../../../types/testing'; import { cleanPermissions } from './permissions'; -import { getAdbFullPath, getAvdManagerFullPath, getEmulatorFullPath } from './binaries'; import { newUser } from './create_account'; import { newContact } from './create_contact'; import { linkedDevice } from './link_device'; import { sleepFor } from './sleep_for'; +import { + getAdbFullPath, + getAndroidSystemImageToUse, + getEmulatorFullPath, + getSdkManagerFullPath, +} from './binaries'; const APPIUM_PORT = 4728; -export const APPIUM_IOS_PORT = 8110; export type SupportedPlatformsType = 'android' | 'ios'; @@ -152,7 +156,15 @@ export const openAppFourDevices = async ( }; async function createAndroidEmulator(emulatorName: string) { - const createCmd = `echo "no" | ${getAvdManagerFullPath()} create avd --name ${emulatorName} -k 'system-images;android-31;google_apis;arm64-v8a' --force --skin pixel_5`; + if (isCI()) { + // on CI, emulators are created during the docker build step. + return emulatorName; + } + const installSystemImageCmd = `${getSdkManagerFullPath()} --install '${getAndroidSystemImageToUse()}'`; + console.warn(installSystemImageCmd); + await runScriptAndLog(installSystemImageCmd); + + const createCmd = `echo "no" | ${getSdkManagerFullPath()} create avd --name ${emulatorName} -k '${getAndroidSystemImageToUse()}' --force --skin pixel_5`; console.info(createCmd); await runScriptAndLog(createCmd); return emulatorName; @@ -161,7 +173,7 @@ async function createAndroidEmulator(emulatorName: string) { async function startAndroidEmulator(emulatorName: string) { await runScriptAndLog(`echo "hw.lcd.density=440" >> ~/.android/avd/${emulatorName}.avd/config.ini `); - const startEmulatorCmd = `${getEmulatorFullPath()} @${emulatorName} -no-snapshot`; + const startEmulatorCmd = `${getEmulatorFullPath()} @${emulatorName}`; console.info(`${startEmulatorCmd} & ; disown`); await runScriptAndLog( startEmulatorCmd // -netdelay none -no-snapshot -wipe-data @@ -184,6 +196,7 @@ async function waitForEmulatorToBeRunning(emulatorName: string) { } while (Date.now() - start < 25000 && !found); if (!found) { + console.warn('isEmulatorRunning failed for 25s'); throw new Error('timedout waiting for emulator to start'); } diff --git a/run/test/specs/utils/utilities.ts b/run/test/specs/utils/utilities.ts index c95d530c..487e0284 100644 --- a/run/test/specs/utils/utilities.ts +++ b/run/test/specs/utils/utilities.ts @@ -1,6 +1,5 @@ import { pick } from 'lodash'; import * as util from 'util'; -import { getAdbFullPath } from './binaries'; import { exec as execNotPromised } from 'child_process'; const exec = util.promisify(execNotPromised); @@ -37,42 +36,12 @@ export async function runScriptAndLog(toRun: string, verbose = false): Promise { - if (!emulatorName) { - throw new Error('emulatorName must be set'); - } - // If needing logs uncomment this - // await runScriptAndLog(`emulator -avd ${emulatorName}`, true); - const adb = getAdbFullPath(); - - // await runScriptAndLog(`${adb} -s ${emulatorName} uninstall io.appium.uiautomator2.server`); - // await runScriptAndLog(`${adb} -s ${emulatorName} uninstall io.appium.uiautomator2.server.test`); - // await runScriptAndLog(`${adb} -s ${emulatorName} uninstall io.appium.unlock`); - // await runScriptAndLog(`${adb} -s ${emulatorName} uninstall io.appium.settings`); - // await runScriptAndLog( - // `${adb} -s ${emulatorName} install -g ./node_modules/appium/node_modules/appium-uiautomator2-server/apks/appium-uiautomator2-server-debug-androidTest.apk` - // ); - // await runScriptAndLog( - // `${adb} -s ${emulatorName} install -g ./node_modules/appium/node_modules/appium-uiautomator2-server/apks/appium-uiautomator2-server-v4.27.0.apk` - // ); - // await sleepFor(100); - // await runScriptAndLog( - // `${adb} -s ${emulatorName} install -g ./node_modules/appium/node_modules/io.appium.settings/apks/settings_apk-debug.apk` - // ); - // await sleepFor(100); - await runScriptAndLog(`${adb} -s ${emulatorName} install -g -t ${appFullPath}`, true); -}; - export const isDeviceIOS = (device: unknown) => { return (device as any).originalCaps.alwaysMatch['appium:platformName']?.toLowerCase() === 'ios'; }; export const isDeviceAndroid = (device: unknown) => !isDeviceIOS(device); + +export const isCI = () => { + return process.env.NODE_CONFIG_ENV === 'ci'; +}; diff --git a/run/types/DeviceWrapper.ts b/run/types/DeviceWrapper.ts index 66936a67..6c3b6fad 100644 --- a/run/types/DeviceWrapper.ts +++ b/run/types/DeviceWrapper.ts @@ -17,7 +17,6 @@ import { englishStripped, TokenString } from '../localizer/i18n/localizedString' import { LocalizerDictionary } from '../localizer/Localizer'; import { ModalDescription, ModalHeading } from '../test/specs/locators/global'; import { clickOnCoordinates, sleepFor } from '../test/specs/utils'; -import { getAdbFullPath } from '../test/specs/utils/binaries'; import { SupportedPlatformsType } from '../test/specs/utils/open_app'; import { isDeviceAndroid, isDeviceIOS, runScriptAndLog } from '../test/specs/utils/utilities'; import { @@ -32,6 +31,7 @@ import { XPath, } from './testing'; import { SaveProfilePictureButton, UserSettings } from '../test/specs/locators/settings'; +import { getAdbFullPath } from '../test/specs/utils/binaries'; export type Coordinates = { x: number; diff --git a/scripts/ci.sh b/scripts/ci.sh new file mode 100644 index 00000000..64296738 --- /dev/null +++ b/scripts/ci.sh @@ -0,0 +1,101 @@ +#!/bin/bash + + +ARCH="x86_64" +TARGET="google_apis_playstore" +API_LEVEL="34" +BUILD_TOOLS="34.0.0" +ANDROID_ARCH=${ANDROID_ARCH_DEFAULT} +ANDROID_API_LEVEL="android-${API_LEVEL}" +ANDROID_APIS="${TARGET};${ARCH}" +EMULATOR_PACKAGE="system-images;${ANDROID_API_LEVEL};${ANDROID_APIS}" +PLATFORM_VERSION="platforms;${ANDROID_API_LEVEL}" +BUILD_TOOL="build-tools;${BUILD_TOOLS}" +ANDROID_CMD="commandlinetools-linux-11076708_latest.zip" +export ANDROID_SDK_PACKAGES="${EMULATOR_PACKAGE} ${PLATFORM_VERSION} ${BUILD_TOOL} platform-tools" +export ANDROID_SDK_ROOT=/opt/android + +export PATH="$PATH:$ANDROID_SDK_ROOT/cmdline-tools/tools:$ANDROID_SDK_ROOT/cmdline-tools/tools/bin:$ANDROID_SDK_ROOT/emulator:$ANDROID_SDK_ROOT/tools/bin:$ANDROID_SDK_ROOT/latform-tools:$ANDROID_SDK_ROOT/build-tools/${BUILD_TOOLS}:$ANDROID_SDK_ROOT/platform-tools/" +export EMULATOR_DEVICE="pixel_6" # all emulators are created with the pixel 6 spec for now + + +# this should only be done when we bump the API version or add a worker to the CI that needs emulators to be setup +# Once you've run this, you must also start_for_snapshots() and force_save_snapshots() (see details below) +function create_emulators() { + sudo apt update + sudo apt install -y ca-certificates curl git vim bash wget unzip tree htop gzip default-jre libnss3 libxcursor1 libqt5gui5 libc++-dev libxcb-cursor0 htop tree tar gzip gh nload + + sudo rm -rf $ANDROID_SDK_ROOT + + sudo mkdir -p $ANDROID_SDK_ROOT + sudo chown $USER:$USER $ANDROID_SDK_ROOT + + wget https://dl.google.com/android/repository/${ANDROID_CMD} -P /tmp && \ + unzip -d $ANDROID_SDK_ROOT /tmp/$ANDROID_CMD && \ + mkdir -p $ANDROID_SDK_ROOT/cmdline-tools/tools && cd $ANDROID_SDK_ROOT/cmdline-tools && mv NOTICE.txt source.properties bin lib tools/ && \ + cd $ANDROID_SDK_ROOT/cmdline-tools/tools && ls + + yes Y | sdkmanager --licenses + yes Y | sdkmanager --verbose --no_https ${ANDROID_SDK_PACKAGES} + + + adb devices + + + yes | sdkmanager emulator + + # make sure to start-adb at least once so it generates a key before we create the avd + + adb start-server + + for i in {1..4} + do + echo "no" | avdmanager --verbose create avd --force --name "emulator$i" --device "${EMULATOR_DEVICE}" --package "${EMULATOR_PACKAGE}" + # Path to the AVD's config.ini file + CONFIG_FILE="$HOME/.android/avd/emulator$i.avd/config.ini" + + # Set the RAM size to 6GB (6144MB) + sed -i 's/^hw\.ramSize=.*/hw.ramSize=6144/' "$CONFIG_FILE" + + done + + cd +} + + + + +function start_for_snapshots() { + for i in {1..4} + do + DISPLAY=:0 emulator @emulator$i -gpu host -accel on -no-snapshot-load & + sleep 20 + done +} + +# let the emulators start and be ready (check cpu usage) before calling this. +# We want to take a snapshot woth emulators state as "done" as we can +function force_save_snapshots() { + values=("5554" "5556" "5558" "5560" "5562" "5564" "5566" "5568") + for val in "${values[@]}" + do + adb -s emulator-$val emu avd snapshot save plop.snapshot + done +} + +function killall_emulators() { + killall qemu-system-x86_64; +} + + +function start_with_snapshots() { + for i in {1..4} + do + EMU_CONFIG_FILE="$HOME/.android/avd/emulator$i.avd/emulator-user.ini" + # set the position fo each emulator to be next to the previous one + sed -i "s/^window.x.*/window.x=$(( 100 + (i-1) * 400))/" "$EMU_CONFIG_FILE" + + DISPLAY=:0 emulator @emulator$i -gpu host -accel on -no-snapshot-save -snapshot plop.snapshot -force-snapshot-load & + sleep 5 + done +} \ No newline at end of file