Home | History | Annotate | Download | only in runner
      1 /*-------------------------------------------------------------------------
      2  * OpenGL Conformance Test Suite
      3  * -----------------------------
      4  *
      5  * Copyright (c) 2016 Google Inc.
      6  * Copyright (c) 2016 The Khronos Group Inc.
      7  *
      8  * Licensed under the Apache License, Version 2.0 (the "License");
      9  * you may not use this file except in compliance with the License.
     10  * You may obtain a copy of the License at
     11  *
     12  *      http://www.apache.org/licenses/LICENSE-2.0
     13  *
     14  * Unless required by applicable law or agreed to in writing, software
     15  * distributed under the License is distributed on an "AS IS" BASIS,
     16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     17  * See the License for the specific language governing permissions and
     18  * limitations under the License.
     19  *
     20  */ /*!
     21  * \file
     22  * \brief CTS runner.
     23  */ /*-------------------------------------------------------------------*/
     24 
     25 #include "glcTestRunner.hpp"
     26 #include "deFilePath.hpp"
     27 #include "deStringUtil.hpp"
     28 #include "deUniquePtr.hpp"
     29 #include "glcConfigList.hpp"
     30 #include "qpXmlWriter.h"
     31 #include "tcuApp.hpp"
     32 #include "tcuCommandLine.hpp"
     33 #include "tcuTestLog.hpp"
     34 #include "tcuTestSessionExecutor.hpp"
     35 
     36 #include <iterator>
     37 
     38 namespace glcts
     39 {
     40 
     41 using std::vector;
     42 using std::string;
     43 
     44 // RunSession
     45 
     46 class RunSession
     47 {
     48 public:
     49 	RunSession(tcu::Platform& platform, tcu::Archive& archive, const int numArgs, const char* const* args)
     50 		: m_cmdLine(numArgs, args)
     51 		, m_log(m_cmdLine.getLogFileName(), m_cmdLine.getLogFlags())
     52 		, m_app(platform, archive, m_log, m_cmdLine)
     53 	{
     54 	}
     55 
     56 	inline bool iterate(void)
     57 	{
     58 		return m_app.iterate();
     59 	}
     60 
     61 	inline const tcu::TestRunStatus& getResult(void) const
     62 	{
     63 		return m_app.getResult();
     64 	}
     65 
     66 private:
     67 	tcu::CommandLine m_cmdLine;
     68 	tcu::TestLog	 m_log;
     69 	tcu::App		 m_app;
     70 };
     71 
     72 static void appendConfigArgs(const Config& config, std::vector<std::string>& args, const char* fboConfig)
     73 {
     74 	if (fboConfig != NULL)
     75 	{
     76 		args.push_back(string("--deqp-gl-config-name=") + fboConfig);
     77 		args.push_back("--deqp-surface-type=fbo");
     78 	}
     79 
     80 	if (config.type != CONFIGTYPE_DEFAULT)
     81 	{
     82 		// \todo [2013-05-06 pyry] Test all surface types for some configs?
     83 		if (fboConfig == NULL)
     84 		{
     85 			if (config.surfaceTypes & SURFACETYPE_WINDOW)
     86 				args.push_back("--deqp-surface-type=window");
     87 			else if (config.surfaceTypes & SURFACETYPE_PBUFFER)
     88 				args.push_back("--deqp-surface-type=pbuffer");
     89 			else if (config.surfaceTypes & SURFACETYPE_PIXMAP)
     90 				args.push_back("--deqp-surface-type=pixmap");
     91 		}
     92 
     93 		args.push_back(string("--deqp-gl-config-id=") + de::toString(config.id));
     94 
     95 		if (config.type == CONFIGTYPE_EGL)
     96 			args.push_back("--deqp-gl-context-type=egl");
     97 		else if (config.type == CONFIGTYPE_WGL)
     98 			args.push_back("--deqp-gl-context-type=wgl");
     99 	}
    100 }
    101 
    102 typedef struct configInfo
    103 {
    104 	deInt32 redBits;
    105 	deInt32 greenBits;
    106 	deInt32 blueBits;
    107 	deInt32 alphaBits;
    108 	deInt32 depthBits;
    109 	deInt32 stencilBits;
    110 	deInt32 samples;
    111 } configInfo;
    112 
    113 static configInfo parseConfigBitsFromName(const char* configName)
    114 {
    115 	configInfo cfgInfo;
    116 	static const struct
    117 	{
    118 		const char* name;
    119 		int			redBits;
    120 		int			greenBits;
    121 		int			blueBits;
    122 		int			alphaBits;
    123 	} colorCfgs[] = {
    124 		{ "rgba8888", 8, 8, 8, 8 }, { "rgb565", 5, 6, 5, 0 },
    125 	};
    126 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(colorCfgs); ndx++)
    127 	{
    128 		if (!strncmp(configName, colorCfgs[ndx].name, strlen(colorCfgs[ndx].name)))
    129 		{
    130 			cfgInfo.redBits   = colorCfgs[ndx].redBits;
    131 			cfgInfo.greenBits = colorCfgs[ndx].greenBits;
    132 			cfgInfo.blueBits  = colorCfgs[ndx].blueBits;
    133 			cfgInfo.alphaBits = colorCfgs[ndx].alphaBits;
    134 
    135 			configName += strlen(colorCfgs[ndx].name);
    136 			break;
    137 		}
    138 	}
    139 
    140 	static const struct
    141 	{
    142 		const char* name;
    143 		int			depthBits;
    144 	} depthCfgs[] = {
    145 		{ "d0", 0 }, { "d24", 24 },
    146 	};
    147 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(depthCfgs); ndx++)
    148 	{
    149 		if (!strncmp(configName, depthCfgs[ndx].name, strlen(depthCfgs[ndx].name)))
    150 		{
    151 			cfgInfo.depthBits = depthCfgs[ndx].depthBits;
    152 
    153 			configName += strlen(depthCfgs[ndx].name);
    154 			break;
    155 		}
    156 	}
    157 
    158 	static const struct
    159 	{
    160 		const char* name;
    161 		int			stencilBits;
    162 	} stencilCfgs[] = {
    163 		{ "s0", 0 }, { "s8", 8 },
    164 	};
    165 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(stencilCfgs); ndx++)
    166 	{
    167 		if (!strncmp(configName, stencilCfgs[ndx].name, strlen(stencilCfgs[ndx].name)))
    168 		{
    169 			cfgInfo.stencilBits = stencilCfgs[ndx].stencilBits;
    170 
    171 			configName += strlen(stencilCfgs[ndx].name);
    172 			break;
    173 		}
    174 	}
    175 
    176 	static const struct
    177 	{
    178 		const char* name;
    179 		int			samples;
    180 	} multiSampleCfgs[] = {
    181 		{ "ms0", 0 }, { "ms4", 4 },
    182 	};
    183 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(multiSampleCfgs); ndx++)
    184 	{
    185 		if (!strncmp(configName, multiSampleCfgs[ndx].name, strlen(multiSampleCfgs[ndx].name)))
    186 		{
    187 			cfgInfo.samples = multiSampleCfgs[ndx].samples;
    188 
    189 			configName += strlen(multiSampleCfgs[ndx].name);
    190 			break;
    191 		}
    192 	}
    193 
    194 	return cfgInfo;
    195 }
    196 
    197 static const char* getApiName(glu::ApiType apiType)
    198 {
    199 	if (apiType == glu::ApiType::es(2, 0))
    200 		return "gles2";
    201 	else if (apiType == glu::ApiType::es(3, 0))
    202 		return "gles3";
    203 	else if (apiType == glu::ApiType::es(3, 1))
    204 		return "gles31";
    205 	else if (apiType == glu::ApiType::es(3, 2))
    206 		return "gles32";
    207 	else if (apiType == glu::ApiType::core(3, 0))
    208 		return "gl30";
    209 	else if (apiType == glu::ApiType::core(3, 1))
    210 		return "gl31";
    211 	else if (apiType == glu::ApiType::core(3, 2))
    212 		return "gl32";
    213 	else if (apiType == glu::ApiType::core(3, 3))
    214 		return "gl33";
    215 	else if (apiType == glu::ApiType::core(4, 0))
    216 		return "gl40";
    217 	else if (apiType == glu::ApiType::core(4, 1))
    218 		return "gl41";
    219 	else if (apiType == glu::ApiType::core(4, 2))
    220 		return "gl42";
    221 	else if (apiType == glu::ApiType::core(4, 3))
    222 		return "gl43";
    223 	else if (apiType == glu::ApiType::core(4, 4))
    224 		return "gl44";
    225 	else if (apiType == glu::ApiType::core(4, 5))
    226 		return "gl45";
    227 	else if (apiType == glu::ApiType::core(4, 6))
    228 		return "gl46";
    229 	else
    230 		throw std::runtime_error("Unknown context type");
    231 }
    232 
    233 static const string getCaseListFileOption(const char* mustpassDir, const char* apiName, const char* mustpassName)
    234 {
    235 #if DE_OS == DE_OS_ANDROID
    236 	const string case_list_option = "--deqp-caselist-resource=";
    237 #else
    238 	const string case_list_option = "--deqp-caselist-file=";
    239 #endif
    240 	return case_list_option + mustpassDir + apiName + "-" + mustpassName + ".txt";
    241 }
    242 
    243 static const string getLogFileName(const char* apiName, const char* configName, const int iterId, const int runId,
    244 								   const int width, const int height, const int seed)
    245 {
    246 	string res = string("config-") + apiName + "-" + configName + "-cfg-" + de::toString(iterId) + "-run-" +
    247 				 de::toString(runId) + "-width-" + de::toString(width) + "-height-" + de::toString(height);
    248 	if (seed != -1)
    249 	{
    250 		res += "-seed-" + de::toString(seed);
    251 	}
    252 	res += ".qpa";
    253 
    254 	return res;
    255 }
    256 
    257 static void getBaseOptions(std::vector<std::string>& args, const char* mustpassDir, const char* apiName,
    258 						   const char* configName, const char* screenRotation, int width, int height)
    259 {
    260 	args.push_back(getCaseListFileOption(mustpassDir, apiName, configName));
    261 	args.push_back(string("--deqp-screen-rotation=") + screenRotation);
    262 	args.push_back(string("--deqp-surface-width=") + de::toString(width));
    263 	args.push_back(string("--deqp-surface-height=") + de::toString(height));
    264 	args.push_back("--deqp-watchdog=disable");
    265 }
    266 
    267 static bool isGLConfigCompatible(configInfo cfgInfo, const AOSPConfig& config)
    268 {
    269 	return cfgInfo.redBits == config.redBits && cfgInfo.greenBits == config.greenBits &&
    270 		   cfgInfo.blueBits == config.blueBits && cfgInfo.alphaBits == config.alphaBits &&
    271 		   cfgInfo.depthBits == config.depthBits && cfgInfo.stencilBits == config.stencilBits &&
    272 		   cfgInfo.samples == config.samples;
    273 }
    274 
    275 static void getTestRunsForAOSPEGL(vector<TestRunParams>& runs, const ConfigList& configs)
    276 {
    277 #include "glcAospMustpassEgl.hpp"
    278 
    279 	for (int i = 0; i < DE_LENGTH_OF_ARRAY(aosp_mustpass_egl_first_cfg); ++i)
    280 	{
    281 		configInfo cfgInfo = parseConfigBitsFromName(aosp_mustpass_egl_first_cfg[i].glConfigName);
    282 
    283 		vector<AOSPConfig>::const_iterator cfgIter;
    284 		for (cfgIter = configs.aospConfigs.begin(); cfgIter != configs.aospConfigs.end(); ++cfgIter)
    285 		{
    286 			// find first compatible config
    287 			if ((*cfgIter).type == CONFIGTYPE_EGL && isGLConfigCompatible(cfgInfo, *cfgIter))
    288 			{
    289 				break;
    290 			}
    291 		}
    292 
    293 		if (cfgIter == configs.aospConfigs.end())
    294 		{
    295 			// No suitable configuration found. Skipping EGL tests
    296 			continue;
    297 		}
    298 
    299 		const char* apiName = "egl";
    300 
    301 		const int width   = aosp_mustpass_egl_first_cfg[i].surfaceWidth;
    302 		const int height  = aosp_mustpass_egl_first_cfg[i].surfaceHeight;
    303 
    304 		TestRunParams params;
    305 		params.logFilename =
    306 			getLogFileName(apiName, aosp_mustpass_egl_first_cfg[i].configName, 1, i, width, height, -1);
    307 		getBaseOptions(params.args, mustpassDir, apiName, aosp_mustpass_egl_first_cfg[i].configName,
    308 					   aosp_mustpass_egl_first_cfg[i].screenRotation, width, height);
    309 
    310 		params.args.push_back(string("--deqp-gl-config-name=") + string(aosp_mustpass_egl_first_cfg[i].glConfigName));
    311 
    312 		runs.push_back(params);
    313 	}
    314 }
    315 
    316 static void getTestRunsForAOSPES(vector<TestRunParams>& runs, const ConfigList& configs, const glu::ApiType apiType)
    317 {
    318 #include "glcAospMustpassEs.hpp"
    319 
    320 	for (int i = 0; i < DE_LENGTH_OF_ARRAY(aosp_mustpass_es_first_cfg); ++i)
    321 	{
    322 		if (!glu::contextSupports(glu::ContextType(apiType), aosp_mustpass_es_first_cfg[i].apiType))
    323 			continue;
    324 
    325 		configInfo cfgInfo = parseConfigBitsFromName(aosp_mustpass_es_first_cfg[i].glConfigName);
    326 
    327 		vector<AOSPConfig>::const_iterator cfgIter;
    328 		for (cfgIter = configs.aospConfigs.begin(); cfgIter != configs.aospConfigs.end(); ++cfgIter)
    329 		{
    330 			// find first compatible config
    331 			if (isGLConfigCompatible(cfgInfo, *cfgIter))
    332 			{
    333 				break;
    334 			}
    335 		}
    336 
    337 		if (cfgIter == configs.aospConfigs.end())
    338 		{
    339 			TCU_FAIL(("No suitable configuration found for GL config " +
    340 					  de::toString(aosp_mustpass_es_first_cfg[i].glConfigName))
    341 						 .c_str());
    342 			return;
    343 		}
    344 
    345 		const char* apiName = getApiName(aosp_mustpass_es_first_cfg[i].apiType);
    346 
    347 		const int width   = aosp_mustpass_es_first_cfg[i].surfaceWidth;
    348 		const int height  = aosp_mustpass_es_first_cfg[i].surfaceHeight;
    349 
    350 		TestRunParams params;
    351 		params.logFilename = getLogFileName(apiName, aosp_mustpass_es_first_cfg[i].configName, 1, i, width, height, -1);
    352 		getBaseOptions(params.args, mustpassDir, apiName, aosp_mustpass_es_first_cfg[i].configName,
    353 					   aosp_mustpass_es_first_cfg[i].screenRotation, width, height);
    354 
    355 		params.args.push_back(string("--deqp-gl-config-name=") + string(aosp_mustpass_es_first_cfg[i].glConfigName));
    356 
    357 		//set surface type
    358 		if ((*cfgIter).surfaceTypes & SURFACETYPE_WINDOW)
    359 			params.args.push_back("--deqp-surface-type=window");
    360 		else if ((*cfgIter).surfaceTypes & SURFACETYPE_PBUFFER)
    361 			params.args.push_back("--deqp-surface-type=pbuffer");
    362 		else if ((*cfgIter).surfaceTypes & SURFACETYPE_PIXMAP)
    363 			params.args.push_back("--deqp-surface-type=pixmap");
    364 		runs.push_back(params);
    365 	}
    366 }
    367 
    368 static void getTestRunsForNoContext(glu::ApiType type, vector<TestRunParams>& runs, const ConfigList& configs, const RunParams* runParams,
    369 									const int numRunParams, const char* mustpassDir)
    370 {
    371 	vector<Config>::const_iterator cfgIter = configs.configs.begin();
    372 
    373 	for (int i = 0; i < numRunParams; ++i)
    374 	{
    375 		if (!glu::contextSupports(glu::ContextType(type), runParams[i].apiType))
    376 				continue;
    377 
    378 		const char* apiName = getApiName(runParams[i].apiType);
    379 
    380 		const int width  = runParams[i].surfaceWidth;
    381 		const int height = runParams[i].surfaceHeight;
    382 		const int seed   = runParams[i].baseSeed;
    383 
    384 		TestRunParams params;
    385 		params.logFilename = getLogFileName(apiName, runParams[i].configName, 1, i, width, height, seed);
    386 
    387 		getBaseOptions(params.args, mustpassDir, apiName, runParams[i].configName, runParams[i].screenRotation, width,
    388 					   height);
    389 
    390 		params.args.push_back(string("--deqp-base-seed=") + de::toString(seed));
    391 
    392 		appendConfigArgs(*cfgIter, params.args, runParams[i].fboConfig);
    393 
    394 		runs.push_back(params);
    395 	}
    396 }
    397 
    398 static void getTestRunsForNoContextES(glu::ApiType type, vector<TestRunParams>& runs, const ConfigList& configs)
    399 {
    400 #include "glcKhronosMustpassEsNocontext.hpp"
    401 	getTestRunsForNoContext(type, runs, configs, khronos_mustpass_es_nocontext_first_cfg,
    402 							DE_LENGTH_OF_ARRAY(khronos_mustpass_es_nocontext_first_cfg), mustpassDir);
    403 }
    404 
    405 static void getTestRunsForES(glu::ApiType type, const ConfigList& configs, vector<TestRunParams>& runs)
    406 {
    407 	getTestRunsForAOSPEGL(runs, configs);
    408 	getTestRunsForAOSPES(runs, configs, type);
    409 	getTestRunsForNoContextES(type, runs, configs);
    410 
    411 #include "glcKhronosMustpassEs.hpp"
    412 
    413 	for (vector<Config>::const_iterator cfgIter = configs.configs.begin(); cfgIter != configs.configs.end(); ++cfgIter)
    414 	{
    415 		const bool isFirst		= cfgIter == configs.configs.begin();
    416 		const int  numRunParams = isFirst ? DE_LENGTH_OF_ARRAY(khronos_mustpass_es_first_cfg) :
    417 										   DE_LENGTH_OF_ARRAY(khronos_mustpass_es_other_cfg);
    418 		const RunParams* runParams = isFirst ? khronos_mustpass_es_first_cfg : khronos_mustpass_es_other_cfg;
    419 
    420 		for (int runNdx = 0; runNdx < numRunParams; runNdx++)
    421 		{
    422 			if (!glu::contextSupports(glu::ContextType(type), runParams[runNdx].apiType))
    423 				continue;
    424 
    425 			const char* apiName = getApiName(runParams[runNdx].apiType);
    426 
    427 			const int width   = runParams[runNdx].surfaceWidth;
    428 			const int height  = runParams[runNdx].surfaceHeight;
    429 			const int seed	= runParams[runNdx].baseSeed;
    430 
    431 			TestRunParams params;
    432 
    433 			params.logFilename =
    434 				getLogFileName(apiName, runParams[runNdx].configName, cfgIter->id, runNdx, width, height, seed);
    435 
    436 			getBaseOptions(params.args, mustpassDir, apiName, runParams[runNdx].configName,
    437 						   runParams[runNdx].screenRotation, width, height);
    438 			params.args.push_back(string("--deqp-base-seed=") + de::toString(seed));
    439 
    440 			appendConfigArgs(*cfgIter, params.args, runParams[runNdx].fboConfig);
    441 
    442 			runs.push_back(params);
    443 		}
    444 	}
    445 }
    446 
    447 static void getTestRunsForNoContextGL(glu::ApiType type, vector<TestRunParams>& runs, const ConfigList& configs)
    448 {
    449 #include "glcKhronosMustpassGlNocontext.hpp"
    450 	getTestRunsForNoContext(type, runs, configs, khronos_mustpass_gl_nocontext_first_cfg,
    451 							DE_LENGTH_OF_ARRAY(khronos_mustpass_gl_nocontext_first_cfg), mustpassDir);
    452 }
    453 
    454 static void getTestRunsForGL(glu::ApiType type, const ConfigList& configs, vector<TestRunParams>& runs)
    455 {
    456 	getTestRunsForNoContextGL(type, runs, configs);
    457 #include "glcKhronosMustpassGl.hpp"
    458 
    459 	for (vector<Config>::const_iterator cfgIter = configs.configs.begin(); cfgIter != configs.configs.end(); ++cfgIter)
    460 	{
    461 		const bool isFirst		= cfgIter == configs.configs.begin();
    462 		const int  numRunParams = isFirst ? DE_LENGTH_OF_ARRAY(khronos_mustpass_gl_first_cfg) :
    463 										   DE_LENGTH_OF_ARRAY(khronos_mustpass_gl_other_cfg);
    464 		const RunParams* runParams = isFirst ? khronos_mustpass_gl_first_cfg : khronos_mustpass_gl_other_cfg;
    465 
    466 		for (int runNdx = 0; runNdx < numRunParams; runNdx++)
    467 		{
    468 			if (type != runParams[runNdx].apiType)
    469 				continue;
    470 
    471 			const char* apiName = getApiName(runParams[runNdx].apiType);
    472 
    473 			const int width   = runParams[runNdx].surfaceWidth;
    474 			const int height  = runParams[runNdx].surfaceHeight;
    475 			const int seed	= runParams[runNdx].baseSeed;
    476 
    477 			TestRunParams params;
    478 
    479 			params.logFilename =
    480 				getLogFileName(apiName, runParams[runNdx].configName, cfgIter->id, runNdx, width, height, seed);
    481 
    482 			getBaseOptions(params.args, mustpassDir, apiName, runParams[runNdx].configName,
    483 						   runParams[runNdx].screenRotation, width, height);
    484 			params.args.push_back(string("--deqp-base-seed=") + de::toString(seed));
    485 
    486 			appendConfigArgs(*cfgIter, params.args, runParams[runNdx].fboConfig);
    487 
    488 			runs.push_back(params);
    489 		}
    490 	}
    491 }
    492 
    493 static void getTestRunParams(glu::ApiType type, const ConfigList& configs, vector<TestRunParams>& runs)
    494 {
    495 	switch (type.getProfile())
    496 	{
    497 	case glu::PROFILE_CORE:
    498 		getTestRunsForGL(type, configs, runs);
    499 		break;
    500 	case glu::PROFILE_ES:
    501 		getTestRunsForES(type, configs, runs);
    502 		break;
    503 	default:
    504 		throw std::runtime_error("Unknown context type");
    505 	}
    506 }
    507 
    508 struct FileDeleter
    509 {
    510 	void operator()(FILE* file) const
    511 	{
    512 		if (file)
    513 			fclose(file);
    514 	}
    515 };
    516 
    517 struct XmlWriterDeleter
    518 {
    519 	void operator()(qpXmlWriter* writer) const
    520 	{
    521 		if (writer)
    522 			qpXmlWriter_destroy(writer);
    523 	}
    524 };
    525 
    526 static const char* getRunTypeName(glu::ApiType type)
    527 {
    528 	if (type == glu::ApiType::es(2, 0))
    529 		return "es2";
    530 	else if (type == glu::ApiType::es(3, 0))
    531 		return "es3";
    532 	else if (type == glu::ApiType::es(3, 1))
    533 		return "es31";
    534 	else if (type == glu::ApiType::es(3, 2))
    535 		return "es32";
    536 	else if (type == glu::ApiType::core(3, 0))
    537 		return "gl30";
    538 	else if (type == glu::ApiType::core(3, 1))
    539 		return "gl31";
    540 	else if (type == glu::ApiType::core(3, 2))
    541 		return "gl32";
    542 	else if (type == glu::ApiType::core(3, 3))
    543 		return "gl33";
    544 	else if (type == glu::ApiType::core(4, 0))
    545 		return "gl40";
    546 	else if (type == glu::ApiType::core(4, 1))
    547 		return "gl41";
    548 	else if (type == glu::ApiType::core(4, 2))
    549 		return "gl42";
    550 	else if (type == glu::ApiType::core(4, 3))
    551 		return "gl43";
    552 	else if (type == glu::ApiType::core(4, 4))
    553 		return "gl44";
    554 	else if (type == glu::ApiType::core(4, 5))
    555 		return "gl45";
    556 	else if (type == glu::ApiType::core(4, 6))
    557 		return "gl46";
    558 	else
    559 		return DE_NULL;
    560 }
    561 
    562 #define XML_CHECK(X) \
    563 	if (!(X))        \
    564 	throw tcu::Exception("Writing XML failed")
    565 
    566 static void writeRunSummary(const TestRunSummary& summary, const char* filename)
    567 {
    568 	de::UniquePtr<FILE, FileDeleter> out(fopen(filename, "wb"));
    569 	if (!out)
    570 		throw tcu::Exception(string("Failed to open ") + filename);
    571 
    572 	de::UniquePtr<qpXmlWriter, XmlWriterDeleter> writer(qpXmlWriter_createFileWriter(out.get(), DE_FALSE, DE_FALSE));
    573 	if (!writer)
    574 		throw std::bad_alloc();
    575 
    576 	XML_CHECK(qpXmlWriter_startDocument(writer.get()));
    577 
    578 	{
    579 		qpXmlAttribute attribs[2];
    580 
    581 		attribs[0] = qpSetStringAttrib("Type", getRunTypeName(summary.runType));
    582 		attribs[1] = qpSetBoolAttrib("Conformant", summary.isConformant ? DE_TRUE : DE_FALSE);
    583 
    584 		XML_CHECK(qpXmlWriter_startElement(writer.get(), "Summary", DE_LENGTH_OF_ARRAY(attribs), attribs));
    585 	}
    586 
    587 	// Config run
    588 	{
    589 		qpXmlAttribute attribs[1];
    590 		attribs[0] = qpSetStringAttrib("FileName", summary.configLogFilename.c_str());
    591 		XML_CHECK(qpXmlWriter_startElement(writer.get(), "Configs", DE_LENGTH_OF_ARRAY(attribs), attribs) &&
    592 				  qpXmlWriter_endElement(writer.get(), "Configs"));
    593 	}
    594 
    595 	// Record test run parameters (log filename & command line).
    596 	for (vector<TestRunParams>::const_iterator runIter = summary.runParams.begin(); runIter != summary.runParams.end();
    597 		 ++runIter)
    598 	{
    599 		string		   cmdLine;
    600 		qpXmlAttribute attribs[2];
    601 
    602 		for (vector<string>::const_iterator argIter = runIter->args.begin(); argIter != runIter->args.end(); ++argIter)
    603 		{
    604 			if (argIter != runIter->args.begin())
    605 				cmdLine += " ";
    606 			cmdLine += *argIter;
    607 		}
    608 
    609 		attribs[0] = qpSetStringAttrib("FileName", runIter->logFilename.c_str());
    610 		attribs[1] = qpSetStringAttrib("CmdLine", cmdLine.c_str());
    611 
    612 		XML_CHECK(qpXmlWriter_startElement(writer.get(), "TestRun", DE_LENGTH_OF_ARRAY(attribs), attribs) &&
    613 				  qpXmlWriter_endElement(writer.get(), "TestRun"));
    614 	}
    615 
    616 	XML_CHECK(qpXmlWriter_endElement(writer.get(), "Summary"));
    617 	XML_CHECK(qpXmlWriter_endDocument(writer.get()));
    618 }
    619 
    620 #undef XML_CHECK
    621 
    622 TestRunner::TestRunner(tcu::Platform& platform, tcu::Archive& archive, const char* logDirPath, glu::ApiType type,
    623 					   deUint32 flags)
    624 	: m_platform(platform)
    625 	, m_archive(archive)
    626 	, m_logDirPath(logDirPath)
    627 	, m_type(type)
    628 	, m_flags(flags)
    629 	, m_iterState(ITERATE_INIT)
    630 	, m_curSession(DE_NULL)
    631 	, m_sessionsExecuted(0)
    632 	, m_sessionsPassed(0)
    633 	, m_sessionsFailed(0)
    634 {
    635 }
    636 
    637 TestRunner::~TestRunner(void)
    638 {
    639 	delete m_curSession;
    640 }
    641 
    642 bool TestRunner::iterate(void)
    643 {
    644 	switch (m_iterState)
    645 	{
    646 	case ITERATE_INIT:
    647 		init();
    648 		m_iterState = (m_sessionIter != m_runSessions.end()) ? ITERATE_INIT_SESSION : ITERATE_DEINIT;
    649 		return true;
    650 
    651 	case ITERATE_DEINIT:
    652 		deinit();
    653 		m_iterState = ITERATE_INIT;
    654 		return false;
    655 
    656 	case ITERATE_INIT_SESSION:
    657 		DE_ASSERT(m_sessionIter != m_runSessions.end());
    658 		initSession(*m_sessionIter);
    659 		if (m_flags & PRINT_SUMMARY)
    660 			m_iterState = ITERATE_DEINIT_SESSION;
    661 		else
    662 			m_iterState = ITERATE_ITERATE_SESSION;
    663 		return true;
    664 
    665 	case ITERATE_DEINIT_SESSION:
    666 		deinitSession();
    667 		++m_sessionIter;
    668 		m_iterState = (m_sessionIter != m_runSessions.end()) ? ITERATE_INIT_SESSION : ITERATE_DEINIT;
    669 		return true;
    670 
    671 	case ITERATE_ITERATE_SESSION:
    672 		if (!iterateSession())
    673 			m_iterState = ITERATE_DEINIT_SESSION;
    674 		return true;
    675 
    676 	default:
    677 		DE_ASSERT(false);
    678 		return false;
    679 	}
    680 }
    681 
    682 void TestRunner::init(void)
    683 {
    684 	DE_ASSERT(m_runSessions.empty() && m_summary.runParams.empty());
    685 
    686 	tcu::print("Running %s conformance\n", glu::getApiTypeDescription(m_type));
    687 
    688 	m_summary.runType = m_type;
    689 
    690 	// Get list of configs to test.
    691 	ConfigList configList;
    692 	getDefaultConfigList(m_platform, m_type, configList);
    693 
    694 	tcu::print("  found %d compatible and %d excluded configs\n", (int)configList.configs.size(),
    695 			   (int)configList.excludedConfigs.size());
    696 
    697 	// Config list run.
    698 	{
    699 		const char*   configLogFilename = "configs.qpa";
    700 		TestRunParams configRun;
    701 
    702 		configRun.logFilename = configLogFilename;
    703 		configRun.args.push_back("--deqp-case=CTS-Configs.*");
    704 		m_runSessions.push_back(configRun);
    705 
    706 		m_summary.configLogFilename = configLogFilename;
    707 	}
    708 
    709 	// Conformance test type specific runs
    710 	getTestRunParams(m_type, configList, m_runSessions);
    711 
    712 	// Record run params for summary.
    713 	for (std::vector<TestRunParams>::const_iterator runIter = m_runSessions.begin() + 1; runIter != m_runSessions.end();
    714 		 ++runIter)
    715 		m_summary.runParams.push_back(*runIter);
    716 
    717 	// Session iterator
    718 	m_sessionIter = m_runSessions.begin();
    719 }
    720 
    721 void TestRunner::deinit(void)
    722 {
    723 	// Print out totals.
    724 	bool isConformant = m_sessionsExecuted == m_sessionsPassed;
    725 	DE_ASSERT(m_sessionsExecuted == m_sessionsPassed + m_sessionsFailed);
    726 	tcu::print("\n%d/%d sessions passed, conformance test %s\n", m_sessionsPassed, m_sessionsExecuted,
    727 			   isConformant ? "PASSED" : "FAILED");
    728 
    729 	m_summary.isConformant = isConformant;
    730 
    731 	// Write out summary.
    732 	writeRunSummary(m_summary, de::FilePath::join(m_logDirPath, "cts-run-summary.xml").getPath());
    733 
    734 	m_runSessions.clear();
    735 	m_summary.clear();
    736 }
    737 
    738 void TestRunner::initSession(const TestRunParams& runParams)
    739 {
    740 	DE_ASSERT(!m_curSession);
    741 
    742 	tcu::print("\n  Test run %d / %d\n", (int)(m_sessionIter - m_runSessions.begin() + 1), (int)m_runSessions.size());
    743 
    744 	// Compute final args for run.
    745 	vector<string> args(runParams.args);
    746 	args.push_back(string("--deqp-log-filename=") + de::FilePath::join(m_logDirPath, runParams.logFilename).getPath());
    747 
    748 	if (!(m_flags & VERBOSE_IMAGES))
    749 		args.push_back("--deqp-log-images=disable");
    750 
    751 	if (!(m_flags & VERBOSE_SHADERS))
    752 		args.push_back("--deqp-log-shader-sources=disable");
    753 
    754 	std::ostringstream			  ostr;
    755 	std::ostream_iterator<string> out_it(ostr, ", ");
    756 	std::copy(args.begin(), args.end(), out_it);
    757 	tcu::print("\n  Config: %s \n\n", ostr.str().c_str());
    758 
    759 	// Translate to argc, argv
    760 	vector<const char*> argv;
    761 	argv.push_back("cts-runner"); // Dummy binary name
    762 	for (vector<string>::const_iterator i = args.begin(); i != args.end(); i++)
    763 		argv.push_back(i->c_str());
    764 
    765 	// Create session
    766 	m_curSession = new RunSession(m_platform, m_archive, (int)argv.size(), &argv[0]);
    767 }
    768 
    769 void TestRunner::deinitSession(void)
    770 {
    771 	DE_ASSERT(m_curSession);
    772 
    773 	// Collect results.
    774 	// \note NotSupported is treated as pass.
    775 	const tcu::TestRunStatus& result = m_curSession->getResult();
    776 	bool					  isOk =
    777 		result.numExecuted == (result.numPassed + result.numNotSupported + result.numWarnings) && result.isComplete;
    778 
    779 	DE_ASSERT(result.numExecuted == result.numPassed + result.numFailed + result.numNotSupported + result.numWarnings);
    780 
    781 	m_sessionsExecuted += 1;
    782 	(isOk ? m_sessionsPassed : m_sessionsFailed) += 1;
    783 
    784 	delete m_curSession;
    785 	m_curSession = DE_NULL;
    786 }
    787 
    788 inline bool TestRunner::iterateSession(void)
    789 {
    790 	return m_curSession->iterate();
    791 }
    792 
    793 } // glcts
    794