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 @@ -102,6 +102,8 @@ public: using StringMapEntryStorage::StringMapEntryStorage; + using ValueType = ValueTy; + StringRef getKey() const { return StringRef(getKeyData(), this->getKeyLength()); } diff --git a/llvm/include/llvm/Testing/ADT/StringMapEntry.h b/llvm/include/llvm/Testing/ADT/StringMapEntry.h --- a/llvm/include/llvm/Testing/ADT/StringMapEntry.h +++ b/llvm/include/llvm/Testing/ADT/StringMapEntry.h @@ -10,6 +10,7 @@ #define LLVM_TESTING_ADT_STRINGMAPENTRY_H_ #include "llvm/ADT/StringMapEntry.h" +#include "gmock/gmock.h" #include #include @@ -39,6 +40,90 @@ return OS << "}"; } +namespace detail { + +template +class StringMapEntryMatcherImpl + : public testing::MatcherInterface { +public: + using ValueT = typename std::remove_reference_t::ValueType; + + 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; +}; + +} // namespace detail + +/// Returns a gMock matcher that matches a `StringMapEntry` whose string key +/// matches `KeyMatcher`, and whose value matches `ValueMatcher`. +template +detail::StringMapEntryMatcher +IsStringMapEntry(KeyMatcherT KM, ValueMatcherT VM) { + return detail::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 --- a/llvm/unittests/Testing/ADT/CMakeLists.txt +++ b/llvm/unittests/Testing/ADT/CMakeLists.txt @@ -3,5 +3,6 @@ ) add_llvm_unittest(TestingADTTests + StringMapEntryTest.cpp StringMapTest.cpp ) diff --git a/llvm/unittests/Testing/ADT/StringMapEntryTest.cpp b/llvm/unittests/Testing/ADT/StringMapEntryTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/Testing/ADT/StringMapEntryTest.cpp @@ -0,0 +1,88 @@ +//===- StringMapEntryTest.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/StringMapEntry.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) { + 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