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