# How GTests work on Android
gtests are [googletest](https://github.com/google/googletest)-based C++ tests.
On Android, they run on a device. In most cases, they're packaged as APKs, but
there are a few cases where they're run as raw executables. The latter is
necessary in a few cases, particularly when manipulating signal handlers, but
isn't possible when the suite needs to call back through the JNI into Java code.
[TOC]
## APKs
### GN
Gtest APKs are built by default by the
[test](https://codesearch.chromium.org/chromium/src/testing/test.gni?type=cs&q=file:%5Esrc%5C/testing%5C/test.gni$+template%5C("test"%5C)&sq=package:chromium)
template, e.g.
```python
test("sample_gtest") {
# ...
}
```
This uses gn's native
[shared_library](https://chromium.googlesource.com/chromium/src/+/main/tools/gn/docs/reference.md#shared_library_Declare-a-shared-library-target)
target type along with the
[unittest_apk](https://codesearch.chromium.org/chromium/src/build/config/android/rules.gni?type=cs&q=file:%5Esrc%5C/build%5C/config%5C/android%5C/rules.gni$+template%5C(%5C"unittest_apk%5C"%5C)&sq=package:chromium)
template to build an APK containing:
- One or more .so files containing the native code on which the test suite
depends
- One or more .dex files containing the Java code on which the test suite
depends
- A [manifest](https://developer.android.com/guide/topics/manifest/manifest-intro.html)
file that contains `<instrumentation>` and `<activity>` elements (among others).
### Harness
GTest APKs are packaged with a harness that consists of:
- [NativeTestInstrumentationTestRunner], an instrumentation entry point that
handles running one or more sequential instances of a test Activity. Typically,
unit test suites will only use one instance of the Activity and will run all of
the specified tests in it, while browser test suites will use multiple instances
and will only run one test per instance.
- Three [Activity](https://developer.android.com/reference/android/app/Activity.html)-based
classes
([NativeUnitTestActivity](https://codesearch.chromium.org/chromium/src/testing/android/native_test/java/src/org/chromium/native_test/NativeUnitTestActivity.java),
[NativeUnitTestNativeActivity](https://codesearch.chromium.org/chromium/src/testing/android/native_test/java/src/org/chromium/native_test/NativeUnitTestNativeActivity.java),
and
[NativeBrowserTestActivity](https://codesearch.chromium.org/chromium/src/testing/android/native_test/java/src/org/chromium/native_test/NativeBrowserTestActivity.java))
that primarily act as process entry points for individual test shards.
Only one is used in any given suite.
- [NativeTest] and [NativeUnitTest],
which handle formatting arguments for googletest and transferring control across
the JNI.
- [testing::android::RunTests](https://codesearch.chromium.org/chromium/src/testing/android/native_test/native_test_launcher.cc?q=file:%5Esrc%5C/testing%5C/android%5C/native_test%5C/native_test_launcher.cc$+RunTests&sq=package:chromium),
the function on the native side, which initializes the native command-line,
redirects stdout either to a FIFO or a regular file, optionally waits for a
debugger to attach to the process, sets up the test data directories, and then
dispatches to googletest's `main` function.
### Runtime
1. The test runner calls `am instrument` with a bunch of arguments,
includes several extras that are arguments to either
[NativeTestInstrumentationTestRunner] or [NativeTest]. This results in an
intent being sent to [NativeTestInstrumentationTestRunner].
2. [NativeTestInstrumentationTestRunner] is created. In its onCreate, it
parses its own arguments from the intent and retains all other arguments
to be passed to the Activities it'll start later. It also creates a
temporary file in the external storage directory for stdout. It finally
starts itself.
3. [NativeTestInstrumentationTestRunner] is started. In its onStart, it prepares
to receive notifications about the start and end of the test run from the
Activities it's about to start. It then creates [ShardStarter]
that will start the first test shard and adds that to the current
[Handler](https://developer.android.com/reference/android/os/Handler.html).
4. The [ShardStarter] is executed, starting the test Activity.
5. The Activity starts, possibly doing some process initialization, and hands
off to the [NativeTest].
6. The [NativeTest] handles some initialization and informs the
[NativeTestInstrumentationTestRunner] that it has started. On hearing this,
the [NativeTestInstrumentationTestRunner] creates a [ShardMonitor]
that will monitor the execution of the test Activity.
7. The [NativeTest] hands off to testing::android::RunTests. The tests run.
8. The [NativeTest] informs the [NativeTestInstrumentationTestRunner] that is has
completed. On hearing this, the [ShardMonitor] creates a [ShardEnder].
9. The [ShardEnder] is executed, killing the child process (if applicable),
parsing the results from the stdout file, and either launching the next
shard via [ShardStarter] (in which case the process returns to #4) or sending
the results out to the test runner and finishing the instrumentation.
## Executables
### GN
Gtest executables are built by passing
`use_raw_android_executable = True` to the
[test](https://codesearch.chromium.org/chromium/src/testing/test.gni?type=cs&q=file:%5Esrc%5C/testing%5C/test.gni$+template%5C("test"%5C)&sq=package:chromium)
template, e.g.
```python
test("sample_gtest_executable") {
if (is_android) {
use_raw_android_executable = true
}
# ...
}
```
This uses gn's native
[executable](https://chromium.googlesource.com/chromium/src/+/main/tools/gn/docs/reference.md#executable_Declare-an-executable-target)
target type, then copies the resulting executable and any requisite shared libraries
to ```${root_out_dir}/${target_name}__dist``` (e.g. ```out/Debug/breakpad_unittests__dist```).
### Harness
Unlike APKs, gtest suites built as executables require no Android-specific harnesses.
### Runtime
The test runner simply executes the binary on the device directly and parses the
stdout on its own.
[NativeTest]: https://codesearch.chromium.org/chromium/src/testing/android/native_test/java/src/org/chromium/native_test/NativeTest.java
[NativeTestInstrumentationTestRunner]: https://codesearch.chromium.org/chromium/src/testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java
[NativeUnitTest]: https://codesearch.chromium.org/chromium/src/testing/android/native_test/java/src/org/chromium/native_test/NativeUnitTest.java
[ShardEnder]: https://codesearch.chromium.org/chromium/src/testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java?q=file:NativeTestInstrumentationTestRunner.java+class:ShardEnder&sq=package:chromium
[ShardMonitor]: https://codesearch.chromium.org/chromium/src/testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java?q=file:NativeTestInstrumentationTestRunner.java+class:ShardMonitor&sq=package:chromium
[ShardStarter]: https://codesearch.chromium.org/chromium/src/testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java?q=file:NativeTestInstrumentationTestRunner.java+class:ShardStarter&sq=package:chromium