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