// 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 #include #include #include #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 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, const DataMap& data = {}) { std::vector> 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 Output Run(std::string in, const DataMap& data = {}) { auto file = std::make_unique("test", in); auto program = reader::wgsl::Parse(file.get()); // Keep this pointer alive after Transform() returns files_.emplace_back(std::move(file)); return Run(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 Output Run(Program&& program, const DataMap& data = {}) { if (!program.IsValid()) { return Output(std::move(program)); } Manager manager; for (auto* transform_ptr : std::initializer_list{new TRANSFORMS()...}) { manager.append(std::unique_ptr(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 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 bool ShouldRun(std::string in, const DataMap& data = {}) { auto file = std::make_unique("test", in); auto program = reader::wgsl::Parse(file.get()); return ShouldRun(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> files_; }; using TransformTest = TransformTestBase; template using TransformTestWithParam = TransformTestBase>; } // namespace tint::transform #endif // SRC_TINT_TRANSFORM_TEST_HELPER_H_