• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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