| // |
| // Copyright 2015 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // angle_deqp_gtest: |
| // dEQP and GoogleTest integration logic. Calls through to the random |
| // order executor. |
| |
| #include <stdint.h> |
| #include <array> |
| #include <fstream> |
| |
| #include <gtest/gtest.h> |
| |
| #include "angle_deqp_libtester.h" |
| #include "common/Optional.h" |
| #include "common/angleutils.h" |
| #include "common/base/anglebase/no_destructor.h" |
| #include "common/debug.h" |
| #include "common/platform.h" |
| #include "common/string_utils.h" |
| #include "common/system_utils.h" |
| #include "platform/PlatformMethods.h" |
| #include "tests/test_utils/runner/TestSuite.h" |
| #include "util/OSWindow.h" |
| #include "util/test_utils.h" |
| |
| namespace angle |
| { |
| namespace |
| { |
| #if !defined(NDEBUG) |
| constexpr bool kIsDebug = true; |
| #else |
| constexpr bool kIsDebug = false; |
| #endif // !defined(NDEBUG) |
| |
| bool gGlobalError = false; |
| bool gExpectError = false; |
| bool gVerbose = false; |
| |
| // Set this to true temporarily to enable image logging in release. Useful for diagnosing |
| // errors. |
| bool gLogImages = kIsDebug; |
| |
| constexpr char kInfoTag[] = "*RESULT"; |
| |
| void HandlePlatformError(PlatformMethods *platform, const char *errorMessage) |
| { |
| if (!gExpectError) |
| { |
| FAIL() << errorMessage; |
| } |
| gGlobalError = true; |
| } |
| |
| // Relative to the ANGLE root folder. |
| constexpr char kCTSRootPath[] = "third_party/VK-GL-CTS/src/"; |
| constexpr char kSupportPath[] = "src/tests/deqp_support/"; |
| |
| #define GLES_CTS_DIR(PATH) "external/openglcts/data/mustpass/gles/" PATH |
| #define GL_CTS_DIR(PATH) "external/openglcts/data/mustpass/gl/" PATH |
| #define EGL_CTS_DIR(PATH) "external/openglcts/data/mustpass/egl/" PATH |
| |
| const char *gCaseListFiles[] = { |
| EGL_CTS_DIR("aosp_mustpass/main/egl-master.txt"), |
| GLES_CTS_DIR("aosp_mustpass/main/gles2-master.txt"), |
| GLES_CTS_DIR("aosp_mustpass/main/gles3-master.txt"), |
| GLES_CTS_DIR("aosp_mustpass/main/gles31-master.txt"), |
| GLES_CTS_DIR("khronos_mustpass/main/gles2-khr-master.txt"), |
| GLES_CTS_DIR("khronos_mustpass/main/gles3-khr-master.txt"), |
| GLES_CTS_DIR("khronos_mustpass/main/gles31-khr-master.txt"), |
| GLES_CTS_DIR("khronos_mustpass/main/gles32-khr-master.txt"), |
| GLES_CTS_DIR("khronos_mustpass_noctx/main/gles2-khr-noctx-master.txt"), |
| GLES_CTS_DIR("khronos_mustpass_noctx/main/gles32-khr-noctx-master.txt"), |
| GLES_CTS_DIR("khronos_mustpass_single/main/gles32-khr-single.txt"), |
| GLES_CTS_DIR("aosp_mustpass/main/gles3-rotate-landscape.txt"), |
| GLES_CTS_DIR("aosp_mustpass/main/gles3-rotate-reverse-portrait.txt"), |
| GLES_CTS_DIR("aosp_mustpass/main/gles3-rotate-reverse-landscape.txt"), |
| GLES_CTS_DIR("aosp_mustpass/main/gles31-rotate-landscape.txt"), |
| GLES_CTS_DIR("aosp_mustpass/main/gles31-rotate-reverse-portrait.txt"), |
| GLES_CTS_DIR("aosp_mustpass/main/gles31-rotate-reverse-landscape.txt"), |
| GLES_CTS_DIR("aosp_mustpass/main/gles3-multisample.txt"), |
| GLES_CTS_DIR("aosp_mustpass/main/gles3-565-no-depth-no-stencil.txt"), |
| GLES_CTS_DIR("aosp_mustpass/main/gles31-multisample.txt"), |
| GLES_CTS_DIR("aosp_mustpass/main/gles31-565-no-depth-no-stencil.txt"), |
| GL_CTS_DIR("khronos_mustpass/main/gl46-master.txt"), |
| }; |
| |
| const std::vector<const char *> gTestSuiteConfigParameters[] = { |
| {"--deqp-gl-config-name=rgba8888d24s8ms0"}, // egl |
| {"--deqp-gl-config-name=rgba8888d24s8ms0"}, // gles2 |
| {"--deqp-gl-config-name=rgba8888d24s8ms0"}, // gles3 |
| {"--deqp-gl-config-name=rgba8888d24s8ms0"}, // gles31 |
| {"--deqp-gl-config-name=rgba8888d24s8ms0"}, // gles2-khr |
| {"--deqp-gl-config-name=rgba8888d24s8ms0"}, // gles3-khr |
| {"--deqp-gl-config-name=rgba8888d24s8ms0"}, // gles31-khr |
| {"--deqp-gl-config-name=rgba8888d24s8ms0"}, // gles32-khr |
| {"--deqp-gl-config-name=rgba8888d24s8ms0"}, // gles2-khr-noctx |
| {"--deqp-gl-config-name=rgba8888d24s8ms0"}, // gles32-khr-noctx |
| {"--deqp-gl-config-name=rgba8888d24s8ms0"}, // gles32-khr-single |
| {"--deqp-gl-config-name=rgba8888d24s8ms0"}, // gles3-rotate90 |
| {"--deqp-gl-config-name=rgba8888d24s8ms0"}, // gles3-rotate180 |
| {"--deqp-gl-config-name=rgba8888d24s8ms0"}, // gles3-rotate270 |
| {"--deqp-gl-config-name=rgba8888d24s8ms0"}, // gles31-rotate90 |
| {"--deqp-gl-config-name=rgba8888d24s8ms0"}, // gles31-rotate180 |
| {"--deqp-gl-config-name=rgba8888d24s8ms0"}, // gles31-rotate270 |
| {"--deqp-gl-config-name=rgba8888d24s8ms4"}, // gles3-multisample |
| {"--deqp-gl-config-name=rgb565d0s0ms0"}, // gles3-rgb565-no-depth-no-stencil |
| {"--deqp-gl-config-name=rgba8888d24s8ms4"}, // gles31-multisample |
| {"--deqp-gl-config-name=rgb565d0s0ms0"}, // gles31-rgb565-no-depth-no-stencil |
| {"--deqp-gl-config-name=rgba8888d24s8ms4"}, // gl46 |
| }; |
| |
| #undef GLES_CTS_DIR |
| #undef GL_CTS_DIR |
| |
| const char *gTestExpectationsFiles[] = { |
| "deqp_egl_test_expectations.txt", |
| "deqp_gles2_test_expectations.txt", |
| "deqp_gles3_test_expectations.txt", |
| "deqp_gles31_test_expectations.txt", |
| "deqp_khr_gles2_test_expectations.txt", |
| "deqp_khr_gles3_test_expectations.txt", |
| "deqp_khr_gles31_test_expectations.txt", |
| "deqp_khr_gles32_test_expectations.txt", |
| "deqp_khr_noctx_gles2_test_expectations.txt", |
| "deqp_khr_noctx_gles32_test_expectations.txt", |
| "deqp_khr_single_gles32_test_expectations.txt", |
| "deqp_gles3_rotate_test_expectations.txt", |
| "deqp_gles3_rotate_test_expectations.txt", |
| "deqp_gles3_rotate_test_expectations.txt", |
| "deqp_gles31_rotate_test_expectations.txt", |
| "deqp_gles31_rotate_test_expectations.txt", |
| "deqp_gles31_rotate_test_expectations.txt", |
| "deqp_gles3_multisample_test_expectations.txt", |
| "deqp_gles3_565_no_depth_no_stencil_test_expectations.txt", |
| "deqp_gles31_multisample_test_expectations.txt", |
| "deqp_gles31_565_no_depth_no_stencil_test_expectations.txt", |
| "deqp_gl46_test_expectations.txt", |
| }; |
| |
| using APIInfo = std::pair<const char *, GPUTestConfig::API>; |
| |
| constexpr APIInfo kEGLDisplayAPIs[] = { |
| {"angle-d3d9", GPUTestConfig::kAPID3D9}, |
| {"angle-d3d11", GPUTestConfig::kAPID3D11}, |
| {"angle-d3d11-ref", GPUTestConfig::kAPID3D11}, |
| {"angle-gl", GPUTestConfig::kAPIGLDesktop}, |
| {"angle-gles", GPUTestConfig::kAPIGLES}, |
| {"angle-metal", GPUTestConfig::kAPIMetal}, |
| {"angle-null", GPUTestConfig::kAPIUnknown}, |
| {"angle-swiftshader", GPUTestConfig::kAPISwiftShader}, |
| {"angle-vulkan", GPUTestConfig::kAPIVulkan}, |
| {"win32", GPUTestConfig::kAPIUnknown}, |
| {"x11", GPUTestConfig::kAPIUnknown}, |
| }; |
| |
| constexpr char kdEQPEGLString[] = "--deqp-egl-display-type="; |
| constexpr char kANGLEEGLString[] = "--use-angle="; |
| constexpr char kANGLEPreRotation[] = "--emulated-pre-rotation="; |
| constexpr char kdEQPCaseString[] = "--deqp-case="; |
| constexpr char kVerboseString[] = "--verbose"; |
| constexpr char kRenderDocString[] = "--renderdoc"; |
| constexpr char kNoRenderDocString[] = "--no-renderdoc"; |
| constexpr char kdEQPFlagsPrefix[] = "--deqp-"; |
| constexpr char kGTestFilter[] = "--gtest_filter="; |
| |
| angle::base::NoDestructor<std::vector<char>> gFilterStringBuffer; |
| |
| // For angle_deqp_gles3*_rotateN_tests, default gOptions.preRotation to N. |
| #if defined(ANGLE_DEQP_GLES3_ROTATE90_TESTS) || defined(ANGLE_DEQP_GLES31_ROTATE90_TESTS) |
| constexpr uint32_t kDefaultPreRotation = 90; |
| #elif defined(ANGLE_DEQP_GLES3_ROTATE180_TESTS) || defined(ANGLE_DEQP_GLES31_ROTATE180_TESTS) |
| constexpr uint32_t kDefaultPreRotation = 180; |
| #elif defined(ANGLE_DEQP_GLES3_ROTATE270_TESTS) || defined(ANGLE_DEQP_GLES31_ROTATE270_TESTS) |
| constexpr uint32_t kDefaultPreRotation = 270; |
| #else |
| constexpr uint32_t kDefaultPreRotation = 0; |
| #endif |
| |
| #if defined(ANGLE_TEST_ENABLE_RENDERDOC_CAPTURE) |
| constexpr bool kEnableRenderDocCapture = true; |
| #else |
| constexpr bool kEnableRenderDocCapture = false; |
| #endif |
| |
| const APIInfo *gInitAPI = nullptr; |
| dEQPOptions gOptions = { |
| kDefaultPreRotation, // preRotation |
| kEnableRenderDocCapture, // enableRenderDocCapture |
| }; |
| |
| constexpr const char gdEQPEGLConfigNameString[] = "--deqp-gl-config-name="; |
| constexpr const char gdEQPLogImagesString[] = "--deqp-log-images="; |
| |
| // Use the config name defined in gTestSuiteConfigParameters by default |
| // If gEGLConfigNameFromCmdLine is overwritten by --deqp-gl-config-name passed from command |
| // line arguments, for example: |
| // out/Debug/angle_deqp_egl_tests --verbose --deqp-gl-config-name=rgba8888d24s8 |
| // use gEGLConfigNameFromCmdLine (rgba8888d24s8) instead. |
| // Invalid --deqp-gl-config-name value passed from command line arguments will be caught by |
| // glu::parseConfigBitsFromName() defined in gluRenderConfig.cpp, and it will cause tests |
| // to fail |
| const char *gEGLConfigNameFromCmdLine = ""; |
| |
| std::vector<const char *> gdEQPForwardFlags; |
| |
| // Returns the default API for a platform. |
| const char *GetDefaultAPIName() |
| { |
| #if defined(ANGLE_PLATFORM_ANDROID) || defined(ANGLE_PLATFORM_LINUX) || \ |
| defined(ANGLE_PLATFORM_WINDOWS) |
| return "angle-vulkan"; |
| #elif defined(ANGLE_PLATFORM_APPLE) |
| return "angle-gl"; |
| #else |
| # error Unknown platform. |
| #endif |
| } |
| |
| const APIInfo *FindAPIInfo(const std::string &arg) |
| { |
| for (auto &displayAPI : kEGLDisplayAPIs) |
| { |
| if (arg == displayAPI.first) |
| { |
| return &displayAPI; |
| } |
| } |
| return nullptr; |
| } |
| |
| const APIInfo *GetDefaultAPIInfo() |
| { |
| const APIInfo *defaultInfo = FindAPIInfo(GetDefaultAPIName()); |
| ASSERT(defaultInfo); |
| return defaultInfo; |
| } |
| |
| std::string GetTestStatLine(const std::string &key, const std::string &value) |
| { |
| return std::string(kInfoTag) + ": " + key + ": " + value + "\n"; |
| } |
| |
| // During the CaseList initialization we cannot use the GTEST FAIL macro to quit the program |
| // because the initialization is called outside of tests the first time. |
| void Die() |
| { |
| exit(EXIT_FAILURE); |
| } |
| |
| Optional<std::string> FindFileFromPath(const char *dirPath, const char *filePath) |
| { |
| std::stringstream strstr; |
| strstr << dirPath << filePath; |
| std::string path = strstr.str(); |
| |
| constexpr size_t kMaxFoundPathLen = 1000; |
| char foundPath[kMaxFoundPathLen]; |
| if (angle::FindTestDataPath(path.c_str(), foundPath, kMaxFoundPathLen)) |
| { |
| return std::string(foundPath); |
| } |
| |
| return Optional<std::string>::Invalid(); |
| } |
| |
| Optional<std::string> FindCaseListPath(size_t testModuleIndex) |
| { |
| return FindFileFromPath(kCTSRootPath, gCaseListFiles[testModuleIndex]); |
| } |
| |
| Optional<std::string> FindTestExpectationsPath(size_t testModuleIndex) |
| { |
| return FindFileFromPath(kSupportPath, gTestExpectationsFiles[testModuleIndex]); |
| } |
| |
| size_t GetTestModuleIndex() |
| { |
| #ifdef ANGLE_DEQP_EGL_TESTS |
| return 0; |
| #endif |
| |
| #ifdef ANGLE_DEQP_GLES2_TESTS |
| return 1; |
| #endif |
| |
| #ifdef ANGLE_DEQP_GLES3_TESTS |
| return 2; |
| #endif |
| |
| #ifdef ANGLE_DEQP_GLES31_TESTS |
| return 3; |
| #endif |
| |
| #ifdef ANGLE_DEQP_KHR_GLES2_TESTS |
| return 4; |
| #endif |
| |
| #ifdef ANGLE_DEQP_KHR_GLES3_TESTS |
| return 5; |
| #endif |
| |
| #ifdef ANGLE_DEQP_KHR_GLES31_TESTS |
| return 6; |
| #endif |
| |
| #ifdef ANGLE_DEQP_KHR_GLES32_TESTS |
| return 7; |
| #endif |
| |
| #ifdef ANGLE_DEQP_KHR_NOCTX_GLES2_TESTS |
| return 8; |
| #endif |
| |
| #ifdef ANGLE_DEQP_KHR_NOCTX_GLES32_TESTS |
| return 9; |
| #endif |
| |
| #ifdef ANGLE_DEQP_KHR_SINGLE_GLES32_TESTS |
| return 10; |
| #endif |
| |
| #ifdef ANGLE_DEQP_GLES3_ROTATE90_TESTS |
| return 11; |
| #endif |
| |
| #ifdef ANGLE_DEQP_GLES3_ROTATE180_TESTS |
| return 12; |
| #endif |
| |
| #ifdef ANGLE_DEQP_GLES3_ROTATE270_TESTS |
| return 13; |
| #endif |
| |
| #ifdef ANGLE_DEQP_GLES31_ROTATE90_TESTS |
| return 14; |
| #endif |
| |
| #ifdef ANGLE_DEQP_GLES31_ROTATE180_TESTS |
| return 15; |
| #endif |
| |
| #ifdef ANGLE_DEQP_GLES31_ROTATE270_TESTS |
| return 16; |
| #endif |
| |
| #ifdef ANGLE_DEQP_GLES3_MULTISAMPLE_TESTS |
| return 17; |
| #endif |
| |
| #ifdef ANGLE_DEQP_GLES3_565_NO_DEPTH_NO_STENCIL_TESTS |
| return 18; |
| #endif |
| |
| #ifdef ANGLE_DEQP_GLES31_MULTISAMPLE_TESTS |
| return 19; |
| #endif |
| |
| #ifdef ANGLE_DEQP_GLES31_565_NO_DEPTH_NO_STENCIL_TESTS |
| return 20; |
| #endif |
| |
| #ifdef ANGLE_DEQP_GL_TESTS |
| return 21; |
| #endif |
| } |
| |
| class dEQPCaseList |
| { |
| public: |
| dEQPCaseList(size_t testModuleIndex); |
| |
| struct CaseInfo |
| { |
| CaseInfo(const std::string &testNameIn, int expectationIn) |
| : testName(testNameIn), expectation(expectationIn) |
| {} |
| |
| std::string testName; |
| int expectation; |
| }; |
| |
| void initialize(); |
| |
| 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; |
| size_t mTestModuleIndex; |
| bool mInitialized = false; |
| }; |
| |
| dEQPCaseList::dEQPCaseList(size_t testModuleIndex) : mTestModuleIndex(testModuleIndex) {} |
| |
| void dEQPCaseList::initialize() |
| { |
| if (mInitialized) |
| { |
| return; |
| } |
| |
| mInitialized = true; |
| |
| Optional<std::string> caseListPath = FindCaseListPath(mTestModuleIndex); |
| if (!caseListPath.valid()) |
| { |
| std::cerr << "Failed to find case list file." << std::endl; |
| Die(); |
| } |
| |
| Optional<std::string> testExpectationsPath = FindTestExpectationsPath(mTestModuleIndex); |
| if (!testExpectationsPath.valid()) |
| { |
| std::cerr << "Failed to find test expectations file." << std::endl; |
| Die(); |
| } |
| |
| GPUTestConfig::API api = GetDefaultAPIInfo()->second; |
| // Set the API from the command line, or using the default platform API. |
| if (gInitAPI) |
| { |
| api = gInitAPI->second; |
| } |
| |
| GPUTestConfig testConfig = GPUTestConfig(api, gOptions.preRotation); |
| |
| #if !defined(ANGLE_PLATFORM_ANDROID) |
| // Note: These prints mess up parsing of test list when running on Android. |
| std::cout << "Using test config with:" << std::endl; |
| for (uint32_t condition : testConfig.getConditions()) |
| { |
| const char *name = GetConditionName(condition); |
| if (name != nullptr) |
| { |
| std::cout << " " << name << std::endl; |
| } |
| } |
| #endif |
| |
| TestSuite *testSuite = TestSuite::GetInstance(); |
| |
| if (!testSuite->loadTestExpectationsFromFileWithConfig(testConfig, |
| testExpectationsPath.value())) |
| { |
| Die(); |
| } |
| |
| std::ifstream caseListStream(caseListPath.value()); |
| if (caseListStream.fail()) |
| { |
| std::cerr << "Failed to load the case list." << std::endl; |
| Die(); |
| } |
| |
| while (!caseListStream.eof()) |
| { |
| std::string inString; |
| std::getline(caseListStream, inString); |
| |
| std::string testName = TrimString(inString, kWhitespaceASCII); |
| if (testName.empty()) |
| continue; |
| int expectation = testSuite->getTestExpectation(testName); |
| mCaseInfoList.push_back(CaseInfo(testName, expectation)); |
| } |
| |
| if (testSuite->logAnyUnusedTestExpectations()) |
| { |
| Die(); |
| } |
| } |
| |
| bool IsPassingResult(dEQPTestResult result) |
| { |
| // Check the global error flag for unexpected platform errors. |
| if (gGlobalError) |
| { |
| gGlobalError = false; |
| return false; |
| } |
| |
| switch (result) |
| { |
| case dEQPTestResult::Fail: |
| case dEQPTestResult::Exception: |
| return false; |
| |
| default: |
| return true; |
| } |
| } |
| |
| const dEQPCaseList &GetTestList(size_t testModuleIndex) |
| { |
| static dEQPCaseList sCaseList(testModuleIndex); |
| sCaseList.initialize(); |
| return sCaseList; |
| } |
| |
| const std::string GetTestCaseName(size_t testModuleIndex, size_t caseIndex) |
| { |
| const auto &caseInfo = GetTestList(testModuleIndex).getCaseInfo(caseIndex); |
| return caseInfo.testName; |
| } |
| |
| class dEQPTestSuiteStats |
| { |
| public: |
| dEQPTestSuiteStats() {} |
| |
| private: |
| void setUpTestStats(); |
| void printTestStats(); |
| void countTestResult(dEQPTestResult 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 dEQPTest; |
| }; |
| |
| void dEQPTestSuiteStats::setUpTestStats() |
| { |
| mPassedTestCount = 0; |
| mFailedTestCount = 0; |
| mNotSupportedTestCount = 0; |
| mTestExceptionCount = 0; |
| mTestCount = 0; |
| mSkippedTestCount = 0; |
| mUnexpectedPasses.clear(); |
| mUnexpectedFailed.clear(); |
| } |
| |
| void dEQPTestSuiteStats::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 dEQPTestSuiteStats::countTestResult(dEQPTestResult result) |
| { |
| switch (result) |
| { |
| case dEQPTestResult::Pass: |
| mPassedTestCount++; |
| break; |
| case dEQPTestResult::Fail: |
| mFailedTestCount++; |
| break; |
| case dEQPTestResult::NotSupported: |
| mNotSupportedTestCount++; |
| break; |
| case dEQPTestResult::Exception: |
| mTestExceptionCount++; |
| break; |
| default: |
| std::cerr << "Unexpected test result code: " << static_cast<int>(result) << "\n"; |
| break; |
| } |
| } |
| |
| class dEQPTest : public testing::Test |
| { |
| public: |
| dEQPTest(size_t testModuleIndex, size_t caseIndex) |
| : mTestModuleIndex(testModuleIndex), mTestCaseIndex(caseIndex) |
| {} |
| |
| static void SetUpTestSuite(); |
| static void TearDownTestSuite(); |
| |
| protected: |
| void TestBody() override; |
| |
| private: |
| size_t mTestModuleIndex = 0; |
| size_t mTestCaseIndex = 0; |
| |
| static dEQPTestSuiteStats sTestSuiteData; |
| }; |
| |
| dEQPTestSuiteStats dEQPTest::sTestSuiteData = dEQPTestSuiteStats(); |
| |
| // static function called once before running all of dEQPTest under the same test suite |
| void dEQPTest::SetUpTestSuite() |
| { |
| sTestSuiteData.setUpTestStats(); |
| |
| std::vector<const char *> argv; |
| |
| // Reserve one argument for the binary name. |
| argv.push_back(""); |
| |
| // Add init api. |
| const char *targetApi = gInitAPI ? gInitAPI->first : GetDefaultAPIName(); |
| std::string apiArgString = std::string(kdEQPEGLString) + targetApi; |
| argv.push_back(apiArgString.c_str()); |
| |
| std::string configNameFromCmdLineString = |
| std::string(gdEQPEGLConfigNameString) + gEGLConfigNameFromCmdLine; |
| |
| // Add test config parameters |
| for (const char *configParam : gTestSuiteConfigParameters[GetTestModuleIndex()]) |
| { |
| // Check if we pass --deqp-gl-config-name from the command line, if yes, use the one from |
| // command line |
| if (std::strlen(gEGLConfigNameFromCmdLine) > 0 && |
| std::strncmp(configParam, gdEQPEGLConfigNameString, strlen(gdEQPEGLConfigNameString)) == |
| 0) |
| { |
| configParam = configNameFromCmdLineString.c_str(); |
| } |
| argv.push_back(configParam); |
| } |
| |
| // Hide SwiftShader window to prevent a race with Xvfb causing hangs on test bots |
| if (gInitAPI && gInitAPI->second == GPUTestConfig::kAPISwiftShader) |
| { |
| argv.push_back("--deqp-visibility=hidden"); |
| } |
| |
| TestSuite *testSuite = TestSuite::GetInstance(); |
| |
| std::stringstream logNameStream; |
| logNameStream << "TestResults"; |
| if (testSuite->getBatchId() != -1) |
| { |
| logNameStream << "-Batch" << std::setfill('0') << std::setw(3) << testSuite->getBatchId(); |
| } |
| logNameStream << ".qpa"; |
| |
| std::stringstream logArgStream; |
| logArgStream << "--deqp-log-filename=" |
| << testSuite->reserveTestArtifactPath(logNameStream.str()); |
| |
| std::string logNameString = logArgStream.str(); |
| argv.push_back(logNameString.c_str()); |
| |
| if (!gLogImages) |
| { |
| argv.push_back("--deqp-log-images=disable"); |
| } |
| |
| // Flushing during multi-process execution punishes HDDs. http://anglebug.com/5157 |
| if (testSuite->getBatchId() != -1) |
| { |
| argv.push_back("--deqp-log-flush=disable"); |
| } |
| |
| // Add any additional flags specified from command line to be forwarded to dEQP. |
| argv.insert(argv.end(), gdEQPForwardFlags.begin(), gdEQPForwardFlags.end()); |
| |
| // Init the platform. |
| if (!deqp_libtester_init_platform(static_cast<int>(argv.size()), argv.data(), |
| reinterpret_cast<void *>(&HandlePlatformError), gOptions)) |
| { |
| std::cout << "Aborting test due to dEQP initialization error." << std::endl; |
| exit(1); |
| } |
| } |
| |
| // static function called once after running all of dEQPTest under the same test suite |
| void dEQPTest::TearDownTestSuite() |
| { |
| sTestSuiteData.printTestStats(); |
| deqp_libtester_shutdown_platform(); |
| } |
| |
| // TestBody() is called once for each dEQPTest |
| void dEQPTest::TestBody() |
| { |
| if (sTestSuiteData.mTestExceptionCount > 1) |
| { |
| std::cout << "Too many exceptions, skipping all remaining tests." << std::endl; |
| return; |
| } |
| |
| const auto &caseInfo = GetTestList(mTestModuleIndex).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 == GPUTestExpectationsParser::kGpuTestSkip) |
| { |
| sTestSuiteData.mSkippedTestCount++; |
| std::cout << "Test skipped.\n"; |
| return; |
| } |
| |
| TestSuite *testSuite = TestSuite::GetInstance(); |
| testSuite->maybeUpdateTestTimeout(caseInfo.expectation); |
| |
| gExpectError = (caseInfo.expectation != GPUTestExpectationsParser::kGpuTestPass); |
| dEQPTestResult result = deqp_libtester_run(caseInfo.testName.c_str()); |
| |
| bool testSucceeded = IsPassingResult(result); |
| |
| if (!testSucceeded && caseInfo.expectation == GPUTestExpectationsParser::kGpuTestFlaky) |
| { |
| result = deqp_libtester_run(caseInfo.testName.c_str()); |
| testSucceeded = IsPassingResult(result); |
| } |
| |
| sTestSuiteData.countTestResult(result); |
| |
| if (caseInfo.expectation == GPUTestExpectationsParser::kGpuTestPass || |
| caseInfo.expectation == GPUTestExpectationsParser::kGpuTestFlaky) |
| { |
| EXPECT_TRUE(testSucceeded); |
| |
| if (!testSucceeded) |
| { |
| sTestSuiteData.mUnexpectedFailed.push_back(caseInfo.testName); |
| } |
| } |
| else if (testSucceeded) |
| { |
| std::cout << "Test expected to fail but passed!" << std::endl; |
| sTestSuiteData.mUnexpectedPasses.push_back(caseInfo.testName); |
| } |
| } |
| |
| void HandleDisplayType(const char *displayTypeString) |
| { |
| std::stringstream argStream; |
| |
| if (gInitAPI) |
| { |
| std::cout << "Cannot specify two EGL displays!" << std::endl; |
| exit(1); |
| } |
| |
| argStream << displayTypeString; |
| std::string arg = argStream.str(); |
| gInitAPI = FindAPIInfo(arg); |
| |
| if (!gInitAPI && strncmp(displayTypeString, "angle-", strlen("angle-")) != 0) |
| { |
| std::stringstream argStream2; |
| argStream2 << "angle-" << displayTypeString; |
| std::string arg2 = argStream2.str(); |
| gInitAPI = FindAPIInfo(arg2); |
| |
| if (!gInitAPI) |
| { |
| std::cout << "Unknown API: " << displayTypeString << std::endl; |
| exit(1); |
| } |
| } |
| } |
| |
| void HandlePreRotation(const char *preRotationString) |
| { |
| std::istringstream argStream(preRotationString); |
| |
| uint32_t preRotation = 0; |
| argStream >> preRotation; |
| |
| if (!argStream || |
| (preRotation != 0 && preRotation != 90 && preRotation != 180 && preRotation != 270)) |
| { |
| std::cout << "Invalid PreRotation '" << preRotationString |
| << "'; must be either 0, 90, 180 or 270" << std::endl; |
| exit(1); |
| } |
| |
| gOptions.preRotation = preRotation; |
| } |
| |
| void HandleEGLConfigName(const char *configNameString) |
| { |
| gEGLConfigNameFromCmdLine = configNameString; |
| } |
| |
| // The --deqp-case flag takes a case expression that is parsed into a --gtest_filter. It |
| // converts the "dEQP" style names (functional.thing.*) into "GoogleTest" style names |
| // (functional_thing_*). Currently it does not handle multiple tests and multiple filters in |
| // different arguments. |
| void HandleFilterArg(const char *filterString, int *argc, int argIndex, char **argv) |
| { |
| std::string googleTestFilter = ReplaceDashesWithQuestionMark(filterString); |
| |
| gFilterStringBuffer->resize(googleTestFilter.size() + 3 + strlen(kGTestFilter), 0); |
| std::fill(gFilterStringBuffer->begin(), gFilterStringBuffer->end(), 0); |
| |
| int bytesWritten = snprintf(gFilterStringBuffer->data(), gFilterStringBuffer->size() - 1, |
| "%s*%s", kGTestFilter, googleTestFilter.c_str()); |
| if (bytesWritten <= 0 || static_cast<size_t>(bytesWritten) >= gFilterStringBuffer->size() - 1) |
| { |
| std::cout << "Error parsing filter string: " << filterString; |
| exit(1); |
| } |
| |
| argv[argIndex] = gFilterStringBuffer->data(); |
| } |
| |
| void HandleLogImages(const char *logImagesString) |
| { |
| if (strcmp(logImagesString, "enable") == 0) |
| { |
| gLogImages = true; |
| } |
| else if (strcmp(logImagesString, "disable") == 0) |
| { |
| gLogImages = false; |
| } |
| else |
| { |
| std::cout << "Error parsing log images setting. Use enable/disable."; |
| exit(1); |
| } |
| } |
| |
| void RegisterGLCTSTests() |
| { |
| size_t testModuleIndex = GetTestModuleIndex(); |
| |
| const dEQPCaseList &caseList = GetTestList(testModuleIndex); |
| |
| for (size_t caseIndex = 0; caseIndex < caseList.numCases(); ++caseIndex) |
| { |
| auto factory = [testModuleIndex, caseIndex]() { |
| return new dEQPTest(testModuleIndex, caseIndex); |
| }; |
| |
| const std::string testCaseName = GetTestCaseName(testModuleIndex, caseIndex); |
| size_t pos = testCaseName.find('.'); |
| ASSERT(pos != std::string::npos); |
| // testCaseName comes from one of the mustpass files in gCaseListFiles. |
| // All of the testCaseName in the same mustpass file starts with the same testSuiteName |
| // prefix. Which mustpass file to load the set of testCaseName depends on testModuleIndex |
| // compiled into the deqp test application binary. For now, only one testModuleIndex is |
| // compiled in a deqp test application binary, meaning all of the tests invoked by same deqp |
| // test application binary are under the same test suite. |
| std::string testSuiteName = testCaseName.substr(0, pos); |
| std::string testName = testCaseName.substr(pos + 1); |
| testing::RegisterTest(testSuiteName.c_str(), testName.c_str(), nullptr, nullptr, __FILE__, |
| __LINE__, factory); |
| } |
| } |
| } // anonymous namespace |
| |
| // Called from main() to process command-line arguments. |
| int RunGLCTSTests(int *argc, char **argv) |
| { |
| int argIndex = 0; |
| while (argIndex < *argc) |
| { |
| if (strncmp(argv[argIndex], kdEQPEGLString, strlen(kdEQPEGLString)) == 0) |
| { |
| HandleDisplayType(argv[argIndex] + strlen(kdEQPEGLString)); |
| } |
| else if (strncmp(argv[argIndex], kANGLEEGLString, strlen(kANGLEEGLString)) == 0) |
| { |
| HandleDisplayType(argv[argIndex] + strlen(kANGLEEGLString)); |
| } |
| else if (strncmp(argv[argIndex], kANGLEPreRotation, strlen(kANGLEPreRotation)) == 0) |
| { |
| HandlePreRotation(argv[argIndex] + strlen(kANGLEPreRotation)); |
| } |
| else if (strncmp(argv[argIndex], gdEQPEGLConfigNameString, |
| strlen(gdEQPEGLConfigNameString)) == 0) |
| { |
| HandleEGLConfigName(argv[argIndex] + strlen(gdEQPEGLConfigNameString)); |
| } |
| else if (strncmp(argv[argIndex], kdEQPCaseString, strlen(kdEQPCaseString)) == 0) |
| { |
| HandleFilterArg(argv[argIndex] + strlen(kdEQPCaseString), argc, argIndex, argv); |
| } |
| else if (strncmp(argv[argIndex], kGTestFilter, strlen(kGTestFilter)) == 0) |
| { |
| HandleFilterArg(argv[argIndex] + strlen(kGTestFilter), argc, argIndex, argv); |
| } |
| else if (strncmp(argv[argIndex], kVerboseString, strlen(kVerboseString)) == 0 || |
| strcmp(argv[argIndex], "-v") == 0) |
| { |
| gVerbose = true; |
| } |
| else if (strncmp(argv[argIndex], gdEQPLogImagesString, strlen(gdEQPLogImagesString)) == 0) |
| { |
| HandleLogImages(argv[argIndex] + strlen(gdEQPLogImagesString)); |
| } |
| else if (strncmp(argv[argIndex], kRenderDocString, strlen(kRenderDocString)) == 0) |
| { |
| gOptions.enableRenderDocCapture = true; |
| } |
| else if (strncmp(argv[argIndex], kNoRenderDocString, strlen(kNoRenderDocString)) == 0) |
| { |
| gOptions.enableRenderDocCapture = false; |
| } |
| else if (strncmp(argv[argIndex], kdEQPFlagsPrefix, strlen(kdEQPFlagsPrefix)) == 0) |
| { |
| gdEQPForwardFlags.push_back(argv[argIndex]); |
| } |
| argIndex++; |
| } |
| |
| GPUTestConfig::API api = GetDefaultAPIInfo()->second; |
| if (gInitAPI) |
| { |
| api = gInitAPI->second; |
| } |
| if (gOptions.preRotation != 0 && api != GPUTestConfig::kAPIVulkan && |
| api != GPUTestConfig::kAPISwiftShader) |
| { |
| std::cout << "PreRotation is only supported on Vulkan" << std::endl; |
| exit(1); |
| } |
| |
| angle::TestSuite testSuite(argc, argv, RegisterGLCTSTests); |
| return testSuite.run(); |
| } |
| } // namespace angle |