jbudorick | 85ec08f | 2016-09-08 01:30:22 | [diff] [blame] | 1 | # How GTests work on Android |
| 2 | |
| 3 | gtests are [googletest](https://github.com/google/googletest)-based C++ tests. |
| 4 | On Android, they run on a device. In most cases, they're packaged as APKs, but |
| 5 | there are a few cases where they're run as raw executables. The latter is |
| 6 | necessary in a few cases, particularly when manipulating signal handlers, but |
| 7 | isn't possible when the suite needs to call back through the JNI into Java code. |
| 8 | |
| 9 | [TOC] |
| 10 | |
| 11 | ## APKs |
| 12 | |
| 13 | ### GN |
| 14 | |
| 15 | Gtest APKs are built by default by the |
| 16 | [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) |
| 17 | template, e.g. |
| 18 | |
| 19 | ```python |
| 20 | test("sample_gtest") { |
| 21 | # ... |
| 22 | } |
| 23 | ``` |
| 24 | |
| 25 | This uses gn's native |
John Palmer | a8515fca | 2021-05-20 03:35:32 | [diff] [blame^] | 26 | [shared_library](https://chromium.googlesource.com/chromium/src/+/main/tools/gn/docs/reference.md#shared_library_Declare-a-shared-library-target) |
jbudorick | 85ec08f | 2016-09-08 01:30:22 | [diff] [blame] | 27 | target type along with the |
| 28 | [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) |
| 29 | template to build an APK containing: |
| 30 | |
| 31 | - One or more .so files containing the native code on which the test suite |
| 32 | depends |
| 33 | - One or more .dex files containing the Java code on which the test suite |
| 34 | depends |
| 35 | - A [manifest](https://developer.android.com/guide/topics/manifest/manifest-intro.html) |
| 36 | file that contains `<instrumentation>` and `<activity>` elements (among others). |
| 37 | |
| 38 | ### Harness |
| 39 | |
| 40 | GTest APKs are packaged with a harness that consists of: |
| 41 | |
| 42 | - [NativeTestInstrumentationTestRunner], an instrumentation entry point that |
| 43 | handles running one or more sequential instances of a test Activity. Typically, |
| 44 | unit test suites will only use one instance of the Activity and will run all of |
| 45 | the specified tests in it, while browser test suites will use multiple instances |
| 46 | and will only run one test per instance. |
| 47 | - Three [Activity](https://developer.android.com/reference/android/app/Activity.html)-based |
| 48 | classes |
| 49 | ([NativeUnitTestActivity](https://codesearch.chromium.org/chromium/src/testing/android/native_test/java/src/org/chromium/native_test/NativeUnitTestActivity.java), |
| 50 | [NativeUnitTestNativeActivity](https://codesearch.chromium.org/chromium/src/testing/android/native_test/java/src/org/chromium/native_test/NativeUnitTestNativeActivity.java), |
| 51 | and |
| 52 | [NativeBrowserTestActivity](https://codesearch.chromium.org/chromium/src/testing/android/native_test/java/src/org/chromium/native_test/NativeBrowserTestActivity.java)) |
| 53 | that primarily act as process entry points for individual test shards. |
| 54 | Only one is used in any given suite. |
| 55 | - [NativeTest] and [NativeUnitTest], |
| 56 | which handle formatting arguments for googletest and transferring control across |
| 57 | the JNI. |
| 58 | - [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), |
| 59 | the function on the native side, which initializes the native command-line, |
| 60 | redirects stdout either to a FIFO or a regular file, optionally waits for a |
| 61 | debugger to attach to the process, sets up the test data directories, and then |
| 62 | dispatches to googletest's `main` function. |
| 63 | |
| 64 | ### Runtime |
| 65 | |
| 66 | 1. The test runner calls `am instrument` with a bunch of arguments, |
| 67 | includes several extras that are arguments to either |
| 68 | [NativeTestInstrumentationTestRunner] or [NativeTest]. This results in an |
| 69 | intent being sent to [NativeTestInstrumentationTestRunner]. |
| 70 | 2. [NativeTestInstrumentationTestRunner] is created. In its onCreate, it |
| 71 | parses its own arguments from the intent and retains all other arguments |
| 72 | to be passed to the Activities it'll start later. It also creates a |
| 73 | temporary file in the external storage directory for stdout. It finally |
| 74 | starts itself. |
| 75 | 3. [NativeTestInstrumentationTestRunner] is started. In its onStart, it prepares |
| 76 | to receive notifications about the start and end of the test run from the |
| 77 | Activities it's about to start. It then creates [ShardStarter] |
| 78 | that will start the first test shard and adds that to the current |
| 79 | [Handler](https://developer.android.com/reference/android/os/Handler.html). |
| 80 | 4. The [ShardStarter] is executed, starting the test Activity. |
| 81 | 5. The Activity starts, possibly doing some process initialization, and hands |
| 82 | off to the [NativeTest]. |
| 83 | 6. The [NativeTest] handles some initialization and informs the |
| 84 | [NativeTestInstrumentationTestRunner] that it has started. On hearing this, |
| 85 | the [NativeTestInstrumentationTestRunner] creates a [ShardMonitor] |
| 86 | that will monitor the execution of the test Activity. |
| 87 | 7. The [NativeTest] hands off to testing::android::RunTests. The tests run. |
| 88 | 8. The [NativeTest] informs the [NativeTestInstrumentationTestRunner] that is has |
| 89 | completed. On hearing this, the [ShardMonitor] creates a [ShardEnder]. |
| 90 | 9. The [ShardEnder] is executed, killing the child process (if applicable), |
| 91 | parsing the results from the stdout file, and either launching the next |
| 92 | shard via [ShardStarter] (in which case the process returns to #4) or sending |
| 93 | the results out to the test runner and finishing the instrumentation. |
| 94 | |
| 95 | ## Executables |
| 96 | |
| 97 | ### GN |
| 98 | |
| 99 | Gtest executables are built by passing |
| 100 | `use_raw_android_executable = True` to the |
| 101 | [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) |
| 102 | template, e.g. |
| 103 | |
| 104 | ```python |
| 105 | test("sample_gtest_executable") { |
| 106 | if (is_android) { |
| 107 | use_raw_android_executable = true |
| 108 | } |
| 109 | # ... |
| 110 | } |
| 111 | ``` |
| 112 | |
| 113 | This uses gn's native |
John Palmer | a8515fca | 2021-05-20 03:35:32 | [diff] [blame^] | 114 | [executable](https://chromium.googlesource.com/chromium/src/+/main/tools/gn/docs/reference.md#executable_Declare-an-executable-target) |
jbudorick | 85ec08f | 2016-09-08 01:30:22 | [diff] [blame] | 115 | target type, then copies the resulting executable and any requisite shared libraries |
| 116 | to ```${root_out_dir}/${target_name}__dist``` (e.g. ```out/Debug/breakpad_unittests__dist```). |
| 117 | |
| 118 | ### Harness |
| 119 | |
| 120 | Unlike APKs, gtest suites built as executables require no Android-specific harnesses. |
| 121 | |
| 122 | ### Runtime |
| 123 | |
| 124 | The test runner simply executes the binary on the device directly and parses the |
| 125 | stdout on its own. |
| 126 | |
| 127 | [NativeTest]: https://codesearch.chromium.org/chromium/src/testing/android/native_test/java/src/org/chromium/native_test/NativeTest.java |
| 128 | [NativeTestInstrumentationTestRunner]: https://codesearch.chromium.org/chromium/src/testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java |
| 129 | [NativeUnitTest]: https://codesearch.chromium.org/chromium/src/testing/android/native_test/java/src/org/chromium/native_test/NativeUnitTest.java |
| 130 | [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 |
| 131 | [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 |
| 132 | [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 |