Integrate NNAPI unit tests into CTS

This CL wraps existing NNAPI gtest-based unit tests as a CTS test. It
changes the previous JNI-based CTS app to a native C++ test.

Bug: 63905942
Test: cts-tradefed run commandAndExit cts -m CtsNNAPITestCases
Change-Id: I97427d78dc0fa2c9adf7e3bb2ec784d599353fbb
diff --git a/tests/tests/neuralnetworks/Android.mk b/tests/tests/neuralnetworks/Android.mk
index 330fde4..cb0c822 100644
--- a/tests/tests/neuralnetworks/Android.mk
+++ b/tests/tests/neuralnetworks/Android.mk
@@ -12,31 +12,44 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# Build the unit tests.
+
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
-
-# Replace "Example" with your name.
-LOCAL_PACKAGE_NAME := CtsNNAPITestCases
-
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := optional
-
-# Include both the 32 and 64 bit versions
+LOCAL_MODULE := CtsNNAPITestCases
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
 LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
 
-# When built, explicitly put it in the data partition.
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SRC_FILES := \
+     src/TestValidation.cpp \
+     src/TestMemory.cpp \
+     src/TestTrivialModel.cpp \
+     src/TestGenerated.cpp
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
-LOCAL_JNI_SHARED_LIBRARIES := libnnapitest_jni
+LOCAL_C_INCLUDES := frameworks/ml/nn/runtime/include/
+LOCAL_C_INCLUDES += frameworks/ml/nn/runtime/test/
+LOCAL_C_INCLUDES += frameworks/ml/nn/runtime/
+LOCAL_C_INCLUDES += frameworks/ml/nn/common/include
+LOCAL_C_INCLUDES += frameworks/ml/nn/tools/test_generator/include
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SHARED_LIBRARIES := liblog libneuralnetworks
 
-LOCAL_SDK_VERSION := current
+# TODO: use the libgtest_ndk_c++ instead
+LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
+
+LOCAL_CTS_TEST_PACKAGE := android.neuralnetworks
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-include $(BUILD_CTS_PACKAGE)
-include $(LOCAL_PATH)/libnnapitest/Android.mk
+LOCAL_CFLAGS := -Werror -Wall
+
+# TODO: use the following two lines instead
+#LOCAL_SDK_VERSION := current
+#LOCAL_NDK_STL_VARIANT := c++_static
+LOCAL_CXX_STL := libc++
+
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/tests/tests/neuralnetworks/AndroidManifest.xml b/tests/tests/neuralnetworks/AndroidManifest.xml
deleted file mode 100644
index 8b59a58..0000000
--- a/tests/tests/neuralnetworks/AndroidManifest.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.nnapi.cts">
-
-    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="android.nnapi.cts"
-                     android:label="CTS tests of NNAPI">
-        <meta-data android:name="listener"
-            android:value="com.android.cts.runner.CtsTestRunListener" />
-    </instrumentation>
-
-</manifest>
-
diff --git a/tests/tests/neuralnetworks/AndroidTest.xml b/tests/tests/neuralnetworks/AndroidTest.xml
index 5d253b2..cd1a0f0 100644
--- a/tests/tests/neuralnetworks/AndroidTest.xml
+++ b/tests/tests/neuralnetworks/AndroidTest.xml
@@ -13,15 +13,18 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="Configuration for NNAPI Tests">
+<configuration description="Configuration for Native NNAPI Tests">
     <option name="config-descriptor:metadata" key="component" value="neuralnetworks" />
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="CtsNNAPITestCases.apk" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsNNAPITestCases->/data/local/tmp/CtsNNAPITestCases" />
+        <option name="append-bitness" value="true" />
     </target_preparer>
-
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="android.nnapi.cts" />
-        <option name="runtime-hint" value="5m30s" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="CtsNNAPITestCases" />
+        <option name="runtime-hint" value="2m" />
+        <!-- test-timeout unit is ms, value = 2 min -->
+        <option name="native-test-timeout" value="120000" />
     </test>
 </configuration>
diff --git a/tests/tests/neuralnetworks/libnnapitest/Android.mk b/tests/tests/neuralnetworks/libnnapitest/Android.mk
deleted file mode 100644
index e0f456f..0000000
--- a/tests/tests/neuralnetworks/libnnapitest/Android.mk
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-#
-# This is the shared library included by the JNI test app.
-#
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_CLANG := true
-LOCAL_MODULE := libnnapitest_jni
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := \
-    test_generated.cpp
-
-LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
-LOCAL_C_INCLUDES += frameworks/ml/nn/runtime/include/
-LOCAL_C_INCLUDES += frameworks/ml/nn/runtime/test/
-
-LOCAL_CPPFLAGS := -std=c++11
-LOCAL_CFLAGS := -Wno-unused-parameter
-
-LOCAL_SHARED_LIBRARIES := libdl liblog libneuralnetworks
-
-# TODO: specify LOCAL_SDK_VERSION when libneuralnetworks become available
-# in NDK
-#LOCAL_SDK_VERSION := 27
-
-LOCAL_CXX_STL := libc++_static
-
-include $(BUILD_SHARED_LIBRARY)
-
-
-
diff --git a/tests/tests/neuralnetworks/libnnapitest/test_generated.cpp b/tests/tests/neuralnetworks/libnnapitest/test_generated.cpp
deleted file mode 100644
index 75a9039..0000000
--- a/tests/tests/neuralnetworks/libnnapitest/test_generated.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Top level driver for models and examples generated by test_generator.py
-
-#include <android/log.h>
-#include <jni.h>
-
-#include <NeuralNetworksWrapper.h>
-
-#include <cmath>
-#include <functional>
-#include <iostream>
-#include <map>
-#include <sstream>
-
-#define LOG_TAG "nnapi"
-#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
-
-using namespace android::nn::wrapper;
-namespace GeneratedTest {
-template <typename T>
-class Example {
-   public:
-    typedef T ElementType;
-    typedef std::pair<std::map<int, std::vector<T>>,
-                      std::map<int, std::vector<T>>>
-        ExampleType;
-
-    static bool Execute(std::function<void(Model*)> create_model,
-                        std::vector<ExampleType>& examples,
-                        std::function<bool(const T, const T)> compare) {
-        android::nn::wrapper::Initialize();
-        Model model;
-        create_model(&model);
-
-        int example_no = 1;
-        bool error = false;
-
-        for (auto& example : examples) {
-            Request request(&model);
-
-            // Go through all inputs
-            for (auto& i : example.first) {
-                std::vector<T>& input = i.second;
-                request.setInput(i.first, (const void*)input.data(),
-                                 input.size() * sizeof(T));
-            }
-
-            std::map<int, std::vector<T>> test_outputs;
-
-            if (example.second.size() != 1) {
-                LOGV("nnapi: output > 1 probably will not work");
-            }
-            int output_no = 0;
-            for (auto& i : example.second) {
-                std::vector<T>& output = i.second;
-                test_outputs[i.first].resize(output.size());
-                std::vector<T>& test_output = test_outputs[i.first];
-                request.setOutput(output_no++, (void*)test_output.data(),
-                                  test_output.size() * sizeof(T));
-            }
-            Result r = request.compute();
-            if (r != Result::NO_ERROR)
-                LOGV("Request was not completed normally");
-            bool mismatch = false;
-            for (auto& i : example.second) {
-                const std::vector<T>& test = test_outputs[i.first];
-                const std::vector<T>& golden = i.second;
-                for (unsigned i = 0; i < golden.size(); i++) {
-                    if (compare(golden[i], test[i])) {
-                        std::stringstream ss;
-                        ss << " output[" << i << "] = " << (float)test[i]
-                           << " (should be " << (float)golden[i] << ")\n";
-                        LOGV("%s", ss.str().c_str());
-                        error = error || true;
-                        mismatch = mismatch || true;
-                    }
-                }
-            }
-            if (mismatch) {
-                std::stringstream ss;
-                ss << "Example: " << example_no++;
-                ss << " failed\n";
-                LOGV("%s", ss.str().c_str());
-            }
-        }
-
-        android::nn::wrapper::Shutdown();
-        return error;
-    }
-};
-};  // namespace GeneratedTest
-
-// Float32 examples
-typedef GeneratedTest::Example<float>::ExampleType Example;
-// Quantized examples
-typedef GeneratedTest::Example<uint8_t>::ExampleType QExample;
-#define assert(x)
-namespace avg_pool_quant8 {
-std::vector<QExample> examples = {
-// Generated avg_pool quantized test
-#include "generated/examples/avg_pool_quant8_tests.example.cc"
-};
-// Generated model constructor
-#include "generated/models/avg_pool_quant8.model.cpp"
-}  // namespace avg_pool_quant8
-
-namespace conv_1_h3_w2_SAME {
-std::vector<Example> examples = {
-// Converted examples
-#include "generated/examples/conv_1_h3_w2_SAME_tests.example.cc"
-};
-// Generated model constructor
-#include "generated/models/conv_1_h3_w2_SAME.model.cpp"
-}  // namespace conv_1_h3_w2_SAME
-
-namespace mobilenet {
-std::vector<Example> examples = {
-// Converted examples
-#include "generated/examples/mobilenet_224_gender_basic_fixed_tests.example.cc"
-};
-// Generated model constructor
-#include "generated/models/mobilenet_224_gender_basic_fixed.model.cpp"
-}  // namespace mobilenet
-
-namespace {
-bool QExecute(std::function<void(Model*)> create_model,
-              std::vector<QExample>& examples) {
-    return GeneratedTest::Example<uint8_t>::Execute(
-        create_model, examples,
-        [](uint8_t golden, uint8_t test) { return golden != test; });
-}
-
-bool Execute(std::function<void(Model*)> create_model,
-             std::vector<Example>& examples) {
-    return GeneratedTest::Example<float>::Execute(
-        create_model, examples, [](float golden, float test) {
-            return std::fabs(golden - test) > 1.5e-5f;
-        });
-}
-
-}  // namespace
-extern "C" JNIEXPORT jboolean JNICALL
-Java_android_cts_nnapi_NNAPIGeneratedTests_avgPoolQuant8(JNIEnv*, jclass) {
-    return QExecute(avg_pool_quant8::CreateModel, avg_pool_quant8::examples);
-}
-
-extern "C" JNIEXPORT jboolean JNICALL
-Java_android_cts_nnapi_NNAPIGeneratedTests_conv1H3W2Same(JNIEnv*, jclass) {
-    return Execute(conv_1_h3_w2_SAME::CreateModel, conv_1_h3_w2_SAME::examples);
-}
-
-extern "C" JNIEXPORT jboolean JNICALL
-Java_android_cts_nnapi_NNAPIGeneratedTests_mobileNet(JNIEnv*, jclass) {
-    return Execute(mobilenet::CreateModel, mobilenet::examples);
-}
diff --git a/tests/tests/neuralnetworks/src/android/cts/nnapi/NNAPITest.java b/tests/tests/neuralnetworks/src/TestGenerated.cpp
similarity index 66%
copy from tests/tests/neuralnetworks/src/android/cts/nnapi/NNAPITest.java
copy to tests/tests/neuralnetworks/src/TestGenerated.cpp
index db37a8f646c..b9044fe 100644
--- a/tests/tests/neuralnetworks/src/android/cts/nnapi/NNAPITest.java
+++ b/tests/tests/neuralnetworks/src/TestGenerated.cpp
@@ -14,19 +14,5 @@
  * limitations under the License.
  */
 
-package android.cts.nnapi;
-
-import android.test.AndroidTestCase;
-
-public class NNAPITest extends AndroidTestCase {
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-    }
-}
+// Include corresponding NNAPI unit tests in frameworks/ml/nn/runtime/test
+#include "test/TestGenerated.cpp"
diff --git a/tests/tests/neuralnetworks/src/android/cts/nnapi/NNAPITest.java b/tests/tests/neuralnetworks/src/TestMemory.cpp
similarity index 66%
rename from tests/tests/neuralnetworks/src/android/cts/nnapi/NNAPITest.java
rename to tests/tests/neuralnetworks/src/TestMemory.cpp
index db37a8f646c..84d2d41 100644
--- a/tests/tests/neuralnetworks/src/android/cts/nnapi/NNAPITest.java
+++ b/tests/tests/neuralnetworks/src/TestMemory.cpp
@@ -14,19 +14,5 @@
  * limitations under the License.
  */
 
-package android.cts.nnapi;
-
-import android.test.AndroidTestCase;
-
-public class NNAPITest extends AndroidTestCase {
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-    }
-}
+// Include corresponding NNAPI unit tests in frameworks/ml/nn/runtime/test
+#include "test/TestMemory.cpp"
diff --git a/tests/tests/neuralnetworks/src/android/cts/nnapi/NNAPITest.java b/tests/tests/neuralnetworks/src/TestTrivialModel.cpp
similarity index 66%
copy from tests/tests/neuralnetworks/src/android/cts/nnapi/NNAPITest.java
copy to tests/tests/neuralnetworks/src/TestTrivialModel.cpp
index db37a8f646c..b41e50f 100644
--- a/tests/tests/neuralnetworks/src/android/cts/nnapi/NNAPITest.java
+++ b/tests/tests/neuralnetworks/src/TestTrivialModel.cpp
@@ -14,19 +14,5 @@
  * limitations under the License.
  */
 
-package android.cts.nnapi;
-
-import android.test.AndroidTestCase;
-
-public class NNAPITest extends AndroidTestCase {
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-    }
-}
+// Include corresponding NNAPI unit tests in frameworks/ml/nn/runtime/test
+#include "test/TestTrivialModel.cpp"
diff --git a/tests/tests/neuralnetworks/src/android/cts/nnapi/NNAPITest.java b/tests/tests/neuralnetworks/src/TestValidation.cpp
similarity index 66%
copy from tests/tests/neuralnetworks/src/android/cts/nnapi/NNAPITest.java
copy to tests/tests/neuralnetworks/src/TestValidation.cpp
index db37a8f646c..eab0f59 100644
--- a/tests/tests/neuralnetworks/src/android/cts/nnapi/NNAPITest.java
+++ b/tests/tests/neuralnetworks/src/TestValidation.cpp
@@ -14,19 +14,5 @@
  * limitations under the License.
  */
 
-package android.cts.nnapi;
-
-import android.test.AndroidTestCase;
-
-public class NNAPITest extends AndroidTestCase {
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-    }
-}
+// Include corresponding NNAPI unit tests in frameworks/ml/nn/runtime/test
+#include "test/TestValidation.cpp"
diff --git a/tests/tests/neuralnetworks/src/android/cts/nnapi/NNAPIGeneratedTests.java b/tests/tests/neuralnetworks/src/android/cts/nnapi/NNAPIGeneratedTests.java
deleted file mode 100644
index 646f2b0..0000000
--- a/tests/tests/neuralnetworks/src/android/cts/nnapi/NNAPIGeneratedTests.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.cts.nnapi;
-
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-public class NNAPIGeneratedTests extends NNAPITest {
-    static {
-        System.loadLibrary("nnapitest_jni");
-    }
-
-    native boolean avgPoolQuant8();
-    public void testAvgPoolQuant8() {
-        assertFalse(avgPoolQuant8());
-    }
-
-    native boolean conv1H3W2Same();
-    public void testConv1H3W2Same() {
-        assertFalse(conv1H3W2Same());
-    }
-
-    native boolean mobileNet();
-    public void testMobileNet() {
-        assertFalse(mobileNet());
-    }
-}