diff --git a/llvm/include/llvm/ADT/StringMapEntry.h b/llvm/include/llvm/ADT/StringMapEntry.h --- a/llvm/include/llvm/ADT/StringMapEntry.h +++ b/llvm/include/llvm/ADT/StringMapEntry.h @@ -101,6 +101,8 @@ template class StringMapEntry final : public StringMapEntryStorage { public: + typedef ValueTy ValueType; + using StringMapEntryStorage::StringMapEntryStorage; StringRef getKey() const { diff --git a/llvm/include/llvm/Testing/ADT/StringMapEntryMatcher.h b/llvm/include/llvm/Testing/ADT/StringMapEntryMatcher.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Testing/ADT/StringMapEntryMatcher.h @@ -0,0 +1,99 @@ +//===- llvm/Testing/Support/StringMapEntryMatcher.h -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TESTING_ADT_STRINGMAPENTRYMATCHER_H_ +#define LLVM_TESTING_ADT_STRINGMAPENTRYMATCHER_H_ + +#include "gmock/gmock.h" +#include + +namespace llvm { +template +class StringMapEntryMatcherImpl + : public testing::MatcherInterface { +public: + typedef + typename std::remove_reference::type::ValueType ValueT; + + template + StringMapEntryMatcherImpl(KeyMatcherT KeyMatcherArg, + ValueMatcherT ValueMatcherArg) + : KeyMatcher( + testing::SafeMatcherCast(KeyMatcherArg)), + ValueMatcher( + testing::SafeMatcherCast(ValueMatcherArg)) {} + + void DescribeTo(std::ostream *OS) const override { + *OS << "has a string key that "; + KeyMatcher.DescribeTo(OS); + *OS << ", and has a value that "; + ValueMatcher.DescribeTo(OS); + } + + void DescribeNegationTo(std::ostream *OS) const override { + *OS << "has a string key that "; + KeyMatcher.DescribeNegationTo(OS); + *OS << ", or has a value that "; + ValueMatcher.DescribeNegationTo(OS); + } + + bool + MatchAndExplain(StringMapEntryT Entry, + testing::MatchResultListener *ResultListener) const override { + testing::StringMatchResultListener KeyListener; + if (!KeyMatcher.MatchAndExplain(Entry.getKey().data(), &KeyListener)) { + *ResultListener << ("which has a string key " + + (KeyListener.str().empty() ? "that doesn't match" + : KeyListener.str())); + return false; + } + testing::StringMatchResultListener ValueListener; + if (!ValueMatcher.MatchAndExplain(Entry.getValue(), &ValueListener)) { + *ResultListener << ("which has a value " + (ValueListener.str().empty() + ? "that doesn't match" + : ValueListener.str())); + return false; + } + *ResultListener << "which is a match"; + return true; + } + +private: + const testing::Matcher KeyMatcher; + const testing::Matcher ValueMatcher; +}; + +template +class StringMapEntryMatcher { +public: + StringMapEntryMatcher(KeyMatcherT KMArg, ValueMatcherT VMArg) + : KM(std::move(KMArg)), VM(std::move(VMArg)) {} + + template + operator testing::Matcher() const { // NOLINT + return testing::Matcher( + new StringMapEntryMatcherImpl(KM, VM)); + } + +private: + const KeyMatcherT KM; + const ValueMatcherT VM; +}; + +/// Returns a gMock matcher that matches a `StringMapEntry` whose string key +/// matches `KeyMatcher`, and whose value matches `ValueMatcher`. +template +StringMapEntryMatcher +IsStringMapEntry(KeyMatcherT KM, ValueMatcherT VM) { + return StringMapEntryMatcher(std::move(KM), + std::move(VM)); +} + +} // namespace llvm + +#endif diff --git a/llvm/unittests/Testing/ADT/CMakeLists.txt b/llvm/unittests/Testing/ADT/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/unittests/Testing/ADT/CMakeLists.txt @@ -0,0 +1,7 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_llvm_unittest(TestingADTTests + StringMapEntryMatcherTest.cpp + ) diff --git a/llvm/unittests/Testing/ADT/StringMapEntryMatcherTest.cpp b/llvm/unittests/Testing/ADT/StringMapEntryMatcherTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/Testing/ADT/StringMapEntryMatcherTest.cpp @@ -0,0 +1,89 @@ +//===- StringMapEntryMatcherTest.cpp --------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Testing/ADT/StringMapEntryMatcher.h" +#include "llvm/ADT/StringMap.h" + +#include "gtest/gtest.h" +#include + +namespace llvm { +namespace { + +using testing::Gt; +using testing::Matcher; +using testing::StrCaseEq; +using testing::StringMatchResultListener; +using testing::UnorderedElementsAre; + +template std::string Describe(const Matcher &M, bool Match) { + std::stringstream SS; + if (Match) { + M.DescribeTo(&SS); + } else { + M.DescribeNegationTo(&SS); + } + return SS.str(); +} + +template +std::string ExplainMatch(const Matcher &Matcher, const V &Value) { + StringMatchResultListener Listener; + Matcher.MatchAndExplain(Value, &Listener); + return Listener.str(); +} + +TEST(IsStringMapEntryTest, InnerMatchersAreExactValues) { + llvm::StringMap Map = {{"A", 1}}; + EXPECT_THAT(*Map.find("A"), IsStringMapEntry("A", 1)); +} + +TEST(IsStringMapEntryTest, InnerMatchersAreOtherMatchers) { + llvm::StringMap Map = {{"A", 1}}; + EXPECT_THAT(*Map.find("A"), IsStringMapEntry(StrCaseEq("a"), Gt(0))); +} + +TEST(IsStringMapEntryTest, UseAsInnerMatcher) { + llvm::StringMap Map = {{"A", 1}, {"B", 2}}; + EXPECT_THAT(Map, UnorderedElementsAre(IsStringMapEntry("A", 1), + IsStringMapEntry("B", 2))); +} + +TEST(IsStringMapEntryTest, DescribeSelf) { + llvm::StringMap Map = {{"A", 1}}; + Matcher> M = IsStringMapEntry("A", 1); + EXPECT_EQ( + R"(has a string key that is equal to "A", and has a value that is equal to 1)", + Describe(M, true)); + EXPECT_EQ( + R"(has a string key that isn't equal to "A", or has a value that isn't equal to 1)", + Describe(M, false)); +} + +TEST(IsStringMapEntryTest, ExplainSelfMatchSuccess) { + llvm::StringMap Map = {{"A", 1}}; + Matcher> M = IsStringMapEntry("A", 1); + EXPECT_EQ(R"(which is a match)", ExplainMatch(M, *Map.find("A"))); +} + +TEST(IsStringMapEntryTest, ExplainSelfMatchFailsOnKey) { + llvm::StringMap Map = {{"B", 1}}; + Matcher> M = IsStringMapEntry("A", 1); + EXPECT_EQ(R"(which has a string key that doesn't match)", + ExplainMatch(M, *Map.find("B"))); +} + +TEST(IsStringMapEntryTest, ExplainSelfMatchFailsOnValue) { + llvm::StringMap Map = {{"A", 2}}; + Matcher> M = IsStringMapEntry("A", 1); + EXPECT_EQ(R"(which has a value that doesn't match)", + ExplainMatch(M, *Map.find("A"))); +} + +} // namespace +} // namespace llvm diff --git a/llvm/unittests/Testing/CMakeLists.txt b/llvm/unittests/Testing/CMakeLists.txt --- a/llvm/unittests/Testing/CMakeLists.txt +++ b/llvm/unittests/Testing/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(Support) +add_subdirectory(ADT) diff --git a/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel b/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel --- a/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel @@ -4175,6 +4175,16 @@ deps = [":gtest"], ) +cc_library( + name = "testing_adt", + testonly = True, + hdrs = glob([ + "include/llvm/Testing/ADT/*.h", + ]), + copts = llvm_copts, + deps = [":gmock"], +) + py_binary( name = "lit", testonly = True, diff --git a/utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel b/utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel --- a/utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel @@ -644,6 +644,23 @@ ], ) +cc_test( + name = "testing_adt_tests", + size = "small", + srcs = glob( + [ + "Testing/ADT/*.cpp", + ], + allow_empty = False, + ), + deps = [ + "//llvm:Support", + "//llvm:gtest", + "//llvm:gtest_main", + "//llvm:testing_adt", + ], +) + cc_test( name = "transforms_tests", size = "small",