From f973d514a113f987907222e1a408b11f8001c128 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Thu, 9 Feb 2023 11:59:47 +0000 Subject: [PATCH] tint: Add ast::CheckIdentifier() test helper A bunch of template magic to recursively test that an identifier (and template arguments) matches the given values. Will be heavily used for AST and parser tests. Bug: tint:1810 Change-Id: I8d83090352c345f0ce1ac7840e163eea96eac8a2 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/119124 Commit-Queue: Ben Clayton Reviewed-by: Dan Sinclair Kokoro: Kokoro --- src/tint/BUILD.gn | 1 + src/tint/CMakeLists.txt | 3 +- src/tint/ast/test_helper.h | 119 +++++++++++++++++++++++++++++++ src/tint/ast/test_helper_test.cc | 42 +++++++++++ 4 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 src/tint/ast/test_helper_test.cc diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn index 2b07fca3a9..60f4fb7dee 100644 --- a/src/tint/BUILD.gn +++ b/src/tint/BUILD.gn @@ -1370,6 +1370,7 @@ if (tint_build_unittests) { "ast/switch_statement_test.cc", "ast/templated_identifier_test.cc", "ast/test_helper.h", + "ast/test_helper_test.cc", "ast/texture_test.cc", "ast/traverse_expressions_test.cc", "ast/type_name_test.cc", diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt index 2d372396d8..de1e5437dc 100644 --- a/src/tint/CMakeLists.txt +++ b/src/tint/CMakeLists.txt @@ -886,8 +886,9 @@ if(TINT_BUILD_TESTS) ast/struct_member_test.cc ast/struct_test.cc ast/switch_statement_test.cc - ast/test_helper.h ast/templated_identifier_test.cc + ast/test_helper.h + ast/test_helper_test.cc ast/texture_test.cc ast/traverse_expressions_test.cc ast/type_name_test.cc diff --git a/src/tint/ast/test_helper.h b/src/tint/ast/test_helper.h index cf9474d1b0..03ecda8c8c 100644 --- a/src/tint/ast/test_helper.h +++ b/src/tint/ast/test_helper.h @@ -15,6 +15,9 @@ #ifndef SRC_TINT_AST_TEST_HELPER_H_ #define SRC_TINT_AST_TEST_HELPER_H_ +#include +#include + #include "gtest/gtest.h" #include "src/tint/program_builder.h" @@ -31,6 +34,122 @@ using TestHelper = TestHelperBase; template using TestParamHelper = TestHelperBase>; +/// A structure to hold a TemplatedIdentifier matcher, used by the CheckIdentifier() test helper +template +struct TemplatedIdentifierMatcher { + /// The expected name of the TemplatedIdentifier + std::string_view name; + /// The expected arguments of the TemplatedIdentifier + std::tuple args; +}; + +/// Deduction guide for TemplatedIdentifierMatcher +template +TemplatedIdentifierMatcher(std::string_view, std::tuple&&) + -> TemplatedIdentifierMatcher; + +/// A helper function for building a TemplatedIdentifierMatcher +/// @param name the name of the TemplatedIdentifier +/// @param args the template arguments +/// @return a TemplatedIdentifierMatcher +template +auto Template(std::string_view name, ARGS&&... args) { + return TemplatedIdentifierMatcher{name, std::make_tuple(std::forward(args)...)}; +} + +/// A traits helper for determining whether the type T is a TemplatedIdentifierMatcher. +template +struct IsTemplatedIdentifierMatcher { + /// True iff T is a TemplatedIdentifierMatcher + static constexpr bool value = false; +}; + +/// IsTemplatedIdentifierMatcher specialization for TemplatedIdentifierMatcher. +template +struct IsTemplatedIdentifierMatcher> { + /// True iff T is a TemplatedIdentifierMatcher + static constexpr bool value = true; +}; + +/// A testing utility for checking that an Identifier and any optional templated arguments match the +/// expected values. +/// @param symbols the symbol table +/// @param got the identifier +/// @param expected the expected identifier name +template +void CheckIdentifier(const SymbolTable& symbols, const Identifier* got, std::string_view expected) { + EXPECT_FALSE(got->Is()); + EXPECT_EQ(symbols.NameFor(got->symbol), expected); +} + +/// A testing utility for checking that an Identifier matches the expected name and template +/// arguments. +/// @param symbols the symbol table +/// @param ident the identifier +/// @param expected the expected identifier name and arguments +template +void CheckIdentifier(const SymbolTable& symbols, + const Identifier* ident, + const TemplatedIdentifierMatcher& expected) { + EXPECT_EQ(symbols.NameFor(ident->symbol), expected.name); + ASSERT_TRUE(ident->Is()); + auto* got = ident->As(); + ASSERT_EQ(got->arguments.Length(), std::tuple_size_v); + + size_t arg_idx = 0; + auto check_arg = [&](auto&& expected_arg) { + const auto* got_arg = got->arguments[arg_idx++]; + + using T = std::decay_t; + if constexpr (traits::IsStringLike) { + ASSERT_TRUE(got_arg->Is()); + ast::CheckIdentifier(symbols, got_arg->As()->identifier, + expected_arg); + } else if constexpr (IsTemplatedIdentifierMatcher::value) { + ASSERT_TRUE(got_arg->Is()); + auto* got_ident = got_arg->As()->identifier; + ASSERT_TRUE(got_ident->Is()); + CheckIdentifier(symbols, got_ident->As(), expected_arg); + } else if constexpr (std::is_same_v) { + ASSERT_TRUE(got_arg->Is()); + EXPECT_EQ(got_arg->As()->value, expected_arg); + } else if constexpr (std::is_same_v) { + ASSERT_TRUE(got_arg->Is()); + EXPECT_EQ(got_arg->As()->suffix, + IntLiteralExpression::Suffix::kNone); + EXPECT_EQ(AInt(got_arg->As()->value), expected_arg); + } else if constexpr (std::is_same_v) { + ASSERT_TRUE(got_arg->Is()); + EXPECT_EQ(got_arg->As()->suffix, + IntLiteralExpression::Suffix::kI); + EXPECT_EQ(i32(got_arg->As()->value), expected_arg); + } else if constexpr (std::is_same_v) { + ASSERT_TRUE(got_arg->Is()); + EXPECT_EQ(got_arg->As()->suffix, + IntLiteralExpression::Suffix::kU); + EXPECT_EQ(u32(got_arg->As()->value), expected_arg); + } else if constexpr (std::is_same_v) { + ASSERT_TRUE(got_arg->Is()); + EXPECT_EQ(got_arg->As()->suffix, + FloatLiteralExpression::Suffix::kNone); + EXPECT_EQ(AFloat(got_arg->As()->value), expected_arg); + } else if constexpr (std::is_same_v) { + ASSERT_TRUE(got_arg->Is()); + EXPECT_EQ(got_arg->As()->suffix, + FloatLiteralExpression::Suffix::kF); + EXPECT_EQ(f32(got_arg->As()->value), expected_arg); + } else if constexpr (std::is_same_v) { + ASSERT_TRUE(got_arg->Is()); + EXPECT_EQ(got_arg->As()->suffix, + FloatLiteralExpression::Suffix::kH); + EXPECT_EQ(f16(got_arg->As()->value), expected_arg); + } else { + FAIL() << "unhandled expected_args type"; + } + }; + std::apply([&](auto&&... args) { ((check_arg(args)), ...); }, expected.args); +} + } // namespace tint::ast #endif // SRC_TINT_AST_TEST_HELPER_H_ diff --git a/src/tint/ast/test_helper_test.cc b/src/tint/ast/test_helper_test.cc new file mode 100644 index 0000000000..fcf339c6ef --- /dev/null +++ b/src/tint/ast/test_helper_test.cc @@ -0,0 +1,42 @@ +// Copyright 2023 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/tint/ast/test_helper.h" + +namespace tint::ast { + +using namespace tint::number_suffixes; // NOLINT + +using AstCheckIdentifierTest = TestHelper; + +TEST_F(AstCheckIdentifierTest, NonTemplated) { + CheckIdentifier(Symbols(), Ident("abc"), "abc"); +} + +TEST_F(AstCheckIdentifierTest, TemplatedScalars) { + CheckIdentifier(Symbols(), Ident("abc", 1_i, 2_u, 3_f, 4_h, 5_a, 6._a, true), // + Template("abc", 1_i, 2_u, 3_f, 4_h, 5_a, 6._a, true)); +} + +TEST_F(AstCheckIdentifierTest, TemplatedIdentifiers) { + CheckIdentifier(Symbols(), Ident("abc", "one", "two", "three"), // + Template("abc", "one", "two", "three")); +} + +TEST_F(AstCheckIdentifierTest, NestedTemplate) { + CheckIdentifier(Symbols(), Ident("abc", "pre", Ident("nested", 42_a), "post"), // + Template("abc", "pre", Template("nested", 42_a), "post")); +} + +} // namespace tint::ast