| 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
|
|
|