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 
38 namespace
39 {
40 static const char*	TESTCASE_STYLESHEET	= "testlog.xsl";
41 static const char*	LOG_TAG				= "dEQP-TestLog";
42 
43 class TestLogListener
44 {
45 public:
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 
61 private:
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 
TestLogListener(JNIEnv * env,jobject object)80 TestLogListener::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 
~TestLogListener(void)104 TestLogListener::~TestLogListener (void)
105 {
106 }
107 
beginSession(void)108 void TestLogListener::beginSession (void)
109 {
110 	m_env->CallVoidMethod(m_object, m_beginSessionID);
111 }
112 
endSession(void)113 void TestLogListener::endSession (void)
114 {
115 	m_env->CallVoidMethod(m_object, m_endSessionID);
116 }
117 
sessionInfo(const char * name,const char * value)118 void 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 
beginTestCase(const char * testCasePath)128 void 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 
endTestCase(void)136 void TestLogListener::endTestCase (void)
137 {
138 	m_env->CallVoidMethod(m_object, m_endTestCaseID);
139 }
140 
terminateTestCase(const char * reason)141 void 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 
testCaseResult(const char * statusCode,const char * details)149 void 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 
testLogData(const char * data)159 void 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 
167 class TestLogParser
168 {
169 public:
170 								TestLogParser	(bool logData);
171 								~TestLogParser	(void);
172 
173 	void						parse			(TestLogListener& listener, const char* buffer, size_t size);
174 
175 private:
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 
TestLogParser(bool logData)188 TestLogParser::TestLogParser (bool logData)
189 	: m_logData			(logData)
190 	, m_inTestCase		(DE_FALSE)
191 	, m_loggedResult	(DE_FALSE)
192 {
193 }
194 
~TestLogParser(void)195 TestLogParser::~TestLogParser (void)
196 {
197 }
198 
parse(TestLogListener & listener,const char * buffer,size_t size)199 void 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 
throwJNIException(JNIEnv * env,const std::exception & e)313 void 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 
326 DE_BEGIN_EXTERN_C
327 
Java_com_drawelements_deqp_testercore_TestLogParser_nativeCreate(JNIEnv * env,jclass,jboolean logData)328 JNIEXPORT 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 
Java_com_drawelements_deqp_testercore_TestLogParser_nativeDestroy(JNIEnv * env,jclass,jlong nativePointer)345 JNIEXPORT 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 
Java_com_drawelements_deqp_testercore_TestLogParser_nativeParse(JNIEnv * env,jclass,jlong nativePointer,jobject instrumentation,jbyteArray buffer,jint size)361 JNIEXPORT 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 
387 DE_END_EXTERN_C
388