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