blob: 236cb89893e9cc101953e60fa538623e320ff108 [file] [log] [blame]
#include <gtest/gtest.h>
#if !defined(__APPLE__)
# include <CL/cl.h>
#endif
#include <stdlib.h>
#include <cassert>
#include <fstream>
#include "harness/testHarness.h"
#include "common/string_utils.h"
#include "common/system_utils.h"
#include "src/tests/test_utils/runner/TestSuite.h"
#include "util/OSWindow.h"
#define MAX_LINE_LENGTH 3000
// The |main| function in each CTS executable is renamed to |ANGLE_oclcts_main|. It is called by
// the test functions.
extern int ANGLE_oclcts_main(int argc, const char **argv);
namespace
{
constexpr char kInfoTag[] = "*RESULT";
std::string TrimString(const std::string &input, const std::string &trimChars)
{
auto begin = input.find_first_not_of(trimChars);
if (begin == std::string::npos)
{
return "";
}
std::string::size_type end = input.find_last_not_of(trimChars);
if (end == std::string::npos)
{
return input.substr(begin);
}
return input.substr(begin, end - begin + 1);
}
class OpenCLCaseList
{
public:
OpenCLCaseList();
struct CaseInfo
{
CaseInfo(const std::string &testNameIn, int expectationIn)
: testCaseDescription(testNameIn), expectation(expectationIn)
{
// The test list is specified in the form <testSuiteName.testName testArgs>. Parse the
// string as such.
size_t startPos = testCaseDescription.find('.');
testSuiteName = testCaseDescription.substr(0, startPos);
testNameWithArgs = "";
if (startPos != std::string::npos)
testNameWithArgs = testCaseDescription.substr(startPos + 1);
}
std::string testCaseDescription;
std::string testSuiteName;
std::string testNameWithArgs;
int expectation;
};
void initialize(angle::TestSuite *instance);
const CaseInfo &getCaseInfo(size_t caseIndex) const
{
assert(mInitialized);
assert(caseIndex < mCaseInfoList.size());
return mCaseInfoList[caseIndex];
}
size_t numCases() const
{
assert(mInitialized);
return mCaseInfoList.size();
}
private:
std::vector<CaseInfo> mCaseInfoList;
bool mInitialized = false;
};
OpenCLCaseList::OpenCLCaseList() {}
const OpenCLCaseList &GetTestList()
{
angle::TestSuite *instance = angle::TestSuite::GetInstance();
static OpenCLCaseList sCaseList;
sCaseList.initialize(instance);
return sCaseList;
}
void OpenCLCaseList::initialize(angle::TestSuite *instance)
{
mInitialized = true;
constexpr char kTestExpectationsPath[] = "src/tests/cl_support/openclcts_expectations.txt";
constexpr char kTestMustPassPath[] = "src/tests/cl_support/openclcts_mustpass.txt";
constexpr size_t kMaxPath = 512;
std::array<char, kMaxPath> foundDataPath;
if (!angle::FindTestDataPath(kTestExpectationsPath, foundDataPath.data(), foundDataPath.size()))
{
std::cerr << "Unable to find test expectations path (" << kTestExpectationsPath << ")\n";
exit(EXIT_FAILURE);
}
if (!instance->loadAllTestExpectationsFromFile(std::string(foundDataPath.data())))
{
exit(EXIT_FAILURE);
}
if (!angle::FindTestDataPath(kTestMustPassPath, foundDataPath.data(), foundDataPath.size()))
{
std::cerr << "Unable to find test must pass list path (" << kTestMustPassPath << ")\n";
exit(EXIT_FAILURE);
}
std::ifstream caseListStream(std::string(foundDataPath.data()));
if (caseListStream.fail())
{
std::cerr << "Failed to load the case list." << std::endl;
exit(EXIT_FAILURE);
}
while (!caseListStream.eof())
{
std::string inString;
std::getline(caseListStream, inString);
std::string testName = TrimString(inString, angle::kWhitespaceASCII);
if (testName.empty())
continue;
int expectation = instance->getTestExpectation(testName);
mCaseInfoList.push_back(CaseInfo(testName, expectation));
}
}
class OpenCLTestSuiteStats
{
public:
OpenCLTestSuiteStats() {}
private:
void setUpTestStats();
void printTestStats();
void countTestResult(int result);
uint32_t mTestCount;
uint32_t mPassedTestCount;
uint32_t mFailedTestCount;
uint32_t mTestExceptionCount;
uint32_t mNotSupportedTestCount;
uint32_t mSkippedTestCount;
std::vector<std::string> mUnexpectedFailed;
std::vector<std::string> mUnexpectedPasses;
friend class OpenCLTest;
};
void OpenCLTestSuiteStats::setUpTestStats()
{
mPassedTestCount = 0;
mFailedTestCount = 0;
mNotSupportedTestCount = 0;
mTestExceptionCount = 0;
mTestCount = 0;
mSkippedTestCount = 0;
mUnexpectedPasses.clear();
mUnexpectedFailed.clear();
}
std::string GetTestStatLine(const std::string &key, const std::string &value)
{
return std::string(kInfoTag) + ": " + key + ": " + value + "\n";
}
void OpenCLTestSuiteStats::printTestStats()
{
uint32_t crashedCount =
mTestCount - (mPassedTestCount + mFailedTestCount + mNotSupportedTestCount +
mTestExceptionCount + mSkippedTestCount);
std::cout << GetTestStatLine("Total", std::to_string(mTestCount));
std::cout << GetTestStatLine("Passed", std::to_string(mPassedTestCount));
std::cout << GetTestStatLine("Failed", std::to_string(mFailedTestCount));
std::cout << GetTestStatLine("Skipped", std::to_string(mSkippedTestCount));
std::cout << GetTestStatLine("Not Supported", std::to_string(mNotSupportedTestCount));
std::cout << GetTestStatLine("Exception", std::to_string(mTestExceptionCount));
std::cout << GetTestStatLine("Crashed", std::to_string(crashedCount));
if (!mUnexpectedPasses.empty())
{
std::cout << GetTestStatLine("Unexpected Passed Count",
std::to_string(mUnexpectedPasses.size()));
for (const std::string &testName : mUnexpectedPasses)
{
std::cout << GetTestStatLine("Unexpected Passed Tests", testName);
}
}
if (!mUnexpectedFailed.empty())
{
std::cout << GetTestStatLine("Unexpected Failed Count",
std::to_string(mUnexpectedFailed.size()));
for (const std::string &testName : mUnexpectedFailed)
{
std::cout << GetTestStatLine("Unexpected Failed Tests", testName);
}
}
}
void OpenCLTestSuiteStats::countTestResult(int result)
{
switch (result)
{
case EXIT_SUCCESS:
mPassedTestCount++;
break;
default:
mFailedTestCount++;
break;
}
}
class OpenCLTest : public testing::Test
{
public:
OpenCLTest(size_t caseIndex) : mTestCaseIndex(caseIndex) {}
static void SetUpTestSuite();
static void TearDownTestSuite();
protected:
void TestBody() override;
private:
size_t mTestCaseIndex = 0;
static OpenCLTestSuiteStats sTestSuiteData;
};
OpenCLTestSuiteStats OpenCLTest::sTestSuiteData = OpenCLTestSuiteStats();
// static function called once before running all of OpenCLTest under the same test suite
void OpenCLTest::SetUpTestSuite()
{
sTestSuiteData.setUpTestStats();
}
// static function called once after running all of OpenCLTest under the same test suite
void OpenCLTest::TearDownTestSuite()
{
sTestSuiteData.printTestStats();
}
void OpenCLTest::TestBody()
{
const auto &caseInfo = GetTestList().getCaseInfo(mTestCaseIndex);
// Tests that crash exit the harness before collecting the result. To tally the number of
// crashed tests we track how many tests we "tried" to run.
sTestSuiteData.mTestCount++;
if (caseInfo.expectation == angle::GPUTestExpectationsParser::kGpuTestSkip)
{
sTestSuiteData.mSkippedTestCount++;
std::cout << "Test skipped.\n";
return;
}
// test name and options
std::vector<std::string> testNameVector;
angle::SplitStringAlongWhitespace(caseInfo.testNameWithArgs, &testNameVector);
std::vector<const char *> argv;
argv.push_back(caseInfo.testSuiteName.c_str());
for (const auto &str : testNameVector)
{
argv.push_back(str.c_str());
}
int argc = argv.size();
// C requires argv[argc] shall be null pointer
argv.push_back(nullptr);
const int result = ANGLE_oclcts_main(argc, argv.data());
sTestSuiteData.countTestResult(result);
if (caseInfo.expectation == angle::GPUTestExpectationsParser::kGpuTestPass ||
caseInfo.expectation == angle::GPUTestExpectationsParser::kGpuTestFlaky)
{
EXPECT_EQ(result, EXIT_SUCCESS);
if (result != EXIT_SUCCESS)
{
sTestSuiteData.mUnexpectedFailed.push_back(caseInfo.testCaseDescription);
}
}
else if (result == EXIT_SUCCESS)
{
std::cout << "Test expected to fail but passed!" << std::endl;
sTestSuiteData.mUnexpectedPasses.push_back(caseInfo.testCaseDescription);
}
}
void RegisterCLCTSTests()
{
const OpenCLCaseList &testList = GetTestList();
for (size_t caseIndex = 0; caseIndex < testList.numCases(); caseIndex++)
{
auto factory = [caseIndex]() { return new OpenCLTest(caseIndex); };
// The mustpass list contains lines in the form suite.name. There is one executable per
// test suite, and the suite name can be found in ANGLE_CL_SUITE_NAME. Tests that are from
// other suites are excluded for this executable.
//
// Note that the CTS has groups of test suites, but so far the test suite names alone are
// unique so the group name is not placed in the mustpass.
const std::string &testSuiteName = testList.getCaseInfo(caseIndex).testSuiteName;
const std::string &testName = testList.getCaseInfo(caseIndex).testNameWithArgs;
if (testSuiteName != ANGLE_CL_SUITE_NAME)
{
continue;
}
testing::RegisterTest(testSuiteName.c_str(), testName.c_str(), nullptr, nullptr, __FILE__,
__LINE__, factory);
}
}
} // anonymous namespace
int main(int argc, char **argv)
{
// Set up the environment for the CTS
#ifdef ANGLE_OPENCL_ICD_PATH
{
// The icd file is placed in the executable directory
const std::string moduleDir = angle::GetModuleDirectory();
const std::string icdPath = angle::ConcatenatePath(moduleDir, ANGLE_OPENCL_ICD_PATH);
angle::SetEnvironmentVar("OCL_ICD_VENDORS", icdPath.c_str());
}
#endif // ANGLE_OPENCL_ICD_PATH
// TODO: Fix TestSuite so that it "consumes" the args that it picks up, leaving any that is not
// recognized. The left over should then be passed to `ANGLE_oclcts_main` in `TestBody`.
// http://anglebug.com/372722560
angle::TestSuite testSuite(&argc, argv, RegisterCLCTSTests);
return testSuite.run();
}