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
79 std::string getDiagnosticString();
80 spv_position_t getErrorPosition();
81 spv_validator_options getValidatorOptions();
82
83 spv_binary binary_;
84 spv_diagnostic diagnostic_;
85 spv_validator_options options_;
86 std::unique_ptr<spvtools::val::ValidationState_t> vstate_;
87 };
88
89 template <typename T>
ValidateBase()90 ValidateBase<T>::ValidateBase() : binary_(nullptr), diagnostic_(nullptr) {
91 // Initialize to default command line options. Different tests can then
92 // specialize specific options as necessary.
93 options_ = spvValidatorOptionsCreate();
94 }
95
96 template <typename T>
get_const_binary()97 spv_const_binary ValidateBase<T>::get_const_binary() {
98 return spv_const_binary(binary_);
99 }
100
101 template <typename T>
TearDown()102 void ValidateBase<T>::TearDown() {
103 if (diagnostic_) {
104 spvDiagnosticPrint(diagnostic_);
105 }
106 DestroyBinary();
107 DestroyDiagnostic();
108 spvValidatorOptionsDestroy(options_);
109 }
110
111 template <typename T>
CompileFailure(std::string code,spv_target_env env,spv_result_t desired_result)112 std::string ValidateBase<T>::CompileFailure(std::string code,
113 spv_target_env env,
114 spv_result_t desired_result) {
115 spv_diagnostic diagnostic = nullptr;
116 spv_result_t actual_result =
117 spvTextToBinary(ScopedContext(env).context, code.c_str(), code.size(),
118 &binary_, &diagnostic);
119 EXPECT_NE(SPV_SUCCESS, actual_result);
120 // optional check for exact result
121 if (desired_result != SPV_SUCCESS) {
122 EXPECT_EQ(actual_result, desired_result);
123 }
124 std::string result(diagnostic->error);
125 spvDiagnosticDestroy(diagnostic);
126 return result;
127 }
128
129 template <typename T>
CompileSuccessfully(std::string code,spv_target_env env)130 void ValidateBase<T>::CompileSuccessfully(std::string code,
131 spv_target_env env) {
132 DestroyBinary();
133 spv_diagnostic diagnostic = nullptr;
134 ScopedContext context(env);
135 auto status = spvTextToBinary(context.context, code.c_str(), code.size(),
136 &binary_, &diagnostic);
137 EXPECT_EQ(SPV_SUCCESS, status)
138 << "ERROR: " << diagnostic->error
139 << "\nSPIR-V could not be compiled into binary:\n"
140 << code;
141 ASSERT_EQ(SPV_SUCCESS, status);
142 spvDiagnosticDestroy(diagnostic);
143 }
144
145 template <typename T>
OverwriteAssembledBinary(uint32_t index,uint32_t word)146 void ValidateBase<T>::OverwriteAssembledBinary(uint32_t index, uint32_t word) {
147 ASSERT_TRUE(index < binary_->wordCount)
148 << "OverwriteAssembledBinary: The given index is larger than the binary "
149 "word count.";
150 binary_->code[index] = word;
151 }
152
153 template <typename T>
ValidateInstructions(spv_target_env env)154 spv_result_t ValidateBase<T>::ValidateInstructions(spv_target_env env) {
155 DestroyDiagnostic();
156 if (binary_ == nullptr) {
157 fprintf(stderr,
158 "ERROR: Attempting to validate a null binary, did you forget to "
159 "call CompileSuccessfully?");
160 fflush(stderr);
161 }
162 assert(binary_ != nullptr);
163 return spvValidateWithOptions(ScopedContext(env).context, options_,
164 get_const_binary(), &diagnostic_);
165 }
166
167 template <typename T>
ValidateAndRetrieveValidationState(spv_target_env env)168 spv_result_t ValidateBase<T>::ValidateAndRetrieveValidationState(
169 spv_target_env env) {
170 DestroyDiagnostic();
171 return spvtools::val::ValidateBinaryAndKeepValidationState(
172 ScopedContext(env).context, options_, get_const_binary()->code,
173 get_const_binary()->wordCount, &diagnostic_, &vstate_);
174 }
175
176 template <typename T>
getDiagnosticString()177 std::string ValidateBase<T>::getDiagnosticString() {
178 return diagnostic_ == nullptr ? std::string()
179 : std::string(diagnostic_->error);
180 }
181
182 template <typename T>
getValidatorOptions()183 spv_validator_options ValidateBase<T>::getValidatorOptions() {
184 return options_;
185 }
186
187 template <typename T>
getErrorPosition()188 spv_position_t ValidateBase<T>::getErrorPosition() {
189 return diagnostic_ == nullptr ? spv_position_t() : diagnostic_->position;
190 }
191
192 } // namespace spvtest
193
194 // For Vulkan testing.
195 // Allows test parameter test to list all possible VUIDs with a delimiter that
196 // is then split here to check if one VUID was in the error message
197 MATCHER_P(AnyVUID, vuid_set, "VUID from the set is in error message") {
198 // use space as delimiter because clang-format will properly line break VUID
199 // strings which is important the entire VUID is in a single line for script
200 // to scan
201 std::string delimiter = " ";
202 std::string token;
203 std::string vuids = std::string(vuid_set);
204 size_t position;
205
206 // Catch case were someone accidentally left spaces by trimming string
207 // clang-format off
208 vuids.erase(std::find_if(vuids.rbegin(), vuids.rend(), [](unsigned char c) {
209 return (c != ' ');
210 }).base(), vuids.end());
211 vuids.erase(vuids.begin(), std::find_if(vuids.begin(), vuids.end(), [](unsigned char c) {
212 return (c != ' ');
213 }));
214 // clang-format on
215
216 do {
217 position = vuids.find(delimiter);
218 if (position != std::string::npos) {
219 token = vuids.substr(0, position);
220 vuids.erase(0, position + delimiter.length());
221 } else {
222 token = vuids.substr(0); // last item
223 }
224
225 // arg contains diagnostic message
226 if (arg.find(token) != std::string::npos) {
227 return true;
228 }
229 } while (position != std::string::npos);
230 return false;
231 }
232
233 #endif // TEST_VAL_VAL_FIXTURES_H_
234