1 //===- llvm/Testing/ADT/StringMapEntry.h ----------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLVM_TESTING_ADT_STRINGMAPENTRY_H_
10 #define LLVM_TESTING_ADT_STRINGMAPENTRY_H_
11 
12 #include "llvm/ADT/StringMapEntry.h"
13 #include "gmock/gmock.h"
14 #include <ostream>
15 #include <type_traits>
16 
17 namespace llvm {
18 namespace detail {
19 
20 template <typename T, typename = std::void_t<>>
21 struct CanOutputToOStream : std::false_type {};
22 
23 template <typename T>
24 struct CanOutputToOStream<T, std::void_t<decltype(std::declval<std::ostream &>()
25                                                   << std::declval<T>())>>
26     : std::true_type {};
27 
28 } // namespace detail
29 
30 /// Support for printing to std::ostream, for use with e.g. producing more
31 /// useful error messages with Google Test.
32 template <typename T>
33 std::ostream &operator<<(std::ostream &OS, const StringMapEntry<T> &E) {
34   OS << "{\"" << E.getKey().data() << "\": ";
35   if constexpr (detail::CanOutputToOStream<decltype(E.getValue())>::value) {
36     OS << E.getValue();
37   } else {
38     OS << "non-printable value";
39   }
40   return OS << "}";
41 }
42 
43 namespace detail {
44 
45 template <typename StringMapEntryT>
46 class StringMapEntryMatcherImpl
47     : public testing::MatcherInterface<StringMapEntryT> {
48 public:
49   using ValueT = typename std::remove_reference_t<StringMapEntryT>::ValueType;
50 
51   template <typename KeyMatcherT, typename ValueMatcherT>
52   StringMapEntryMatcherImpl(KeyMatcherT KeyMatcherArg,
53                             ValueMatcherT ValueMatcherArg)
54       : KeyMatcher(
55             testing::SafeMatcherCast<const std::string &>(KeyMatcherArg)),
56         ValueMatcher(
57             testing::SafeMatcherCast<const ValueT &>(ValueMatcherArg)) {}
58 
59   void DescribeTo(std::ostream *OS) const override {
60     *OS << "has a string key that ";
61     KeyMatcher.DescribeTo(OS);
62     *OS << ", and has a value that ";
63     ValueMatcher.DescribeTo(OS);
64   }
65 
66   void DescribeNegationTo(std::ostream *OS) const override {
67     *OS << "has a string key that ";
68     KeyMatcher.DescribeNegationTo(OS);
69     *OS << ", or has a value that ";
70     ValueMatcher.DescribeNegationTo(OS);
71   }
72 
73   bool
74   MatchAndExplain(StringMapEntryT Entry,
75                   testing::MatchResultListener *ResultListener) const override {
76     testing::StringMatchResultListener KeyListener;
77     if (!KeyMatcher.MatchAndExplain(Entry.getKey().data(), &KeyListener)) {
78       *ResultListener << ("which has a string key " +
79                           (KeyListener.str().empty() ? "that doesn't match"
80                                                      : KeyListener.str()));
81       return false;
82     }
83     testing::StringMatchResultListener ValueListener;
84     if (!ValueMatcher.MatchAndExplain(Entry.getValue(), &ValueListener)) {
85       *ResultListener << ("which has a value " + (ValueListener.str().empty()
86                                                       ? "that doesn't match"
87                                                       : ValueListener.str()));
88       return false;
89     }
90     *ResultListener << "which is a match";
91     return true;
92   }
93 
94 private:
95   const testing::Matcher<const std::string &> KeyMatcher;
96   const testing::Matcher<const ValueT &> ValueMatcher;
97 };
98 
99 template <typename KeyMatcherT, typename ValueMatcherT>
100 class StringMapEntryMatcher {
101 public:
102   StringMapEntryMatcher(KeyMatcherT KMArg, ValueMatcherT VMArg)
103       : KM(std::move(KMArg)), VM(std::move(VMArg)) {}
104 
105   template <typename StringMapEntryT>
106   operator testing::Matcher<StringMapEntryT>() const { // NOLINT
107     return testing::Matcher<StringMapEntryT>(
108         new StringMapEntryMatcherImpl<const StringMapEntryT &>(KM, VM));
109   }
110 
111 private:
112   const KeyMatcherT KM;
113   const ValueMatcherT VM;
114 };
115 
116 } // namespace detail
117 
118 /// Returns a gMock matcher that matches a `StringMapEntry` whose string key
119 /// matches `KeyMatcher`, and whose value matches `ValueMatcher`.
120 template <typename KeyMatcherT, typename ValueMatcherT>
121 detail::StringMapEntryMatcher<KeyMatcherT, ValueMatcherT>
122 IsStringMapEntry(KeyMatcherT KM, ValueMatcherT VM) {
123   return detail::StringMapEntryMatcher<KeyMatcherT, ValueMatcherT>(
124       std::move(KM), std::move(VM));
125 }
126 
127 } // namespace llvm
128 
129 #endif
130