1/*------------------------------------------------------------------------- 2 * drawElements Quality Program Tester Core 3 * ---------------------------------------- 4 * 5 * Copyright 2014 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 *//*! 20 * \file 21 * \brief Android JNI interface for instrumentations log parsing. 22 *//*--------------------------------------------------------------------*/ 23 24#include "tcuDefs.hpp" 25 26#include "xeTestResultParser.hpp" 27#include "xeTestCaseResult.hpp" 28#include "xeContainerFormatParser.hpp" 29#include "xeTestLogWriter.hpp" 30#include "xeXMLWriter.hpp" 31 32#include <jni.h> 33#include <stdlib.h> 34#include <android/log.h> 35 36#include <sstream> 37 38namespace 39{ 40static const char* TESTCASE_STYLESHEET = "testlog.xsl"; 41static const char* LOG_TAG = "dEQP-TestLog"; 42 43class TestLogListener 44{ 45public: 46 TestLogListener (JNIEnv* env, jobject object); 47 ~TestLogListener (void); 48 49 void beginSession (void); 50 void endSession (void); 51 void sessionInfo (const char* name, const char* value); 52 53 void beginTestCase (const char* testCasePath); 54 void endTestCase (void); 55 56 void terminateTestCase (const char* reason); 57 void testCaseResult (const char* statusCode, const char* details); 58 59 void testLogData (const char* data); 60 61private: 62 JNIEnv* m_env; 63 jobject m_object; 64 jclass m_class; 65 66 jmethodID m_sessionInfoID; 67 jmethodID m_beginSessionID; 68 jmethodID m_endSessionID; 69 70 jmethodID m_beginTestCaseID; 71 jmethodID m_endTestCaseID; 72 jmethodID m_terminateTestCaseID; 73 jmethodID m_testCaseResultID; 74 jmethodID m_testLogData; 75 76 TestLogListener (const TestLogListener&); 77 TestLogListener& operator= (const TestLogListener&); 78}; 79 80TestLogListener::TestLogListener (JNIEnv* env, jobject object) 81 : m_env (env) 82 , m_object (object) 83{ 84 m_class = m_env->GetObjectClass(m_object); 85 m_sessionInfoID = m_env->GetMethodID(m_class, "sessionInfo", "(Ljava/lang/String;Ljava/lang/String;)V"); 86 m_beginSessionID = m_env->GetMethodID(m_class, "beginSession", "()V"); 87 m_endSessionID = m_env->GetMethodID(m_class, "endSession", "()V"); 88 m_beginTestCaseID = m_env->GetMethodID(m_class, "beginTestCase", "(Ljava/lang/String;)V"); 89 m_endTestCaseID = m_env->GetMethodID(m_class, "endTestCase", "()V"); 90 m_terminateTestCaseID = m_env->GetMethodID(m_class, "terminateTestCase", "(Ljava/lang/String;)V"); 91 m_testCaseResultID = m_env->GetMethodID(m_class, "testCaseResult", "(Ljava/lang/String;Ljava/lang/String;)V"); 92 m_testLogData = m_env->GetMethodID(m_class, "testLogData", "(Ljava/lang/String;)V"); 93 94 TCU_CHECK_INTERNAL(m_beginSessionID); 95 TCU_CHECK_INTERNAL(m_endSessionID); 96 TCU_CHECK_INTERNAL(m_sessionInfoID); 97 TCU_CHECK_INTERNAL(m_beginTestCaseID); 98 TCU_CHECK_INTERNAL(m_endTestCaseID); 99 TCU_CHECK_INTERNAL(m_terminateTestCaseID); 100 TCU_CHECK_INTERNAL(m_testCaseResultID); 101 TCU_CHECK_INTERNAL(m_testLogData); 102} 103 104TestLogListener::~TestLogListener (void) 105{ 106} 107 108void TestLogListener::beginSession (void) 109{ 110 m_env->CallVoidMethod(m_object, m_beginSessionID); 111} 112 113void TestLogListener::endSession (void) 114{ 115 m_env->CallVoidMethod(m_object, m_endSessionID); 116} 117 118void TestLogListener::sessionInfo (const char* name, const char* value) 119{ 120 jstring jName = m_env->NewStringUTF(name); 121 jstring jValue = m_env->NewStringUTF(value); 122 123 m_env->CallVoidMethod(m_object, m_sessionInfoID, jName, jValue); 124 m_env->DeleteLocalRef(jName); 125 m_env->DeleteLocalRef(jValue); 126} 127 128void TestLogListener::beginTestCase (const char* testCasePath) 129{ 130 jstring jTestCasePath = m_env->NewStringUTF(testCasePath); 131 132 m_env->CallVoidMethod(m_object, m_beginTestCaseID, jTestCasePath); 133 m_env->DeleteLocalRef(jTestCasePath); 134} 135 136void TestLogListener::endTestCase (void) 137{ 138 m_env->CallVoidMethod(m_object, m_endTestCaseID); 139} 140 141void TestLogListener::terminateTestCase (const char* reason) 142{ 143 jstring jReason = m_env->NewStringUTF(reason); 144 145 m_env->CallVoidMethod(m_object, m_terminateTestCaseID, jReason); 146 m_env->DeleteLocalRef(jReason); 147} 148 149void TestLogListener::testCaseResult (const char* statusCode, const char* details) 150{ 151 jstring jStatusCode = m_env->NewStringUTF(statusCode); 152 jstring jDetails = m_env->NewStringUTF(details); 153 154 m_env->CallVoidMethod(m_object, m_testCaseResultID, jStatusCode, jDetails); 155 m_env->DeleteLocalRef(jStatusCode); 156 m_env->DeleteLocalRef(jDetails); 157} 158 159void TestLogListener::testLogData (const char* data) 160{ 161 jstring logData = m_env->NewStringUTF(data); 162 163 m_env->CallVoidMethod(m_object, m_testLogData, logData); 164 m_env->DeleteLocalRef(logData); 165} 166 167class TestLogParser 168{ 169public: 170 TestLogParser (bool logData); 171 ~TestLogParser (void); 172 173 void parse (TestLogListener& listener, const char* buffer, size_t size); 174 175private: 176 const bool m_logData; 177 178 bool m_inTestCase; 179 bool m_loggedResult; 180 xe::ContainerFormatParser m_containerParser; 181 xe::TestCaseResult m_testCaseResult; 182 xe::TestResultParser m_testResultParser; 183 184 TestLogParser (const TestLogParser&); 185 TestLogParser& operator= (const TestLogParser&); 186}; 187 188TestLogParser::TestLogParser (bool logData) 189 : m_logData (logData) 190 , m_inTestCase (DE_FALSE) 191 , m_loggedResult (DE_FALSE) 192{ 193} 194 195TestLogParser::~TestLogParser (void) 196{ 197} 198 199void TestLogParser::parse (TestLogListener& listener, const char* buffer, size_t size) 200{ 201 m_containerParser.feed((const deUint8*)buffer, size); 202 203 while (m_containerParser.getElement() != xe::CONTAINERELEMENT_INCOMPLETE) 204 { 205 switch (m_containerParser.getElement()) 206 { 207 case xe::CONTAINERELEMENT_END_OF_STRING: 208 // Do nothing 209 break; 210 211 case xe::CONTAINERELEMENT_BEGIN_SESSION: 212 listener.beginSession(); 213 break; 214 215 case xe::CONTAINERELEMENT_END_SESSION: 216 listener.endSession(); 217 break; 218 219 case xe::CONTAINERELEMENT_SESSION_INFO: 220 listener.sessionInfo(m_containerParser.getSessionInfoAttribute(), m_containerParser.getSessionInfoValue()); 221 break; 222 223 case xe::CONTAINERELEMENT_BEGIN_TEST_CASE_RESULT: 224 listener.beginTestCase(m_containerParser.getTestCasePath()); 225 226 m_inTestCase = DE_TRUE; 227 m_loggedResult = DE_FALSE; 228 m_testCaseResult = xe::TestCaseResult(); 229 230 m_testResultParser.init(&m_testCaseResult); 231 break; 232 233 case xe::CONTAINERELEMENT_END_TEST_CASE_RESULT: 234 if (m_testCaseResult.statusCode != xe::TESTSTATUSCODE_LAST && !m_loggedResult) 235 { 236 listener.testCaseResult(xe::getTestStatusCodeName(m_testCaseResult.statusCode), m_testCaseResult.statusDetails.c_str()); 237 m_loggedResult = DE_TRUE; 238 } 239 240 if (m_logData) 241 { 242 std::ostringstream testLog; 243 xe::xml::Writer xmlWriter(testLog); 244 245 testLog << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" 246 << "<?xml-stylesheet href=\"" << TESTCASE_STYLESHEET << "\" type=\"text/xsl\"?>\n"; 247 248 xe::writeTestResult(m_testCaseResult, xmlWriter); 249 250 listener.testLogData(testLog.str().c_str()); 251 } 252 253 listener.endTestCase(); 254 255 m_inTestCase = DE_FALSE; 256 break; 257 258 case xe::CONTAINERELEMENT_TERMINATE_TEST_CASE_RESULT: 259 if (m_logData) 260 { 261 std::ostringstream testLog; 262 xe::xml::Writer xmlWriter(testLog); 263 264 testLog << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" 265 << "<?xml-stylesheet href=\"" << TESTCASE_STYLESHEET << "\" type=\"text/xsl\"?>\n"; 266 267 xe::writeTestResult(m_testCaseResult, xmlWriter); 268 269 listener.testLogData(testLog.str().c_str()); 270 } 271 272 if (m_testCaseResult.statusCode != xe::TESTSTATUSCODE_LAST && !m_loggedResult) 273 { 274 listener.testCaseResult(xe::getTestStatusCodeName(m_testCaseResult.statusCode), m_testCaseResult.statusDetails.c_str()); 275 m_loggedResult = DE_TRUE; 276 } 277 278 listener.terminateTestCase(m_containerParser.getTerminateReason()); 279 m_inTestCase = DE_FALSE; 280 break; 281 282 case xe::CONTAINERELEMENT_TEST_LOG_DATA: 283 { 284 if (m_inTestCase) 285 { 286 std::vector<deUint8> data(m_containerParser.getDataSize()); 287 m_containerParser.getData(&(data[0]), (int)data.size(), 0); 288 289 //tcu::print("%d %s :%s %s", __LINE__, std::string((const char*)&data[0], data.size()).c_str(), __func__, __FILE__); 290 291 if (m_testResultParser.parse(&(data[0]), (int)data.size()) == xe::TestResultParser::PARSERESULT_CHANGED) 292 { 293 if (m_testCaseResult.statusCode != xe::TESTSTATUSCODE_LAST && !m_loggedResult) 294 { 295 listener.testCaseResult(xe::getTestStatusCodeName(m_testCaseResult.statusCode), m_testCaseResult.statusDetails.c_str()); 296 m_loggedResult = DE_TRUE; 297 } 298 } 299 } 300 301 break; 302 } 303 304 default: 305 DE_ASSERT(DE_FALSE); 306 307 }; 308 309 m_containerParser.advance(); 310 } 311} 312 313void throwJNIException (JNIEnv* env, const std::exception& e) 314{ 315 jclass exClass; 316 317 exClass = env->FindClass("java/lang/Exception"); 318 319 TCU_CHECK_INTERNAL(exClass != DE_NULL); 320 321 TCU_CHECK_INTERNAL(env->ThrowNew(exClass, e.what()) == 0); 322} 323 324} // anonymous 325 326DE_BEGIN_EXTERN_C 327 328JNIEXPORT jlong JNICALL Java_com_drawelements_deqp_testercore_TestLogParser_nativeCreate (JNIEnv* env, jclass, jboolean logData) 329{ 330 DE_UNREF(env); 331 332 try 333 { 334 return (jlong)new TestLogParser(logData); 335 } 336 catch (const std::exception& e) 337 { 338 __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "%s", e.what()); 339 340 throwJNIException(env, e); 341 return 0; 342 } 343} 344 345JNIEXPORT void JNICALL Java_com_drawelements_deqp_testercore_TestLogParser_nativeDestroy (JNIEnv* env, jclass, jlong nativePointer) 346{ 347 DE_UNREF(env); 348 349 try 350 { 351 delete ((TestLogParser*)nativePointer); 352 } 353 catch (const std::exception& e) 354 { 355 __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "%s", e.what()); 356 357 throwJNIException(env, e); 358 } 359} 360 361JNIEXPORT void JNICALL Java_com_drawelements_deqp_testercore_TestLogParser_nativeParse (JNIEnv* env, jclass, jlong nativePointer, jobject instrumentation, jbyteArray buffer, jint size) 362{ 363 jbyte* logData = DE_NULL; 364 365 try 366 { 367 TestLogParser* parser = (TestLogParser*)nativePointer; 368 TestLogListener listener (env, instrumentation); 369 370 logData = env->GetByteArrayElements(buffer, NULL); 371 372 parser->parse(listener, (const char*)logData, (size_t)size); 373 env->ReleaseByteArrayElements(buffer, logData, JNI_ABORT); 374 logData = DE_NULL; 375 } 376 catch (const std::exception& e) 377 { 378 __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "%s", e.what()); 379 380 if (logData) 381 env->ReleaseByteArrayElements(buffer, logData, JNI_ABORT); 382 383 throwJNIException(env, e); 384 } 385} 386 387DE_END_EXTERN_C 388