Repository: ReactiveCircus/android-emulator-runner (js)

Issue:
Allow non-integer api-level
I would like to use upsidedown cake for the unit test. But the `api-level` only allows integers. How about allowing a non-integer `api-level`?

Hilariously, I posted this request since tiramusu #225 but nearly a year past, this is still not supported.


Pull request:
Support non-integer API level
Fix #315 

Patch:
diff --git a/lib/input-validator.js b/lib/input-validator.js
index 95959a5..7839358 100644
--- a/lib/input-validator.js
+++ b/lib/input-validator.js
@@ -6,6 +6,8 @@ exports.VALID_TARGETS = ['default', 'google_apis', 'aosp_atd', 'google_atd', 'go
 exports.VALID_ARCHS = ['x86', 'x86_64', 'arm64-v8a'];
 exports.VALID_CHANNELS = ['stable', 'beta', 'dev', 'canary'];
 function checkApiLevel(apiLevel) {
+    if (apiLevel.startsWith('UpsideDownCake') || apiLevel === 'TiramisuPrivacySandbox')
+        return;
     if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) {
         throw new Error(`Unexpected API level: '${apiLevel}'.`);
     }
diff --git a/lib/main.js b/lib/main.js
index a4fd8d3..99ce8da 100644
--- a/lib/main.js
+++ b/lib/main.js
@@ -61,9 +61,8 @@ function run() {
                 }
             }
             // API level of the platform and system image
-            const apiLevelInput = core.getInput('api-level', { required: true });
-            (0, input_validator_1.checkApiLevel)(apiLevelInput);
-            const apiLevel = Number(apiLevelInput);
+            const apiLevel = core.getInput('api-level', { required: true });
+            (0, input_validator_1.checkApiLevel)(apiLevel);
             console.log(`API level: ${apiLevel}`);
             // target of the system image
             const targetInput = core.getInput('target');
diff --git a/lib/sdk-installer.js b/lib/sdk-installer.js
index a224329..781ec61 100644
--- a/lib/sdk-installer.js
+++ b/lib/sdk-installer.js
@@ -68,7 +68,7 @@ function installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndk
             // accept all Android SDK licenses
             yield exec.exec(`sh -c \\"yes | sdkmanager --licenses > /dev/null"`);
             console.log('Installing latest build tools, platform tools, and platform.');
-            yield exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}' > /dev/null"`);
+            yield exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools > /dev/null"`);
             console.log('Installing latest emulator.');
             yield exec.exec(`sh -c \\"sdkmanager --install emulator --channel=${channelId} > /dev/null"`);
             if (emulatorBuild) {
diff --git a/src/emulator-manager.ts b/src/emulator-manager.ts
index 4c348fb..b596249 100644
--- a/src/emulator-manager.ts
+++ b/src/emulator-manager.ts
@@ -7,7 +7,7 @@ const EMULATOR_BOOT_TIMEOUT_SECONDS = 600;
  * Creates and launches a new AVD instance with the specified configurations.
  */
 export async function launchEmulator(
-  apiLevel: number,
+  apiLevel: string,
   target: string,
   arch: string,
   profile: string,
diff --git a/src/input-validator.ts b/src/input-validator.ts
index 7cf1e1b..e52d792 100644
--- a/src/input-validator.ts
+++ b/src/input-validator.ts
@@ -4,6 +4,7 @@ export const VALID_ARCHS: Array<string> = ['x86', 'x86_64', 'arm64-v8a'];
 export const VALID_CHANNELS: Array<string> = ['stable', 'beta', 'dev', 'canary'];
 
 export function checkApiLevel(apiLevel: string): void {
+  if (apiLevel.startsWith('UpsideDownCake') || apiLevel === 'TiramisuPrivacySandbox') return;
   if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) {
     throw new Error(`Unexpected API level: '${apiLevel}'.`);
   }
diff --git a/src/main.ts b/src/main.ts
index 48827a9..4cb0424 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -40,9 +40,8 @@ async function run() {
     }
 
     // API level of the platform and system image
-    const apiLevelInput = core.getInput('api-level', { required: true });
-    checkApiLevel(apiLevelInput);
-    const apiLevel = Number(apiLevelInput);
+    const apiLevel = core.getInput('api-level', { required: true });
+    checkApiLevel(apiLevel);
     console.log(`API level: ${apiLevel}`);
 
     // target of the system image
diff --git a/src/sdk-installer.ts b/src/sdk-installer.ts
index a421f2f..4383c6b 100644
--- a/src/sdk-installer.ts
+++ b/src/sdk-installer.ts
@@ -12,7 +12,7 @@ const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/comman
  * Installs & updates the Android SDK for the macOS platform, including SDK platform for the chosen API level, latest build tools, platform tools, Android Emulator,
  * and the system image for the chosen API level, CPU arch, and target.
  */
-export async function installAndroidSdk(apiLevel: number, target: string, arch: string, channelId: number, emulatorBuild?: string, ndkVersion?: string, cmakeVersion?: string): Promise<void> {
+export async function installAndroidSdk(apiLevel: string, target: string, arch: string, channelId: number, emulatorBuild?: string, ndkVersion?: string, cmakeVersion?: string): Promise<void> {
   try {
     console.log(`::group::Install Android SDK`);
     const isOnMac = process.platform === 'darwin';
@@ -41,7 +41,7 @@ export async function installAndroidSdk(apiLevel: number, target: string, arch:
 
     console.log('Installing latest build tools, platform tools, and platform.');
 
-    await exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}' > /dev/null"`);
+    await exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools > /dev/null"`);
 
     console.log('Installing latest emulator.');
     await exec.exec(`sh -c \\"sdkmanager --install emulator --channel=${channelId} > /dev/null"`);


Test patch:
diff --git a/__tests__/input-validator.test.ts b/__tests__/input-validator.test.ts
index ba7e20e..59201f4 100644
--- a/__tests__/input-validator.test.ts
+++ b/__tests__/input-validator.test.ts
@@ -32,6 +32,14 @@ describe('api-level validator tests', () => {
       validator.checkApiLevel('29');
     };
     expect(func2).not.toThrow();
+    const func3 = () => {
+      validator.checkApiLevel('UpsideDownCake-ext5');
+    };
+    expect(func3).not.toThrow();
+    const func4 = () => {
+      validator.checkApiLevel('TiramisuPrivacySandbox');
+    };
+    expect(func4).not.toThrow();
   });
 });