| /* |
| * Copyright (c) 2020-2021 Project CHIP Authors |
| * All rights reserved. |
| * |
| * 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. |
| * |
| */ |
| |
| /** |
| * @file |
| * Implementation of JNI bridge for CHIP Test for Android apps |
| * |
| */ |
| |
| #include <dlfcn.h> |
| #include <jni.h> |
| |
| #include <core/CHIPError.h> |
| #include <platform/android/AndroidChipPlatform-JNI.h> |
| #include <support/CHIPJNIError.h> |
| #include <support/CodeUtils.h> |
| #include <support/JniReferences.h> |
| #include <support/UnitTestRegistration.h> |
| |
| #include <nlunit-test.h> |
| |
| using namespace chip; |
| |
| namespace { |
| JavaVM * sJVM; |
| jclass sTestEngineCls = NULL; |
| jclass sTestEngineExceptionCls = NULL; |
| } // namespace |
| |
| static void ThrowError(JNIEnv * env, CHIP_ERROR errToThrow); |
| static CHIP_ERROR N2J_Error(JNIEnv * env, CHIP_ERROR inErr, jthrowable & outEx); |
| // static void ReportError(JNIEnv * env, CHIP_ERROR cbErr, const char * functName); |
| |
| jint JNI_OnLoad(JavaVM * jvm, void * reserved) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| JNIEnv * env; |
| ChipLogProgress(Test, "JNI_OnLoad() called"); |
| |
| // Save a reference to the JVM. Will need this to call back into Java. |
| JniReferences::GetInstance().SetJavaVm(jvm, "com/tcl/chip/chiptest/TestEngine"); |
| sJVM = jvm; |
| |
| // Get a JNI environment object. |
| env = JniReferences::GetInstance().GetEnvForCurrentThread(); |
| VerifyOrExit(env != NULL, err = CHIP_JNI_ERROR_NO_ENV); |
| |
| ChipLogProgress(Test, "Loading Java class references."); |
| |
| // Get various class references need by the API. |
| err = JniReferences::GetInstance().GetClassRef(env, "com/tcl/chip/chiptest/TestEngine", sTestEngineCls); |
| SuccessOrExit(err); |
| |
| err = JniReferences::GetInstance().GetClassRef(env, "com/tcl/chip/chiptest/TestEngineException", sTestEngineExceptionCls); |
| SuccessOrExit(err); |
| ChipLogProgress(Test, "Java class references loaded."); |
| |
| err = AndroidChipPlatformJNI_OnLoad(jvm, reserved); |
| SuccessOrExit(err); |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ThrowError(env, err); |
| JNI_OnUnload(jvm, reserved); |
| } |
| |
| return (err == CHIP_NO_ERROR) ? JNI_VERSION_1_6 : JNI_ERR; |
| } |
| |
| void JNI_OnUnload(JavaVM * jvm, void * reserved) |
| { |
| ChipLogProgress(Test, "JNI_OnUnload() called"); |
| |
| AndroidChipPlatformJNI_OnUnload(jvm, reserved); |
| |
| sJVM = NULL; |
| } |
| |
| void ReportError(JNIEnv * env, CHIP_ERROR cbErr, const char * functName) |
| { |
| if (cbErr == CHIP_JNI_ERROR_EXCEPTION_THROWN) |
| { |
| ChipLogError(Test, "Java exception thrown in %s", functName); |
| env->ExceptionDescribe(); |
| } |
| else |
| { |
| const char * errStr; |
| switch (cbErr.AsInteger()) |
| { |
| case CHIP_JNI_ERROR_TYPE_NOT_FOUND.AsInteger(): |
| errStr = "JNI type not found"; |
| break; |
| case CHIP_JNI_ERROR_METHOD_NOT_FOUND.AsInteger(): |
| errStr = "JNI method not found"; |
| break; |
| case CHIP_JNI_ERROR_FIELD_NOT_FOUND.AsInteger(): |
| errStr = "JNI field not found"; |
| break; |
| default: |
| errStr = ErrorStr(cbErr); |
| break; |
| } |
| ChipLogError(Test, "Error in %s : %s", functName, errStr); |
| } |
| } |
| |
| void ThrowError(JNIEnv * env, CHIP_ERROR errToThrow) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| jthrowable ex; |
| |
| err = N2J_Error(env, errToThrow, ex); |
| if (err == CHIP_NO_ERROR) |
| { |
| env->Throw(ex); |
| } |
| } |
| |
| CHIP_ERROR N2J_Error(JNIEnv * env, CHIP_ERROR inErr, jthrowable & outEx) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| const char * errStr = NULL; |
| jstring errStrObj = NULL; |
| jmethodID constructor; |
| |
| env->ExceptionClear(); |
| constructor = env->GetMethodID(sTestEngineExceptionCls, "<init>", "(ILjava/lang/String;)V"); |
| VerifyOrExit(constructor != NULL, err = CHIP_JNI_ERROR_METHOD_NOT_FOUND); |
| |
| switch (inErr.AsInteger()) |
| { |
| case CHIP_JNI_ERROR_TYPE_NOT_FOUND.AsInteger(): |
| errStr = "CHIP Device Test Error: JNI type not found"; |
| break; |
| case CHIP_JNI_ERROR_METHOD_NOT_FOUND.AsInteger(): |
| errStr = "CHIP Device Test Error: JNI method not found"; |
| break; |
| case CHIP_JNI_ERROR_FIELD_NOT_FOUND.AsInteger(): |
| errStr = "CHIP Device Test Error: JNI field not found"; |
| break; |
| case CHIP_JNI_ERROR_DEVICE_NOT_FOUND.AsInteger(): |
| errStr = "CHIP Device Test Error: Device not found"; |
| break; |
| default: |
| errStr = ErrorStr(inErr); |
| break; |
| } |
| errStrObj = (errStr != NULL) ? env->NewStringUTF(errStr) : NULL; |
| |
| outEx = (jthrowable) env->NewObject(sTestEngineExceptionCls, constructor, static_cast<jint>(inErr.AsInteger()), errStrObj); |
| VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); |
| |
| exit: |
| env->DeleteLocalRef(errStrObj); |
| return err; |
| } |
| |
| static void onLog(const char * fmt, ...) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| jmethodID method; |
| jstring strObj = NULL; |
| char str[512] = { 0 }; |
| va_list args; |
| |
| ChipLogProgress(Test, "Received onLog"); |
| JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); |
| VerifyOrExit(env != NULL, err = CHIP_JNI_ERROR_NO_ENV); |
| |
| method = env->GetStaticMethodID(sTestEngineCls, "onTestLog", "(Ljava/lang/String;)V"); |
| VerifyOrExit(method != NULL, err = CHIP_JNI_ERROR_NO_ENV); |
| |
| va_start(args, fmt); |
| vsnprintf(str, sizeof(str), fmt, args); |
| va_end(args); |
| strObj = env->NewStringUTF(str); |
| |
| ChipLogProgress(Test, "Calling Java onTestLog"); |
| |
| env->ExceptionClear(); |
| env->CallStaticVoidMethod(sTestEngineCls, method, strObj); |
| VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); |
| |
| exit: |
| env->ExceptionClear(); |
| env->DeleteLocalRef(strObj); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ReportError(env, err, __FUNCTION__); |
| } |
| } |
| |
| static void jni_log_name(struct _nlTestSuite * inSuite) |
| { |
| onLog("[ %s ]\n", inSuite->name); |
| } |
| |
| static void jni_log_initialize(struct _nlTestSuite * inSuite, int inResult, int inWidth) |
| { |
| onLog("[ %s : %-*s ] : %s\n", inSuite->name, inWidth, "Initialize", inResult == FAILURE ? "FAILED" : "PASSED"); |
| } |
| static void jni_log_terminate(struct _nlTestSuite * inSuite, int inResult, int inWidth) |
| { |
| onLog("[ %s : %-*s ] : %s\n", inSuite->name, inWidth, "Terminate", inResult == FAILURE ? "FAILED" : "PASSED"); |
| } |
| |
| static void jni_log_setup(struct _nlTestSuite * inSuite, int inResult, int inWidth) |
| { |
| onLog("[ %s : %-*s ] : %s\n", inSuite->name, inWidth, "Setup", inResult == FAILURE ? "FAILED" : "PASSED"); |
| } |
| |
| static void jni_log_test(struct _nlTestSuite * inSuite, int inWidth, int inIndex) |
| { |
| onLog("[ %s : %-*s ] : %s\n", inSuite->name, inWidth, inSuite->tests[inIndex].name, inSuite->flagError ? "FAILED" : "PASSED"); |
| } |
| |
| static void jni_log_teardown(struct _nlTestSuite * inSuite, int inResult, int inWidth) |
| { |
| onLog("[ %s : %-*s ] : %s\n", inSuite->name, inWidth, "TearDown", inResult == FAILURE ? "FAILED" : "PASSED"); |
| } |
| |
| static void jni_log_statTest(struct _nlTestSuite * inSuite) |
| { |
| onLog("Failed Tests: %d / %d\n", inSuite->failedTests, inSuite->runTests); |
| } |
| |
| static void jni_log_statAssert(struct _nlTestSuite * inSuite) |
| { |
| onLog("Failed Asserts: %d / %d\n", inSuite->failedAssertions, inSuite->performedAssertions); |
| } |
| |
| static nl_test_output_logger_t jni_test_logger = { |
| jni_log_name, jni_log_initialize, jni_log_terminate, jni_log_setup, |
| jni_log_test, jni_log_teardown, jni_log_statTest, jni_log_statAssert, |
| }; |
| |
| extern "C" JNIEXPORT jint Java_com_tcl_chip_chiptest_TestEngine_runTest(JNIEnv * env, jclass clazz) |
| { |
| nlTestSetLogger(&jni_test_logger); |
| |
| jint ret = RunRegisteredUnitTests(); |
| |
| return ret; |
| } |