blob: d2db781c389a337c13768091030e89ffedb8288e [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2016 Google Inc.
* Copyright (c) 2016 The Khronos Group Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/ /*!
* \file
* \brief CTS runner.
*/ /*-------------------------------------------------------------------*/
#include "glcTestRunner.hpp"
#include "deFilePath.hpp"
#include "deStringUtil.hpp"
#include "deUniquePtr.hpp"
#include "glcConfigList.hpp"
#include "qpXmlWriter.h"
#include "tcuApp.hpp"
#include "tcuCommandLine.hpp"
#include "tcuTestLog.hpp"
#include "tcuTestSessionExecutor.hpp"
#include <iterator>
namespace glcts
{
using std::vector;
using std::string;
// RunSession
class RunSession
{
public:
RunSession(tcu::Platform& platform, tcu::Archive& archive, const int numArgs, const char* const* args)
: m_cmdLine(numArgs, args)
, m_log(m_cmdLine.getLogFileName(), m_cmdLine.getLogFlags())
, m_app(platform, archive, m_log, m_cmdLine)
{
const std::string sessionInfo = "#sessionInfo commandLineParameters \"";
m_log.writeSessionInfo(sessionInfo + m_cmdLine.getInitialCmdLine() + "\"\n");
}
inline bool iterate(void)
{
return m_app.iterate();
}
inline const tcu::TestRunStatus& getResult(void) const
{
return m_app.getResult();
}
private:
tcu::CommandLine m_cmdLine;
tcu::TestLog m_log;
tcu::App m_app;
};
static void appendConfigArgs(const Config& config, std::vector<std::string>& args, const char* fboConfig)
{
if (fboConfig != NULL)
{
args.push_back(string("--deqp-gl-config-name=") + fboConfig);
args.push_back("--deqp-surface-type=fbo");
}
if (config.type != CONFIGTYPE_DEFAULT)
{
// \todo [2013-05-06 pyry] Test all surface types for some configs?
if (fboConfig == NULL)
{
if (config.surfaceTypes & SURFACETYPE_WINDOW)
args.push_back("--deqp-surface-type=window");
else if (config.surfaceTypes & SURFACETYPE_PBUFFER)
args.push_back("--deqp-surface-type=pbuffer");
else if (config.surfaceTypes & SURFACETYPE_PIXMAP)
args.push_back("--deqp-surface-type=pixmap");
}
args.push_back(string("--deqp-gl-config-id=") + de::toString(config.id));
if (config.type == CONFIGTYPE_EGL)
args.push_back("--deqp-gl-context-type=egl");
else if (config.type == CONFIGTYPE_WGL)
args.push_back("--deqp-gl-context-type=wgl");
}
}
typedef struct configInfo
{
deInt32 redBits;
deInt32 greenBits;
deInt32 blueBits;
deInt32 alphaBits;
deInt32 depthBits;
deInt32 stencilBits;
deInt32 samples;
} configInfo;
static configInfo parseConfigBitsFromName(const char* configName)
{
configInfo cfgInfo;
static const struct
{
const char* name;
int redBits;
int greenBits;
int blueBits;
int alphaBits;
} colorCfgs[] = {
{ "rgba8888", 8, 8, 8, 8 }, { "rgb565", 5, 6, 5, 0 },
};
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(colorCfgs); ndx++)
{
if (!strncmp(configName, colorCfgs[ndx].name, strlen(colorCfgs[ndx].name)))
{
cfgInfo.redBits = colorCfgs[ndx].redBits;
cfgInfo.greenBits = colorCfgs[ndx].greenBits;
cfgInfo.blueBits = colorCfgs[ndx].blueBits;
cfgInfo.alphaBits = colorCfgs[ndx].alphaBits;
configName += strlen(colorCfgs[ndx].name);
break;
}
}
static const struct
{
const char* name;
int depthBits;
} depthCfgs[] = {
{ "d0", 0 }, { "d24", 24 },
};
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(depthCfgs); ndx++)
{
if (!strncmp(configName, depthCfgs[ndx].name, strlen(depthCfgs[ndx].name)))
{
cfgInfo.depthBits = depthCfgs[ndx].depthBits;
configName += strlen(depthCfgs[ndx].name);
break;
}
}
static const struct
{
const char* name;
int stencilBits;
} stencilCfgs[] = {
{ "s0", 0 }, { "s8", 8 },
};
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(stencilCfgs); ndx++)
{
if (!strncmp(configName, stencilCfgs[ndx].name, strlen(stencilCfgs[ndx].name)))
{
cfgInfo.stencilBits = stencilCfgs[ndx].stencilBits;
configName += strlen(stencilCfgs[ndx].name);
break;
}
}
static const struct
{
const char* name;
int samples;
} multiSampleCfgs[] = {
{ "ms0", 0 }, { "ms4", 4 },
};
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(multiSampleCfgs); ndx++)
{
if (!strncmp(configName, multiSampleCfgs[ndx].name, strlen(multiSampleCfgs[ndx].name)))
{
cfgInfo.samples = multiSampleCfgs[ndx].samples;
configName += strlen(multiSampleCfgs[ndx].name);
break;
}
}
return cfgInfo;
}
static const char* getApiName(glu::ApiType apiType)
{
if (apiType == glu::ApiType::es(2, 0))
return "gles2";
else if (apiType == glu::ApiType::es(3, 0))
return "gles3";
else if (apiType == glu::ApiType::es(3, 1))
return "gles31";
else if (apiType == glu::ApiType::es(3, 2))
return "gles32";
else if (apiType == glu::ApiType::core(3, 0))
return "gl30";
else if (apiType == glu::ApiType::core(3, 1))
return "gl31";
else if (apiType == glu::ApiType::core(3, 2))
return "gl32";
else if (apiType == glu::ApiType::core(3, 3))
return "gl33";
else if (apiType == glu::ApiType::core(4, 0))
return "gl40";
else if (apiType == glu::ApiType::core(4, 1))
return "gl41";
else if (apiType == glu::ApiType::core(4, 2))
return "gl42";
else if (apiType == glu::ApiType::core(4, 3))
return "gl43";
else if (apiType == glu::ApiType::core(4, 4))
return "gl44";
else if (apiType == glu::ApiType::core(4, 5))
return "gl45";
else if (apiType == glu::ApiType::core(4, 6))
return "gl46";
else
throw std::runtime_error("Unknown context type");
}
static const string getCaseListFileOption(const char* mustpassDir, const char* apiName, const char* mustpassName)
{
#if DE_OS == DE_OS_ANDROID
const string case_list_option = "--deqp-caselist-resource=";
#else
const string case_list_option = "--deqp-caselist-file=";
#endif
return case_list_option + mustpassDir + apiName + "-" + mustpassName + ".txt";
}
static const string getLogFileName(const char* apiName, const char* configName, const int iterId, const int runId,
const int width, const int height, const int seed)
{
string res = string("config-") + apiName + "-" + configName + "-cfg-" + de::toString(iterId) + "-run-" +
de::toString(runId) + "-width-" + de::toString(width) + "-height-" + de::toString(height);
if (seed != -1)
{
res += "-seed-" + de::toString(seed);
}
res += ".qpa";
return res;
}
static void getBaseOptions(std::vector<std::string>& args, const char* mustpassDir, const char* apiName,
const char* configName, const char* screenRotation, int width, int height)
{
args.push_back(getCaseListFileOption(mustpassDir, apiName, configName));
args.push_back(string("--deqp-screen-rotation=") + screenRotation);
args.push_back(string("--deqp-surface-width=") + de::toString(width));
args.push_back(string("--deqp-surface-height=") + de::toString(height));
args.push_back("--deqp-watchdog=disable");
}
static bool isGLConfigCompatible(configInfo cfgInfo, const AOSPConfig& config)
{
return cfgInfo.redBits == config.redBits && cfgInfo.greenBits == config.greenBits &&
cfgInfo.blueBits == config.blueBits && cfgInfo.alphaBits == config.alphaBits &&
cfgInfo.depthBits == config.depthBits && cfgInfo.stencilBits == config.stencilBits &&
cfgInfo.samples == config.samples;
}
static void getTestRunsForAOSPEGL(vector<TestRunParams>& runs, const ConfigList& configs)
{
#include "glcAospMustpassEgl.hpp"
for (int i = 0; i < DE_LENGTH_OF_ARRAY(aosp_mustpass_egl_first_cfg); ++i)
{
configInfo cfgInfo = parseConfigBitsFromName(aosp_mustpass_egl_first_cfg[i].glConfigName);
vector<AOSPConfig>::const_iterator cfgIter;
for (cfgIter = configs.aospConfigs.begin(); cfgIter != configs.aospConfigs.end(); ++cfgIter)
{
// find first compatible config
if ((*cfgIter).type == CONFIGTYPE_EGL && isGLConfigCompatible(cfgInfo, *cfgIter))
{
break;
}
}
if (cfgIter == configs.aospConfigs.end())
{
// No suitable configuration found. Skipping EGL tests
continue;
}
const char* apiName = "egl";
const int width = aosp_mustpass_egl_first_cfg[i].surfaceWidth;
const int height = aosp_mustpass_egl_first_cfg[i].surfaceHeight;
TestRunParams params;
params.logFilename =
getLogFileName(apiName, aosp_mustpass_egl_first_cfg[i].configName, 1, i, width, height, -1);
getBaseOptions(params.args, mustpassDir, apiName, aosp_mustpass_egl_first_cfg[i].configName,
aosp_mustpass_egl_first_cfg[i].screenRotation, width, height);
params.args.push_back(string("--deqp-gl-config-name=") + string(aosp_mustpass_egl_first_cfg[i].glConfigName));
runs.push_back(params);
}
}
static void getTestRunsForAOSPES(vector<TestRunParams>& runs, const ConfigList& configs, const glu::ApiType apiType)
{
#include "glcAospMustpassEs.hpp"
for (int i = 0; i < DE_LENGTH_OF_ARRAY(aosp_mustpass_es_first_cfg); ++i)
{
if (!glu::contextSupports(glu::ContextType(apiType), aosp_mustpass_es_first_cfg[i].apiType))
continue;
configInfo cfgInfo = parseConfigBitsFromName(aosp_mustpass_es_first_cfg[i].glConfigName);
vector<AOSPConfig>::const_iterator cfgIter;
for (cfgIter = configs.aospConfigs.begin(); cfgIter != configs.aospConfigs.end(); ++cfgIter)
{
// find first compatible config
if (isGLConfigCompatible(cfgInfo, *cfgIter))
{
break;
}
}
if (cfgIter == configs.aospConfigs.end())
{
TCU_FAIL(("No suitable configuration found for GL config " +
de::toString(aosp_mustpass_es_first_cfg[i].glConfigName))
.c_str());
return;
}
const char* apiName = getApiName(aosp_mustpass_es_first_cfg[i].apiType);
const int width = aosp_mustpass_es_first_cfg[i].surfaceWidth;
const int height = aosp_mustpass_es_first_cfg[i].surfaceHeight;
TestRunParams params;
params.logFilename = getLogFileName(apiName, aosp_mustpass_es_first_cfg[i].configName, 1, i, width, height, -1);
getBaseOptions(params.args, mustpassDir, apiName, aosp_mustpass_es_first_cfg[i].configName,
aosp_mustpass_es_first_cfg[i].screenRotation, width, height);
params.args.push_back(string("--deqp-gl-config-name=") + string(aosp_mustpass_es_first_cfg[i].glConfigName));
//set surface type
if ((*cfgIter).surfaceTypes & SURFACETYPE_WINDOW)
params.args.push_back("--deqp-surface-type=window");
else if ((*cfgIter).surfaceTypes & SURFACETYPE_PBUFFER)
params.args.push_back("--deqp-surface-type=pbuffer");
else if ((*cfgIter).surfaceTypes & SURFACETYPE_PIXMAP)
params.args.push_back("--deqp-surface-type=pixmap");
runs.push_back(params);
}
}
static void getTestRunsForNoContext(glu::ApiType type, vector<TestRunParams>& runs, const ConfigList& configs, const RunParams* runParams,
const int numRunParams, const char* mustpassDir)
{
vector<Config>::const_iterator cfgIter = configs.configs.begin();
for (int i = 0; i < numRunParams; ++i)
{
if (!glu::contextSupports(glu::ContextType(type), runParams[i].apiType))
continue;
const char* apiName = getApiName(runParams[i].apiType);
const int width = runParams[i].surfaceWidth;
const int height = runParams[i].surfaceHeight;
const int seed = runParams[i].baseSeed;
TestRunParams params;
params.logFilename = getLogFileName(apiName, runParams[i].configName, 1, i, width, height, seed);
getBaseOptions(params.args, mustpassDir, apiName, runParams[i].configName, runParams[i].screenRotation, width,
height);
params.args.push_back(string("--deqp-base-seed=") + de::toString(seed));
appendConfigArgs(*cfgIter, params.args, runParams[i].fboConfig);
runs.push_back(params);
}
}
static void getTestRunsForNoContextES(glu::ApiType type, vector<TestRunParams>& runs, const ConfigList& configs)
{
#include "glcKhronosMustpassEsNocontext.hpp"
getTestRunsForNoContext(type, runs, configs, khronos_mustpass_es_nocontext_first_cfg,
DE_LENGTH_OF_ARRAY(khronos_mustpass_es_nocontext_first_cfg), mustpassDir);
}
static void getTestRunsForSingleConfig(glu::ApiType type, vector<TestRunParams>& runs, const ConfigList& configs, const RunParams* runParams,
const int numRunParams, const char* mustpassDir)
{
vector<Config>::const_iterator cfgIter = configs.configs.begin();
for (int i = 0; i < numRunParams; ++i)
{
if (type != runParams[i].apiType)
continue;
const char* apiName = getApiName(runParams[i].apiType);
const int width = runParams[i].surfaceWidth;
const int height = runParams[i].surfaceHeight;
const int seed = runParams[i].baseSeed;
TestRunParams params;
params.logFilename = getLogFileName(apiName, runParams[i].configName, 1, i, width, height, seed);
getBaseOptions(params.args, mustpassDir, apiName, runParams[i].configName, runParams[i].screenRotation, width,
height);
params.args.push_back(string("--deqp-base-seed=") + de::toString(seed));
appendConfigArgs(*cfgIter, params.args, runParams[i].fboConfig);
runs.push_back(params);
}
}
static void getTestRunsForSingleConfigES(glu::ApiType type, vector<TestRunParams>& runs, const ConfigList& configs)
{
#include "glcKhronosMustpassEsSingleConfig.hpp"
getTestRunsForSingleConfig(type, runs, configs, khronos_mustpass_es_single_config_first_cfg,
DE_LENGTH_OF_ARRAY(khronos_mustpass_es_single_config_first_cfg), mustpassDir);
}
static void getTestRunsForES(glu::ApiType type, const ConfigList& configs, vector<TestRunParams>& runs)
{
getTestRunsForAOSPEGL(runs, configs);
getTestRunsForAOSPES(runs, configs, type);
getTestRunsForNoContextES(type, runs, configs);
getTestRunsForSingleConfigES(type, runs, configs);
#include "glcKhronosMustpassEs.hpp"
for (vector<Config>::const_iterator cfgIter = configs.configs.begin(); cfgIter != configs.configs.end(); ++cfgIter)
{
const bool isFirst = cfgIter == configs.configs.begin();
const int numRunParams = isFirst ? DE_LENGTH_OF_ARRAY(khronos_mustpass_es_first_cfg) :
DE_LENGTH_OF_ARRAY(khronos_mustpass_es_other_cfg);
const RunParams* runParams = isFirst ? khronos_mustpass_es_first_cfg : khronos_mustpass_es_other_cfg;
for (int runNdx = 0; runNdx < numRunParams; runNdx++)
{
if (!glu::contextSupports(glu::ContextType(type), runParams[runNdx].apiType))
continue;
const char* apiName = getApiName(runParams[runNdx].apiType);
const int width = runParams[runNdx].surfaceWidth;
const int height = runParams[runNdx].surfaceHeight;
const int seed = runParams[runNdx].baseSeed;
TestRunParams params;
params.logFilename =
getLogFileName(apiName, runParams[runNdx].configName, cfgIter->id, runNdx, width, height, seed);
getBaseOptions(params.args, mustpassDir, apiName, runParams[runNdx].configName,
runParams[runNdx].screenRotation, width, height);
params.args.push_back(string("--deqp-base-seed=") + de::toString(seed));
appendConfigArgs(*cfgIter, params.args, runParams[runNdx].fboConfig);
runs.push_back(params);
}
}
}
static void getTestRunsForNoContextGL(glu::ApiType type, vector<TestRunParams>& runs, const ConfigList& configs)
{
#include "glcKhronosMustpassGlNocontext.hpp"
getTestRunsForNoContext(type, runs, configs, khronos_mustpass_gl_nocontext_first_cfg,
DE_LENGTH_OF_ARRAY(khronos_mustpass_gl_nocontext_first_cfg), mustpassDir);
}
static void getTestRunsForSingleConfigGL(glu::ApiType type, vector<TestRunParams>& runs, const ConfigList& configs)
{
#include "glcKhronosMustpassGlSingleConfig.hpp"
getTestRunsForSingleConfig(type, runs, configs, khronos_mustpass_gl_single_config_first_cfg,
DE_LENGTH_OF_ARRAY(khronos_mustpass_gl_single_config_first_cfg), mustpassDir);
}
static void getTestRunsForESForGL(glu::ApiType type, vector<TestRunParams>& runs, const ConfigList& configs)
{
DE_UNREF(type);
#include "glcKhronosMustpassAospForGl.hpp"
vector<Config>::const_iterator cfgIter = configs.configs.begin();
const int numRunParams = DE_LENGTH_OF_ARRAY(khronos_mustpass_aosp_for_gl_first_cfg);
const RunParams* runParams = khronos_mustpass_aosp_for_gl_first_cfg;
for (int i = 0; i < numRunParams; ++i)
{
const char* apiName = getApiName(runParams[i].apiType);
const int width = runParams[i].surfaceWidth;
const int height = runParams[i].surfaceHeight;
const int seed = runParams[i].baseSeed;
TestRunParams params;
params.logFilename = getLogFileName(apiName, runParams[i].configName, 1, i, width, height, seed);
getBaseOptions(params.args, mustpassDir, apiName, runParams[i].configName, runParams[i].screenRotation, width,
height);
params.args.push_back(string("--deqp-base-seed=") + de::toString(seed));
appendConfigArgs(*cfgIter, params.args, runParams[i].fboConfig);
runs.push_back(params);
}
}
static void getTestRunsForGL(glu::ApiType type, const ConfigList& configs, vector<TestRunParams>& runs)
{
getTestRunsForESForGL(type, runs, configs);
getTestRunsForNoContextGL(type, runs, configs);
getTestRunsForSingleConfigGL(type, runs, configs);
#include "glcKhronosMustpassGl.hpp"
for (vector<Config>::const_iterator cfgIter = configs.configs.begin(); cfgIter != configs.configs.end(); ++cfgIter)
{
const bool isFirst = cfgIter == configs.configs.begin();
const int numRunParams = isFirst ? DE_LENGTH_OF_ARRAY(khronos_mustpass_gl_first_cfg) :
DE_LENGTH_OF_ARRAY(khronos_mustpass_gl_other_cfg);
const RunParams* runParams = isFirst ? khronos_mustpass_gl_first_cfg : khronos_mustpass_gl_other_cfg;
for (int runNdx = 0; runNdx < numRunParams; runNdx++)
{
if (type != runParams[runNdx].apiType)
continue;
const char* apiName = getApiName(runParams[runNdx].apiType);
const int width = runParams[runNdx].surfaceWidth;
const int height = runParams[runNdx].surfaceHeight;
const int seed = runParams[runNdx].baseSeed;
TestRunParams params;
params.logFilename =
getLogFileName(apiName, runParams[runNdx].configName, cfgIter->id, runNdx, width, height, seed);
getBaseOptions(params.args, mustpassDir, apiName, runParams[runNdx].configName,
runParams[runNdx].screenRotation, width, height);
params.args.push_back(string("--deqp-base-seed=") + de::toString(seed));
appendConfigArgs(*cfgIter, params.args, runParams[runNdx].fboConfig);
runs.push_back(params);
}
}
}
static void getTestRunParams(glu::ApiType type, const ConfigList& configs, vector<TestRunParams>& runs)
{
switch (type.getProfile())
{
case glu::PROFILE_CORE:
getTestRunsForGL(type, configs, runs);
break;
case glu::PROFILE_ES:
getTestRunsForES(type, configs, runs);
break;
default:
throw std::runtime_error("Unknown context type");
}
}
struct FileDeleter
{
void operator()(FILE* file) const
{
if (file)
fclose(file);
}
};
struct XmlWriterDeleter
{
void operator()(qpXmlWriter* writer) const
{
if (writer)
qpXmlWriter_destroy(writer);
}
};
static const char* getRunTypeName(glu::ApiType type)
{
if (type == glu::ApiType::es(2, 0))
return "es2";
else if (type == glu::ApiType::es(3, 0))
return "es3";
else if (type == glu::ApiType::es(3, 1))
return "es31";
else if (type == glu::ApiType::es(3, 2))
return "es32";
else if (type == glu::ApiType::core(3, 0))
return "gl30";
else if (type == glu::ApiType::core(3, 1))
return "gl31";
else if (type == glu::ApiType::core(3, 2))
return "gl32";
else if (type == glu::ApiType::core(3, 3))
return "gl33";
else if (type == glu::ApiType::core(4, 0))
return "gl40";
else if (type == glu::ApiType::core(4, 1))
return "gl41";
else if (type == glu::ApiType::core(4, 2))
return "gl42";
else if (type == glu::ApiType::core(4, 3))
return "gl43";
else if (type == glu::ApiType::core(4, 4))
return "gl44";
else if (type == glu::ApiType::core(4, 5))
return "gl45";
else if (type == glu::ApiType::core(4, 6))
return "gl46";
else
return DE_NULL;
}
#define XML_CHECK(X) \
if (!(X)) \
throw tcu::Exception("Writing XML failed")
static void writeRunSummary(const TestRunSummary& summary, const char* filename)
{
de::UniquePtr<FILE, FileDeleter> out(fopen(filename, "wb"));
if (!out)
throw tcu::Exception(string("Failed to open ") + filename);
de::UniquePtr<qpXmlWriter, XmlWriterDeleter> writer(qpXmlWriter_createFileWriter(out.get(), DE_FALSE, DE_FALSE));
if (!writer)
throw std::bad_alloc();
XML_CHECK(qpXmlWriter_startDocument(writer.get()));
{
qpXmlAttribute attribs[2];
attribs[0] = qpSetStringAttrib("Type", getRunTypeName(summary.runType));
attribs[1] = qpSetBoolAttrib("Conformant", summary.isConformant ? DE_TRUE : DE_FALSE);
XML_CHECK(qpXmlWriter_startElement(writer.get(), "Summary", DE_LENGTH_OF_ARRAY(attribs), attribs));
}
// Config run
{
qpXmlAttribute attribs[1];
attribs[0] = qpSetStringAttrib("FileName", summary.configLogFilename.c_str());
XML_CHECK(qpXmlWriter_startElement(writer.get(), "Configs", DE_LENGTH_OF_ARRAY(attribs), attribs) &&
qpXmlWriter_endElement(writer.get(), "Configs"));
}
// Record test run parameters (log filename & command line).
for (vector<TestRunParams>::const_iterator runIter = summary.runParams.begin(); runIter != summary.runParams.end();
++runIter)
{
string cmdLine;
qpXmlAttribute attribs[2];
for (vector<string>::const_iterator argIter = runIter->args.begin(); argIter != runIter->args.end(); ++argIter)
{
if (argIter != runIter->args.begin())
cmdLine += " ";
cmdLine += *argIter;
}
attribs[0] = qpSetStringAttrib("FileName", runIter->logFilename.c_str());
attribs[1] = qpSetStringAttrib("CmdLine", cmdLine.c_str());
XML_CHECK(qpXmlWriter_startElement(writer.get(), "TestRun", DE_LENGTH_OF_ARRAY(attribs), attribs) &&
qpXmlWriter_endElement(writer.get(), "TestRun"));
}
XML_CHECK(qpXmlWriter_endElement(writer.get(), "Summary"));
XML_CHECK(qpXmlWriter_endDocument(writer.get()));
}
#undef XML_CHECK
TestRunner::TestRunner(tcu::Platform& platform, tcu::Archive& archive, const char* waiverPath,
const char* logDirPath, glu::ApiType type, deUint32 flags)
: m_platform(platform)
, m_archive(archive)
, m_waiverPath(waiverPath)
, m_logDirPath(logDirPath)
, m_type(type)
, m_flags(flags)
, m_iterState(ITERATE_INIT)
, m_curSession(DE_NULL)
, m_sessionsExecuted(0)
, m_sessionsPassed(0)
, m_sessionsFailed(0)
{
}
TestRunner::~TestRunner(void)
{
delete m_curSession;
}
bool TestRunner::iterate(void)
{
switch (m_iterState)
{
case ITERATE_INIT:
init();
m_iterState = (m_sessionIter != m_runSessions.end()) ? ITERATE_INIT_SESSION : ITERATE_DEINIT;
return true;
case ITERATE_DEINIT:
deinit();
m_iterState = ITERATE_INIT;
return false;
case ITERATE_INIT_SESSION:
DE_ASSERT(m_sessionIter != m_runSessions.end());
initSession(*m_sessionIter);
if (m_flags & PRINT_SUMMARY)
m_iterState = ITERATE_DEINIT_SESSION;
else
m_iterState = ITERATE_ITERATE_SESSION;
return true;
case ITERATE_DEINIT_SESSION:
deinitSession();
++m_sessionIter;
m_iterState = (m_sessionIter != m_runSessions.end()) ? ITERATE_INIT_SESSION : ITERATE_DEINIT;
return true;
case ITERATE_ITERATE_SESSION:
if (!iterateSession())
m_iterState = ITERATE_DEINIT_SESSION;
return true;
default:
DE_ASSERT(false);
return false;
}
}
void TestRunner::init(void)
{
DE_ASSERT(m_runSessions.empty() && m_summary.runParams.empty());
tcu::print("Running %s conformance\n", glu::getApiTypeDescription(m_type));
m_summary.runType = m_type;
// Get list of configs to test.
ConfigList configList;
getDefaultConfigList(m_platform, m_type, configList);
tcu::print(" found %d compatible and %d excluded configs\n", (int)configList.configs.size(),
(int)configList.excludedConfigs.size());
// Config list run.
{
const char* configLogFilename = "configs.qpa";
TestRunParams configRun;
configRun.logFilename = configLogFilename;
configRun.args.push_back("--deqp-case=CTS-Configs.*");
m_runSessions.push_back(configRun);
m_summary.configLogFilename = configLogFilename;
}
// Conformance test type specific runs
getTestRunParams(m_type, configList, m_runSessions);
// Record run params for summary.
for (std::vector<TestRunParams>::const_iterator runIter = m_runSessions.begin() + 1; runIter != m_runSessions.end();
++runIter)
m_summary.runParams.push_back(*runIter);
// Session iterator
m_sessionIter = m_runSessions.begin();
}
void TestRunner::deinit(void)
{
// Print out totals.
bool isConformant_ = m_sessionsExecuted == m_sessionsPassed;
DE_ASSERT(m_sessionsExecuted == m_sessionsPassed + m_sessionsFailed);
tcu::print("\n%d/%d sessions passed, conformance test %s\n", m_sessionsPassed, m_sessionsExecuted,
isConformant_ ? "PASSED" : "FAILED");
m_summary.isConformant = isConformant_;
// Write out summary.
writeRunSummary(m_summary, de::FilePath::join(m_logDirPath, "cts-run-summary.xml").getPath());
m_runSessions.clear();
}
void TestRunner::initSession(const TestRunParams& runParams)
{
DE_ASSERT(!m_curSession);
tcu::print("\n Test run %d / %d\n", (int)(m_sessionIter - m_runSessions.begin() + 1), (int)m_runSessions.size());
// Compute final args for run.
vector<string> args(runParams.args);
args.push_back(string("--deqp-log-filename=") + de::FilePath::join(m_logDirPath, runParams.logFilename).getPath());
if (!(m_flags & VERBOSE_IMAGES))
args.push_back("--deqp-log-images=disable");
if (!(m_flags & VERBOSE_SHADERS))
args.push_back("--deqp-log-shader-sources=disable");
if (!m_waiverPath.empty())
args.push_back(string("--deqp-waiver-file=") + m_waiverPath);
std::ostringstream ostr;
std::ostream_iterator<string> out_it(ostr, ", ");
std::copy(args.begin(), args.end(), out_it);
tcu::print("\n Config: %s \n\n", ostr.str().c_str());
// Translate to argc, argv
vector<const char*> argv;
argv.push_back("cts-runner"); // Dummy binary name
for (vector<string>::const_iterator i = args.begin(); i != args.end(); i++)
argv.push_back(i->c_str());
// Create session
m_curSession = new RunSession(m_platform, m_archive, (int)argv.size(), &argv[0]);
}
void TestRunner::deinitSession(void)
{
DE_ASSERT(m_curSession);
// Collect results.
// \note NotSupported is treated as pass.
const tcu::TestRunStatus& result = m_curSession->getResult();
bool isOk = result.numFailed == 0 && result.isComplete;
DE_ASSERT(result.numExecuted == result.numPassed + result.numFailed + result.numNotSupported + result.numWarnings + result.numWaived);
m_sessionsExecuted += 1;
(isOk ? m_sessionsPassed : m_sessionsFailed) += 1;
delete m_curSession;
m_curSession = DE_NULL;
}
inline bool TestRunner::iterateSession(void)
{
return m_curSession->iterate();
}
} // glcts