• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program Test Executor
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 Test log writer.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "xeTestLogWriter.hpp"
25 #include "xeXMLWriter.hpp"
26 #include "deStringUtil.hpp"
27 
28 #include <fstream>
29 
30 namespace xe
31 {
32 
33 /* Batch result writer. */
34 
35 struct ContainerValue
36 {
ContainerValuexe::ContainerValue37 	ContainerValue (const std::string& value_)	: value(value_) {}
ContainerValuexe::ContainerValue38 	ContainerValue (const char* value_)			: value(value_) {}
39 	std::string value;
40 };
41 
operator <<(std::ostream & stream,const ContainerValue & value)42 std::ostream& operator<< (std::ostream& stream, const ContainerValue& value)
43 {
44 	if (value.value.find(' ') != std::string::npos)
45 	{
46 		// Escape.
47 		stream << '"';
48 		for (std::string::const_iterator i = value.value.begin(); i != value.value.end(); i++)
49 		{
50 			if (*i == '"' || *i == '\\')
51 				stream << '\\';
52 			stream << *i;
53 		}
54 		stream << '"';
55 	}
56 	else
57 		stream << value.value;
58 
59 	return stream;
60 }
61 
writeSessionInfo(const SessionInfo & info,std::ostream & stream)62 static void writeSessionInfo (const SessionInfo& info, std::ostream& stream)
63 {
64 	if (!info.releaseName.empty())
65 		stream << "#sessionInfo releaseName " << ContainerValue(info.releaseName) << "\n";
66 
67 	if (!info.releaseId.empty())
68 		stream << "#sessionInfo releaseId " << ContainerValue(info.releaseId) << "\n";
69 
70 	if (!info.targetName.empty())
71 		stream << "#sessionInfo targetName " << ContainerValue(info.targetName) << "\n";
72 
73 	if (!info.candyTargetName.empty())
74 		stream << "#sessionInfo candyTargetName " << ContainerValue(info.candyTargetName) << "\n";
75 
76 	if (!info.configName.empty())
77 		stream << "#sessionInfo configName " << ContainerValue(info.configName) << "\n";
78 
79 	if (!info.resultName.empty())
80 		stream << "#sessionInfo resultName " << ContainerValue(info.resultName) << "\n";
81 
82 	// \note Current format uses unescaped timestamps for some strange reason.
83 	if (!info.timestamp.empty())
84 		stream << "#sessionInfo timestamp " << info.timestamp << "\n";
85 }
86 
writeTestCase(const TestCaseResultData & caseData,std::ostream & stream)87 static void writeTestCase (const TestCaseResultData& caseData, std::ostream& stream)
88 {
89 	stream << "\n#beginTestCaseResult " << caseData.getTestCasePath() << "\n";
90 
91 	if (caseData.getDataSize() > 0)
92 	{
93 		stream.write((const char*)caseData.getData(), caseData.getDataSize());
94 
95 		deUint8 lastCh = caseData.getData()[caseData.getDataSize()-1];
96 		if (lastCh != '\n' && lastCh != '\r')
97 			stream << "\n";
98 	}
99 
100 	TestStatusCode dataCode = caseData.getStatusCode();
101 	if (dataCode == TESTSTATUSCODE_CRASH	||
102 		dataCode == TESTSTATUSCODE_TIMEOUT	||
103 		dataCode == TESTSTATUSCODE_TERMINATED)
104 		stream << "#terminateTestCaseResult " << getTestStatusCodeName(dataCode) << "\n";
105 	else
106 		stream << "#endTestCaseResult\n";
107 }
108 
writeTestLog(const BatchResult & result,std::ostream & stream)109 void writeTestLog (const BatchResult& result, std::ostream& stream)
110 {
111 	writeSessionInfo(result.getSessionInfo(), stream);
112 
113 	stream << "#beginSession\n";
114 
115 	for (int ndx = 0; ndx < result.getNumTestCaseResults(); ndx++)
116 	{
117 		ConstTestCaseResultPtr caseData = result.getTestCaseResult(ndx);
118 		writeTestCase(*caseData, stream);
119 	}
120 
121 	stream << "\n#endSession\n";
122 }
123 
writeBatchResultToFile(const BatchResult & result,const char * filename)124 void writeBatchResultToFile (const BatchResult& result, const char* filename)
125 {
126 	std::ofstream str(filename, std::ofstream::binary|std::ofstream::trunc);
127 	writeTestLog(result, str);
128 	str.close();
129 }
130 
131 /* Test result log writer. */
132 
getImageFormatName(ri::Image::Format format)133 static const char* getImageFormatName (ri::Image::Format format)
134 {
135 	switch (format)
136 	{
137 		case ri::Image::FORMAT_RGB888:		return "RGB888";
138 		case ri::Image::FORMAT_RGBA8888:	return "RGBA8888";
139 		default:
140 			DE_ASSERT(false);
141 			return DE_NULL;
142 	}
143 }
144 
getImageCompressionName(ri::Image::Compression compression)145 static const char* getImageCompressionName (ri::Image::Compression compression)
146 {
147 	switch (compression)
148 	{
149 		case ri::Image::COMPRESSION_NONE:	return "None";
150 		case ri::Image::COMPRESSION_PNG:	return "PNG";
151 		default:
152 			DE_ASSERT(false);
153 			return DE_NULL;
154 	}
155 }
156 
getSampleValueTagName(ri::ValueInfo::ValueTag tag)157 static const char* getSampleValueTagName (ri::ValueInfo::ValueTag tag)
158 {
159 	switch (tag)
160 	{
161 		case ri::ValueInfo::VALUETAG_PREDICTOR:	return "Predictor";
162 		case ri::ValueInfo::VALUETAG_RESPONSE:	return "Response";
163 		default:
164 			DE_ASSERT(false);
165 			return DE_NULL;
166 	}
167 }
168 
getBoolName(bool val)169 inline const char* getBoolName (bool val)
170 {
171 	return val ? "True" : "False";
172 }
173 
174 // \todo [2012-09-07 pyry] Move to tcutil?
175 class Base64Formatter
176 {
177 public:
178 	const deUint8*	data;
179 	int				numBytes;
180 
Base64Formatter(const deUint8 * data_,int numBytes_)181 	Base64Formatter (const deUint8* data_, int numBytes_) : data(data_), numBytes(numBytes_) {}
182 };
183 
operator <<(std::ostream & str,const Base64Formatter & fmt)184 std::ostream& operator<< (std::ostream& str, const Base64Formatter& fmt)
185 {
186 	static const char s_base64Table[64] =
187 	{
188 		'A','B','C','D','E','F','G','H','I','J','K','L','M',
189 		'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
190 		'a','b','c','d','e','f','g','h','i','j','k','l','m',
191 		'n','o','p','q','r','s','t','u','v','w','x','y','z',
192 		'0','1','2','3','4','5','6','7','8','9','+','/'
193 	};
194 
195 	const deUint8*	data		= fmt.data;
196 	int				numBytes	= fmt.numBytes;
197 	int				srcNdx		= 0;
198 
199 	DE_ASSERT(data && (numBytes > 0));
200 
201 	/* Loop all input chars. */
202 	while (srcNdx < numBytes)
203 	{
204 		int		numRead	= de::min(3, numBytes - srcNdx);
205 		deUint8	s0		= data[srcNdx];
206 		deUint8	s1		= (numRead >= 2) ? data[srcNdx+1] : 0;
207 		deUint8	s2		= (numRead >= 3) ? data[srcNdx+2] : 0;
208 		char	d[4];
209 
210 		srcNdx += numRead;
211 
212 		d[0] = s_base64Table[s0 >> 2];
213 		d[1] = s_base64Table[((s0&0x3)<<4) | (s1>>4)];
214 		d[2] = s_base64Table[((s1&0xF)<<2) | (s2>>6)];
215 		d[3] = s_base64Table[s2&0x3F];
216 
217 		if (numRead < 3) d[3] = '=';
218 		if (numRead < 2) d[2] = '=';
219 
220 		/* Write data. */
221 		str.write(&d[0], sizeof(d));
222 	}
223 
224 	return str;
225 }
226 
toBase64(const deUint8 * bytes,int numBytes)227 inline Base64Formatter toBase64 (const deUint8* bytes, int numBytes) { return Base64Formatter(bytes, numBytes); }
228 
getStatusName(bool value)229 static const char* getStatusName (bool value)
230 {
231 	return value ? "OK" : "Fail";
232 }
233 
writeResultItem(const ri::Item & item,xml::Writer & dst)234 static void writeResultItem (const ri::Item& item, xml::Writer& dst)
235 {
236 	using xml::Writer;
237 
238 	switch (item.getType())
239 	{
240 		case ri::TYPE_RESULT:
241 			// Ignored here, written at end.
242 			break;
243 
244 		case ri::TYPE_TEXT:
245 			dst << Writer::BeginElement("Text") << static_cast<const ri::Text&>(item).text << Writer::EndElement;
246 			break;
247 
248 		case ri::TYPE_NUMBER:
249 		{
250 			const ri::Number& number = static_cast<const ri::Number&>(item);
251 			dst << Writer::BeginElement("Number")
252 				<< Writer::Attribute("Name",		number.name)
253 				<< Writer::Attribute("Description",	number.description)
254 				<< Writer::Attribute("Unit",		number.unit)
255 				<< Writer::Attribute("Tag",			number.tag)
256 				<< number.value
257 				<< Writer::EndElement;
258 			break;
259 		}
260 
261 		case ri::TYPE_IMAGE:
262 		{
263 			const ri::Image& image = static_cast<const ri::Image&>(item);
264 			dst << Writer::BeginElement("Image")
265 				<< Writer::Attribute("Name",			image.name)
266 				<< Writer::Attribute("Description",		image.description)
267 				<< Writer::Attribute("Width",			de::toString(image.width))
268 				<< Writer::Attribute("Height",			de::toString(image.height))
269 				<< Writer::Attribute("Format",			getImageFormatName(image.format))
270 				<< Writer::Attribute("CompressionMode",	getImageCompressionName(image.compression))
271 				<< toBase64(&image.data[0], (int)image.data.size())
272 				<< Writer::EndElement;
273 			break;
274 		}
275 
276 		case ri::TYPE_IMAGESET:
277 		{
278 			const ri::ImageSet& imageSet = static_cast<const ri::ImageSet&>(item);
279 			dst << Writer::BeginElement("ImageSet")
280 				<< Writer::Attribute("Name",		imageSet.name)
281 				<< Writer::Attribute("Description",	imageSet.description);
282 
283 			for (int ndx = 0; ndx < imageSet.images.getNumItems(); ndx++)
284 				writeResultItem(imageSet.images.getItem(ndx), dst);
285 
286 			dst << Writer::EndElement;
287 			break;
288 		}
289 
290 		case ri::TYPE_SHADER:
291 		{
292 			const ri::Shader&	shader		= static_cast<const ri::Shader&>(item);
293 			const char*			tagName		= DE_NULL;
294 
295 			switch (shader.shaderType)
296 			{
297 				case ri::Shader::SHADERTYPE_VERTEX:				tagName = "VertexShader";			break;
298 				case ri::Shader::SHADERTYPE_FRAGMENT:			tagName = "FragmentShader";			break;
299 				case ri::Shader::SHADERTYPE_GEOMETRY:			tagName = "GeometryShader";			break;
300 				case ri::Shader::SHADERTYPE_TESS_CONTROL:		tagName = "TessControlShader";		break;
301 				case ri::Shader::SHADERTYPE_TESS_EVALUATION:	tagName = "TessEvaluationShader";	break;
302 				case ri::Shader::SHADERTYPE_COMPUTE:			tagName = "ComputeShader";			break;
303 				case ri::Shader::SHADERTYPE_RAYGEN:				tagName = "RaygenShader";			break;
304 				case ri::Shader::SHADERTYPE_ANY_HIT:			tagName = "AnyHitShader";			break;
305 				case ri::Shader::SHADERTYPE_CLOSEST_HIT:		tagName = "ClosestHitShader";		break;
306 				case ri::Shader::SHADERTYPE_MISS:				tagName = "MissShader";				break;
307 				case ri::Shader::SHADERTYPE_INTERSECTION:		tagName = "IntersectionShader";		break;
308 				case ri::Shader::SHADERTYPE_CALLABLE:			tagName = "CallableShader";			break;
309 				case ri::Shader::SHADERTYPE_TASK:				tagName = "TaskShader";				break;
310 				case ri::Shader::SHADERTYPE_MESH:				tagName = "MeshShader";				break;
311 
312 				default:
313 					throw Error("Unknown shader type");
314 			}
315 
316 			dst << Writer::BeginElement(tagName)
317 				<< Writer::Attribute("CompileStatus",	getStatusName(shader.compileStatus));
318 
319 			writeResultItem(shader.source, dst);
320 			writeResultItem(shader.infoLog, dst);
321 
322 			dst << Writer::EndElement;
323 			break;
324 		}
325 
326 		case ri::TYPE_SHADERPROGRAM:
327 		{
328 			const ri::ShaderProgram& program = static_cast<const ri::ShaderProgram&>(item);
329 			dst << Writer::BeginElement("ShaderProgram")
330 				<< Writer::Attribute("LinkStatus",	getStatusName(program.linkStatus));
331 
332 			writeResultItem(program.linkInfoLog, dst);
333 
334 			for (int ndx = 0; ndx < program.shaders.getNumItems(); ndx++)
335 				writeResultItem(program.shaders.getItem(ndx), dst);
336 
337 			dst << Writer::EndElement;
338 			break;
339 		}
340 
341 		case ri::TYPE_SHADERSOURCE:
342 			dst << Writer::BeginElement("ShaderSource") << static_cast<const ri::ShaderSource&>(item).source << Writer::EndElement;
343 			break;
344 
345 		case ri::TYPE_SPIRVSOURCE:
346 			dst << Writer::BeginElement("SpirVAssemblySource") << static_cast<const ri::SpirVSource&>(item).source << Writer::EndElement;
347 			break;
348 
349 		case ri::TYPE_INFOLOG:
350 			dst << Writer::BeginElement("InfoLog") << static_cast<const ri::InfoLog&>(item).log << Writer::EndElement;
351 			break;
352 
353 		case ri::TYPE_SECTION:
354 		{
355 			const ri::Section& section = static_cast<const ri::Section&>(item);
356 			dst << Writer::BeginElement("Section")
357 				<< Writer::Attribute("Name",		section.name)
358 				<< Writer::Attribute("Description",	section.description);
359 
360 			for (int ndx = 0; ndx < section.items.getNumItems(); ndx++)
361 				writeResultItem(section.items.getItem(ndx), dst);
362 
363 			dst << Writer::EndElement;
364 			break;
365 		}
366 
367 		case ri::TYPE_KERNELSOURCE:
368 			dst << Writer::BeginElement("KernelSource") << static_cast<const ri::KernelSource&>(item).source << Writer::EndElement;
369 			break;
370 
371 		case ri::TYPE_COMPILEINFO:
372 		{
373 			const ri::CompileInfo& compileInfo = static_cast<const ri::CompileInfo&>(item);
374 			dst << Writer::BeginElement("CompileInfo")
375 				<< Writer::Attribute("Name",			compileInfo.name)
376 				<< Writer::Attribute("Description",		compileInfo.description)
377 				<< Writer::Attribute("CompileStatus",	getStatusName(compileInfo.compileStatus));
378 
379 			writeResultItem(compileInfo.infoLog, dst);
380 
381 			dst << Writer::EndElement;
382 			break;
383 		}
384 
385 		case ri::TYPE_EGLCONFIG:
386 		{
387 			const ri::EglConfig& config = static_cast<const ri::EglConfig&>(item);
388 			dst << Writer::BeginElement("EglConfig")
389 				<< Writer::Attribute("BufferSize",				de::toString(config.bufferSize))
390 				<< Writer::Attribute("RedSize",					de::toString(config.redSize))
391 				<< Writer::Attribute("GreenSize",				de::toString(config.greenSize))
392 				<< Writer::Attribute("BlueSize",				de::toString(config.blueSize))
393 				<< Writer::Attribute("LuminanceSize",			de::toString(config.luminanceSize))
394 				<< Writer::Attribute("AlphaSize",				de::toString(config.alphaSize))
395 				<< Writer::Attribute("AlphaMaskSize",			de::toString(config.alphaMaskSize))
396 				<< Writer::Attribute("BindToTextureRGB",		getBoolName(config.bindToTextureRGB))
397 				<< Writer::Attribute("BindToTextureRGBA",		getBoolName(config.bindToTextureRGBA))
398 				<< Writer::Attribute("ColorBufferType",			config.colorBufferType)
399 				<< Writer::Attribute("ConfigCaveat",			config.configCaveat)
400 				<< Writer::Attribute("ConfigID",				de::toString(config.configID))
401 				<< Writer::Attribute("Conformant",				config.conformant)
402 				<< Writer::Attribute("DepthSize",				de::toString(config.depthSize))
403 				<< Writer::Attribute("Level",					de::toString(config.level))
404 				<< Writer::Attribute("MaxPBufferWidth",			de::toString(config.maxPBufferWidth))
405 				<< Writer::Attribute("MaxPBufferHeight",		de::toString(config.maxPBufferHeight))
406 				<< Writer::Attribute("MaxPBufferPixels",		de::toString(config.maxPBufferPixels))
407 				<< Writer::Attribute("MaxSwapInterval",			de::toString(config.maxSwapInterval))
408 				<< Writer::Attribute("MinSwapInterval",			de::toString(config.minSwapInterval))
409 				<< Writer::Attribute("NativeRenderable",		getBoolName(config.nativeRenderable))
410 				<< Writer::Attribute("RenderableType",			config.renderableType)
411 				<< Writer::Attribute("SampleBuffers",			de::toString(config.sampleBuffers))
412 				<< Writer::Attribute("Samples",					de::toString(config.samples))
413 				<< Writer::Attribute("StencilSize",				de::toString(config.stencilSize))
414 				<< Writer::Attribute("SurfaceTypes",			config.surfaceTypes)
415 				<< Writer::Attribute("TransparentType",			config.transparentType)
416 				<< Writer::Attribute("TransparentRedValue",		de::toString(config.transparentRedValue))
417 				<< Writer::Attribute("TransparentGreenValue",	de::toString(config.transparentGreenValue))
418 				<< Writer::Attribute("TransparentBlueValue",	de::toString(config.transparentBlueValue))
419 				<< Writer::EndElement;
420 			break;
421 		}
422 
423 		case ri::TYPE_EGLCONFIGSET:
424 		{
425 			const ri::EglConfigSet& configSet = static_cast<const ri::EglConfigSet&>(item);
426 			dst << Writer::BeginElement("EglConfigSet")
427 				<< Writer::Attribute("Name",			configSet.name)
428 				<< Writer::Attribute("Description",		configSet.description);
429 
430 			for (int ndx = 0; ndx < configSet.configs.getNumItems(); ndx++)
431 				writeResultItem(configSet.configs.getItem(ndx), dst);
432 
433 			dst << Writer::EndElement;
434 			break;
435 		}
436 
437 		case ri::TYPE_SAMPLELIST:
438 		{
439 			const ri::SampleList& list = static_cast<const ri::SampleList&>(item);
440 			dst << Writer::BeginElement("SampleList")
441 				<< Writer::Attribute("Name",		list.name)
442 				<< Writer::Attribute("Description",	list.description);
443 
444 			writeResultItem(list.sampleInfo, dst);
445 
446 			for (int ndx = 0; ndx < list.samples.getNumItems(); ndx++)
447 				writeResultItem(list.samples.getItem(ndx), dst);
448 
449 			dst << Writer::EndElement;
450 			break;
451 		}
452 
453 		case ri::TYPE_SAMPLEINFO:
454 		{
455 			const ri::SampleInfo& info = static_cast<const ri::SampleInfo&>(item);
456 			dst << Writer::BeginElement("SampleInfo");
457 			for (int ndx = 0; ndx < info.valueInfos.getNumItems(); ndx++)
458 				writeResultItem(info.valueInfos.getItem(ndx), dst);
459 			dst << Writer::EndElement;
460 			break;
461 		}
462 
463 		case ri::TYPE_VALUEINFO:
464 		{
465 			const ri::ValueInfo& info = static_cast<const ri::ValueInfo&>(item);
466 			dst << Writer::BeginElement("ValueInfo")
467 				<< Writer::Attribute("Name",		info.name)
468 				<< Writer::Attribute("Description",	info.description)
469 				<< Writer::Attribute("Tag",			getSampleValueTagName(info.tag));
470 			if (!info.unit.empty())
471 				dst << Writer::Attribute("Unit", info.unit);
472 			dst << Writer::EndElement;
473 			break;
474 		}
475 
476 		case ri::TYPE_SAMPLE:
477 		{
478 			const ri::Sample& sample = static_cast<const ri::Sample&>(item);
479 			dst << Writer::BeginElement("Sample");
480 			for (int ndx = 0; ndx < sample.values.getNumItems(); ndx++)
481 				writeResultItem(sample.values.getItem(ndx), dst);
482 			dst << Writer::EndElement;
483 			break;
484 		}
485 
486 		case ri::TYPE_SAMPLEVALUE:
487 		{
488 			const ri::SampleValue& value = static_cast<const ri::SampleValue&>(item);
489 			dst << Writer::BeginElement("Value")
490 				<< value.value
491 				<< Writer::EndElement;
492 			break;
493 		}
494 
495 		default:
496 			XE_FAIL("Unsupported result item");
497 	}
498 }
499 
writeTestResult(const TestCaseResult & result,xe::xml::Writer & xmlWriter)500 void writeTestResult (const TestCaseResult& result, xe::xml::Writer& xmlWriter)
501 {
502 	using xml::Writer;
503 
504 	xmlWriter << Writer::BeginElement("TestCaseResult")
505 			  << Writer::Attribute("Version", result.caseVersion)
506 			  << Writer::Attribute("CasePath", result.casePath)
507 			  << Writer::Attribute("CaseType", getTestCaseTypeName(result.caseType));
508 
509 	for (int ndx = 0; ndx < result.resultItems.getNumItems(); ndx++)
510 		writeResultItem(result.resultItems.getItem(ndx), xmlWriter);
511 
512 	// Result item is not logged until end.
513 	xmlWriter << Writer::BeginElement("Result")
514 			  << Writer::Attribute("StatusCode", getTestStatusCodeName(result.statusCode))
515 			  << result.statusDetails
516 			  << Writer::EndElement;
517 
518 	xmlWriter << Writer::EndElement;
519 }
520 
writeTestResult(const TestCaseResult & result,std::ostream & stream)521 void writeTestResult (const TestCaseResult& result, std::ostream& stream)
522 {
523 	xml::Writer xmlWriter(stream);
524 	stream << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
525 	writeTestResult(result, xmlWriter);
526 }
527 
writeTestResultToFile(const TestCaseResult & result,const char * filename)528 void writeTestResultToFile (const TestCaseResult& result, const char* filename)
529 {
530 	std::ofstream str(filename, std::ofstream::binary|std::ofstream::trunc);
531 	writeTestResult(result, str);
532 	str.close();
533 }
534 
535 } // xe
536