Index: components/safe_json/json_sanitizer_unittest.cc |
diff --git a/components/safe_json/json_sanitizer_unittest.cc b/components/safe_json/json_sanitizer_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0a75ad35380e1bb63c8402acf8251808e07eabef |
--- /dev/null |
+++ b/components/safe_json/json_sanitizer_unittest.cc |
@@ -0,0 +1,184 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/bind.h" |
+#include "base/json/json_reader.h" |
+#include "base/json/json_writer.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/message_loop/message_loop.h" |
+#include "base/run_loop.h" |
+#include "base/values.h" |
+#include "components/safe_json/json_sanitizer.h" |
+#include "components/safe_json/safe_json_parser.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+#if !defined(OS_ANDROID) |
+#include "components/safe_json/testing_json_parser.h" |
+#endif |
+ |
+namespace safe_json { |
+ |
+class JsonSanitizerTest : public ::testing::Test { |
+ public: |
+ void TearDown() override { |
+ // Flush any tasks from the message loop to avoid leaks. |
+ base::RunLoop().RunUntilIdle(); |
+ } |
+ |
+ protected: |
+ void CheckSuccess(const std::string& json); |
+ void CheckError(const std::string& json); |
+ |
+ private: |
+ enum class State { |
+ // ERROR is a #define on Windows, so we prefix the values with STATE_. |
+ STATE_IDLE, |
+ STATE_SUCCESS, |
+ STATE_ERROR, |
+ }; |
+ |
+ void Sanitize(const std::string& json); |
+ |
+ void OnSuccess(const std::string& json); |
+ void OnError(const std::string& error); |
+ |
+ base::MessageLoop message_loop_; |
+ |
+#if !defined(OS_ANDROID) |
+ safe_json::TestingJsonParser::ScopedFactoryOverride factory_override_; |
+#endif |
+ |
+ std::string result_; |
+ std::string error_; |
+ State state_; |
+ |
+ scoped_ptr<base::RunLoop> run_loop_; |
+}; |
+ |
+void JsonSanitizerTest::CheckSuccess(const std::string& json) { |
+ SCOPED_TRACE(json); |
+ Sanitize(json); |
+ scoped_ptr<base::Value> parsed = base::JSONReader::Read(json); |
+ ASSERT_TRUE(parsed); |
+ EXPECT_EQ(State::STATE_SUCCESS, state_) << "Error: " << error_; |
+ |
+ // The JSON parser should accept the result. |
+ int error_code; |
+ std::string error; |
+ scoped_ptr<base::Value> reparsed = base::JSONReader::ReadAndReturnError( |
+ result_, base::JSON_PARSE_RFC, &error_code, &error); |
+ EXPECT_TRUE(reparsed) |
+ << "Invalid result: " << error; |
+ |
+ // The parsed values should be equal. |
+ EXPECT_TRUE(reparsed->Equals(parsed.get())); |
+} |
+ |
+void JsonSanitizerTest::CheckError(const std::string& json) { |
+ SCOPED_TRACE(json); |
+ Sanitize(json); |
+ EXPECT_EQ(State::STATE_ERROR, state_) << "Result: " << result_; |
+} |
+ |
+void JsonSanitizerTest::Sanitize(const std::string& json) { |
+ state_ = State::STATE_IDLE; |
+ result_.clear(); |
+ error_.clear(); |
+ run_loop_.reset(new base::RunLoop); |
+ JsonSanitizer::Sanitize( |
+ json, |
+ base::Bind(&JsonSanitizerTest::OnSuccess, base::Unretained(this)), |
+ base::Bind(&JsonSanitizerTest::OnError, base::Unretained(this))); |
+ |
+ // We should never get a result immediately. |
+ EXPECT_EQ(State::STATE_IDLE, state_); |
+ run_loop_->Run(); |
+} |
+ |
+void JsonSanitizerTest::OnSuccess(const std::string& json) { |
+ ASSERT_EQ(State::STATE_IDLE, state_); |
+ state_ = State::STATE_SUCCESS; |
+ result_ = json; |
+ run_loop_->Quit(); |
+} |
+ |
+void JsonSanitizerTest::OnError(const std::string& error) { |
+ ASSERT_EQ(State::STATE_IDLE, state_); |
+ state_ = State::STATE_ERROR; |
+ error_ = error; |
+ run_loop_->Quit(); |
+} |
+ |
+TEST_F(JsonSanitizerTest, Json) { |
+ // Valid JSON: |
+ CheckSuccess("{\n \"foo\": \"bar\"\n}"); |
+ CheckSuccess("[true]"); |
+ CheckSuccess("[42]"); |
+ CheckSuccess("[3.14]"); |
+ CheckSuccess("[4.0]"); |
+ CheckSuccess("[null]"); |
+ CheckSuccess("[\"foo\", \"bar\"]"); |
+ |
+ // JSON syntax errors: |
+ CheckError(""); |
+ CheckError("["); |
+ CheckError("null"); |
+ |
+ // Unterminated array. |
+ CheckError("[1,2,3,]"); |
+} |
+ |
+TEST_F(JsonSanitizerTest, Nesting) { |
+ // 99 nested arrays are fine. |
+ std::string nested(99u, '['); |
+ nested.append(99u, ']'); |
+ CheckSuccess(nested); |
+ |
+ // 100 nested arrays is too much. |
+ CheckError(std::string(100u, '[') + std::string(100u, ']')); |
+} |
+ |
+TEST_F(JsonSanitizerTest, Unicode) { |
+ // Non-ASCII characters encoded either directly as UTF-8 or escaped as UTF-16: |
+ CheckSuccess("[\"☃\"]"); |
+ CheckSuccess("[\"\\u2603\"]"); |
+ CheckSuccess("[\"😃\"]"); |
+ CheckSuccess("[\"\\ud83d\\ude03\"]"); |
+ |
+ // Malformed UTF-8: |
+ // A continuation byte outside of a sequence. |
+ CheckError("[\"\x80\"]"); |
+ |
+ // A start byte that is missing a continuation byte. |
+ CheckError("[\"\xc0\"]"); |
+ |
+ // An invalid byte in UTF-8. |
+ CheckError("[\"\xfe\"]"); |
+ |
+ // An overlong encoding (of the letter 'A'). |
+ CheckError("[\"\xc1\x81\"]"); |
+ |
+ // U+D83D, a code point reserved for (high) surrogates. |
+ CheckError("[\"\xed\xa0\xbd\"]"); |
+ |
+ // U+4567890, a code point outside of the valid range for Unicode. |
+ CheckError("[\"\xfc\x84\x95\xa7\xa2\x90\"]"); |
+ |
+ // Malformed escaped UTF-16: |
+ // An unmatched high surrogate. |
+ CheckError("[\"\\ud83d\"]"); |
+ |
+ // An unmatched low surrogate. |
+ CheckError("[\"\\ude03\"]"); |
+ |
+ // A low surrogate followed by a high surrogate. |
+ CheckError("[\"\\ude03\\ud83d\"]"); |
+ |
+ // Valid escaped UTF-16 that encodes non-characters: |
+ CheckError("[\"\\ufdd0\"]"); |
+ CheckError("[\"\\ufffe\"]"); |
+ CheckError("[\"\\ud83f\\udffe\"]"); |
+} |
+ |
+} // namespace safe_json |