• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2022 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 "utils/json_parser.h"
20 
21 #include "gtest/gtest.h"
22 #include "gmock/gmock.h"
23 
24 #include <algorithm>
25 #include <iomanip>
26 #include <iostream>
27 #include <iterator>
28 #include <tuple>
29 #include <utility>
30 #include <vector>
31 
32 namespace panda::tooling::inspector::test {
33 template <typename PropertyType>
34 constexpr const char *PropertyTypeName();
35 
36 template <>
37 constexpr const char *PropertyTypeName<JsonObject::ArrayT>()
38 {
39     return "array";
40 }
41 
42 template <>
43 constexpr const char *PropertyTypeName<JsonObject::BoolT>()
44 {
45     return "boolean";
46 }
47 
48 template <>
49 constexpr const char *PropertyTypeName<JsonObject::JsonObjPointer>()
50 {
51     return "object";
52 }
53 
54 template <>
55 constexpr const char *PropertyTypeName<JsonObject::NumT>()
56 {
57     return "number";
58 }
59 
60 template <>
61 constexpr const char *PropertyTypeName<JsonObject::StringT>()
62 {
63     return "string";
64 }
65 
66 template <typename PropertyType>
67 struct JsonProperty {
68     const char *key;
69     testing::Matcher<PropertyType> valueMatcher;
70 
71     template <typename OutputStream>
DescribeKeyToJsonProperty72     void DescribeKeyTo(OutputStream *os) const
73     {
74         *os << "property " << std::quoted(key) << " of type " << PropertyTypeName<PropertyType>();
75     }
76 
DescribeToJsonProperty77     void DescribeTo(std::ostream *os) const
78     {
79         DescribeKeyTo(os);
80         *os << " which ";
81         valueMatcher.DescribeTo(os);
82     }
83 };
84 
85 template <typename PropertyType>
86 class HasJsonPropertyMatcher : public testing::MatcherInterface<const JsonObject &> {
87 public:
HasJsonPropertyMatcher(JsonProperty<PropertyType> property)88     explicit HasJsonPropertyMatcher(JsonProperty<PropertyType> property) : property_(std::move(property)) {}
89 
HasJsonPropertyMatcher(const char * key,testing::Matcher<PropertyType> valueMatcher)90     HasJsonPropertyMatcher(const char *key, testing::Matcher<PropertyType> valueMatcher) : property_ {key, valueMatcher}
91     {
92     }
93 
DescribeTo(std::ostream * os)94     void DescribeTo(std::ostream *os) const override
95     {
96         *os << "has ";
97         property_.DescribeTo(os);
98     }
99 
DescribeNegationTo(std::ostream * os)100     void DescribeNegationTo(std::ostream *os) const override
101     {
102         *os << "does not have a ";
103         property_.DescribeTo(os);
104     }
105 
MatchAndExplain(const JsonObject & object,testing::MatchResultListener * os)106     bool MatchAndExplain(const JsonObject &object, testing::MatchResultListener *os) const override
107     {
108         auto valuePtr = object.GetValue<PropertyType>(property_.key);
109 
110         if (!valuePtr) {
111             *os << "no ";
112             property_.DescribeKeyTo(os);
113             return false;
114         }
115 
116         return property_.valueMatcher.MatchAndExplain(*valuePtr, os);
117     }
118 
119 private:
120     JsonProperty<PropertyType> property_;
121 };
122 
123 template <typename... PropertyType>
124 class JsonObjectMatcher : public testing::MatcherInterface<const JsonObject &> {
125 public:
JsonObjectMatcher(JsonProperty<PropertyType>...property)126     explicit JsonObjectMatcher(JsonProperty<PropertyType>... property) : properties_ {property...} {}
127 
DescribeTo(std::ostream * os)128     void DescribeTo(std::ostream *os) const override
129     {
130         *os << "is ";
131         DescribeInternalTo(os);
132     }
133 
DescribeNegationTo(std::ostream * os)134     void DescribeNegationTo(std::ostream *os) const override
135     {
136         *os << "isn't ";
137         DescribeInternalTo(os);
138     }
139 
MatchAndExplain(const JsonObject & object,testing::MatchResultListener * os)140     bool MatchAndExplain(const JsonObject &object, testing::MatchResultListener *os) const override
141     {
142         if (object.GetSize() != sizeof...(PropertyType)) {
143             *os << "number of properties doesn't match";
144             return false;
145         }
146 
147         return std::apply(
148             [&](JsonProperty<PropertyType>... property) {
149                 return (HasJsonPropertyMatcher<PropertyType>(property).MatchAndExplain(object, os) && ...);
150             },
151             properties_);
152     }
153 
154 private:
DescribeInternalTo(std::ostream * os)155     void DescribeInternalTo(std::ostream *os) const
156     {
157         if constexpr (sizeof...(PropertyType) == 0) {  // NOLINT(readability-braces-around-statements)
158             *os << "an empty JsonObject";
159         } else {  // NOLINT(readability-misleading-indentation)
160             *os << "a JsonObject with properties: ";
161 
162             std::apply(
163                 [os](auto firstProperty, auto... property) {
164                     firstProperty.DescribeTo(os);
165                     ((*os << ", ", property.DescribeTo(os)), ...);
166                 },
167                 properties_);
168         }
169     }
170 
171     std::tuple<JsonProperty<PropertyType>...> properties_;
172 };
173 
174 template <typename... PropertyType>
JsonProperties(JsonProperty<PropertyType>...property)175 auto JsonProperties(JsonProperty<PropertyType>... property)
176 {
177     return testing::Matcher<const JsonObject &>(new JsonObjectMatcher<PropertyType...>(property...));
178 }
179 
180 template <typename... PropertyType>
JsonElements(testing::Matcher<PropertyType>...matcher)181 auto JsonElements(testing::Matcher<PropertyType>... matcher)
182 {
183     return testing::ElementsAre(
184         Property(PropertyTypeName<PropertyType>(), &JsonObject::Value::Get<PropertyType>, Pointee(matcher))...);
185 }
186 
187 template <typename PropertyType, template <typename...> class Container, typename... Param>
JsonElementsAreArray(const Container<testing::Matcher<PropertyType>,Param...> & container)188 auto JsonElementsAreArray(const Container<testing::Matcher<PropertyType>, Param...> &container)
189 {
190     std::vector<testing::Matcher<const JsonObject::Value &>> elements;
191 
192     std::transform(container.begin(), container.end(), std::back_inserter(elements), [](auto &matcher) {
193         return Property(PropertyTypeName<PropertyType>(), &JsonObject::Value::Get<PropertyType>, Pointee(matcher));
194     });
195 
196     return ElementsAreArray(elements);
197 }
198 }  // namespace panda::tooling::inspector::test
199 
200 namespace panda {
201 inline std::ostream &operator<<(std::ostream &os, const JsonObject &object)
202 {
203     os << '{';
204 
205     for (unsigned index = 0; index < object.GetSize(); ++index) {
206         os << (index != 0 ? ", " : "") << std::quoted(object.GetKeyByIndex(index)) << ": ...";
207     }
208 
209     return os << '}';
210 }
211 }  // namespace panda
212 
213 #endif  // PANDA_TOOLING_INSPECTOR_TEST_JSON_OBJECT_MATCHER_H
214