1 # Adding a New Native Test: A Complete Example 2 3 [TOC] 4 5 If you are new to Android platform development, you might find this complete 6 example of adding a brand new native test from scratch useful to demonstrate the 7 typical workflow involved. 8 9 Note that this guide assumes that you already have some knowledge in the 10 platform source tree workflow. If not, please refer to 11 https://source.android.com/source/requirements. 12 13 In addition, if you are also unfamiliar with the gtest framework for C++, please 14 check out its project page first: 15 16 * https://github.com/google/googletest 17 18 This guide uses the follow test to serve as an sample: 19 20 * [Hello World Native Test](../../tests/example/native) 21 22 It's recommended to browse through the code first to get a rough impression 23 before proceeding. 24 25 ## Deciding on a Source Location 26 27 Typically your team will already have an established pattern of places to check 28 in code, and places to add tests. Most team owns a single git repository, or 29 share one with other teams but have a dedicated sub directory that contains 30 component source code. 31 32 Assuming the root location for your component source is at `<component source 33 root>`, most components have `src` and `tests` folders under it, and some 34 additional files such as `Android.mk` (or broken up into additional `.mk` 35 files). 36 37 Since you are adding a brand new test, you'll probably need to create the 38 `tests` directory next to your component `src`, and populate it with content. 39 40 In some cases, your team might have further directory structures under `tests` 41 due to the need to package different suites of tests into individual binaries. 42 And in this case, you'll need to create a new sub directory under `tests`. 43 44 To illustrate, here's a typical directory outline for components with a single 45 `tests` folder: 46 47 ``` 48 \ 49 <component source root> 50 \-- Android.mk (component makefile) 51 \-- AndroidTest.mk (test config file) 52 \-- src (component source) 53 | \-- foo.cpp 54 | \-- ... 55 \-- tests (test source root) 56 \-- Android.mk (test makefile) 57 \-- src (test source) 58 \-- foo_test.cpp 59 \-- ... 60 ``` 61 62 and here's a typical directory outline for components with multiple test source 63 directories: 64 65 ``` 66 \ 67 <component source root> 68 \-- Android.mk (component makefile) 69 \-- AndroidTest.mk (test config file) 70 \-- src (component source) 71 | \-- foo.cpp 72 | \-- ... 73 \-- tests (test source root) 74 \-- Android.mk (test makefile) 75 \-- testFoo (sub test source root) 76 | \-- Android.mk (sub test makefile) 77 | \-- src (sub test source) 78 | \-- test_foo.cpp 79 | \-- ... 80 \-- testBar 81 | \-- Android.mk 82 | \-- src 83 | \-- test_bar.cpp 84 | \-- ... 85 \-- ... 86 ``` 87 88 Regardless of the structure, you'll end up populating the `tests` directory or 89 the newly created sub directory with files similar to what's in `native` 90 directory in the sample gerrit change. The sections below will explain in 91 further details of each file. 92 93 ## Makefile 94 95 Each new test module must have a makefile to direct the build system with module 96 metadata, compile time dependencies and packaging instructions. 97 98 [Latest version of the makefile](../../tests/example/native/Android.mk) 99 100 A snapshot is included here for convenience: 101 102 ```makefile 103 LOCAL_PATH := $(call my-dir) 104 105 include $(CLEAR_VARS) 106 107 LOCAL_SRC_FILES := \ 108 HelloWorldTest.cpp 109 110 LOCAL_MODULE := hello_world_test 111 LOCAL_MODULE_TAGS := tests 112 113 LOCAL_COMPATIBILITY_SUITE := device-tests 114 115 include $(BUILD_NATIVE_TEST) 116 ``` 117 118 Some select remarks on the makefile: 119 120 ```makefile 121 LOCAL_MODULE := hello_world_test 122 ``` 123 124 This setting declares the module name, which must be unique in the entire build 125 tree. It will also be used as the name as the binary executable of your test, as 126 well as a make target name, so that you can use `make [options] <LOCAL_MODULE>` 127 to build your test binary and all its dependencies. 128 129 ```makefile 130 LOCAL_MODULE_TAGS := tests 131 ``` 132 133 This setting declares the module as a test module, which will instruct the build 134 system to generate the native test binaries under "data" output directory, so 135 that they can be packaged into test artifact file bundle. 136 137 ```makefile 138 LOCAL_COMPATIBILITY_SUITE := device-tests 139 ``` 140 141 This line builds the testcase as part of the device-tests suite, which is 142 meant to target a specific device and not a general ABI. 143 144 ```makefile 145 include $(BUILD_NATIVE_TEST) 146 ``` 147 148 This includes a core makefile in build system that performs the necessary steps 149 to compile your test, together with gtest framework under `external/gtest`, into 150 a native test binary. The generated binary will have the same name as 151 `LOCAL_MODULE`. And if `tests` is used as `LOCAL_MODULE_TAGS` and there are no 152 other customizations, you should be able to find your test binary in: 153 154 * `${OUT}/data/nativetest[64]/<LOCAL_MODULE>/<LOCAL_MODULE>` 155 156 e.g. `${OUT}/data/nativetest[64]/hello_world_test/hello_world_test` 157 158 And you will also find it: 159 * ${OUT}/target/product/<target>/testcases/<LOCAL_MODULE>/<arch>/<LOCAL_MODULE> 160 161 e.g. ${OUT}/target/product/arm64-generic/testcases/hello_world_test/arm/hello_world_test 162 & ${OUT}/target/product/arm64-generic/testcases/hello_world_test/arm64/hello_world_test 163 164 Note: if the native ABI type of device is 64bit, such as angler, bullhead etc, 165 the directory name will be suffixed with `64`. 166 167 Please also note that currently the native tests in APCT does not support use of 168 dynamically linked libraries, which means that the dependencies needs to be 169 statically linked into the test binary. 170 171 ## Source code 172 173 [Latest source code](../../tests/example/native/HelloWorldTest.cpp) 174 175 Annotated source code is listed below: 176 177 ```c++ 178 #include <gtest/gtest.h> 179 ``` 180 181 Header file include for gtest. Note that the include file dependency is 182 automatically resolved by using `BUILD_NATIVE_TEST` in the makefile 183 184 ```c++ 185 #include <stdio.h> 186 187 TEST(HelloWorldTest, PrintHelloWorld) { 188 printf("Hello, World!"); 189 } 190 ``` 191 192 gtests are written by using `TEST` macro: the first parameter is the test case 193 name, and the second is test name; together with test binary name, they form the 194 hierarchy below when visualized in result dashboard: 195 196 ``` 197 <test binary 1> 198 | \-- <test case 1> 199 | | \-- <test 1> 200 | | \-- <test 2> 201 | | \-- ... 202 | \-- <test case 2> 203 | | \-- <test 1> 204 | | \-- ... 205 | \-- ... 206 <test binary 2> 207 | 208 ... 209 ``` 210 211 For more information on writing tests with gtest, see its documentation: 212 213 * https://github.com/google/googletest/blob/master/googletest/docs/Primer.md 214 215 ## Test Config 216 217 In order to simplify test execution, you also need write a test configuration 218 file for Android's test harness, [TradeFederation](https://source.android.com/devices/tech/test_infra/tradefed/). 219 220 The test configuration can specify special device setup options and default 221 arguments to supply the test class. 222 223 [LATEST TEST CONFIG](../../tests/example/native/AndroidTest.xml) 224 225 A snapshot is included here for convenience: 226 ```xml 227 <configuration description="Config for APCT native hello world test cases"> 228 <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> 229 <option name="cleanup" value="true" /> 230 <option name="push" value="hello_world_test->/data/local/tmp/hello_world_test" /> 231 </target_preparer> 232 <test class="com.android.tradefed.testtype.GTest" > 233 <option name="native-test-device-path" value="/data/local/tmp" /> 234 <option name="module-name" value="hello_world_test" /> 235 <option name="runtime-hint" value="8m" /> 236 </test> 237 </configuration> 238 ``` 239 240 Some select remarks on the test configuration file: 241 242 ```xml 243 <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> 244 <option name="cleanup" value="true" /> 245 <option name="push" value="hello_world_test->/data/local/tmp/hello_world_test" /> 246 </target_preparer> 247 ``` 248 249 This tells TradeFederation to install the hello_world_test binary onto the target 250 device using a specified target_preparer. There are many target preparers 251 available to developers in TradeFederation and these can be used to ensure 252 the device is setup properly prior to test execution. 253 254 ```xml 255 <test class="com.android.tradefed.testtype.GTest" > 256 <option name="native-test-device-path" value="/data/local/tmp" /> 257 <option name="module-name" value="hello_world_test" /> 258 <option name="runtime-hint" value="8m" /> 259 </test> 260 ``` 261 262 This specifies the TradeFederation test class to use to execute the test and 263 passes in the native test location that it was installed. 264 265 Look here for more information on [Test Module Configs](../test-config.md) 266 267 ## Build & Test Locally 268 269 Follow these [Instructions](../native.md) to build and execute your test 270