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