Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <jni.h>
     18 #include <nativehelper/ScopedLocalRef.h>
     19 #include <gtest/gtest.h>
     20 
     21 static struct {
     22     jclass clazz;
     23 
     24     /** static methods **/
     25     jmethodID createTestDescription;
     26 
     27     /** methods **/
     28     jmethodID addChild;
     29 } gDescription;
     30 
     31 static struct {
     32     jclass clazz;
     33 
     34     jmethodID fireTestStarted;
     35     jmethodID fireTestIgnored;
     36     jmethodID fireTestFailure;
     37     jmethodID fireTestFinished;
     38 
     39 } gRunNotifier;
     40 
     41 static struct {
     42     jclass clazz;
     43     jmethodID ctor;
     44 } gAssertionFailure;
     45 
     46 static struct {
     47     jclass clazz;
     48     jmethodID ctor;
     49 } gFailure;
     50 
     51 jobject gEmptyAnnotationsArray;
     52 
     53 static jobject createTestDescription(JNIEnv* env, const char* className, const char* testName) {
     54     ScopedLocalRef<jstring> jClassName(env, env->NewStringUTF(className));
     55     ScopedLocalRef<jstring> jTestName(env, env->NewStringUTF(testName));
     56     return env->CallStaticObjectMethod(gDescription.clazz, gDescription.createTestDescription,
     57             jClassName.get(), jTestName.get(), gEmptyAnnotationsArray);
     58 }
     59 
     60 static void addChild(JNIEnv* env, jobject description, jobject childDescription) {
     61     env->CallVoidMethod(description, gDescription.addChild, childDescription);
     62 }
     63 
     64 
     65 class JUnitNotifyingListener : public ::testing::EmptyTestEventListener {
     66 public:
     67 
     68     JUnitNotifyingListener(JNIEnv* env, jobject runNotifier)
     69             : mEnv(env)
     70             , mRunNotifier(runNotifier)
     71             , mCurrentTestDescription{env, nullptr}
     72     {}
     73     virtual ~JUnitNotifyingListener() {}
     74 
     75     virtual void OnTestStart(const testing::TestInfo &testInfo) override {
     76         mCurrentTestDescription.reset(
     77                 createTestDescription(mEnv, testInfo.test_case_name(), testInfo.name()));
     78         notify(gRunNotifier.fireTestStarted);
     79     }
     80 
     81     virtual void OnTestPartResult(const testing::TestPartResult &testPartResult) override {
     82         if (!testPartResult.passed()) {
     83             char message[1024];
     84             snprintf(message, 1024, "%s:%d\n%s", testPartResult.file_name(), testPartResult.line_number(),
     85                     testPartResult.message());
     86             ScopedLocalRef<jstring> jmessage(mEnv, mEnv->NewStringUTF(message));
     87             ScopedLocalRef<jobject> jthrowable(mEnv, mEnv->NewObject(gAssertionFailure.clazz,
     88                     gAssertionFailure.ctor, jmessage.get()));
     89             ScopedLocalRef<jobject> jfailure(mEnv, mEnv->NewObject(gFailure.clazz,
     90                     gFailure.ctor, mCurrentTestDescription.get(), jthrowable.get()));
     91             mEnv->CallVoidMethod(mRunNotifier, gRunNotifier.fireTestFailure, jfailure.get());
     92         }
     93     }
     94 
     95     virtual void OnTestEnd(const testing::TestInfo&) override {
     96         notify(gRunNotifier.fireTestFinished);
     97         mCurrentTestDescription.reset();
     98     }
     99 
    100     virtual void OnTestProgramEnd(const testing::UnitTest& unitTest) override {
    101         // Invoke the notifiers for all the disabled tests
    102         for (int testCaseIndex = 0; testCaseIndex < unitTest.total_test_case_count(); testCaseIndex++) {
    103             auto testCase = unitTest.GetTestCase(testCaseIndex);
    104             for (int testIndex = 0; testIndex < testCase->total_test_count(); testIndex++) {
    105                 auto testInfo = testCase->GetTestInfo(testIndex);
    106                 if (!testInfo->should_run()) {
    107                     mCurrentTestDescription.reset(
    108                             createTestDescription(mEnv, testCase->name(), testInfo->name()));
    109                     notify(gRunNotifier.fireTestIgnored);
    110                     mCurrentTestDescription.reset();
    111                 }
    112             }
    113         }
    114     }
    115 
    116 private:
    117     void notify(jmethodID method) {
    118         mEnv->CallVoidMethod(mRunNotifier, method, mCurrentTestDescription.get());
    119     }
    120 
    121     JNIEnv* mEnv;
    122     jobject mRunNotifier;
    123     ScopedLocalRef<jobject> mCurrentTestDescription;
    124 };
    125 
    126 extern "C"
    127 JNIEXPORT void JNICALL
    128 Java_com_android_gtestrunner_GtestRunner_nInitialize(JNIEnv *env, jclass, jobject suite) {
    129     // Initialize gtest, removing the default result printer
    130     int argc = 1;
    131     const char* argv[] = { "gtest_wrapper" };
    132     ::testing::InitGoogleTest(&argc, (char**) argv);
    133 
    134     auto& listeners = ::testing::UnitTest::GetInstance()->listeners();
    135     delete listeners.Release(listeners.default_result_printer());
    136 
    137     gDescription.clazz = (jclass) env->NewGlobalRef(env->FindClass("org/junit/runner/Description"));
    138     gDescription.createTestDescription = env->GetStaticMethodID(gDescription.clazz, "createTestDescription",
    139             "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/annotation/Annotation;)Lorg/junit/runner/Description;");
    140     gDescription.addChild = env->GetMethodID(gDescription.clazz, "addChild",
    141             "(Lorg/junit/runner/Description;)V");
    142 
    143     jclass annotations = env->FindClass("java/lang/annotation/Annotation");
    144     gEmptyAnnotationsArray = env->NewGlobalRef(env->NewObjectArray(0, annotations, nullptr));
    145 
    146     gAssertionFailure.clazz = (jclass) env->NewGlobalRef(env->FindClass("java/lang/AssertionError"));
    147     gAssertionFailure.ctor = env->GetMethodID(gAssertionFailure.clazz, "<init>", "(Ljava/lang/Object;)V");
    148 
    149     gFailure.clazz = (jclass) env->NewGlobalRef(env->FindClass("org/junit/runner/notification/Failure"));
    150     gFailure.ctor = env->GetMethodID(gFailure.clazz, "<init>",
    151             "(Lorg/junit/runner/Description;Ljava/lang/Throwable;)V");
    152 
    153     gRunNotifier.clazz = (jclass) env->NewGlobalRef(
    154             env->FindClass("org/junit/runner/notification/RunNotifier"));
    155     gRunNotifier.fireTestStarted = env->GetMethodID(gRunNotifier.clazz, "fireTestStarted",
    156             "(Lorg/junit/runner/Description;)V");
    157     gRunNotifier.fireTestIgnored = env->GetMethodID(gRunNotifier.clazz, "fireTestIgnored",
    158             "(Lorg/junit/runner/Description;)V");
    159     gRunNotifier.fireTestFinished = env->GetMethodID(gRunNotifier.clazz, "fireTestFinished",
    160             "(Lorg/junit/runner/Description;)V");
    161     gRunNotifier.fireTestFailure = env->GetMethodID(gRunNotifier.clazz, "fireTestFailure",
    162             "(Lorg/junit/runner/notification/Failure;)V");
    163 
    164     auto unitTest = ::testing::UnitTest::GetInstance();
    165     for (int testCaseIndex = 0; testCaseIndex < unitTest->total_test_case_count(); testCaseIndex++) {
    166         auto testCase = unitTest->GetTestCase(testCaseIndex);
    167         for (int testIndex = 0; testIndex < testCase->total_test_count(); testIndex++) {
    168             auto testInfo = testCase->GetTestInfo(testIndex);
    169             ScopedLocalRef<jobject> testDescription(env,
    170                     createTestDescription(env, testCase->name(), testInfo->name()));
    171             addChild(env, suite, testDescription.get());
    172         }
    173     }
    174 }
    175 
    176 extern "C"
    177 JNIEXPORT jboolean JNICALL
    178 Java_com_android_gtestrunner_GtestRunner_nRun(JNIEnv *env, jclass, jobject notifier) {
    179     auto& listeners = ::testing::UnitTest::GetInstance()->listeners();
    180     JUnitNotifyingListener junitListener{env, notifier};
    181     listeners.Append(&junitListener);
    182     int success = RUN_ALL_TESTS();
    183     listeners.Release(&junitListener);
    184     return success == 0;
    185 }
    186