xref: /aosp_15_r20/external/angle/third_party/spirv-tools/src/test/test_fixture.h (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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 #ifndef TEST_TEST_FIXTURE_H_
16 #define TEST_TEST_FIXTURE_H_
17 
18 #include <algorithm>
19 #include <string>
20 #include <vector>
21 
22 #include "test/unit_spirv.h"
23 
24 namespace spvtest {
25 
26 // RAII for spv_context.
27 struct ScopedContext {
28   ScopedContext(spv_target_env env = SPV_ENV_UNIVERSAL_1_0)
contextScopedContext29       : context(spvContextCreate(env)) {}
~ScopedContextScopedContext30   ~ScopedContext() { spvContextDestroy(context); }
31   spv_context context;
32 };
33 
34 // Common setup for TextToBinary tests. SetText() should be called to populate
35 // the actual test text.
36 template <typename T>
37 class TextToBinaryTestBase : public T {
38  public:
39   // Shorthand for SPIR-V compilation result.
40   using SpirvVector = std::vector<uint32_t>;
41 
42   // Offset into a SpirvVector at which the first instruction starts.
43   static const SpirvVector::size_type kFirstInstruction = 5;
44 
TextToBinaryTestBase()45   TextToBinaryTestBase() : diagnostic(nullptr), text(), binary(nullptr) {
46     char textStr[] = "substitute the text member variable with your test";
47     text = {textStr, strlen(textStr)};
48   }
49 
~TextToBinaryTestBase()50   ~TextToBinaryTestBase() override {
51     DestroyBinary();
52     if (diagnostic) spvDiagnosticDestroy(diagnostic);
53   }
54 
55   // Returns subvector v[from:end).
Subvector(const SpirvVector & v,SpirvVector::size_type from)56   SpirvVector Subvector(const SpirvVector& v, SpirvVector::size_type from) {
57     assert(from <= v.size());
58     return SpirvVector(v.begin() + from, v.end());
59   }
60 
61   // Compiles SPIR-V text in the given assembly syntax format, asserting
62   // compilation success. Returns the compiled code.
63   SpirvVector CompileSuccessfully(const std::string& txt,
64                                   spv_target_env env = SPV_ENV_UNIVERSAL_1_0) {
65     DestroyBinary();
66     DestroyDiagnostic();
67     spv_result_t status =
68         spvTextToBinary(ScopedContext(env).context, txt.c_str(), txt.size(),
69                         &binary, &diagnostic);
70     EXPECT_EQ(SPV_SUCCESS, status) << txt;
71     SpirvVector code_copy;
72     if (status == SPV_SUCCESS) {
73       code_copy = SpirvVector(binary->code, binary->code + binary->wordCount);
74       DestroyBinary();
75     } else {
76       spvDiagnosticPrint(diagnostic);
77     }
78     return code_copy;
79   }
80 
81   // Compiles SPIR-V text with the given format, asserting compilation failure.
82   // Returns the error message(s).
83   std::string CompileFailure(const std::string& txt,
84                              spv_target_env env = SPV_ENV_UNIVERSAL_1_0) {
85     DestroyBinary();
86     DestroyDiagnostic();
87     EXPECT_NE(SPV_SUCCESS,
88               spvTextToBinary(ScopedContext(env).context, txt.c_str(),
89                               txt.size(), &binary, &diagnostic))
90         << txt;
91     DestroyBinary();
92     return diagnostic->error;
93   }
94 
95   // Potentially flip the words in the binary representation to the other
96   // endianness
97   template <class It>
MaybeFlipWords(bool flip_words,It begin,It end)98   void MaybeFlipWords(bool flip_words, It begin, It end) {
99     SCOPED_TRACE(flip_words ? "Flipped Endianness" : "Normal Endianness");
100     if (flip_words) {
101       std::transform(begin, end, begin, [](const uint32_t raw_word) {
102         return spvFixWord(raw_word, I32_ENDIAN_HOST == I32_ENDIAN_BIG
103                                         ? SPV_ENDIANNESS_LITTLE
104                                         : SPV_ENDIANNESS_BIG);
105       });
106     }
107   }
108 
109   // Encodes SPIR-V text into binary and then decodes the binary using
110   // given options. Returns the decoded text.
111   std::string EncodeAndDecodeSuccessfully(
112       const std::string& txt,
113       uint32_t disassemble_options = SPV_BINARY_TO_TEXT_OPTION_NONE,
114       uint32_t assemble_options = SPV_TEXT_TO_BINARY_OPTION_NONE,
115       spv_target_env env = SPV_ENV_UNIVERSAL_1_0, bool flip_words = false) {
116     DestroyBinary();
117     DestroyDiagnostic();
118     ScopedContext context(env);
119     disassemble_options |= SPV_BINARY_TO_TEXT_OPTION_NO_HEADER;
120     spv_result_t error =
121         spvTextToBinaryWithOptions(context.context, txt.c_str(), txt.size(),
122                                    assemble_options, &binary, &diagnostic);
123     if (error) {
124       spvDiagnosticPrint(diagnostic);
125       spvDiagnosticDestroy(diagnostic);
126     }
127     EXPECT_EQ(SPV_SUCCESS, error);
128     if (!binary) return "";
129 
130     MaybeFlipWords(flip_words, binary->code, binary->code + binary->wordCount);
131 
132     spv_text decoded_text;
133     error = spvBinaryToText(context.context, binary->code, binary->wordCount,
134                             disassemble_options, &decoded_text, &diagnostic);
135     if (error) {
136       spvDiagnosticPrint(diagnostic);
137       spvDiagnosticDestroy(diagnostic);
138     }
139     EXPECT_EQ(SPV_SUCCESS, error) << txt;
140 
141     const std::string decoded_string = decoded_text->str;
142     spvTextDestroy(decoded_text);
143 
144     return decoded_string;
145   }
146 
147   // Encodes SPIR-V text into binary. This is expected to succeed.
148   // The given words are then appended to the binary, and the result
149   // is then decoded. This is expected to fail.
150   // Returns the error message.
EncodeSuccessfullyDecodeFailed(const std::string & txt,const SpirvVector & words_to_append)151   std::string EncodeSuccessfullyDecodeFailed(
152       const std::string& txt, const SpirvVector& words_to_append) {
153     DestroyBinary();
154     DestroyDiagnostic();
155     SpirvVector code =
156         spvtest::Concatenate({CompileSuccessfully(txt), words_to_append});
157 
158     spv_text decoded_text;
159     EXPECT_NE(SPV_SUCCESS,
160               spvBinaryToText(ScopedContext().context, code.data(), code.size(),
161                               SPV_BINARY_TO_TEXT_OPTION_NONE, &decoded_text,
162                               &diagnostic));
163     if (diagnostic) {
164       std::string error_message = diagnostic->error;
165       spvDiagnosticDestroy(diagnostic);
166       diagnostic = nullptr;
167       return error_message;
168     }
169     return "";
170   }
171 
172   // Compiles SPIR-V text, asserts success, and returns the words representing
173   // the instructions.  In particular, skip the words in the SPIR-V header.
174   SpirvVector CompiledInstructions(const std::string& txt,
175                                    spv_target_env env = SPV_ENV_UNIVERSAL_1_0) {
176     const SpirvVector code = CompileSuccessfully(txt, env);
177     SpirvVector result;
178     // Extract just the instructions.
179     // If the code fails to compile, then return the empty vector.
180     // In any case, don't crash or invoke undefined behaviour.
181     if (code.size() >= kFirstInstruction)
182       result = Subvector(code, kFirstInstruction);
183     return result;
184   }
185 
SetText(const std::string & code)186   void SetText(const std::string& code) {
187     textString = code;
188     text.str = textString.c_str();
189     text.length = textString.size();
190   }
191 
192   // Destroys the binary, if it exists.
DestroyBinary()193   void DestroyBinary() {
194     spvBinaryDestroy(binary);
195     binary = nullptr;
196   }
197 
198   // Destroys the diagnostic, if it exists.
DestroyDiagnostic()199   void DestroyDiagnostic() {
200     spvDiagnosticDestroy(diagnostic);
201     diagnostic = nullptr;
202   }
203 
204   spv_diagnostic diagnostic;
205 
206   std::string textString;
207   spv_text_t text;
208   spv_binary binary;
209 };
210 
211 using TextToBinaryTest = TextToBinaryTestBase<::testing::Test>;
212 }  // namespace spvtest
213 
214 using RoundTripTest =
215     spvtest::TextToBinaryTestBase<::testing::TestWithParam<std::string>>;
216 
217 #endif  // TEST_TEST_FIXTURE_H_
218