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 #include "src/reader/spirv/namer.h"
16
17 #include "gmock/gmock.h"
18
19 namespace tint {
20 namespace reader {
21 namespace spirv {
22 namespace {
23
24 using ::testing::Eq;
25
26 class SpvNamerTest : public testing::Test {
27 public:
SpvNamerTest()28 SpvNamerTest() : fail_stream_(&success_, &errors_) {}
29
30 /// @returns the accumulated diagnostic strings
error()31 std::string error() { return errors_.str(); }
32
33 protected:
34 std::stringstream errors_;
35 bool success_ = true;
36 FailStream fail_stream_;
37 };
38
TEST_F(SpvNamerTest,SanitizeEmpty)39 TEST_F(SpvNamerTest, SanitizeEmpty) {
40 EXPECT_THAT(Namer::Sanitize(""), Eq("empty"));
41 }
42
TEST_F(SpvNamerTest,SanitizeLeadingUnderscore)43 TEST_F(SpvNamerTest, SanitizeLeadingUnderscore) {
44 EXPECT_THAT(Namer::Sanitize("_"), Eq("x_"));
45 }
46
TEST_F(SpvNamerTest,SanitizeLeadingDigit)47 TEST_F(SpvNamerTest, SanitizeLeadingDigit) {
48 EXPECT_THAT(Namer::Sanitize("7zip"), Eq("x7zip"));
49 }
50
TEST_F(SpvNamerTest,SanitizeOkChars)51 TEST_F(SpvNamerTest, SanitizeOkChars) {
52 EXPECT_THAT(Namer::Sanitize("_abcdef12345"), Eq("x_abcdef12345"));
53 }
54
TEST_F(SpvNamerTest,SanitizeNonIdentifierChars)55 TEST_F(SpvNamerTest, SanitizeNonIdentifierChars) {
56 EXPECT_THAT(Namer::Sanitize("a:1.2'f\n"), "a_1_2_f_");
57 }
58
TEST_F(SpvNamerTest,NoFailureToStart)59 TEST_F(SpvNamerTest, NoFailureToStart) {
60 Namer namer(fail_stream_);
61 EXPECT_TRUE(success_);
62 EXPECT_TRUE(error().empty());
63 }
64
TEST_F(SpvNamerTest,FailLogsError)65 TEST_F(SpvNamerTest, FailLogsError) {
66 Namer namer(fail_stream_);
67 const bool converted_result = namer.Fail() << "st. johns wood";
68 EXPECT_FALSE(converted_result);
69 EXPECT_EQ(error(), "st. johns wood");
70 EXPECT_FALSE(success_);
71 }
72
TEST_F(SpvNamerTest,NoNameRecorded)73 TEST_F(SpvNamerTest, NoNameRecorded) {
74 Namer namer(fail_stream_);
75
76 EXPECT_FALSE(namer.HasName(12));
77 EXPECT_TRUE(success_);
78 EXPECT_TRUE(error().empty());
79 }
80
TEST_F(SpvNamerTest,FindUnusedDerivedName_NoRecordedName)81 TEST_F(SpvNamerTest, FindUnusedDerivedName_NoRecordedName) {
82 Namer namer(fail_stream_);
83 EXPECT_THAT(namer.FindUnusedDerivedName("eleanor"), Eq("eleanor"));
84 // Prove that it wasn't registered when first found.
85 EXPECT_THAT(namer.FindUnusedDerivedName("eleanor"), Eq("eleanor"));
86 }
87
TEST_F(SpvNamerTest,FindUnusedDerivedName_HasRecordedName)88 TEST_F(SpvNamerTest, FindUnusedDerivedName_HasRecordedName) {
89 Namer namer(fail_stream_);
90 namer.Register(12, "rigby");
91 EXPECT_THAT(namer.FindUnusedDerivedName("rigby"), Eq("rigby_1"));
92 }
93
TEST_F(SpvNamerTest,FindUnusedDerivedName_HasMultipleConflicts)94 TEST_F(SpvNamerTest, FindUnusedDerivedName_HasMultipleConflicts) {
95 Namer namer(fail_stream_);
96 namer.Register(12, "rigby");
97 namer.Register(13, "rigby_1");
98 namer.Register(14, "rigby_3");
99 // It picks the first non-conflicting suffix.
100 EXPECT_THAT(namer.FindUnusedDerivedName("rigby"), Eq("rigby_2"));
101 }
102
TEST_F(SpvNamerTest,IsRegistered_NoRecordedName)103 TEST_F(SpvNamerTest, IsRegistered_NoRecordedName) {
104 Namer namer(fail_stream_);
105 EXPECT_FALSE(namer.IsRegistered("abbey"));
106 }
107
TEST_F(SpvNamerTest,IsRegistered_RegisteredById)108 TEST_F(SpvNamerTest, IsRegistered_RegisteredById) {
109 Namer namer(fail_stream_);
110 namer.Register(1, "abbey");
111 EXPECT_TRUE(namer.IsRegistered("abbey"));
112 }
113
TEST_F(SpvNamerTest,IsRegistered_RegisteredByDerivation)114 TEST_F(SpvNamerTest, IsRegistered_RegisteredByDerivation) {
115 Namer namer(fail_stream_);
116 const auto got = namer.MakeDerivedName("abbey");
117 EXPECT_TRUE(namer.IsRegistered("abbey"));
118 EXPECT_EQ(got, "abbey");
119 }
120
TEST_F(SpvNamerTest,MakeDerivedName_NoRecordedName)121 TEST_F(SpvNamerTest, MakeDerivedName_NoRecordedName) {
122 Namer namer(fail_stream_);
123 EXPECT_THAT(namer.MakeDerivedName("eleanor"), Eq("eleanor"));
124 // Prove that it was registered when first found.
125 EXPECT_THAT(namer.MakeDerivedName("eleanor"), Eq("eleanor_1"));
126 }
127
TEST_F(SpvNamerTest,MakeDerivedName_HasRecordedName)128 TEST_F(SpvNamerTest, MakeDerivedName_HasRecordedName) {
129 Namer namer(fail_stream_);
130 namer.Register(12, "rigby");
131 EXPECT_THAT(namer.MakeDerivedName("rigby"), Eq("rigby_1"));
132 }
133
TEST_F(SpvNamerTest,MakeDerivedName_HasMultipleConflicts)134 TEST_F(SpvNamerTest, MakeDerivedName_HasMultipleConflicts) {
135 Namer namer(fail_stream_);
136 namer.Register(12, "rigby");
137 namer.Register(13, "rigby_1");
138 namer.Register(14, "rigby_3");
139 // It picks the first non-conflicting suffix.
140 EXPECT_THAT(namer.MakeDerivedName("rigby"), Eq("rigby_2"));
141 }
142
TEST_F(SpvNamerTest,RegisterWithoutId_Once)143 TEST_F(SpvNamerTest, RegisterWithoutId_Once) {
144 Namer namer(fail_stream_);
145
146 const std::string n("abbey");
147 EXPECT_FALSE(namer.IsRegistered(n));
148 EXPECT_TRUE(namer.RegisterWithoutId(n));
149 EXPECT_TRUE(namer.IsRegistered(n));
150 EXPECT_TRUE(success_);
151 EXPECT_TRUE(error().empty());
152 }
153
TEST_F(SpvNamerTest,RegisterWithoutId_Twice)154 TEST_F(SpvNamerTest, RegisterWithoutId_Twice) {
155 Namer namer(fail_stream_);
156
157 const std::string n("abbey");
158 EXPECT_FALSE(namer.IsRegistered(n));
159 EXPECT_TRUE(namer.RegisterWithoutId(n));
160 // Fails on second attempt.
161 EXPECT_FALSE(namer.RegisterWithoutId(n));
162 EXPECT_FALSE(success_);
163 EXPECT_EQ(error(), "internal error: name already registered: abbey");
164 }
165
TEST_F(SpvNamerTest,RegisterWithoutId_ConflictsWithIdRegisteredName)166 TEST_F(SpvNamerTest, RegisterWithoutId_ConflictsWithIdRegisteredName) {
167 Namer namer(fail_stream_);
168
169 const std::string n("abbey");
170 EXPECT_TRUE(namer.Register(1, n));
171 EXPECT_TRUE(namer.IsRegistered(n));
172 // Fails on attempt to register without ID.
173 EXPECT_FALSE(namer.RegisterWithoutId(n));
174 EXPECT_FALSE(success_);
175 EXPECT_EQ(error(), "internal error: name already registered: abbey");
176 }
177
TEST_F(SpvNamerTest,Register_Once)178 TEST_F(SpvNamerTest, Register_Once) {
179 Namer namer(fail_stream_);
180
181 const uint32_t id = 9;
182 EXPECT_FALSE(namer.HasName(id));
183 const bool save_result = namer.Register(id, "abbey road");
184 EXPECT_TRUE(save_result);
185 EXPECT_TRUE(namer.HasName(id));
186 EXPECT_EQ(namer.GetName(id), "abbey road");
187 EXPECT_TRUE(success_);
188 EXPECT_TRUE(error().empty());
189 }
190
TEST_F(SpvNamerTest,Register_TwoIds)191 TEST_F(SpvNamerTest, Register_TwoIds) {
192 Namer namer(fail_stream_);
193
194 EXPECT_FALSE(namer.HasName(8));
195 EXPECT_FALSE(namer.HasName(9));
196 EXPECT_TRUE(namer.Register(8, "abbey road"));
197 EXPECT_TRUE(namer.Register(9, "rubber soul"));
198 EXPECT_TRUE(namer.HasName(8));
199 EXPECT_TRUE(namer.HasName(9));
200 EXPECT_EQ(namer.GetName(9), "rubber soul");
201 EXPECT_EQ(namer.GetName(8), "abbey road");
202 EXPECT_TRUE(success_);
203 EXPECT_TRUE(error().empty());
204 }
205
TEST_F(SpvNamerTest,Register_FailsDueToIdReuse)206 TEST_F(SpvNamerTest, Register_FailsDueToIdReuse) {
207 Namer namer(fail_stream_);
208
209 const uint32_t id = 9;
210 EXPECT_TRUE(namer.Register(id, "abbey road"));
211 EXPECT_FALSE(namer.Register(id, "rubber soul"));
212 EXPECT_TRUE(namer.HasName(id));
213 EXPECT_EQ(namer.GetName(id), "abbey road");
214 EXPECT_FALSE(success_);
215 EXPECT_FALSE(error().empty());
216 }
217
TEST_F(SpvNamerTest,SuggestSanitizedName_TakeSuggestionWhenNoConflict)218 TEST_F(SpvNamerTest, SuggestSanitizedName_TakeSuggestionWhenNoConflict) {
219 Namer namer(fail_stream_);
220
221 EXPECT_TRUE(namer.SuggestSanitizedName(1, "father"));
222 EXPECT_THAT(namer.GetName(1), Eq("father"));
223 }
224
TEST_F(SpvNamerTest,SuggestSanitizedName_RejectSuggestionWhenConflictOnSameId)225 TEST_F(SpvNamerTest,
226 SuggestSanitizedName_RejectSuggestionWhenConflictOnSameId) {
227 Namer namer(fail_stream_);
228
229 namer.Register(1, "lennon");
230 EXPECT_FALSE(namer.SuggestSanitizedName(1, "mccartney"));
231 EXPECT_THAT(namer.GetName(1), Eq("lennon"));
232 }
233
TEST_F(SpvNamerTest,SuggestSanitizedName_SanitizeSuggestion)234 TEST_F(SpvNamerTest, SuggestSanitizedName_SanitizeSuggestion) {
235 Namer namer(fail_stream_);
236
237 EXPECT_TRUE(namer.SuggestSanitizedName(9, "m:kenzie"));
238 EXPECT_THAT(namer.GetName(9), Eq("m_kenzie"));
239 }
240
TEST_F(SpvNamerTest,SuggestSanitizedName_GenerateNewNameWhenConflictOnDifferentId)241 TEST_F(SpvNamerTest,
242 SuggestSanitizedName_GenerateNewNameWhenConflictOnDifferentId) {
243 Namer namer(fail_stream_);
244
245 namer.Register(7, "rice");
246 EXPECT_TRUE(namer.SuggestSanitizedName(9, "rice"));
247 EXPECT_THAT(namer.GetName(9), Eq("rice_1"));
248 }
249
TEST_F(SpvNamerTest,GetMemberName_EmptyStringForUnvisitedStruct)250 TEST_F(SpvNamerTest, GetMemberName_EmptyStringForUnvisitedStruct) {
251 Namer namer(fail_stream_);
252 EXPECT_THAT(namer.GetMemberName(1, 2), Eq(""));
253 }
254
TEST_F(SpvNamerTest,GetMemberName_EmptyStringForUnvisitedMember)255 TEST_F(SpvNamerTest, GetMemberName_EmptyStringForUnvisitedMember) {
256 Namer namer(fail_stream_);
257 namer.SuggestSanitizedMemberName(1, 2, "mother");
258 EXPECT_THAT(namer.GetMemberName(1, 0), Eq(""));
259 }
260
TEST_F(SpvNamerTest,SuggestSanitizedMemberName_TakeSuggestionWhenNoConflict)261 TEST_F(SpvNamerTest, SuggestSanitizedMemberName_TakeSuggestionWhenNoConflict) {
262 Namer namer(fail_stream_);
263 EXPECT_TRUE(namer.SuggestSanitizedMemberName(1, 2, "mother"));
264 EXPECT_THAT(namer.GetMemberName(1, 2), Eq("mother"));
265 }
266
TEST_F(SpvNamerTest,SuggestSanitizedMemberName_TakeSanitizedSuggestion)267 TEST_F(SpvNamerTest, SuggestSanitizedMemberName_TakeSanitizedSuggestion) {
268 Namer namer(fail_stream_);
269 EXPECT_TRUE(namer.SuggestSanitizedMemberName(1, 2, "m:t%er"));
270 EXPECT_THAT(namer.GetMemberName(1, 2), Eq("m_t_er"));
271 }
272
TEST_F(SpvNamerTest,SuggestSanitizedMemberName_TakeSuggestionWhenNoConflictAfterSuggestionForLowerMember)273 TEST_F(
274 SpvNamerTest,
275 SuggestSanitizedMemberName_TakeSuggestionWhenNoConflictAfterSuggestionForLowerMember) { // NOLINT
276 Namer namer(fail_stream_);
277 EXPECT_TRUE(namer.SuggestSanitizedMemberName(1, 7, "mother"));
278 EXPECT_THAT(namer.GetMemberName(1, 2), Eq(""));
279 EXPECT_TRUE(namer.SuggestSanitizedMemberName(1, 2, "mary"));
280 EXPECT_THAT(namer.GetMemberName(1, 2), Eq("mary"));
281 }
282
TEST_F(SpvNamerTest,SuggestSanitizedMemberName_RejectSuggestionIfConflictOnMember)283 TEST_F(SpvNamerTest,
284 SuggestSanitizedMemberName_RejectSuggestionIfConflictOnMember) {
285 Namer namer(fail_stream_);
286 EXPECT_TRUE(namer.SuggestSanitizedMemberName(1, 2, "mother"));
287 EXPECT_FALSE(namer.SuggestSanitizedMemberName(1, 2, "mary"));
288 EXPECT_THAT(namer.GetMemberName(1, 2), Eq("mother"));
289 }
290
TEST_F(SpvNamerTest,Name_GeneratesNameIfNoneRegistered)291 TEST_F(SpvNamerTest, Name_GeneratesNameIfNoneRegistered) {
292 Namer namer(fail_stream_);
293 EXPECT_THAT(namer.Name(14), Eq("x_14"));
294 }
295
TEST_F(SpvNamerTest,Name_GeneratesNameWithoutConflict)296 TEST_F(SpvNamerTest, Name_GeneratesNameWithoutConflict) {
297 Namer namer(fail_stream_);
298 namer.Register(42, "x_14");
299 EXPECT_THAT(namer.Name(14), Eq("x_14_1"));
300 }
301
TEST_F(SpvNamerTest,Name_ReturnsRegisteredName)302 TEST_F(SpvNamerTest, Name_ReturnsRegisteredName) {
303 Namer namer(fail_stream_);
304 namer.Register(14, "hello");
305 EXPECT_THAT(namer.Name(14), Eq("hello"));
306 }
307
TEST_F(SpvNamerTest,ResolveMemberNamesForStruct_GeneratesRegularNamesOnItsOwn)308 TEST_F(SpvNamerTest,
309 ResolveMemberNamesForStruct_GeneratesRegularNamesOnItsOwn) {
310 Namer namer(fail_stream_);
311 namer.ResolveMemberNamesForStruct(2, 4);
312 EXPECT_THAT(namer.GetMemberName(2, 0), Eq("field0"));
313 EXPECT_THAT(namer.GetMemberName(2, 1), Eq("field1"));
314 EXPECT_THAT(namer.GetMemberName(2, 2), Eq("field2"));
315 EXPECT_THAT(namer.GetMemberName(2, 3), Eq("field3"));
316 }
317
TEST_F(SpvNamerTest,ResolveMemberNamesForStruct_ResolvesConflictBetweenSuggestedNames)318 TEST_F(SpvNamerTest,
319 ResolveMemberNamesForStruct_ResolvesConflictBetweenSuggestedNames) {
320 Namer namer(fail_stream_);
321 namer.SuggestSanitizedMemberName(2, 0, "apple");
322 namer.SuggestSanitizedMemberName(2, 1, "apple");
323 namer.ResolveMemberNamesForStruct(2, 2);
324 EXPECT_THAT(namer.GetMemberName(2, 0), Eq("apple"));
325 EXPECT_THAT(namer.GetMemberName(2, 1), Eq("apple_1"));
326 }
327
TEST_F(SpvNamerTest,ResolveMemberNamesForStruct_FillsUnsuggestedGaps)328 TEST_F(SpvNamerTest, ResolveMemberNamesForStruct_FillsUnsuggestedGaps) {
329 Namer namer(fail_stream_);
330 namer.SuggestSanitizedMemberName(2, 1, "apple");
331 namer.SuggestSanitizedMemberName(2, 2, "core");
332 namer.ResolveMemberNamesForStruct(2, 4);
333 EXPECT_THAT(namer.GetMemberName(2, 0), Eq("field0"));
334 EXPECT_THAT(namer.GetMemberName(2, 1), Eq("apple"));
335 EXPECT_THAT(namer.GetMemberName(2, 2), Eq("core"));
336 EXPECT_THAT(namer.GetMemberName(2, 3), Eq("field3"));
337 }
338
TEST_F(SpvNamerTest,ResolveMemberNamesForStruct_GeneratedNameAvoidsConflictWithSuggestion)339 TEST_F(SpvNamerTest,
340 ResolveMemberNamesForStruct_GeneratedNameAvoidsConflictWithSuggestion) {
341 Namer namer(fail_stream_);
342 namer.SuggestSanitizedMemberName(2, 0, "field1");
343 namer.ResolveMemberNamesForStruct(2, 2);
344 EXPECT_THAT(namer.GetMemberName(2, 0), Eq("field1"));
345 EXPECT_THAT(namer.GetMemberName(2, 1), Eq("field1_1"));
346 }
347
TEST_F(SpvNamerTest,ResolveMemberNamesForStruct_TruncatesOutOfBoundsSuggestion)348 TEST_F(SpvNamerTest,
349 ResolveMemberNamesForStruct_TruncatesOutOfBoundsSuggestion) {
350 Namer namer(fail_stream_);
351 namer.SuggestSanitizedMemberName(2, 3, "sitar");
352 EXPECT_THAT(namer.GetMemberName(2, 3), Eq("sitar"));
353 namer.ResolveMemberNamesForStruct(2, 2);
354 EXPECT_THAT(namer.GetMemberName(2, 0), Eq("field0"));
355 EXPECT_THAT(namer.GetMemberName(2, 1), Eq("field1"));
356 EXPECT_THAT(namer.GetMemberName(2, 3), Eq(""));
357 }
358
359 using SpvNamerReservedWordTest = ::testing::TestWithParam<std::string>;
360
TEST_P(SpvNamerReservedWordTest,ReservedWordsAreUsed)361 TEST_P(SpvNamerReservedWordTest, ReservedWordsAreUsed) {
362 bool success;
363 std::stringstream errors;
364 FailStream fail_stream(&success, &errors);
365 Namer namer(fail_stream);
366 const std::string reserved = GetParam();
367 // Since it's reserved, it's marked as used, and we can't register an ID
368 EXPECT_THAT(namer.FindUnusedDerivedName(reserved), Eq(reserved + "_1"));
369 }
370
371 INSTANTIATE_TEST_SUITE_P(SpvParserTest_ReservedWords,
372 SpvNamerReservedWordTest,
373 ::testing::ValuesIn(std::vector<std::string>{
374 // Please keep this list sorted.
375 "array", "as", "asm",
376 "bf16", "binding", "block",
377 "bool", "break", "builtin",
378 "case", "cast", "compute",
379 "const", "continue", "default",
380 "discard", "do", "else",
381 "elseif", "entry_point", "enum",
382 "f16", "f32", "fallthrough",
383 "false", "fn", "for",
384 "fragment", "i16", "i32",
385 "i64", "i8", "if",
386 "image", "import", "in",
387 "let", "location", "loop",
388 "mat2x2", "mat2x3", "mat2x4",
389 "mat3x2", "mat3x3", "mat3x4",
390 "mat4x2", "mat4x3", "mat4x4",
391 "offset", "out", "override",
392 "premerge", "private", "ptr",
393 "regardless", "return", "set",
394 "storage", "struct", "switch",
395 "true", "type", "typedef",
396 "u16", "u32", "u64",
397 "u8", "uniform", "uniform_constant",
398 "unless", "using", "var",
399 "vec2", "vec3", "vec4",
400 "vertex", "void", "while",
401 "workgroup",
402 }));
403
404 } // namespace
405 } // namespace spirv
406 } // namespace reader
407 } // namespace tint
408