| // Copyright 2017 The Dawn & Tint Authors | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are met: | |
| // | |
| // 1. Redistributions of source code must retain the above copyright notice, this | |
| // list of conditions and the following disclaimer. | |
| // | |
| // 2. Redistributions in binary form must reproduce the above copyright notice, | |
| // this list of conditions and the following disclaimer in the documentation | |
| // and/or other materials provided with the distribution. | |
| // | |
| // 3. Neither the name of the copyright holder nor the names of its | |
| // contributors may be used to endorse or promote products derived from | |
| // this software without specific prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| #ifndef SRC_DAWN_TESTS_UNITTESTS_VALIDATION_VALIDATIONTEST_H_ | |
| #define SRC_DAWN_TESTS_UNITTESTS_VALIDATION_VALIDATIONTEST_H_ | |
| #include <gmock/gmock.h> | |
| #include <gtest/gtest.h> | |
| #include <webgpu/webgpu_cpp.h> | |
| #include <memory> | |
| #include <string> | |
| #include <tuple> | |
| #include <vector> | |
| #include "dawn/native/DawnNative.h" | |
| #include "dawn/utils/ComboLimits.h" | |
| #include "src/dawn/native/BindGroupLayout.h" | |
| #include "src/utils/log.h" | |
| // Argument helpers to allow macro overriding. | |
| #define UNIMPLEMENTED_MACRO(...) DAWN_UNREACHABLE() | |
| #define GET_3RD_ARG_HELPER_(_1, _2, NAME, ...) NAME | |
| #define GET_3RD_ARG_(args) GET_3RD_ARG_HELPER_ args | |
| // Overloaded to allow further validation of the error messages given an error is expected. | |
| // Especially useful to verify that the expected errors are occuring, not just any error. | |
| // | |
| // Example usages: | |
| // 1 Argument Case: | |
| // ASSERT_DEVICE_ERROR(FunctionThatExpectsError()); | |
| // | |
| // 2 Argument Case: | |
| // ASSERT_DEVICE_ERROR(FunctionThatHasLongError(), HasSubstr("partial match")) | |
| // ASSERT_DEVICE_ERROR(FunctionThatHasShortError(), Eq("exact match")); | |
| #define ASSERT_DEVICE_ERROR(...) \ | |
| GET_3RD_ARG_((__VA_ARGS__, ASSERT_DEVICE_ERROR_IMPL_2_, ASSERT_DEVICE_ERROR_IMPL_1_, \ | |
| UNIMPLEMENTED_MACRO)) \ | |
| (__VA_ARGS__) | |
| #define ASSERT_DEVICE_ERROR_IMPL_1_(statement) \ | |
| StartExpectDeviceError(); \ | |
| statement; \ | |
| device.Tick(); \ | |
| FlushWire(); \ | |
| if (!EndExpectDeviceError()) { \ | |
| ADD_FAILURE() << "Expected device error in:\n " << #statement; \ | |
| } \ | |
| do { \ | |
| } while (0) | |
| #define ASSERT_DEVICE_ERROR_IMPL_2_(statement, matcher) \ | |
| StartExpectDeviceError(matcher); \ | |
| statement; \ | |
| device.Tick(); \ | |
| FlushWire(); \ | |
| if (!EndExpectDeviceError()) { \ | |
| ADD_FAILURE() << "Expected device error in:\n " << #statement; \ | |
| } \ | |
| do { \ | |
| } while (0) | |
| #define ASSERT_DEVICE_LOG(statement, type, messageMatcher) \ | |
| StartExpectDeviceLog(type, messageMatcher); \ | |
| statement; \ | |
| device.Tick(); \ | |
| FlushWire(); \ | |
| if (!EndExpectDeviceLog()) { \ | |
| ADD_FAILURE() << "Missing expected device log in:\n " << #statement; \ | |
| } \ | |
| do { \ | |
| } while (0) | |
| #define ASSERT_NO_DEVICE_LOG(statement) \ | |
| StartExpectNoDeviceLog(); \ | |
| statement; \ | |
| device.Tick(); \ | |
| FlushWire(); \ | |
| EndExpectNoDeviceLog(); \ | |
| do { \ | |
| } while (0) | |
| // Skip a test when the given condition is satisfied. | |
| #define DAWN_SKIP_TEST_IF(condition) \ | |
| do { \ | |
| if (condition) { \ | |
| dawn::InfoLog() << "Test skipped: " #condition "."; \ | |
| GTEST_SKIP(); \ | |
| return; \ | |
| } \ | |
| } while (0) | |
| #define EXPECT_DEPRECATION_WARNINGS(statement, n) \ | |
| do { \ | |
| FlushWire(); \ | |
| uint64_t warningsBefore = GetInstanceDeprecationCountForTesting(); \ | |
| EXPECT_EQ(mLastWarningCount, warningsBefore); \ | |
| statement; \ | |
| FlushWire(); \ | |
| uint64_t warningsAfter = GetInstanceDeprecationCountForTesting(); \ | |
| EXPECT_EQ(warningsAfter, warningsBefore + n); \ | |
| mLastWarningCount = warningsAfter; \ | |
| } while (0) | |
| #define EXPECT_DEPRECATION_WARNING(statement) EXPECT_DEPRECATION_WARNINGS(statement, 1) | |
| // Gmock matcher helpers that may be used throughout other tests. | |
| // BindGroupLayouts can either be cache equivalent meaning that they may have different | |
| // compatibility tokens but same internal layout, or fully equivalent meaning that they have the | |
| // same token and internal layout. Note that being fully equivalent implies that they are cache | |
| // equivalent. | |
| MATCHER_P(BindGroupLayoutCacheEq, other, "") { | |
| return dawn::native::FromAPI(arg.Get())->GetInternalBindGroupLayout() == | |
| dawn::native::FromAPI(other.Get())->GetInternalBindGroupLayout(); | |
| } | |
| MATCHER_P(BindGroupLayoutEq, other, "") { | |
| return dawn::native::FromAPI(arg.Get())->IsLayoutEqual(dawn::native::FromAPI(other.Get())); | |
| } | |
| namespace dawn::utils { | |
| class WireHelper; | |
| } // namespace dawn::utils | |
| void InitDawnValidationTestEnvironment(int argc, char** argv); | |
| class ValidationTest : public testing::Test { | |
| public: | |
| ValidationTest(); | |
| ~ValidationTest() override; | |
| // The default setup initializes the Instance with AllowUnsafeAPIs enabled and additional | |
| // toggles and features via the getters enabled/disabled on the device. | |
| void SetUp() override; | |
| void TearDown() override; | |
| void StartExpectDeviceError(testing::Matcher<std::string> errorMatcher); | |
| void StartExpectDeviceError(); | |
| bool EndExpectDeviceError(); | |
| std::string GetLastDeviceErrorMessage() const; | |
| void StartExpectDeviceLog(wgpu::LoggingType type, testing::Matcher<std::string> messageMatcher); | |
| bool EndExpectDeviceLog(); | |
| void StartExpectNoDeviceLog(); | |
| void EndExpectNoDeviceLog(); | |
| void ExpectDeviceDestruction(); | |
| bool UsesWire() const; | |
| void FlushWire(); | |
| void WaitForAllOperations(); | |
| // Helper functions to create objects to test validation. | |
| struct PlaceholderRenderPass : public wgpu::RenderPassDescriptor { | |
| public: | |
| explicit PlaceholderRenderPass(const wgpu::Device& device); | |
| wgpu::Texture attachment; | |
| wgpu::TextureFormat attachmentFormat; | |
| uint32_t width; | |
| uint32_t height; | |
| private: | |
| wgpu::RenderPassColorAttachment mColorAttachment; | |
| }; | |
| const dawn::native::ToggleInfo* GetToggleInfo(const char* name) const; | |
| bool HasToggleEnabled(const char* toggle) const; | |
| const dawn::utils::ComboLimits& GetSupportedLimits() const; | |
| dawn::utils::WireHelper* GetWireHelper() const; | |
| protected: | |
| dawn::native::Adapter& GetBackendAdapter(); | |
| // Called during SetUp() to get the required features and toggles to be enabled for the tests. | |
| // Override these appropriately for different tests. | |
| virtual bool AllowUnsafeAPIs(); | |
| virtual std::vector<wgpu::FeatureName> GetRequiredFeatures(); | |
| // Note implementations of this can assume `required` starts as default-initialized. | |
| virtual void GetRequiredLimits(const dawn::utils::ComboLimits& supported, | |
| dawn::utils::ComboLimits& required); | |
| virtual std::vector<const char*> GetEnabledToggles(); | |
| virtual std::vector<const char*> GetDisabledToggles(); | |
| virtual std::vector<const char*> GetWGSLBlocklistedFeatures(); | |
| // Sets up the internal members by initializing the instances, adapter, and device. | |
| void SetUp(const wgpu::InstanceDescriptor* nativeDesc, | |
| const wgpu::InstanceDescriptor* wireDesc = nullptr); | |
| uint64_t GetInstanceDeprecationCountForTesting(); | |
| // Request device and handle deprecation warning emitted during creating device. | |
| wgpu::Device RequestDeviceSync(const wgpu::DeviceDescriptor& deviceDesc); | |
| virtual bool UseCompatibilityMode() const; | |
| wgpu::Device device; | |
| dawn::utils::ComboLimits deviceLimits; | |
| wgpu::Adapter adapter; | |
| WGPUDevice backendDevice; | |
| wgpu::Instance instance; | |
| uint64_t mLastWarningCount = 0; | |
| private: | |
| std::unique_ptr<dawn::native::Instance> mDawnInstance; | |
| dawn::native::Adapter mBackendAdapter; | |
| std::unique_ptr<dawn::utils::WireHelper> mWireHelper; | |
| WGPUDevice mLastCreatedBackendDevice; | |
| std::string mDeviceErrorMessage; | |
| bool mExpectError = false; | |
| bool mError = false; | |
| testing::Matcher<std::string> mErrorMatcher; | |
| static constexpr wgpu::LoggingType kExpectNoLog{0}; | |
| std::optional<std::tuple<wgpu::LoggingType, testing::Matcher<std::string>>> mExpectLog; | |
| bool mGotLog = false; | |
| bool mExpectDestruction = false; | |
| }; | |
| template <typename T, typename Base = ValidationTest> | |
| class ValidationTestWithParam : public Base, public testing::WithParamInterface<T> {}; | |
| #endif // SRC_DAWN_TESTS_UNITTESTS_VALIDATION_VALIDATIONTEST_H_ |