Add force-avd-creation to skip avd creation if avd with same name exists. Update workflow to test snapshot caching.

This commit is contained in:
Yang
2021-06-18 04:13:06 +10:00
parent 98e515da59
commit 854ed15ed0
11 changed files with 145 additions and 31 deletions
+34 -3
View File
@@ -64,21 +64,52 @@ jobs:
~/.gradle/caches ~/.gradle/caches
~/.gradle/wrapper ~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }} key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
- uses: actions/cache@v2
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
~/.android/debug.keystore
key: avd-${{ matrix.api-level }}-${{ matrix.os }}-${{ matrix.target }}
- name: assemble tests
run: |
cd ./test-fixture/
./gradlew assembleAndroidTest
- name: run emulator to generate snapshot for caching
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: ./
with:
api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }}
arch: x86
profile: Galaxy Nexus
cores: 2
sdcard-path-or-size: 100M
avd-name: test
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: false
working-directory: ./test-fixture/
script: echo "Generated AVD snapshot for caching."
- name: run action - name: run action
uses: ./ uses: ./
with: with:
api-level: ${{ matrix.api-level }} api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }} target: ${{ matrix.target }}
arch: x86 arch: x86
profile: Nexus 6 profile: Galaxy Nexus
cores: 2 cores: 2
sdcard-path-or-size: 100M sdcard-path-or-size: 100M
avd-name: test avd-name: test
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-back none force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true disable-animations: true
working-directory: ./test-fixture/ working-directory: ./test-fixture/
script: | script: |
echo $GITHUB_REPOSITORY echo $GITHUB_REPOSITORY
adb devices adb devices
./gradlew help
./gradlew connectedDebugAndroidTest ./gradlew connectedDebugAndroidTest
+21
View File
@@ -82,6 +82,27 @@ describe('arch validator tests', () => {
}); });
}); });
describe('force-avd-creation validator tests', () => {
it('Throws if force-avd-creation is not a boolean', () => {
const func = () => {
validator.checkForceAvdCreation('yes');
};
expect(func).toThrowError(`Input for input.force-avd-creation should be either 'true' or 'false'.`);
});
it('Validates successfully if force-avd-creation is either true or false', () => {
const func1 = () => {
validator.checkForceAvdCreation('true');
};
expect(func1).not.toThrow();
const func2 = () => {
validator.checkForceAvdCreation('false');
};
expect(func2).not.toThrow();
});
});
describe('disable-animations validator tests', () => { describe('disable-animations validator tests', () => {
it('Throws if disable-animations is not a boolean', () => { it('Throws if disable-animations is not a boolean', () => {
const func = () => { const func = () => {
+5 -2
View File
@@ -24,6 +24,9 @@ inputs:
avd-name: avd-name:
description: 'custom AVD name used for creating the Android Virtual Device' description: 'custom AVD name used for creating the Android Virtual Device'
default: 'test' default: 'test'
force-avd-creation:
description: 'whether to force create the AVD by overwriting an existing AVD with the same name as `avd-name` - `true` or `false`'
default: 'true'
emulator-options: emulator-options:
description: 'command-line options used when launching the emulator - e.g. `-no-window -no-snapshot -camera-back emulated`' description: 'command-line options used when launching the emulator - e.g. `-no-window -no-snapshot -camera-back emulated`'
default: '-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim' default: '-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim'
@@ -31,10 +34,10 @@ inputs:
description: 'whether to disable animations - true or false' description: 'whether to disable animations - true or false'
default: 'true' default: 'true'
disable-spellchecker: disable-spellchecker:
description: Whether to disable spellchecker - `true` or `false`. description: 'whether to disable spellchecker - `true` or `false`'
default: 'false' default: 'false'
disable-linux-hw-accel: disable-linux-hw-accel:
description: Whether to disable hardware acceleration on Linux machines - `true` or `false`. description: 'whether to disable hardware acceleration on Linux machines - `true` or `false`'
default: 'true' default: 'true'
emulator-build: emulator-build:
description: 'build number of a specific version of the emulator binary to use - e.g. `6061023` for emulator v29.3.0.0' description: 'build number of a specific version of the emulator binary to use - e.g. `6061023` for emulator v29.3.0.0'
+10 -6
View File
@@ -30,27 +30,31 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.killEmulator = exports.launchEmulator = void 0; exports.killEmulator = exports.launchEmulator = void 0;
const exec = __importStar(require("@actions/exec")); const exec = __importStar(require("@actions/exec"));
const fs = __importStar(require("fs"));
const EMULATOR_BOOT_TIMEOUT_SECONDS = 600; const EMULATOR_BOOT_TIMEOUT_SECONDS = 600;
/** /**
* Creates and launches a new AVD instance with the specified configurations. * Creates and launches a new AVD instance with the specified configurations.
*/ */
function launchEmulator(apiLevel, target, arch, profile, cores, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration) { function launchEmulator(apiLevel, target, arch, profile, cores, sdcardPathOrSize, avdName, forceAvdCreation, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
// create a new AVD // create a new AVD if AVD directory does not already exist or forceAvdCreation is true
const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`;
if (!fs.existsSync(avdPath) || forceAvdCreation) {
const profileOption = profile.trim() !== '' ? `--device '${profile}'` : ''; const profileOption = profile.trim() !== '' ? `--device '${profile}'` : '';
const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : ''; const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : '';
console.log(`Creating AVD.`); console.log(`Creating AVD.`);
yield exec.exec(`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`); yield exec.exec(`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`);
if (cores) {
yield exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ~/.android/avd/"${avdName}".avd"/config.ini`);
} }
// start emulator if (cores) {
console.log('Starting emulator.'); yield exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
}
//turn off hardware acceleration on Linux //turn off hardware acceleration on Linux
if (process.platform === 'linux' && disableLinuxHardwareAcceleration) { if (process.platform === 'linux' && disableLinuxHardwareAcceleration) {
console.log('Disabling Linux hardware acceleration.'); console.log('Disabling Linux hardware acceleration.');
emulatorOptions += ' -accel off'; emulatorOptions += ' -accel off';
} }
// start emulator
console.log('Starting emulator.');
yield exec.exec(`sh -c \\"${process.env.ANDROID_SDK_ROOT}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], { yield exec.exec(`sh -c \\"${process.env.ANDROID_SDK_ROOT}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], {
listeners: { listeners: {
stderr: (data) => { stderr: (data) => {
+7 -1
View File
@@ -1,6 +1,6 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.checkEmulatorBuild = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkArch = exports.checkTarget = exports.checkApiLevel = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0; exports.checkEmulatorBuild = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkForceAvdCreation = exports.checkArch = exports.checkTarget = exports.checkApiLevel = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0;
exports.MIN_API_LEVEL = 15; exports.MIN_API_LEVEL = 15;
exports.VALID_TARGETS = ['default', 'google_apis', 'google_apis_playstore']; exports.VALID_TARGETS = ['default', 'google_apis', 'google_apis_playstore'];
exports.VALID_ARCHS = ['x86', 'x86_64', 'arm64-v8a']; exports.VALID_ARCHS = ['x86', 'x86_64', 'arm64-v8a'];
@@ -25,6 +25,12 @@ function checkArch(arch) {
} }
} }
exports.checkArch = checkArch; exports.checkArch = checkArch;
function checkForceAvdCreation(forceAvdCreation) {
if (!isValidBoolean(forceAvdCreation)) {
throw new Error(`Input for input.force-avd-creation should be either 'true' or 'false'.`);
}
}
exports.checkForceAvdCreation = checkForceAvdCreation;
function checkDisableAnimations(disableAnimations) { function checkDisableAnimations(disableAnimations) {
if (!isValidBoolean(disableAnimations)) { if (!isValidBoolean(disableAnimations)) {
throw new Error(`Input for input.disable-animations should be either 'true' or 'false'.`); throw new Error(`Input for input.disable-animations should be either 'true' or 'false'.`);
+6 -1
View File
@@ -72,6 +72,11 @@ function run() {
// custom name used for creating the AVD // custom name used for creating the AVD
const avdName = core.getInput('avd-name'); const avdName = core.getInput('avd-name');
console.log(`AVD name: ${avdName}`); console.log(`AVD name: ${avdName}`);
// force AVD creation
const forceAvdCreationInput = core.getInput('force-avd-creation');
input_validator_1.checkForceAvdCreation(forceAvdCreationInput);
const forceAvdCreation = forceAvdCreationInput === 'true';
console.log(`force avd creation: ${forceAvdCreation}`);
// emulator options // emulator options
const emulatorOptions = core.getInput('emulator-options').trim(); const emulatorOptions = core.getInput('emulator-options').trim();
console.log(`emulator options: ${emulatorOptions}`); console.log(`emulator options: ${emulatorOptions}`);
@@ -125,7 +130,7 @@ function run() {
// install SDK // install SDK
yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion); yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion);
// launch an emulator // launch an emulator
yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, cores, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration); yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, cores, sdcardPathOrSize, avdName, forceAvdCreation, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration);
// execute the custom script // execute the custom script
try { try {
// move to custom working directory if set // move to custom working directory if set
+2
View File
@@ -57,6 +57,8 @@ function installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cm
} }
// add paths for commandline-tools and platform-tools // add paths for commandline-tools and platform-tools
core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_SDK_ROOT}/platform-tools`); core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_SDK_ROOT}/platform-tools`);
// set standard AVD path
core.exportVariable('ANDROID_AVD_HOME', `${process.env.HOME}/.android/avd`);
// additional permission and license requirements for Linux // additional permission and license requirements for Linux
const sdkPreviewLicensePath = `${process.env.ANDROID_SDK_ROOT}/licenses/android-sdk-preview-license`; const sdkPreviewLicensePath = `${process.env.ANDROID_SDK_ROOT}/licenses/android-sdk-preview-license`;
if (!isOnMac && !fs.existsSync(sdkPreviewLicensePath)) { if (!isOnMac && !fs.existsSync(sdkPreviewLicensePath)) {
+11 -6
View File
@@ -1,4 +1,5 @@
import * as exec from '@actions/exec'; import * as exec from '@actions/exec';
import * as fs from 'fs';
const EMULATOR_BOOT_TIMEOUT_SECONDS = 600; const EMULATOR_BOOT_TIMEOUT_SECONDS = 600;
@@ -13,25 +14,26 @@ export async function launchEmulator(
cores: string, cores: string,
sdcardPathOrSize: string, sdcardPathOrSize: string,
avdName: string, avdName: string,
forceAvdCreation: boolean,
emulatorOptions: string, emulatorOptions: string,
disableAnimations: boolean, disableAnimations: boolean,
disableSpellChecker: boolean, disableSpellChecker: boolean,
disableLinuxHardwareAcceleration: boolean disableLinuxHardwareAcceleration: boolean
): Promise<void> { ): Promise<void> {
// create a new AVD // create a new AVD if AVD directory does not already exist or forceAvdCreation is true
const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`;
if (!fs.existsSync(avdPath) || forceAvdCreation) {
const profileOption = profile.trim() !== '' ? `--device '${profile}'` : ''; const profileOption = profile.trim() !== '' ? `--device '${profile}'` : '';
const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : ''; const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : '';
console.log(`Creating AVD.`); console.log(`Creating AVD.`);
await exec.exec( await exec.exec(
`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"` `sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`
); );
if (cores) {
await exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ~/.android/avd/"${avdName}".avd"/config.ini`);
} }
// start emulator if (cores) {
console.log('Starting emulator.'); await exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
}
//turn off hardware acceleration on Linux //turn off hardware acceleration on Linux
if (process.platform === 'linux' && disableLinuxHardwareAcceleration) { if (process.platform === 'linux' && disableLinuxHardwareAcceleration) {
@@ -39,6 +41,9 @@ export async function launchEmulator(
emulatorOptions += ' -accel off'; emulatorOptions += ' -accel off';
} }
// start emulator
console.log('Starting emulator.');
await exec.exec(`sh -c \\"${process.env.ANDROID_SDK_ROOT}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], { await exec.exec(`sh -c \\"${process.env.ANDROID_SDK_ROOT}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], {
listeners: { listeners: {
stderr: (data: Buffer) => { stderr: (data: Buffer) => {
+6
View File
@@ -23,6 +23,12 @@ export function checkArch(arch: string): void {
} }
} }
export function checkForceAvdCreation(forceAvdCreation: string): void {
if (!isValidBoolean(forceAvdCreation)) {
throw new Error(`Input for input.force-avd-creation should be either 'true' or 'false'.`);
}
}
export function checkDisableAnimations(disableAnimations: string): void { export function checkDisableAnimations(disableAnimations: string): void {
if (!isValidBoolean(disableAnimations)) { if (!isValidBoolean(disableAnimations)) {
throw new Error(`Input for input.disable-animations should be either 'true' or 'false'.`); throw new Error(`Input for input.disable-animations should be either 'true' or 'false'.`);
+30 -2
View File
@@ -1,6 +1,15 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import { installAndroidSdk } from './sdk-installer'; import { installAndroidSdk } from './sdk-installer';
import { checkApiLevel, checkTarget, checkArch, checkDisableAnimations, checkEmulatorBuild, checkDisableSpellchecker, checkDisableLinuxHardwareAcceleration } from './input-validator'; import {
checkApiLevel,
checkTarget,
checkArch,
checkDisableAnimations,
checkEmulatorBuild,
checkDisableSpellchecker,
checkDisableLinuxHardwareAcceleration,
checkForceAvdCreation
} from './input-validator';
import { launchEmulator, killEmulator } from './emulator-manager'; import { launchEmulator, killEmulator } from './emulator-manager';
import * as exec from '@actions/exec'; import * as exec from '@actions/exec';
import { parseScript } from './script-parser'; import { parseScript } from './script-parser';
@@ -51,6 +60,12 @@ async function run() {
const avdName = core.getInput('avd-name'); const avdName = core.getInput('avd-name');
console.log(`AVD name: ${avdName}`); console.log(`AVD name: ${avdName}`);
// force AVD creation
const forceAvdCreationInput = core.getInput('force-avd-creation');
checkForceAvdCreation(forceAvdCreationInput);
const forceAvdCreation = forceAvdCreationInput === 'true';
console.log(`force avd creation: ${forceAvdCreation}`);
// emulator options // emulator options
const emulatorOptions = core.getInput('emulator-options').trim(); const emulatorOptions = core.getInput('emulator-options').trim();
console.log(`emulator options: ${emulatorOptions}`); console.log(`emulator options: ${emulatorOptions}`);
@@ -114,7 +129,20 @@ async function run() {
await installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion); await installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion);
// launch an emulator // launch an emulator
await launchEmulator(apiLevel, target, arch, profile, cores, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration); await launchEmulator(
apiLevel,
target,
arch,
profile,
cores,
sdcardPathOrSize,
avdName,
forceAvdCreation,
emulatorOptions,
disableAnimations,
disableSpellchecker,
disableLinuxHardwareAcceleration
);
// execute the custom script // execute the custom script
try { try {
+3
View File
@@ -31,6 +31,9 @@ export async function installAndroidSdk(apiLevel: number, target: string, arch:
// add paths for commandline-tools and platform-tools // add paths for commandline-tools and platform-tools
core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_SDK_ROOT}/platform-tools`); core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_SDK_ROOT}/platform-tools`);
// set standard AVD path
core.exportVariable('ANDROID_AVD_HOME', `${process.env.HOME}/.android/avd`);
// additional permission and license requirements for Linux // additional permission and license requirements for Linux
const sdkPreviewLicensePath = `${process.env.ANDROID_SDK_ROOT}/licenses/android-sdk-preview-license`; const sdkPreviewLicensePath = `${process.env.ANDROID_SDK_ROOT}/licenses/android-sdk-preview-license`;
if (!isOnMac && !fs.existsSync(sdkPreviewLicensePath)) { if (!isOnMac && !fs.existsSync(sdkPreviewLicensePath)) {