• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/test/values_test_util.h"
6 
7 #include <ostream>
8 #include <utility>
9 
10 #include "base/files/file_util.h"
11 #include "base/json/json_reader.h"
12 #include "base/json/json_writer.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/types/optional_util.h"
16 #include "base/values.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "third_party/abseil-cpp/absl/types/optional.h"
19 
20 namespace base {
21 
ExpectDictBooleanValue(bool expected_value,const Value::Dict & dict,StringPiece path)22 void ExpectDictBooleanValue(bool expected_value,
23                             const Value::Dict& dict,
24                             StringPiece path) {
25   EXPECT_EQ(dict.FindBoolByDottedPath(path),
26             absl::make_optional(expected_value))
27       << path;
28 }
29 
ExpectDictIntegerValue(int expected_value,const Value::Dict & dict,StringPiece path)30 void ExpectDictIntegerValue(int expected_value,
31                             const Value::Dict& dict,
32                             StringPiece path) {
33   EXPECT_EQ(dict.FindIntByDottedPath(path), absl::make_optional(expected_value))
34       << path;
35 }
36 
ExpectDictStringValue(StringPiece expected_value,const Value::Dict & dict,StringPiece path)37 void ExpectDictStringValue(StringPiece expected_value,
38                            const Value::Dict& dict,
39                            StringPiece path) {
40   EXPECT_EQ(OptionalFromPtr(dict.FindStringByDottedPath(path)),
41             absl::make_optional(expected_value))
42       << path;
43 }
44 
ExpectDictValue(const Value::Dict & expected_value,const Value::Dict & dict,StringPiece path)45 void ExpectDictValue(const Value::Dict& expected_value,
46                      const Value::Dict& dict,
47                      StringPiece path) {
48   const Value* found_value = dict.FindByDottedPath(path);
49   ASSERT_TRUE(found_value) << path;
50   EXPECT_EQ(*found_value, expected_value) << path;
51 }
52 
ExpectDictValue(const Value & expected_value,const Value::Dict & dict,StringPiece path)53 void ExpectDictValue(const Value& expected_value,
54                      const Value::Dict& dict,
55                      StringPiece path) {
56   const Value* found_value = dict.FindByDottedPath(path);
57   ASSERT_TRUE(found_value) << path;
58   EXPECT_EQ(*found_value, expected_value) << path;
59 }
60 
ExpectStringValue(const std::string & expected_str,const Value & actual)61 void ExpectStringValue(const std::string& expected_str, const Value& actual) {
62   EXPECT_EQ(Value::Type::STRING, actual.type());
63   EXPECT_EQ(expected_str, actual.GetString());
64 }
65 
66 namespace test {
67 
68 namespace {
69 
FormatAsJSON(ValueView value)70 std::string FormatAsJSON(ValueView value) {
71   std::string json;
72   JSONWriter::Write(value, &json);
73   return json;
74 }
75 
76 class DictionaryHasValueMatcher
77     : public testing::MatcherInterface<const base::Value::Dict&> {
78  public:
DictionaryHasValueMatcher(const std::string & key,const base::Value & expected_value)79   DictionaryHasValueMatcher(const std::string& key,
80                             const base::Value& expected_value)
81       : key_(key), expected_value_(expected_value.Clone()) {}
82 
83   DictionaryHasValueMatcher(const DictionaryHasValueMatcher& other) = delete;
84   DictionaryHasValueMatcher& operator=(const DictionaryHasValueMatcher& other) =
85       delete;
86 
MatchAndExplain(const base::Value::Dict & value,testing::MatchResultListener * listener) const87   bool MatchAndExplain(const base::Value::Dict& value,
88                        testing::MatchResultListener* listener) const override {
89     const base::Value* sub_value = value.Find(key_);
90     if (!sub_value) {
91       *listener << "Dictionary '" << FormatAsJSON(value)
92                 << "' does not have key '" << key_ << "'";
93       return false;
94     }
95     if (*sub_value != expected_value_) {
96       *listener << "Dictionary value under key '" << key_ << "' is '"
97                 << FormatAsJSON(*sub_value) << "', expected '"
98                 << FormatAsJSON(expected_value_) << "'";
99       return false;
100     }
101     return true;
102   }
103 
DescribeTo(std::ostream * os) const104   void DescribeTo(std::ostream* os) const override {
105     *os << "has key '" << key_ << "' with value '"
106         << FormatAsJSON(expected_value_) << "'";
107   }
108 
DescribeNegationTo(std::ostream * os) const109   void DescribeNegationTo(std::ostream* os) const override {
110     *os << "does not have key '" << key_ << "' with value '"
111         << FormatAsJSON(expected_value_) << "'";
112   }
113 
114  private:
115   const std::string key_;
116   const base::Value expected_value_;
117 };
118 
119 class DictionaryHasValuesMatcher
120     : public testing::MatcherInterface<const base::Value::Dict&> {
121  public:
DictionaryHasValuesMatcher(const base::Value::Dict & template_value)122   explicit DictionaryHasValuesMatcher(const base::Value::Dict& template_value)
123       : template_value_(template_value.Clone()) {}
124 
125   DictionaryHasValuesMatcher(const DictionaryHasValuesMatcher& other) = delete;
126   DictionaryHasValuesMatcher& operator=(
127       const DictionaryHasValuesMatcher& other) = delete;
128 
MatchAndExplain(const base::Value::Dict & value,testing::MatchResultListener * listener) const129   bool MatchAndExplain(const base::Value::Dict& value,
130                        testing::MatchResultListener* listener) const override {
131     bool ok = true;
132     for (auto template_dict_item : template_value_) {
133       const base::Value* sub_value = value.Find(template_dict_item.first);
134       if (!sub_value) {
135         *listener << "\nDictionary does not have key '"
136                   << template_dict_item.first << "'";
137         ok = false;
138         continue;
139       }
140       if (*sub_value != template_dict_item.second) {
141         *listener << "\nDictionary value under key '"
142                   << template_dict_item.first << "' is '"
143                   << FormatAsJSON(*sub_value) << "', expected '"
144                   << FormatAsJSON(template_dict_item.second) << "'";
145         ok = false;
146       }
147     }
148     return ok;
149   }
150 
DescribeTo(std::ostream * os) const151   void DescribeTo(std::ostream* os) const override {
152     *os << "contains all key-values from '" << FormatAsJSON(template_value_)
153         << "'";
154   }
155 
DescribeNegationTo(std::ostream * os) const156   void DescribeNegationTo(std::ostream* os) const override {
157     *os << "does not contain key-values from '" << FormatAsJSON(template_value_)
158         << "'";
159   }
160 
161  private:
162   const base::Value::Dict template_value_;
163 };
164 
165 // Attempts to parse `json` as JSON. Returns resulting Value on success, has an
166 // EXPECT failure and returns nullopt on failure. If `expected_type` is
167 // provided, treats `json` parsing as a Value of a different type as a failure.
168 //
ParseJsonHelper(StringPiece json,absl::optional<Value::Type> expected_type)169 absl::optional<Value> ParseJsonHelper(
170     StringPiece json,
171     absl::optional<Value::Type> expected_type) {
172   auto result = JSONReader::ReadAndReturnValueWithError(
173       json, JSON_PARSE_CHROMIUM_EXTENSIONS | JSON_ALLOW_TRAILING_COMMAS);
174   if (!result.has_value()) {
175     ADD_FAILURE() << "Failed to parse \"" << json
176                   << "\": " << result.error().message;
177     return absl::nullopt;
178   }
179   if (expected_type && result->type() != *expected_type) {
180     ADD_FAILURE() << "JSON is of wrong type: " << json;
181     return absl::nullopt;
182   }
183   return std::move(*result);
184 }
185 
186 }  // namespace
187 
DictionaryHasValue(const std::string & key,const base::Value & expected_value)188 testing::Matcher<const base::Value::Dict&> DictionaryHasValue(
189     const std::string& key,
190     const base::Value& expected_value) {
191   return testing::MakeMatcher(
192       new DictionaryHasValueMatcher(key, expected_value));
193 }
194 
DictionaryHasValues(const base::Value::Dict & template_value)195 testing::Matcher<const base::Value::Dict&> DictionaryHasValues(
196     const base::Value::Dict& template_value) {
197   return testing::MakeMatcher(new DictionaryHasValuesMatcher(template_value));
198 }
199 
IsJsonMatcher(base::StringPiece json)200 IsJsonMatcher::IsJsonMatcher(base::StringPiece json)
201     : expected_value_(test::ParseJson(json)) {}
202 
IsJsonMatcher(const base::Value & value)203 IsJsonMatcher::IsJsonMatcher(const base::Value& value)
204     : expected_value_(value.Clone()) {}
205 
IsJsonMatcher(const base::Value::Dict & value)206 IsJsonMatcher::IsJsonMatcher(const base::Value::Dict& value)
207     : expected_value_(base::Value(value.Clone())) {}
208 
IsJsonMatcher(const base::Value::List & value)209 IsJsonMatcher::IsJsonMatcher(const base::Value::List& value)
210     : expected_value_(base::Value(value.Clone())) {}
211 
IsJsonMatcher(const IsJsonMatcher & other)212 IsJsonMatcher::IsJsonMatcher(const IsJsonMatcher& other)
213     : expected_value_(other.expected_value_.Clone()) {}
214 
operator =(const IsJsonMatcher & other)215 IsJsonMatcher& IsJsonMatcher::operator=(const IsJsonMatcher& other) {
216   expected_value_ = other.expected_value_.Clone();
217   return *this;
218 }
219 
220 IsJsonMatcher::~IsJsonMatcher() = default;
221 
MatchAndExplain(base::StringPiece json,testing::MatchResultListener * listener) const222 bool IsJsonMatcher::MatchAndExplain(
223     base::StringPiece json,
224     testing::MatchResultListener* listener) const {
225   // This is almost the same logic as ParseJson, but the parser uses stricter
226   // options for JSON data that is assumed to be generated by the code under
227   // test rather than written by hand as part of a unit test.
228   auto ret = JSONReader::ReadAndReturnValueWithError(json, JSON_PARSE_RFC);
229   if (!ret.has_value()) {
230     *listener << "Failed to parse \"" << json << "\": " << ret.error().message;
231     return false;
232   }
233   return MatchAndExplain(*ret, listener);
234 }
235 
MatchAndExplain(const base::Value & value,testing::MatchResultListener *) const236 bool IsJsonMatcher::MatchAndExplain(
237     const base::Value& value,
238     testing::MatchResultListener* /* listener */) const {
239   return expected_value_ == value;
240 }
241 
MatchAndExplain(const base::Value::Dict & dict,testing::MatchResultListener *) const242 bool IsJsonMatcher::MatchAndExplain(
243     const base::Value::Dict& dict,
244     testing::MatchResultListener* /* listener */) const {
245   return expected_value_.is_dict() && expected_value_.GetDict() == dict;
246 }
247 
MatchAndExplain(const base::Value::List & list,testing::MatchResultListener *) const248 bool IsJsonMatcher::MatchAndExplain(
249     const base::Value::List& list,
250     testing::MatchResultListener* /* listener */) const {
251   return expected_value_.is_list() && expected_value_.GetList() == list;
252 }
253 
DescribeTo(std::ostream * os) const254 void IsJsonMatcher::DescribeTo(std::ostream* os) const {
255   *os << "is the JSON value " << expected_value_;
256 }
257 
DescribeNegationTo(std::ostream * os) const258 void IsJsonMatcher::DescribeNegationTo(std::ostream* os) const {
259   *os << "is not the JSON value " << expected_value_;
260 }
261 
ParseJson(StringPiece json)262 Value ParseJson(StringPiece json) {
263   absl::optional<Value> result =
264       ParseJsonHelper(json, /*expected_type=*/absl::nullopt);
265   return result.has_value() ? std::move(*result) : Value();
266 }
267 
ParseJsonDict(StringPiece json)268 Value::Dict ParseJsonDict(StringPiece json) {
269   absl::optional<Value> result =
270       ParseJsonHelper(json, /*expected_type=*/Value::Type::DICT);
271   return result.has_value() ? std::move(*result).TakeDict() : Value::Dict();
272 }
273 
ParseJsonList(StringPiece json)274 Value::List ParseJsonList(StringPiece json) {
275   absl::optional<Value> result =
276       ParseJsonHelper(json, /*expected_type=*/Value::Type::LIST);
277   return result.has_value() ? std::move(*result).TakeList() : Value::List();
278 }
279 
ParseJsonDictFromFile(const FilePath & json_file_path)280 Value::Dict ParseJsonDictFromFile(const FilePath& json_file_path) {
281   std::string json;
282   if (!ReadFileToString(json_file_path, &json)) {
283     ADD_FAILURE() << "Failed to load json file for parsing. path="
284                   << json_file_path;
285     return {};
286   }
287   return ParseJsonDict(json);
288 }
289 
WriteJsonFile(const FilePath & json_file_path,ValueView root)290 expected<void, WriteJsonError> WriteJsonFile(const FilePath& json_file_path,
291                                              ValueView root) {
292   std::string json;
293   if (!JSONWriter::Write(root, &json)) {
294     return unexpected(WriteJsonError::kGenerateJsonFailure);
295   }
296   if (!WriteFile(json_file_path, json)) {
297     return unexpected(WriteJsonError::kWriteFileFailure);
298   }
299   return {};
300 }
301 
302 }  // namespace test
303 }  // namespace base
304