• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef PANDA_TOOLING_INSPECTOR_TEST_JSON_OBJECT_MATCHER_H
17 #define PANDA_TOOLING_INSPECTOR_TEST_JSON_OBJECT_MATCHER_H
18 
19 #include <algorithm>
20 #include <iomanip>
21 #include <iostream>
22 #include <iterator>
23 #include <tuple>
24 #include <utility>
25 #include <vector>
26 
27 #include "gmock/gmock.h"
28 #include "gtest/gtest.h"
29 
30 #include "macros.h"
31 #include "utils/json_parser.h"
32 
33 // NOLINTBEGIN
34 
35 namespace ark::tooling::inspector::test {
36 template <typename PropertyType>
37 constexpr const char *PropertyTypeName();
38 
39 template <>
40 constexpr const char *PropertyTypeName<JsonObject::ArrayT>()
41 {
42     return "array";
43 }
44 
45 template <>
46 constexpr const char *PropertyTypeName<JsonObject::BoolT>()
47 {
48     return "boolean";
49 }
50 
51 template <>
52 constexpr const char *PropertyTypeName<JsonObject::JsonObjPointer>()
53 {
54     return "object";
55 }
56 
57 template <>
58 constexpr const char *PropertyTypeName<JsonObject::NumT>()
59 {
60     return "number";
61 }
62 
63 template <>
64 constexpr const char *PropertyTypeName<JsonObject::StringT>()
65 {
66     return "string";
67 }
68 
69 template <typename PropertyType>
70 struct JsonProperty {
71     const char *key;
72     testing::Matcher<PropertyType> value_matcher;
73 
74     template <typename OutputStream>
DescribeKeyToJsonProperty75     void DescribeKeyTo(OutputStream *os) const
76     {
77         *os << "property " << std::quoted(key) << " of type " << PropertyTypeName<PropertyType>();
78     }
79 
DescribeToJsonProperty80     void DescribeTo(std::ostream *os) const
81     {
82         DescribeKeyTo(os);
83         *os << " which ";
84         value_matcher.DescribeTo(os);
85     }
86 };
87 
88 template <typename PropertyType>
89 class HasJsonPropertyMatcher : public testing::MatcherInterface<const JsonObject &> {
90 public:
HasJsonPropertyMatcher(JsonProperty<PropertyType> property)91     explicit HasJsonPropertyMatcher(JsonProperty<PropertyType> property) : property_(std::move(property)) {}
92 
HasJsonPropertyMatcher(const char * key,testing::Matcher<PropertyType> value_matcher)93     HasJsonPropertyMatcher(const char *key, testing::Matcher<PropertyType> value_matcher)
94         : property_ {key, value_matcher}
95     {
96     }
97 
DescribeTo(std::ostream * os)98     void DescribeTo(std::ostream *os) const override
99     {
100         *os << "has ";
101         property_.DescribeTo(os);
102     }
103 
DescribeNegationTo(std::ostream * os)104     void DescribeNegationTo(std::ostream *os) const override
105     {
106         *os << "does not have a ";
107         property_.DescribeTo(os);
108     }
109 
MatchAndExplain(const JsonObject & object,testing::MatchResultListener * os)110     bool MatchAndExplain(const JsonObject &object, testing::MatchResultListener *os) const override
111     {
112         auto value_ptr = object.GetValue<PropertyType>(property_.key);
113 
114         if (!value_ptr) {
115             *os << "no ";
116             property_.DescribeKeyTo(os);
117             return false;
118         }
119 
120         return property_.value_matcher.MatchAndExplain(*value_ptr, os);
121     }
122 
123 private:
124     JsonProperty<PropertyType> property_;
125 };
126 
127 template <typename... PropertyType>
128 class JsonObjectMatcher : public testing::MatcherInterface<const JsonObject &> {
129 public:
JsonObjectMatcher(JsonProperty<PropertyType>...property)130     explicit JsonObjectMatcher(JsonProperty<PropertyType>... property) : properties_ {property...} {}
131 
DescribeTo(std::ostream * os)132     void DescribeTo(std::ostream *os) const override
133     {
134         *os << "is ";
135         DescribeInternalTo(os);
136     }
137 
DescribeNegationTo(std::ostream * os)138     void DescribeNegationTo(std::ostream *os) const override
139     {
140         *os << "isn't ";
141         DescribeInternalTo(os);
142     }
143 
MatchAndExplain(const JsonObject & object,testing::MatchResultListener * os)144     bool MatchAndExplain(const JsonObject &object, testing::MatchResultListener *os) const override
145     {
146         if (object.GetSize() != sizeof...(PropertyType)) {
147             *os << "number of properties doesn't match";
148             return false;
149         }
150 
151         return std::apply(
152             [&](JsonProperty<PropertyType>... property) {
153                 return (HasJsonPropertyMatcher<PropertyType>(property).MatchAndExplain(object, os) && ...);
154             },
155             properties_);
156     }
157 
158 private:
DescribeInternalTo(std::ostream * os)159     void DescribeInternalTo(std::ostream *os) const
160     {
161         if constexpr (sizeof...(PropertyType) == 0) {
162             *os << "an empty JsonObject";
163         } else {
164             *os << "a JsonObject with properties: ";
165 
166             std::apply(
167                 [os](auto first_property, auto... property) {
168                     first_property.DescribeTo(os);
169                     ((*os << ", ", property.DescribeTo(os)), ...);
170                 },
171                 properties_);
172         }
173     }
174 
175     std::tuple<JsonProperty<PropertyType>...> properties_;
176 };
177 
178 template <typename... PropertyType>
JsonProperties(JsonProperty<PropertyType>...property)179 auto JsonProperties(JsonProperty<PropertyType>... property)
180 {
181     return testing::Matcher<const JsonObject &>(new JsonObjectMatcher<PropertyType...>(property...));
182 }
183 
184 template <typename... PropertyType>
JsonElements(testing::Matcher<PropertyType>...matcher)185 auto JsonElements(testing::Matcher<PropertyType>... matcher)
186 {
187     return testing::ElementsAre(
188         Property(PropertyTypeName<PropertyType>(), &JsonObject::Value::Get<PropertyType>, Pointee(matcher))...);
189 }
190 
191 template <typename PropertyType, template <typename...> class Container, typename... Param>
JsonElementsAreArray(const Container<testing::Matcher<PropertyType>,Param...> & container)192 auto JsonElementsAreArray(const Container<testing::Matcher<PropertyType>, Param...> &container)
193 {
194     std::vector<testing::Matcher<const JsonObject::Value &>> elements;
195 
196     std::transform(container.begin(), container.end(), std::back_inserter(elements), [](auto &matcher) {
197         return Property(PropertyTypeName<PropertyType>(), &JsonObject::Value::Get<PropertyType>, Pointee(matcher));
198     });
199 
200     return ElementsAreArray(elements);
201 }
202 }  // namespace ark::tooling::inspector::test
203 
204 namespace ark {
205 std::ostream &operator<<(std::ostream &os, const JsonObject::Value &value);
206 
207 inline std::ostream &operator<<(std::ostream &os, const JsonObject &object)
208 {
209     os << '{';
210 
211     const char *delim = "";
212 
213     for (auto &[key, value] : object.GetUnorderedMap()) {
214         os << delim << std::quoted(key) << ": " << value;
215         delim = ", ";
216     }
217 
218     return os << '}';
219 }
220 
221 inline std::ostream &operator<<(std::ostream &os, const JsonObject::ArrayT &array)
222 {
223     os << '[';
224 
225     const char *delim = "";
226 
227     for (auto &value : array) {
228         os << delim << value;
229         delim = ", ";
230     }
231 
232     return os << ']';
233 }
234 }  // namespace ark
235 
236 // NOLINTEND
237 
238 #endif  // PANDA_TOOLING_INSPECTOR_TEST_JSON_OBJECT_MATCHER_H
239