| // Copyright 2020 The Tint Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "src/reader/spirv/namer.h" |
| |
| #include <cstdint> |
| #include <sstream> |
| #include <string> |
| |
| #include "gmock/gmock.h" |
| #include "src/reader/spirv/fail_stream.h" |
| |
| namespace tint { |
| namespace reader { |
| namespace spirv { |
| namespace { |
| |
| using ::testing::Eq; |
| |
| class SpvNamerTest : public testing::Test { |
| public: |
| SpvNamerTest() : fail_stream_(&success_, &errors_) {} |
| |
| /// @returns the accumulated diagnostic strings |
| std::string error() { return errors_.str(); } |
| |
| protected: |
| std::stringstream errors_; |
| bool success_ = true; |
| FailStream fail_stream_; |
| }; |
| |
| TEST_F(SpvNamerTest, SanitizeEmpty) { |
| EXPECT_THAT(Namer::Sanitize(""), Eq("empty")); |
| } |
| |
| TEST_F(SpvNamerTest, SanitizeLeadingUnderscore) { |
| EXPECT_THAT(Namer::Sanitize("_"), Eq("x_")); |
| } |
| |
| TEST_F(SpvNamerTest, SanitizeLeadingDigit) { |
| EXPECT_THAT(Namer::Sanitize("7zip"), Eq("x7zip")); |
| } |
| |
| TEST_F(SpvNamerTest, SanitizeOkChars) { |
| EXPECT_THAT(Namer::Sanitize("_abcdef12345"), Eq("x_abcdef12345")); |
| } |
| |
| TEST_F(SpvNamerTest, SanitizeNonIdentifierChars) { |
| EXPECT_THAT(Namer::Sanitize("a:1.2'f\n"), "a_1_2_f_"); |
| } |
| |
| TEST_F(SpvNamerTest, NoFailureToStart) { |
| Namer namer(fail_stream_); |
| EXPECT_TRUE(success_); |
| EXPECT_TRUE(error().empty()); |
| } |
| |
| TEST_F(SpvNamerTest, FailLogsError) { |
| Namer namer(fail_stream_); |
| const bool converted_result = namer.Fail() << "st. johns wood"; |
| EXPECT_FALSE(converted_result); |
| EXPECT_EQ(error(), "st. johns wood"); |
| EXPECT_FALSE(success_); |
| } |
| |
| TEST_F(SpvNamerTest, NoNameRecorded) { |
| Namer namer(fail_stream_); |
| |
| EXPECT_FALSE(namer.HasName(12)); |
| EXPECT_TRUE(success_); |
| EXPECT_TRUE(error().empty()); |
| } |
| |
| TEST_F(SpvNamerTest, FindUnusedDerivedName_NoRecordedName) { |
| Namer namer(fail_stream_); |
| EXPECT_THAT(namer.FindUnusedDerivedName("eleanor"), Eq("eleanor")); |
| // Prove that it wasn't registered when first found. |
| EXPECT_THAT(namer.FindUnusedDerivedName("eleanor"), Eq("eleanor")); |
| } |
| |
| TEST_F(SpvNamerTest, FindUnusedDerivedName_HasRecordedName) { |
| Namer namer(fail_stream_); |
| namer.SaveName(12, "rigby"); |
| EXPECT_THAT(namer.FindUnusedDerivedName("rigby"), Eq("rigby_1")); |
| } |
| |
| TEST_F(SpvNamerTest, FindUnusedDerivedName_HasMultipleConflicts) { |
| Namer namer(fail_stream_); |
| namer.SaveName(12, "rigby"); |
| namer.SaveName(13, "rigby_1"); |
| namer.SaveName(14, "rigby_3"); |
| // It picks the first non-conflicting suffix. |
| EXPECT_THAT(namer.FindUnusedDerivedName("rigby"), Eq("rigby_2")); |
| } |
| |
| TEST_F(SpvNamerTest, MakeDerivedName_NoRecordedName) { |
| Namer namer(fail_stream_); |
| EXPECT_THAT(namer.MakeDerivedName("eleanor"), Eq("eleanor")); |
| // Prove that it was registered when first found. |
| EXPECT_THAT(namer.MakeDerivedName("eleanor"), Eq("eleanor_1")); |
| } |
| |
| TEST_F(SpvNamerTest, MakeDerivedName_HasRecordedName) { |
| Namer namer(fail_stream_); |
| namer.SaveName(12, "rigby"); |
| EXPECT_THAT(namer.MakeDerivedName("rigby"), Eq("rigby_1")); |
| } |
| |
| TEST_F(SpvNamerTest, MakeDerivedName_HasMultipleConflicts) { |
| Namer namer(fail_stream_); |
| namer.SaveName(12, "rigby"); |
| namer.SaveName(13, "rigby_1"); |
| namer.SaveName(14, "rigby_3"); |
| // It picks the first non-conflicting suffix. |
| EXPECT_THAT(namer.MakeDerivedName("rigby"), Eq("rigby_2")); |
| } |
| |
| TEST_F(SpvNamerTest, SaveNameOnce) { |
| Namer namer(fail_stream_); |
| |
| const uint32_t id = 9; |
| EXPECT_FALSE(namer.HasName(id)); |
| const bool save_result = namer.SaveName(id, "abbey road"); |
| EXPECT_TRUE(save_result); |
| EXPECT_TRUE(namer.HasName(id)); |
| EXPECT_EQ(namer.GetName(id), "abbey road"); |
| EXPECT_TRUE(success_); |
| EXPECT_TRUE(error().empty()); |
| } |
| |
| TEST_F(SpvNamerTest, SaveNameTwoIds) { |
| Namer namer(fail_stream_); |
| |
| EXPECT_FALSE(namer.HasName(8)); |
| EXPECT_FALSE(namer.HasName(9)); |
| EXPECT_TRUE(namer.SaveName(8, "abbey road")); |
| EXPECT_TRUE(namer.SaveName(9, "rubber soul")); |
| EXPECT_TRUE(namer.HasName(8)); |
| EXPECT_TRUE(namer.HasName(9)); |
| EXPECT_EQ(namer.GetName(9), "rubber soul"); |
| EXPECT_EQ(namer.GetName(8), "abbey road"); |
| EXPECT_TRUE(success_); |
| EXPECT_TRUE(error().empty()); |
| } |
| |
| TEST_F(SpvNamerTest, SaveNameFailsDueToIdReuse) { |
| Namer namer(fail_stream_); |
| |
| const uint32_t id = 9; |
| EXPECT_TRUE(namer.SaveName(id, "abbey road")); |
| EXPECT_FALSE(namer.SaveName(id, "rubber soul")); |
| EXPECT_TRUE(namer.HasName(id)); |
| EXPECT_EQ(namer.GetName(id), "abbey road"); |
| EXPECT_FALSE(success_); |
| EXPECT_FALSE(error().empty()); |
| } |
| |
| TEST_F(SpvNamerTest, SuggestSanitizedName_TakeSuggestionWhenNoConflict) { |
| Namer namer(fail_stream_); |
| |
| EXPECT_TRUE(namer.SuggestSanitizedName(1, "father")); |
| EXPECT_THAT(namer.GetName(1), Eq("father")); |
| } |
| |
| TEST_F(SpvNamerTest, |
| SuggestSanitizedName_RejectSuggestionWhenConflictOnSameId) { |
| Namer namer(fail_stream_); |
| |
| namer.SaveName(1, "lennon"); |
| EXPECT_FALSE(namer.SuggestSanitizedName(1, "mccartney")); |
| EXPECT_THAT(namer.GetName(1), Eq("lennon")); |
| } |
| |
| TEST_F(SpvNamerTest, SuggestSanitizedName_SanitizeSuggestion) { |
| Namer namer(fail_stream_); |
| |
| EXPECT_TRUE(namer.SuggestSanitizedName(9, "m:kenzie")); |
| EXPECT_THAT(namer.GetName(9), Eq("m_kenzie")); |
| } |
| |
| TEST_F(SpvNamerTest, |
| SuggestSanitizedName_GenerateNewNameWhenConflictOnDifferentId) { |
| Namer namer(fail_stream_); |
| |
| namer.SaveName(7, "rice"); |
| EXPECT_TRUE(namer.SuggestSanitizedName(9, "rice")); |
| EXPECT_THAT(namer.GetName(9), Eq("rice_1")); |
| } |
| |
| TEST_F(SpvNamerTest, GetMemberName_EmptyStringForUnvisitedStruct) { |
| Namer namer(fail_stream_); |
| EXPECT_THAT(namer.GetMemberName(1, 2), Eq("")); |
| } |
| |
| TEST_F(SpvNamerTest, GetMemberName_EmptyStringForUnvisitedMember) { |
| Namer namer(fail_stream_); |
| namer.SuggestSanitizedMemberName(1, 2, "mother"); |
| EXPECT_THAT(namer.GetMemberName(1, 0), Eq("")); |
| } |
| |
| TEST_F(SpvNamerTest, SuggestSanitizedMemberName_TakeSuggestionWhenNoConflict) { |
| Namer namer(fail_stream_); |
| EXPECT_TRUE(namer.SuggestSanitizedMemberName(1, 2, "mother")); |
| EXPECT_THAT(namer.GetMemberName(1, 2), Eq("mother")); |
| } |
| |
| TEST_F(SpvNamerTest, SuggestSanitizedMemberName_TakeSanitizedSuggestion) { |
| Namer namer(fail_stream_); |
| EXPECT_TRUE(namer.SuggestSanitizedMemberName(1, 2, "m:t%er")); |
| EXPECT_THAT(namer.GetMemberName(1, 2), Eq("m_t_er")); |
| } |
| |
| TEST_F( |
| SpvNamerTest, |
| SuggestSanitizedMemberName_TakeSuggestionWhenNoConflictAfterSuggestionForLowerMember) { // NOLINT |
| Namer namer(fail_stream_); |
| EXPECT_TRUE(namer.SuggestSanitizedMemberName(1, 7, "mother")); |
| EXPECT_THAT(namer.GetMemberName(1, 2), Eq("")); |
| EXPECT_TRUE(namer.SuggestSanitizedMemberName(1, 2, "mary")); |
| EXPECT_THAT(namer.GetMemberName(1, 2), Eq("mary")); |
| } |
| |
| TEST_F(SpvNamerTest, |
| SuggestSanitizedMemberName_RejectSuggestionIfConflictOnMember) { |
| Namer namer(fail_stream_); |
| EXPECT_TRUE(namer.SuggestSanitizedMemberName(1, 2, "mother")); |
| EXPECT_FALSE(namer.SuggestSanitizedMemberName(1, 2, "mary")); |
| EXPECT_THAT(namer.GetMemberName(1, 2), Eq("mother")); |
| } |
| |
| TEST_F(SpvNamerTest, Name_GeneratesNameIfNoneRegistered) { |
| Namer namer(fail_stream_); |
| EXPECT_THAT(namer.Name(14), Eq("x_14")); |
| } |
| |
| TEST_F(SpvNamerTest, Name_GeneratesNameWithoutConflict) { |
| Namer namer(fail_stream_); |
| namer.SaveName(42, "x_14"); |
| EXPECT_THAT(namer.Name(14), Eq("x_14_1")); |
| } |
| |
| TEST_F(SpvNamerTest, Name_ReturnsRegisteredName) { |
| Namer namer(fail_stream_); |
| namer.SaveName(14, "hello"); |
| EXPECT_THAT(namer.Name(14), Eq("hello")); |
| } |
| |
| TEST_F(SpvNamerTest, |
| ResolveMemberNamesForStruct_GeneratesRegularNamesOnItsOwn) { |
| Namer namer(fail_stream_); |
| namer.ResolveMemberNamesForStruct(2, 4); |
| EXPECT_THAT(namer.GetMemberName(2, 0), Eq("field0")); |
| EXPECT_THAT(namer.GetMemberName(2, 1), Eq("field1")); |
| EXPECT_THAT(namer.GetMemberName(2, 2), Eq("field2")); |
| EXPECT_THAT(namer.GetMemberName(2, 3), Eq("field3")); |
| } |
| |
| TEST_F(SpvNamerTest, |
| ResolveMemberNamesForStruct_ResolvesConflictBetweenSuggestedNames) { |
| Namer namer(fail_stream_); |
| namer.SuggestSanitizedMemberName(2, 0, "apple"); |
| namer.SuggestSanitizedMemberName(2, 1, "apple"); |
| namer.ResolveMemberNamesForStruct(2, 2); |
| EXPECT_THAT(namer.GetMemberName(2, 0), Eq("apple")); |
| EXPECT_THAT(namer.GetMemberName(2, 1), Eq("apple_1")); |
| } |
| |
| TEST_F(SpvNamerTest, ResolveMemberNamesForStruct_FillsUnsuggestedGaps) { |
| Namer namer(fail_stream_); |
| namer.SuggestSanitizedMemberName(2, 1, "apple"); |
| namer.SuggestSanitizedMemberName(2, 2, "core"); |
| namer.ResolveMemberNamesForStruct(2, 4); |
| EXPECT_THAT(namer.GetMemberName(2, 0), Eq("field0")); |
| EXPECT_THAT(namer.GetMemberName(2, 1), Eq("apple")); |
| EXPECT_THAT(namer.GetMemberName(2, 2), Eq("core")); |
| EXPECT_THAT(namer.GetMemberName(2, 3), Eq("field3")); |
| } |
| |
| TEST_F(SpvNamerTest, |
| ResolveMemberNamesForStruct_GeneratedNameAvoidsConflictWithSuggestion) { |
| Namer namer(fail_stream_); |
| namer.SuggestSanitizedMemberName(2, 0, "field1"); |
| namer.ResolveMemberNamesForStruct(2, 2); |
| EXPECT_THAT(namer.GetMemberName(2, 0), Eq("field1")); |
| EXPECT_THAT(namer.GetMemberName(2, 1), Eq("field1_1")); |
| } |
| |
| TEST_F(SpvNamerTest, |
| ResolveMemberNamesForStruct_TruncatesOutOfBoundsSuggestion) { |
| Namer namer(fail_stream_); |
| namer.SuggestSanitizedMemberName(2, 3, "sitar"); |
| EXPECT_THAT(namer.GetMemberName(2, 3), Eq("sitar")); |
| namer.ResolveMemberNamesForStruct(2, 2); |
| EXPECT_THAT(namer.GetMemberName(2, 0), Eq("field0")); |
| EXPECT_THAT(namer.GetMemberName(2, 1), Eq("field1")); |
| EXPECT_THAT(namer.GetMemberName(2, 3), Eq("")); |
| } |
| |
| using SpvNamerReservedWordTest = ::testing::TestWithParam<std::string>; |
| |
| TEST_P(SpvNamerReservedWordTest, ReservedWordsAreUsed) { |
| bool success; |
| std::stringstream errors; |
| FailStream fail_stream(&success, &errors); |
| Namer namer(fail_stream); |
| const std::string reserved = GetParam(); |
| // Since it's reserved, it's marked as used, and we can't register an ID |
| EXPECT_THAT(namer.FindUnusedDerivedName(reserved), Eq(reserved + "_1")); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(SpvParserTest_ReservedWords, |
| SpvNamerReservedWordTest, |
| ::testing::ValuesIn(std::vector<std::string>{ |
| // Please keep this list sorted. |
| "array", |
| "as", |
| "asm", |
| "bf16", |
| "binding", |
| "block", |
| "bool", |
| "break", |
| "builtin", |
| "case", |
| "cast", |
| "compute", |
| "const", |
| "constant_id", |
| "continue", |
| "default", |
| "do", |
| "else", |
| "elseif", |
| "entry_point", |
| "enum", |
| "f16", |
| "f32", |
| "fallthrough", |
| "false", |
| "fn", |
| "for", |
| "fragment", |
| "i16", |
| "i32", |
| "i64", |
| "i8", |
| "if", |
| "image", |
| "import", |
| "in", |
| "kill", |
| "let", |
| "location", |
| "loop", |
| "mat2x2", |
| "mat2x3", |
| "mat2x4", |
| "mat3x2", |
| "mat3x3", |
| "mat3x4", |
| "mat4x2", |
| "mat4x3", |
| "mat4x4", |
| "offset", |
| "out", |
| "premerge", |
| "private", |
| "ptr", |
| "regardless", |
| "return", |
| "set", |
| "storage_buffer", |
| "struct", |
| "switch", |
| "true", |
| "type", |
| "typedef", |
| "u16", |
| "u32", |
| "u64", |
| "u8", |
| "uniform", |
| "uniform_constant", |
| "unless", |
| "using", |
| "var", |
| "vec2", |
| "vec3", |
| "vec4", |
| "vertex", |
| "void", |
| "while", |
| "workgroup", |
| })); |
| |
| } // namespace |
| } // namespace spirv |
| } // namespace reader |
| } // namespace tint |