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