1 // Copyright (c) 2015-2016 The Khronos Group Inc.
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 // Common validation fixtures for unit tests
16
17 #ifndef TEST_VAL_VAL_FIXTURES_H_
18 #define TEST_VAL_VAL_FIXTURES_H_
19
20 #include <memory>
21 #include <string>
22
23 #include "source/val/validation_state.h"
24 #include "spirv-tools/libspirv.h"
25 #include "test/test_fixture.h"
26 #include "test/unit_spirv.h"
27
28 namespace spvtest {
29
30 template <typename T>
31 class ValidateBase : public ::testing::Test,
32 public ::testing::WithParamInterface<T> {
33 public:
34 ValidateBase();
35
36 virtual void TearDown();
37
38 // Returns the a spv_const_binary struct
39 spv_const_binary get_const_binary();
40
41 // Assembles the given SPIR-V text, checks that it fails to assemble,
42 // and returns resulting diagnostic. No internal state is updated.
43 // Setting the desired_result to SPV_SUCCESS is used to allow all results
44 std::string CompileFailure(std::string code,
45 spv_target_env env = SPV_ENV_UNIVERSAL_1_0,
46 spv_result_t desired_result = SPV_SUCCESS);
47
48 // Checks that 'code' is valid SPIR-V text representation and stores the
49 // binary version for further method calls.
50 void CompileSuccessfully(std::string code,
51 spv_target_env env = SPV_ENV_UNIVERSAL_1_0);
52
53 // Overwrites the word at index 'index' with the given word.
54 // For testing purposes, it is often useful to be able to manipulate the
55 // assembled binary before running the validator on it.
56 // This function overwrites the word at the given index with a new word.
57 void OverwriteAssembledBinary(uint32_t index, uint32_t word);
58
59 // Performs validation on the SPIR-V code.
60 spv_result_t ValidateInstructions(spv_target_env env = SPV_ENV_UNIVERSAL_1_0);
61
62 // Performs validation. Returns the status and stores validation state into
63 // the vstate_ member.
64 spv_result_t ValidateAndRetrieveValidationState(
65 spv_target_env env = SPV_ENV_UNIVERSAL_1_0);
66
67 // Destroys the stored binary.
DestroyBinary()68 void DestroyBinary() {
69 spvBinaryDestroy(binary_);
70 binary_ = nullptr;
71 }
72
73 // Destroys the stored diagnostic.
DestroyDiagnostic()74 void DestroyDiagnostic() {
75 spvDiagnosticDestroy(diagnostic_);
76 diagnostic_ = nullptr;
77 }
78
SetAssembleOptions(uint32_t options)79 void SetAssembleOptions(uint32_t options) { assemble_options_ = options; }
80
81 std::string getDiagnosticString();
82 spv_position_t getErrorPosition();
83 spv_validator_options getValidatorOptions();
84
85 spv_binary binary_;
86 spv_diagnostic diagnostic_;
87 spv_validator_options options_;
88 std::unique_ptr<spvtools::val::ValidationState_t> vstate_;
89 uint32_t assemble_options_ = SPV_TEXT_TO_BINARY_OPTION_NONE;
90 };
91
92 template <typename T>
ValidateBase()93 ValidateBase<T>::ValidateBase() : binary_(nullptr), diagnostic_(nullptr) {
94 // Initialize to default command line options. Different tests can then
95 // specialize specific options as necessary.
96 options_ = spvValidatorOptionsCreate();
97 }
98
99 template <typename T>
get_const_binary()100 spv_const_binary ValidateBase<T>::get_const_binary() {
101 return spv_const_binary(binary_);
102 }
103
104 template <typename T>
TearDown()105 void ValidateBase<T>::TearDown() {
106 if (diagnostic_) {
107 spvDiagnosticPrint(diagnostic_);
108 }
109 DestroyBinary();
110 DestroyDiagnostic();
111 spvValidatorOptionsDestroy(options_);
112 }
113
114 template <typename T>
CompileFailure(std::string code,spv_target_env env,spv_result_t desired_result)115 std::string ValidateBase<T>::CompileFailure(std::string code,
116 spv_target_env env,
117 spv_result_t desired_result) {
118 spv_diagnostic diagnostic = nullptr;
119 spv_result_t actual_result =
120 spvTextToBinary(ScopedContext(env).context, code.c_str(), code.size(),
121 &binary_, &diagnostic);
122 EXPECT_NE(SPV_SUCCESS, actual_result);
123 // optional check for exact result
124 if (desired_result != SPV_SUCCESS) {
125 EXPECT_EQ(actual_result, desired_result);
126 }
127 std::string result(diagnostic->error);
128 spvDiagnosticDestroy(diagnostic);
129 return result;
130 }
131
132 template <typename T>
CompileSuccessfully(std::string code,spv_target_env env)133 void ValidateBase<T>::CompileSuccessfully(std::string code,
134 spv_target_env env) {
135 DestroyBinary();
136 spv_diagnostic diagnostic = nullptr;
137 ScopedContext context(env);
138 auto status =
139 spvTextToBinaryWithOptions(context.context, code.c_str(), code.size(),
140 assemble_options_, &binary_, &diagnostic);
141 EXPECT_EQ(SPV_SUCCESS, status)
142 << "ERROR: " << diagnostic->error
143 << "\nSPIR-V could not be compiled into binary:\n"
144 << code;
145 ASSERT_EQ(SPV_SUCCESS, status);
146 spvDiagnosticDestroy(diagnostic);
147 }
148
149 template <typename T>
OverwriteAssembledBinary(uint32_t index,uint32_t word)150 void ValidateBase<T>::OverwriteAssembledBinary(uint32_t index, uint32_t word) {
151 ASSERT_TRUE(index < binary_->wordCount)
152 << "OverwriteAssembledBinary: The given index is larger than the binary "
153 "word count.";
154 binary_->code[index] = word;
155 }
156
157 template <typename T>
ValidateInstructions(spv_target_env env)158 spv_result_t ValidateBase<T>::ValidateInstructions(spv_target_env env) {
159 DestroyDiagnostic();
160 if (binary_ == nullptr) {
161 fprintf(stderr,
162 "ERROR: Attempting to validate a null binary, did you forget to "
163 "call CompileSuccessfully?");
164 fflush(stderr);
165 }
166 assert(binary_ != nullptr);
167 return spvValidateWithOptions(ScopedContext(env).context, options_,
168 get_const_binary(), &diagnostic_);
169 }
170
171 template <typename T>
ValidateAndRetrieveValidationState(spv_target_env env)172 spv_result_t ValidateBase<T>::ValidateAndRetrieveValidationState(
173 spv_target_env env) {
174 DestroyDiagnostic();
175 return spvtools::val::ValidateBinaryAndKeepValidationState(
176 ScopedContext(env).context, options_, get_const_binary()->code,
177 get_const_binary()->wordCount, &diagnostic_, &vstate_);
178 }
179
180 template <typename T>
getDiagnosticString()181 std::string ValidateBase<T>::getDiagnosticString() {
182 return diagnostic_ == nullptr ? std::string()
183 : std::string(diagnostic_->error);
184 }
185
186 template <typename T>
getValidatorOptions()187 spv_validator_options ValidateBase<T>::getValidatorOptions() {
188 return options_;
189 }
190
191 template <typename T>
getErrorPosition()192 spv_position_t ValidateBase<T>::getErrorPosition() {
193 return diagnostic_ == nullptr ? spv_position_t() : diagnostic_->position;
194 }
195
196 } // namespace spvtest
197
198 // For Vulkan testing.
199 // Allows test parameter test to list all possible VUIDs with a delimiter that
200 // is then split here to check if one VUID was in the error message
201 MATCHER_P(AnyVUID, vuid_set, "VUID from the set is in error message") {
202 // use space as delimiter because clang-format will properly line break VUID
203 // strings which is important the entire VUID is in a single line for script
204 // to scan
205 std::string delimiter = " ";
206 std::string token;
207 std::string vuids = std::string(vuid_set);
208 size_t position;
209
210 // Catch case were someone accidentally left spaces by trimming string
211 // clang-format off
212 vuids.erase(std::find_if(vuids.rbegin(), vuids.rend(), [](unsigned char c) {
213 return (c != ' ');
214 }).base(), vuids.end());
215 vuids.erase(vuids.begin(), std::find_if(vuids.begin(), vuids.end(), [](unsigned char c) {
216 return (c != ' ');
217 }));
218 // clang-format on
219
220 do {
221 position = vuids.find(delimiter);
222 if (position != std::string::npos) {
223 token = vuids.substr(0, position);
224 vuids.erase(0, position + delimiter.length());
225 } else {
226 token = vuids.substr(0); // last item
227 }
228
229 // arg contains diagnostic message
230 if (arg.find(token) != std::string::npos) {
231 return true;
232 }
233 } while (position != std::string::npos);
234 return false;
235 }
236
237 #endif // TEST_VAL_VAL_FIXTURES_H_
238