/** * Copyright (c) 2022-2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef PANDA_TOOLING_INSPECTOR_TEST_JSON_OBJECT_MATCHER_H #define PANDA_TOOLING_INSPECTOR_TEST_JSON_OBJECT_MATCHER_H #include #include #include #include #include #include #include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "macros.h" #include "utils/json_parser.h" // NOLINTBEGIN namespace ark::tooling::inspector::test { template constexpr const char *PropertyTypeName(); template <> constexpr const char *PropertyTypeName() { return "array"; } template <> constexpr const char *PropertyTypeName() { return "boolean"; } template <> constexpr const char *PropertyTypeName() { return "object"; } template <> constexpr const char *PropertyTypeName() { return "number"; } template <> constexpr const char *PropertyTypeName() { return "string"; } template struct JsonProperty { const char *key; testing::Matcher value_matcher; template void DescribeKeyTo(OutputStream *os) const { *os << "property " << std::quoted(key) << " of type " << PropertyTypeName(); } void DescribeTo(std::ostream *os) const { DescribeKeyTo(os); *os << " which "; value_matcher.DescribeTo(os); } }; template class HasJsonPropertyMatcher : public testing::MatcherInterface { public: explicit HasJsonPropertyMatcher(JsonProperty property) : property_(std::move(property)) {} HasJsonPropertyMatcher(const char *key, testing::Matcher value_matcher) : property_ {key, value_matcher} { } void DescribeTo(std::ostream *os) const override { *os << "has "; property_.DescribeTo(os); } void DescribeNegationTo(std::ostream *os) const override { *os << "does not have a "; property_.DescribeTo(os); } bool MatchAndExplain(const JsonObject &object, testing::MatchResultListener *os) const override { auto value_ptr = object.GetValue(property_.key); if (!value_ptr) { *os << "no "; property_.DescribeKeyTo(os); return false; } return property_.value_matcher.MatchAndExplain(*value_ptr, os); } private: JsonProperty property_; }; template class JsonObjectMatcher : public testing::MatcherInterface { public: explicit JsonObjectMatcher(JsonProperty... property) : properties_ {property...} {} void DescribeTo(std::ostream *os) const override { *os << "is "; DescribeInternalTo(os); } void DescribeNegationTo(std::ostream *os) const override { *os << "isn't "; DescribeInternalTo(os); } bool MatchAndExplain(const JsonObject &object, testing::MatchResultListener *os) const override { if (object.GetSize() != sizeof...(PropertyType)) { *os << "number of properties doesn't match"; return false; } return std::apply( [&](JsonProperty... property) { return (HasJsonPropertyMatcher(property).MatchAndExplain(object, os) && ...); }, properties_); } private: void DescribeInternalTo(std::ostream *os) const { if constexpr (sizeof...(PropertyType) == 0) { *os << "an empty JsonObject"; } else { *os << "a JsonObject with properties: "; std::apply( [os](auto first_property, auto... property) { first_property.DescribeTo(os); ((*os << ", ", property.DescribeTo(os)), ...); }, properties_); } } std::tuple...> properties_; }; template auto JsonProperties(JsonProperty... property) { return testing::Matcher(new JsonObjectMatcher(property...)); } template auto JsonElements(testing::Matcher... matcher) { return testing::ElementsAre( Property(PropertyTypeName(), &JsonObject::Value::Get, Pointee(matcher))...); } template class Container, typename... Param> auto JsonElementsAreArray(const Container, Param...> &container) { std::vector> elements; std::transform(container.begin(), container.end(), std::back_inserter(elements), [](auto &matcher) { return Property(PropertyTypeName(), &JsonObject::Value::Get, Pointee(matcher)); }); return ElementsAreArray(elements); } } // namespace ark::tooling::inspector::test namespace ark { std::ostream &operator<<(std::ostream &os, const JsonObject::Value &value); inline std::ostream &operator<<(std::ostream &os, const JsonObject &object) { os << '{'; const char *delim = ""; for (auto &[key, value] : object.GetUnorderedMap()) { os << delim << std::quoted(key) << ": " << value; delim = ", "; } return os << '}'; } inline std::ostream &operator<<(std::ostream &os, const JsonObject::ArrayT &array) { os << '['; const char *delim = ""; for (auto &value : array) { os << delim << value; delim = ", "; } return os << ']'; } } // namespace ark // NOLINTEND #endif // PANDA_TOOLING_INSPECTOR_TEST_JSON_OBJECT_MATCHER_H