Repository: ReactiveCircus/android-emulator-runner
Pull request #219 (2022-02-01 17:17:02)

Allow increasing main storage size (sdcard is separate storage)
Android has two drives - main storage for apps and external storage for downloads and user files.
`sdcard-path-or-size` controls the second - external storage.

# The Problem

Older devices default to 800M main storage. After the system apps, there is 309M left.
Most today's apps take more than that. Debug versions are not minimized, instrumented tests take extra space. Long story short - my app cannot fit. I get `INSTALL_FAILED_INSUFFICIENT_STORAGE` and related errors. Issue #184 is caused by this.

![800M](https://user-images.githubusercontent.com/812793/152016476-2b0d8768-13ac-4228-b78f-d98c8262ea7c.png)

# The PR

There is no option in `avdcreate`, but `/Users/runner/.android/avd/${avdName}.avd/config.ini` has the config option we want: `disk.dataPartition.size=800M`

This PR would allow appending a new `disk.dataPartition.size` size, when supplied as action argument named `disk-size`.

# The Workaround

Until this or similar PR is merged, I have a workaround:

```yaml
- name: Create AVD
  uses: reactivecircus/android-emulator-runner@v2
  with:
    api-level: 21
    arch: x86_64
    target: google_apis
    script: echo "Generated AVD snapshot."

- name: increate storage size for emulator
  run: echo "disk.dataPartition.size=2G" >> /Users/runner/.android/avd/test.avd/config.ini

- name: Run tests
  uses: reactivecircus/android-emulator-runner@v2
  with:
    api-level: 21
    arch: x86_64
    target: google_apis
    force-avd-creation: false
    script: ./gradlew connectedCheck
```

ViliusSutkus89: As an alternative, we could parse config.ini and increase this size for low storage devices without any user supplied input.

Patch:
diff --git a/README.md b/README.md
index 8de39b5..429b6c7 100644
--- a/README.md
+++ b/README.md
@@ -149,6 +149,7 @@ jobs:
 | `cores` | Optional | 2 | Number of cores to use for the emulator (`hw.cpu.ncore` in config.ini). |
 | `ram-size` | Optional | N/A | Size of RAM to use for this AVD, in KB or MB, denoted with K or M. - e.g. `2048M` |
 | `sdcard-path-or-size` | Optional | N/A | Path to the SD card image for this AVD or the size of a new SD card image to create for this AVD, in KB or MB, denoted with K or M. - e.g. `path/to/sdcard`, or `1000M`. |
+| `disk-size` | Optional | N/A | Disk size to use for this AVD. Either in bytes or KB, MB or GB, when denoted with K, M or G. - e.g. `2048M` |
 | `avd-name` | Optional | `test` | Custom AVD name used for creating the Android Virtual Device. |
 | `force-avd-creation` | Optional | `true` | Whether to force create the AVD by overwriting an existing AVD with the same name as `avd-name` - `true` or `false`. |
 | `emulator-options` | Optional | See below | Command-line options used when launching the emulator (replacing all default options) - e.g. `-no-window -no-snapshot -camera-back emulated`. |
diff --git a/action.yml b/action.yml
index 71467db..e8b418f 100644
--- a/action.yml
+++ b/action.yml
@@ -23,6 +23,8 @@ inputs:
     description: 'size of RAM to use for this AVD, in KB or MB, denoted with K or M. - e.g. `2048M`'
   sdcard-path-or-size:
     description: 'path to the SD card image for this AVD or the size of a new SD card image to create for this AVD, in KB or MB, denoted with K or M. - e.g. `path/to/sdcard`, or `1000M`'
+  disk-size:
+    description: 'disk size to use for this AVD. Either in bytes or KB, MB or GB, when denoted with K, M or G'
   avd-name:
     description: 'custom AVD name used for creating the Android Virtual Device'
     default: 'test'
diff --git a/src/emulator-manager.ts b/src/emulator-manager.ts
index fe56ee9..9a2938e 100644
--- a/src/emulator-manager.ts
+++ b/src/emulator-manager.ts
@@ -14,6 +14,7 @@ export async function launchEmulator(
   cores: string,
   ramSize: string,
   sdcardPathOrSize: string,
+  diskSize: string,
   avdName: string,
   forceAvdCreation: boolean,
   emulatorOptions: string,
@@ -45,6 +46,10 @@ export async function launchEmulator(
     await exec.exec(`sh -c \\"printf 'hw.keyboard=yes\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
   }
 
+  if (diskSize) {
+    await exec.exec(`sh -c \\"printf 'disk.dataPartition.size=${diskSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
+  }
+
   //turn off hardware acceleration on Linux
   if (process.platform === 'linux' && disableLinuxHardwareAcceleration) {
     console.log('Disabling Linux hardware acceleration.');
diff --git a/src/input-validator.ts b/src/input-validator.ts
index ac74762..cdafa7f 100644
--- a/src/input-validator.ts
+++ b/src/input-validator.ts
@@ -69,3 +69,20 @@ export function checkEmulatorBuild(emulatorBuild: string): void {
 function isValidBoolean(value: string): boolean {
   return value === 'true' || value === 'false';
 }
+
+export function checkDiskSize(diskSize: string): void {
+  // Disk size can be empty - the default value
+  if (diskSize) {
+    // Can also be number of bytes
+    if (isNaN(Number(diskSize)) || !Number.isInteger(Number(diskSize))) {
+      // Disk size can have a size multiplier at the end K, M or G
+      const diskSizeUpperCase = diskSize.toUpperCase();
+      if (diskSizeUpperCase.endsWith('K') || diskSizeUpperCase.endsWith('M') || diskSizeUpperCase.endsWith('G')) {
+        const diskSizeNoModifier: string = diskSize.slice(0, -1);
+        if (0 == diskSizeNoModifier.length || isNaN(Number(diskSizeNoModifier)) || !Number.isInteger(Number(diskSizeNoModifier))) {
+          throw new Error(`Unexpected disk size: '${diskSize}'.`);
+        }
+      }
+    }
+  }
+}
diff --git a/src/main.ts b/src/main.ts
index e167b93..596917d 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -10,7 +10,8 @@ import {
   checkDisableLinuxHardwareAcceleration,
   checkForceAvdCreation,
   checkChannel,
-  checkEnableHardwareKeyboard
+  checkEnableHardwareKeyboard,
+  checkDiskSize
 } from './input-validator';
 import { launchEmulator, killEmulator } from './emulator-manager';
 import * as exec from '@actions/exec';
@@ -63,6 +64,10 @@ async function run() {
     const sdcardPathOrSize = core.getInput('sdcard-path-or-size');
     console.log(`SD card path or size: ${sdcardPathOrSize}`);
 
+    const diskSize = core.getInput('disk-size');
+    checkDiskSize(diskSize);
+    console.log(`Disk size: ${diskSize}`);
+
     // custom name used for creating the AVD
     const avdName = core.getInput('avd-name');
     console.log(`AVD name: ${avdName}`);
@@ -156,6 +161,7 @@ async function run() {
       cores,
       ramSize,
       sdcardPathOrSize,
+      diskSize,
       avdName,
       forceAvdCreation,
       emulatorOptions,


Test patch:
diff --git a/__tests__/input-validator.test.ts b/__tests__/input-validator.test.ts
index 8aeb687..5c52a2c 100644
--- a/__tests__/input-validator.test.ts
+++ b/__tests__/input-validator.test.ts
@@ -270,3 +270,54 @@ describe('emulator-build validator tests', () => {
     expect(func).not.toThrow();
   });
 });
+
+describe('checkDiskSize validator tests', () => {
+  it('Empty size is acceptable, means default', () => {
+    const func = () => {
+      validator.checkDiskSize('');
+    };
+    expect(func).not.toThrow();
+  });
+
+  it('Numbers means bytes', () => {
+    expect(() => {
+      validator.checkDiskSize('8000000000');
+    }).not.toThrow();
+  });
+
+  it('Uppercase size modifier', () => {
+    expect(() => {
+      validator.checkDiskSize('8000000K');
+    }).not.toThrow();
+    expect(() => {
+      validator.checkDiskSize('8000M');
+    }).not.toThrow();
+    expect(() => {
+      validator.checkDiskSize('8G');
+    }).not.toThrow();
+  });
+
+  it('Lowercase size modifier', () => {
+    expect(() => {
+      validator.checkDiskSize('8000000k');
+    }).not.toThrow();
+    expect(() => {
+      validator.checkDiskSize('8000m');
+    }).not.toThrow();
+    expect(() => {
+      validator.checkDiskSize('8g');
+    }).not.toThrow();
+  });
+
+  it('Modifier without a number is unacceptable', () => {
+    expect(() => {
+      validator.checkDiskSize('G');
+    }).toThrowError(`Unexpected disk size: 'G'.`);
+  });
+
+  it('Double modifier is unacceptable', () => {
+    expect(() => {
+      validator.checkDiskSize('14gg');
+    }).toThrowError(`Unexpected disk size: '14gg'.`);
+  });
+});