普通文本  |  270行  |  8.46 KB

# Adding a New Native Test: A Complete Example

[TOC]

If you are new to Android platform development, you might find this complete
example of adding a brand new native test from scratch useful to demonstrate the
typical workflow involved.

Note that this guide assumes that you already have some knowledge in the
platform source tree workflow. If not, please refer to
https://source.android.com/source/requirements.

In addition, if you are also unfamiliar with the gtest framework for C++, please
check out its project page first:

*   https://github.com/google/googletest

This guide uses the follow test to serve as an sample:

*   [Hello World Native Test](../../tests/example/native)

It's recommended to browse through the code first to get a rough impression
before proceeding.

## Deciding on a Source Location

Typically your team will already have an established pattern of places to check
in code, and places to add tests. Most team owns a single git repository, or
share one with other teams but have a dedicated sub directory that contains
component source code.

Assuming the root location for your component source is at `<component source
root>`, most components have `src` and `tests` folders under it, and some
additional files such as `Android.mk` (or broken up into additional `.mk`
files).

Since you are adding a brand new test, you'll probably need to create the
`tests` directory next to your component `src`, and populate it with content.

In some cases, your team might have further directory structures under `tests`
due to the need to package different suites of tests into individual binaries.
And in this case, you'll need to create a new sub directory under `tests`.

To illustrate, here's a typical directory outline for components with a single
`tests` folder:

```
\
 <component source root>
  \-- Android.mk (component makefile)
  \-- AndroidTest.mk (test config file)
  \-- src (component source)
  |    \-- foo.cpp
  |    \-- ...
  \-- tests (test source root)
      \-- Android.mk (test makefile)
      \-- src (test source)
          \-- foo_test.cpp
          \-- ...
```

and here's a typical directory outline for components with multiple test source
directories:

```
\
 <component source root>
  \-- Android.mk (component makefile)
  \-- AndroidTest.mk (test config file)
  \-- src (component source)
  |    \-- foo.cpp
  |    \-- ...
  \-- tests (test source root)
      \-- Android.mk (test makefile)
      \-- testFoo (sub test source root)
      |   \-- Android.mk (sub test makefile)
      |   \-- src (sub test source)
      |       \-- test_foo.cpp
      |       \-- ...
      \-- testBar
      |   \-- Android.mk
      |   \-- src
      |       \-- test_bar.cpp
      |       \-- ...
      \-- ...
```

Regardless of the structure, you'll end up populating the `tests` directory or
the newly created sub directory with files similar to what's in `native`
directory in the sample gerrit change. The sections below will explain in
further details of each file.

## Makefile

Each new test module must have a makefile to direct the build system with module
metadata, compile time dependencies and packaging instructions.

[Latest version of the makefile](../../tests/example/native/Android.mk)

A snapshot is included here for convenience:

```makefile
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES := \
  HelloWorldTest.cpp

LOCAL_MODULE := hello_world_test
LOCAL_MODULE_TAGS := tests

LOCAL_COMPATIBILITY_SUITE := device-tests

include $(BUILD_NATIVE_TEST)
```

Some select remarks on the makefile:

```makefile
LOCAL_MODULE := hello_world_test
```

This setting declares the module name, which must be unique in the entire build
tree. It will also be used as the name as the binary executable of your test, as
well as a make target name, so that you can use `make [options] <LOCAL_MODULE>`
to build your test binary and all its dependencies.

```makefile
LOCAL_MODULE_TAGS := tests
```

This setting declares the module as a test module, which will instruct the build
system to generate the native test binaries under "data" output directory, so
that they can be packaged into test artifact file bundle.

```makefile
LOCAL_COMPATIBILITY_SUITE := device-tests
```

This line builds the testcase as part of the device-tests suite, which is
meant to target a specific device and not a general ABI.

```makefile
include $(BUILD_NATIVE_TEST)
```

This includes a core makefile in build system that performs the necessary steps
to compile your test, together with gtest framework under `external/gtest`, into
a native test binary. The generated binary will have the same name as
`LOCAL_MODULE`. And if `tests` is used as `LOCAL_MODULE_TAGS` and there are no
other customizations, you should be able to find your test binary in:

*   `${OUT}/data/nativetest[64]/<LOCAL_MODULE>/<LOCAL_MODULE>`

e.g. `${OUT}/data/nativetest[64]/hello_world_test/hello_world_test`

And you will also find it:
*    ${OUT}/target/product/<target>/testcases/<LOCAL_MODULE>/<arch>/<LOCAL_MODULE>

e.g. ${OUT}/target/product/arm64-generic/testcases/hello_world_test/arm/hello_world_test
&    ${OUT}/target/product/arm64-generic/testcases/hello_world_test/arm64/hello_world_test

Note: if the native ABI type of device is 64bit, such as angler, bullhead etc,
the directory name will be suffixed with `64`.

Please also note that currently the native tests in APCT does not support use of
dynamically linked libraries, which means that the dependencies needs to be
statically linked into the test binary.

## Source code

[Latest source code](../../tests/example/native/HelloWorldTest.cpp)

Annotated source code is listed below:

```c++
#include <gtest/gtest.h>
```

Header file include for gtest. Note that the include file dependency is
automatically resolved by using `BUILD_NATIVE_TEST` in the makefile

```c++
#include <stdio.h>

TEST(HelloWorldTest, PrintHelloWorld) {
    printf("Hello, World!");
}
```

gtests are written by using `TEST` macro: the first parameter is the test case
name, and the second is test name; together with test binary name, they form the
hierarchy below when visualized in result dashboard:

```
<test binary 1>
| \-- <test case 1>
| |   \-- <test 1>
| |   \-- <test 2>
| |   \-- ...
| \-- <test case 2>
| |   \-- <test 1>
| |   \-- ...
| \-- ...
<test binary 2>
|
...
```

For more information on writing tests with gtest, see its documentation:

*   https://github.com/google/googletest/blob/master/googletest/docs/Primer.md

## Test Config

In order to simplify test execution, you also need write a test configuration
file for Android's test harness, [TradeFederation](https://source.android.com/devices/tech/test_infra/tradefed/).

The test configuration can specify special device setup options and default
arguments to supply the test class.

[LATEST TEST CONFIG](../../tests/example/native/AndroidTest.xml)

A snapshot is included here for convenience:
```xml
<configuration description="Config for APCT native hello world test cases">
    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
        <option name="cleanup" value="true" />
        <option name="push" value="hello_world_test->/data/local/tmp/hello_world_test" />
    </target_preparer>
    <test class="com.android.tradefed.testtype.GTest" >
        <option name="native-test-device-path" value="/data/local/tmp" />
        <option name="module-name" value="hello_world_test" />
        <option name="runtime-hint" value="8m" />
    </test>
</configuration>
```

Some select remarks on the test configuration file:

```xml
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
    <option name="cleanup" value="true" />
    <option name="push" value="hello_world_test->/data/local/tmp/hello_world_test" />
</target_preparer>
```

This tells TradeFederation to install the hello_world_test binary onto the target
device using a specified target_preparer. There are many target preparers
available to developers in TradeFederation and these can be used to ensure
the device is setup properly prior to test execution.

```xml
<test class="com.android.tradefed.testtype.GTest" >
    <option name="native-test-device-path" value="/data/local/tmp" />
    <option name="module-name" value="hello_world_test" />
    <option name="runtime-hint" value="8m" />
</test>
```

This specifies the TradeFederation test class to use to execute the test and
passes in the native test location that it was installed.

Look here for more information on [Test Module Configs](test-config.md)

## Build & Test Locally

Follow these [Instructions](native.md) to build and execute your test