| // Copyright 2021 The Tint Authors. | |
| // | |
| // 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. | |
| #ifndef SRC_TINT_TRANSFORM_TEST_HELPER_H_ | |
| #define SRC_TINT_TRANSFORM_TEST_HELPER_H_ | |
| #include <memory> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include "gtest/gtest.h" | |
| #include "src/tint/reader/wgsl/parser.h" | |
| #include "src/tint/transform/manager.h" | |
| #include "src/tint/writer/wgsl/generator.h" | |
| namespace tint::transform { | |
| /// @param program the program to get an output WGSL string from | |
| /// @returns the output program as a WGSL string, or an error string if the | |
| /// program is not valid. | |
| inline std::string str(const Program& program) { | |
| diag::Formatter::Style style; | |
| style.print_newline_at_end = false; | |
| if (!program.IsValid()) { | |
| return diag::Formatter(style).format(program.Diagnostics()); | |
| } | |
| writer::wgsl::Options options; | |
| auto result = writer::wgsl::Generate(&program, options); | |
| if (!result.success) { | |
| return "WGSL writer failed:\n" + result.error; | |
| } | |
| auto res = result.wgsl; | |
| if (res.empty()) { | |
| return res; | |
| } | |
| // The WGSL sometimes has two trailing newlines. Strip them | |
| while (res.back() == '\n') { | |
| res.pop_back(); | |
| } | |
| if (res.empty()) { | |
| return res; | |
| } | |
| return "\n" + res + "\n"; | |
| } | |
| /// Helper class for testing transforms | |
| template <typename BASE> | |
| class TransformTestBase : public BASE { | |
| public: | |
| /// Transforms and returns the WGSL source `in`, transformed using | |
| /// `transform`. | |
| /// @param transform the transform to apply | |
| /// @param in the input WGSL source | |
| /// @param data the optional DataMap to pass to Transform::Run() | |
| /// @return the transformed output | |
| Output Run(std::string in, | |
| std::unique_ptr<transform::Transform> transform, | |
| const DataMap& data = {}) { | |
| std::vector<std::unique_ptr<transform::Transform>> transforms; | |
| transforms.emplace_back(std::move(transform)); | |
| return Run(std::move(in), std::move(transforms), data); | |
| } | |
| /// Transforms and returns the WGSL source `in`, transformed using | |
| /// a transform of type `TRANSFORM`. | |
| /// @param in the input WGSL source | |
| /// @param data the optional DataMap to pass to Transform::Run() | |
| /// @return the transformed output | |
| template <typename... TRANSFORMS> | |
| Output Run(std::string in, const DataMap& data = {}) { | |
| auto file = std::make_unique<Source::File>("test", in); | |
| auto program = reader::wgsl::Parse(file.get()); | |
| // Keep this pointer alive after Transform() returns | |
| files_.emplace_back(std::move(file)); | |
| return Run<TRANSFORMS...>(std::move(program), data); | |
| } | |
| /// Transforms and returns program `program`, transformed using a transform of | |
| /// type `TRANSFORM`. | |
| /// @param program the input Program | |
| /// @param data the optional DataMap to pass to Transform::Run() | |
| /// @return the transformed output | |
| template <typename... TRANSFORMS> | |
| Output Run(Program&& program, const DataMap& data = {}) { | |
| if (!program.IsValid()) { | |
| return Output(std::move(program)); | |
| } | |
| Manager manager; | |
| for (auto* transform_ptr : std::initializer_list<Transform*>{new TRANSFORMS()...}) { | |
| manager.append(std::unique_ptr<Transform>(transform_ptr)); | |
| } | |
| return manager.Run(&program, data); | |
| } | |
| /// @param program the input program | |
| /// @param data the optional DataMap to pass to Transform::Run() | |
| /// @return true if the transform should be run for the given input. | |
| template <typename TRANSFORM> | |
| bool ShouldRun(Program&& program, const DataMap& data = {}) { | |
| if (!program.IsValid()) { | |
| ADD_FAILURE() << "ShouldRun() called with invalid program: " | |
| << program.Diagnostics().str(); | |
| return false; | |
| } | |
| const Transform& t = TRANSFORM(); | |
| DataMap outputs; | |
| auto result = t.Apply(&program, data, outputs); | |
| if (!result) { | |
| return false; | |
| } | |
| if (!result->IsValid()) { | |
| ADD_FAILURE() << "Apply() called by ShouldRun() returned errors: " | |
| << result->Diagnostics().str(); | |
| return true; | |
| } | |
| return result.has_value(); | |
| } | |
| /// @param in the input WGSL source | |
| /// @param data the optional DataMap to pass to Transform::Run() | |
| /// @return true if the transform should be run for the given input. | |
| template <typename TRANSFORM> | |
| bool ShouldRun(std::string in, const DataMap& data = {}) { | |
| auto file = std::make_unique<Source::File>("test", in); | |
| auto program = reader::wgsl::Parse(file.get()); | |
| return ShouldRun<TRANSFORM>(std::move(program), data); | |
| } | |
| /// @param output the output of the transform | |
| /// @returns the output program as a WGSL string, or an error string if the | |
| /// program is not valid. | |
| std::string str(const Output& output) { return transform::str(output.program); } | |
| private: | |
| std::vector<std::unique_ptr<Source::File>> files_; | |
| }; | |
| using TransformTest = TransformTestBase<testing::Test>; | |
| template <typename T> | |
| using TransformTestWithParam = TransformTestBase<testing::TestWithParam<T>>; | |
| } // namespace tint::transform | |
| #endif // SRC_TINT_TRANSFORM_TEST_HELPER_H_ |