1 // Copyright 2020 The Tint Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef SRC_WRITER_SPIRV_TEST_HELPER_H_ 16 #define SRC_WRITER_SPIRV_TEST_HELPER_H_ 17 18 #include <memory> 19 #include <string> 20 #include <utility> 21 22 #include "gtest/gtest.h" 23 #include "spirv-tools/libspirv.hpp" 24 #include "src/writer/spirv/binary_writer.h" 25 26 namespace tint { 27 namespace writer { 28 namespace spirv { 29 30 /// Helper class for testing 31 template <typename BASE> 32 class TestHelperBase : public ProgramBuilder, public BASE { 33 public: 34 TestHelperBase() = default; 35 ~TestHelperBase() override = default; 36 37 /// Builds and returns a spirv::Builder from the program. 38 /// @note The spirv::Builder is only built once. Multiple calls to Build() 39 /// will return the same spirv::Builder without rebuilding. 40 /// @return the built spirv::Builder Build()41 spirv::Builder& Build() { 42 if (spirv_builder) { 43 return *spirv_builder; 44 } 45 [&]() { 46 ASSERT_TRUE(IsValid()) << "Builder program is not valid\n" 47 << diag::Formatter().format(Diagnostics()); 48 }(); 49 program = std::make_unique<Program>(std::move(*this)); 50 [&]() { 51 ASSERT_TRUE(program->IsValid()) 52 << diag::Formatter().format(program->Diagnostics()); 53 }(); 54 spirv_builder = std::make_unique<spirv::Builder>(program.get()); 55 return *spirv_builder; 56 } 57 58 /// Builds the program, runs the program through the transform::Spirv 59 /// sanitizer and returns a spirv::Builder from the sanitized program. 60 /// @note The spirv::Builder is only built once. Multiple calls to Build() 61 /// will return the same spirv::Builder without rebuilding. 62 /// @return the built spirv::Builder SanitizeAndBuild()63 spirv::Builder& SanitizeAndBuild() { 64 if (spirv_builder) { 65 return *spirv_builder; 66 } 67 [&]() { 68 ASSERT_TRUE(IsValid()) << "Builder program is not valid\n" 69 << diag::Formatter().format(Diagnostics()); 70 }(); 71 program = std::make_unique<Program>(std::move(*this)); 72 [&]() { 73 ASSERT_TRUE(program->IsValid()) 74 << diag::Formatter().format(program->Diagnostics()); 75 }(); 76 auto result = Sanitize(program.get()); 77 [&]() { 78 ASSERT_TRUE(result.program.IsValid()) 79 << diag::Formatter().format(result.program.Diagnostics()); 80 }(); 81 *program = std::move(result.program); 82 spirv_builder = std::make_unique<spirv::Builder>(program.get()); 83 return *spirv_builder; 84 } 85 86 /// Validate passes the generated SPIR-V of the builder `b` to the SPIR-V 87 /// Tools Validator. If the validator finds problems the test will fail. 88 /// @param b the spirv::Builder containing the built SPIR-V module Validate(spirv::Builder & b)89 void Validate(spirv::Builder& b) { 90 BinaryWriter writer; 91 writer.WriteHeader(b.id_bound()); 92 writer.WriteBuilder(&b); 93 auto binary = writer.result(); 94 95 std::string spv_errors; 96 auto msg_consumer = [&spv_errors](spv_message_level_t level, const char*, 97 const spv_position_t& position, 98 const char* message) { 99 switch (level) { 100 case SPV_MSG_FATAL: 101 case SPV_MSG_INTERNAL_ERROR: 102 case SPV_MSG_ERROR: 103 spv_errors += "error: line " + std::to_string(position.index) + ": " + 104 message + "\n"; 105 break; 106 case SPV_MSG_WARNING: 107 spv_errors += "warning: line " + std::to_string(position.index) + 108 ": " + message + "\n"; 109 break; 110 case SPV_MSG_INFO: 111 spv_errors += "info: line " + std::to_string(position.index) + ": " + 112 message + "\n"; 113 break; 114 case SPV_MSG_DEBUG: 115 break; 116 } 117 }; 118 119 spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_2); 120 tools.SetMessageConsumer(msg_consumer); 121 ASSERT_TRUE(tools.Validate(binary)) << spv_errors; 122 } 123 124 /// The program built with a call to Build() 125 std::unique_ptr<Program> program; 126 127 private: 128 std::unique_ptr<spirv::Builder> spirv_builder; 129 }; 130 using TestHelper = TestHelperBase<testing::Test>; 131 132 template <typename T> 133 using TestParamHelper = TestHelperBase<testing::TestWithParam<T>>; 134 135 } // namespace spirv 136 } // namespace writer 137 } // namespace tint 138 139 #endif // SRC_WRITER_SPIRV_TEST_HELPER_H_ 140