// Copyright (c) 2011 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 "chrome/common/json_schema_validator_unittest_base.h"

#include <cfloat>
#include <cmath>
#include <limits>

#include "base/file_util.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
#include "base/stringprintf.h"
#include "base/values.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/json_schema_validator.h"
#include "content/common/json_value_serializer.h"

namespace {

#define TEST_SOURCE base::StringPrintf("%s:%i", __FILE__, __LINE__)

Value* LoadValue(const std::string& filename) {
  FilePath path;
  PathService::Get(chrome::DIR_TEST_DATA, &path);
  path = path.AppendASCII("json_schema_validator").AppendASCII(filename);
  EXPECT_TRUE(file_util::PathExists(path));

  std::string error_message;
  JSONFileValueSerializer serializer(path);
  Value* result = serializer.Deserialize(NULL, &error_message);
  if (!result)
    ADD_FAILURE() << "Could not parse JSON: " << error_message;
  return result;
}

Value* LoadValue(const std::string& filename, Value::ValueType type) {
  scoped_ptr<Value> result(LoadValue(filename));
  if (!result.get())
    return NULL;
  if (!result->IsType(type)) {
    ADD_FAILURE() << "Expected type " << type << ", got: " << result->GetType();
    return NULL;
  }
  return result.release();
}

ListValue* LoadList(const std::string& filename) {
  return static_cast<ListValue*>(
      LoadValue(filename, Value::TYPE_LIST));
}

DictionaryValue* LoadDictionary(const std::string& filename) {
  return static_cast<DictionaryValue*>(
      LoadValue(filename, Value::TYPE_DICTIONARY));
}

}  // namespace


JSONSchemaValidatorTestBase::JSONSchemaValidatorTestBase(
    JSONSchemaValidatorTestBase::ValidatorType type)
    : type_(type) {
}

void JSONSchemaValidatorTestBase::RunTests() {
  TestComplex();
  TestStringPattern();
  TestEnum();
  TestChoices();
  TestExtends();
  TestObject();
  TestTypeReference();
  TestArrayTuple();
  TestArrayNonTuple();
  TestString();
  TestNumber();
  TestTypeClassifier();
  TestTypes();
}

void JSONSchemaValidatorTestBase::TestComplex() {
  scoped_ptr<DictionaryValue> schema(LoadDictionary("complex_schema.json"));
  scoped_ptr<ListValue> instance(LoadList("complex_instance.json"));

  ASSERT_TRUE(schema.get());
  ASSERT_TRUE(instance.get());

  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
  instance->Remove(instance->GetSize() - 1, NULL);
  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
  instance->Append(new DictionaryValue());
  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "1",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "number", "object"));
  instance->Remove(instance->GetSize() - 1, NULL);

  DictionaryValue* item = NULL;
  ASSERT_TRUE(instance->GetDictionary(0, &item));
  item->SetString("url", "xxxxxxxxxxx");

  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL,
                 "0.url",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kStringMaxLength, "10"));
}

void JSONSchemaValidatorTestBase::TestStringPattern() {
  // Regex patterns not supported in CPP validator.
  if (type_ == CPP)
    return;

  scoped_ptr<DictionaryValue> schema(new DictionaryValue());
  schema->SetString("type", "string");
  schema->SetString("pattern", "foo+");

  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateStringValue("foo")).get(),
              schema.get(), NULL);
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateStringValue("foooooo")).get(),
              schema.get(), NULL);
  ExpectNotValid(TEST_SOURCE,
                 scoped_ptr<Value>(Value::CreateStringValue("bar")).get(),
                 schema.get(), NULL, "",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kStringPattern, "foo+"));
}

void JSONSchemaValidatorTestBase::TestEnum() {
  scoped_ptr<DictionaryValue> schema(LoadDictionary("enum_schema.json"));

  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateStringValue("foo")).get(),
              schema.get(), NULL);
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateIntegerValue(42)).get(),
              schema.get(), NULL);
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateBooleanValue(false)).get(),
              schema.get(), NULL);

  ExpectNotValid(TEST_SOURCE,
                 scoped_ptr<Value>(Value::CreateStringValue("42")).get(),
                 schema.get(), NULL, "", JSONSchemaValidator::kInvalidEnum);
  ExpectNotValid(TEST_SOURCE,
                 scoped_ptr<Value>(Value::CreateNullValue()).get(),
                 schema.get(), NULL, "", JSONSchemaValidator::kInvalidEnum);
}

void JSONSchemaValidatorTestBase::TestChoices() {
  scoped_ptr<DictionaryValue> schema(LoadDictionary("choices_schema.json"));

  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateNullValue()).get(),
              schema.get(), NULL);
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateIntegerValue(42)).get(),
              schema.get(), NULL);

  scoped_ptr<DictionaryValue> instance(new DictionaryValue());
  instance->SetString("foo", "bar");
  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);

  ExpectNotValid(TEST_SOURCE,
                 scoped_ptr<Value>(Value::CreateStringValue("foo")).get(),
                 schema.get(), NULL, "", JSONSchemaValidator::kInvalidChoice);
  ExpectNotValid(TEST_SOURCE,
                 scoped_ptr<Value>(new ListValue()).get(),
                 schema.get(), NULL, "", JSONSchemaValidator::kInvalidChoice);

  instance->SetInteger("foo", 42);
  ExpectNotValid(TEST_SOURCE, instance.get(),
                 schema.get(), NULL, "", JSONSchemaValidator::kInvalidChoice);
}

void JSONSchemaValidatorTestBase::TestExtends() {
  // TODO(aa): JS only
}

void JSONSchemaValidatorTestBase::TestObject() {
  scoped_ptr<DictionaryValue> schema(new DictionaryValue());
  schema->SetString("type", "object");
  schema->SetString("properties.foo.type", "string");
  schema->SetString("properties.bar.type", "integer");

  scoped_ptr<DictionaryValue> instance(new DictionaryValue());
  instance->SetString("foo", "foo");
  instance->SetInteger("bar", 42);

  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);

  instance->SetBoolean("extra", true);
  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL,
                 "extra", JSONSchemaValidator::kUnexpectedProperty);

  instance->Remove("extra", NULL);
  instance->Remove("bar", NULL);
  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "bar",
                 JSONSchemaValidator::kObjectPropertyIsRequired);

  instance->SetString("bar", "42");
  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "bar",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "integer", "string"));

  DictionaryValue* additional_properties = new DictionaryValue();
  additional_properties->SetString("type", "any");
  schema->Set("additionalProperties", additional_properties);

  instance->SetInteger("bar", 42);
  instance->SetBoolean("extra", true);
  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);

  instance->SetString("extra", "foo");
  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);

  additional_properties->SetString("type", "boolean");
  instance->SetBoolean("extra", true);
  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);

  instance->SetString("extra", "foo");
  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL,
                 "extra", JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "boolean", "string"));

  DictionaryValue* properties = NULL;
  DictionaryValue* bar_property = NULL;
  ASSERT_TRUE(schema->GetDictionary("properties", &properties));
  ASSERT_TRUE(properties->GetDictionary("bar", &bar_property));

  bar_property->SetBoolean("optional", true);
  instance->Remove("extra", NULL);
  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
  instance->Remove("bar", NULL);
  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
  instance->Set("bar", Value::CreateNullValue());
  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL,
                 "bar", JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "integer", "null"));
  instance->SetString("bar", "42");
  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL,
                 "bar", JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "integer", "string"));
}

void JSONSchemaValidatorTestBase::TestTypeReference() {
  scoped_ptr<ListValue> types(LoadList("reference_types.json"));
  ASSERT_TRUE(types.get());

  scoped_ptr<DictionaryValue> schema(new DictionaryValue());
  schema->SetString("type", "object");
  schema->SetString("properties.foo.type", "string");
  schema->SetString("properties.bar.$ref", "Max10Int");
  schema->SetString("properties.baz.$ref", "MinLengthString");

  scoped_ptr<DictionaryValue> schema_inline(new DictionaryValue());
  schema_inline->SetString("type", "object");
  schema_inline->SetString("properties.foo.type", "string");
  schema_inline->SetString("properties.bar.id", "NegativeInt");
  schema_inline->SetString("properties.bar.type", "integer");
  schema_inline->SetInteger("properties.bar.maximum", 0);
  schema_inline->SetString("properties.baz.$ref", "NegativeInt");

  scoped_ptr<DictionaryValue> instance(new DictionaryValue());
  instance->SetString("foo", "foo");
  instance->SetInteger("bar", 4);
  instance->SetString("baz", "ab");

  scoped_ptr<DictionaryValue> instance_inline(new DictionaryValue());
  instance_inline->SetString("foo", "foo");
  instance_inline->SetInteger("bar", -4);
  instance_inline->SetInteger("baz", -2);

  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), types.get());
  ExpectValid(TEST_SOURCE, instance_inline.get(), schema_inline.get(), NULL);

  // Validation failure, but successful schema reference.
  instance->SetString("baz", "a");
  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), types.get(),
                 "baz", JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kStringMinLength, "2"));

  instance_inline->SetInteger("bar", 20);
  ExpectNotValid(TEST_SOURCE, instance_inline.get(), schema_inline.get(), NULL,
                 "bar", JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kNumberMaximum, "0"));

  // Remove MinLengthString type.
  types->Remove(types->GetSize() - 1, NULL);
  instance->SetString("baz", "ab");
  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), types.get(),
                 "bar", JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kUnknownTypeReference,
                     "Max10Int"));

  // Remove internal type "NegativeInt".
  schema_inline->Remove("properties.bar", NULL);
  instance_inline->Remove("bar", NULL);
  ExpectNotValid(TEST_SOURCE, instance_inline.get(), schema_inline.get(), NULL,
                 "baz", JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kUnknownTypeReference,
                     "NegativeInt"));
}

void JSONSchemaValidatorTestBase::TestArrayTuple() {
  scoped_ptr<DictionaryValue> schema(LoadDictionary("array_tuple_schema.json"));
  ASSERT_TRUE(schema.get());

  scoped_ptr<ListValue> instance(new ListValue());
  instance->Append(Value::CreateStringValue("42"));
  instance->Append(Value::CreateIntegerValue(42));

  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);

  instance->Append(Value::CreateStringValue("anything"));
  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kArrayMaxItems, "2"));

  instance->Remove(1, NULL);
  instance->Remove(1, NULL);
  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "1",
                 JSONSchemaValidator::kArrayItemRequired);

  instance->Set(0, Value::CreateIntegerValue(42));
  instance->Append(Value::CreateIntegerValue(42));
  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "0",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "string", "integer"));

  DictionaryValue* additional_properties = new DictionaryValue();
  additional_properties->SetString("type", "any");
  schema->Set("additionalProperties", additional_properties);
  instance->Set(0, Value::CreateStringValue("42"));
  instance->Append(Value::CreateStringValue("anything"));
  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
  instance->Set(2, new ListValue());
  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);

  additional_properties->SetString("type", "boolean");
  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "2",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "boolean", "array"));
  instance->Set(2, Value::CreateBooleanValue(false));
  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);

  ListValue* items_schema = NULL;
  DictionaryValue* item0_schema = NULL;
  ASSERT_TRUE(schema->GetList("items", &items_schema));
  ASSERT_TRUE(items_schema->GetDictionary(0, &item0_schema));
  item0_schema->SetBoolean("optional", true);
  instance->Remove(2, NULL);
  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
  // TODO(aa): I think this is inconsistent with the handling of NULL+optional
  // for objects.
  instance->Set(0, Value::CreateNullValue());
  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
  instance->Set(0, Value::CreateIntegerValue(42));
  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "0",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "string", "integer"));
}

void JSONSchemaValidatorTestBase::TestArrayNonTuple() {
  scoped_ptr<DictionaryValue> schema(new DictionaryValue());
  schema->SetString("type", "array");
  schema->SetString("items.type", "string");
  schema->SetInteger("minItems", 2);
  schema->SetInteger("maxItems", 3);

  scoped_ptr<ListValue> instance(new ListValue());
  instance->Append(Value::CreateStringValue("x"));
  instance->Append(Value::CreateStringValue("x"));

  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
  instance->Append(Value::CreateStringValue("x"));
  ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);

  instance->Append(Value::CreateStringValue("x"));
  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kArrayMaxItems, "3"));
  instance->Remove(1, NULL);
  instance->Remove(1, NULL);
  instance->Remove(1, NULL);
  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kArrayMinItems, "2"));

  instance->Remove(1, NULL);
  instance->Append(Value::CreateIntegerValue(42));
  ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "1",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "string", "integer"));
}

void JSONSchemaValidatorTestBase::TestString() {
  scoped_ptr<DictionaryValue> schema(new DictionaryValue());
  schema->SetString("type", "string");
  schema->SetInteger("minLength", 1);
  schema->SetInteger("maxLength", 10);

  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateStringValue("x")).get(),
              schema.get(), NULL);
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateStringValue("xxxxxxxxxx")).get(),
              schema.get(), NULL);

  ExpectNotValid(TEST_SOURCE,
                 scoped_ptr<Value>(Value::CreateStringValue("")).get(),
                 schema.get(), NULL, "",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kStringMinLength, "1"));
  ExpectNotValid(
      TEST_SOURCE,
      scoped_ptr<Value>(Value::CreateStringValue("xxxxxxxxxxx")).get(),
      schema.get(), NULL, "",
      JSONSchemaValidator::FormatErrorMessage(
          JSONSchemaValidator::kStringMaxLength, "10"));

}

void JSONSchemaValidatorTestBase::TestNumber() {
  scoped_ptr<DictionaryValue> schema(new DictionaryValue());
  schema->SetString("type", "number");
  schema->SetInteger("minimum", 1);
  schema->SetInteger("maximum", 100);
  schema->SetInteger("maxDecimal", 2);

  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateIntegerValue(1)).get(),
              schema.get(), NULL);
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateIntegerValue(50)).get(),
              schema.get(), NULL);
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateIntegerValue(100)).get(),
              schema.get(), NULL);
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateDoubleValue(88.88)).get(),
              schema.get(), NULL);

  ExpectNotValid(
      TEST_SOURCE,
      scoped_ptr<Value>(Value::CreateDoubleValue(0.5)).get(),
      schema.get(), NULL, "",
      JSONSchemaValidator::FormatErrorMessage(
          JSONSchemaValidator::kNumberMinimum, "1"));
  ExpectNotValid(
      TEST_SOURCE,
      scoped_ptr<Value>(Value::CreateDoubleValue(100.1)).get(),
      schema.get(), NULL, "",
      JSONSchemaValidator::FormatErrorMessage(
          JSONSchemaValidator::kNumberMaximum, "100"));
}

void JSONSchemaValidatorTestBase::TestTypeClassifier() {
  EXPECT_EQ("boolean", JSONSchemaValidator::GetJSONSchemaType(
      scoped_ptr<Value>(Value::CreateBooleanValue(true)).get()));
  EXPECT_EQ("boolean", JSONSchemaValidator::GetJSONSchemaType(
      scoped_ptr<Value>(Value::CreateBooleanValue(false)).get()));

  // It doesn't matter whether the C++ type is 'integer' or 'real'. If the
  // number is integral and within the representable range of integers in
  // double, it's classified as 'integer'.
  EXPECT_EQ("integer", JSONSchemaValidator::GetJSONSchemaType(
      scoped_ptr<Value>(Value::CreateIntegerValue(42)).get()));
  EXPECT_EQ("integer", JSONSchemaValidator::GetJSONSchemaType(
      scoped_ptr<Value>(Value::CreateIntegerValue(0)).get()));
  EXPECT_EQ("integer", JSONSchemaValidator::GetJSONSchemaType(
      scoped_ptr<Value>(Value::CreateDoubleValue(42)).get()));
  EXPECT_EQ("integer", JSONSchemaValidator::GetJSONSchemaType(
      scoped_ptr<Value>(
          Value::CreateDoubleValue(pow(2.0, DBL_MANT_DIG))).get()));
  EXPECT_EQ("integer", JSONSchemaValidator::GetJSONSchemaType(
      scoped_ptr<Value>(
          Value::CreateDoubleValue(pow(-2.0, DBL_MANT_DIG))).get()));

  // "number" is only used for non-integral numbers, or numbers beyond what
  // double can accurately represent.
  EXPECT_EQ("number", JSONSchemaValidator::GetJSONSchemaType(
      scoped_ptr<Value>(Value::CreateDoubleValue(88.8)).get()));
  EXPECT_EQ("number", JSONSchemaValidator::GetJSONSchemaType(
      scoped_ptr<Value>(Value::CreateDoubleValue(
          pow(2.0, DBL_MANT_DIG) * 2)).get()));
  EXPECT_EQ("number", JSONSchemaValidator::GetJSONSchemaType(
      scoped_ptr<Value>(Value::CreateDoubleValue(
          pow(-2.0, DBL_MANT_DIG) * 2)).get()));

  EXPECT_EQ("string", JSONSchemaValidator::GetJSONSchemaType(
      scoped_ptr<Value>(Value::CreateStringValue("foo")).get()));
  EXPECT_EQ("array", JSONSchemaValidator::GetJSONSchemaType(
      scoped_ptr<Value>(new ListValue()).get()));
  EXPECT_EQ("object", JSONSchemaValidator::GetJSONSchemaType(
      scoped_ptr<Value>(new DictionaryValue()).get()));
  EXPECT_EQ("null", JSONSchemaValidator::GetJSONSchemaType(
      scoped_ptr<Value>(Value::CreateNullValue()).get()));
}

void JSONSchemaValidatorTestBase::TestTypes() {
  scoped_ptr<DictionaryValue> schema(new DictionaryValue());

  // valid
  schema->SetString("type", "object");
  ExpectValid(TEST_SOURCE, scoped_ptr<Value>(new DictionaryValue()).get(),
              schema.get(), NULL);

  schema->SetString("type", "array");
  ExpectValid(TEST_SOURCE, scoped_ptr<Value>(new ListValue()).get(),
              schema.get(), NULL);

  schema->SetString("type", "string");
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateStringValue("foobar")).get(),
              schema.get(), NULL);

  schema->SetString("type", "number");
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateDoubleValue(88.8)).get(),
              schema.get(), NULL);
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateDoubleValue(42)).get(),
              schema.get(), NULL);
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateIntegerValue(42)).get(),
              schema.get(), NULL);
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateIntegerValue(0)).get(),
              schema.get(), NULL);

  schema->SetString("type", "integer");
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateIntegerValue(42)).get(),
              schema.get(), NULL);
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateDoubleValue(42)).get(),
              schema.get(), NULL);
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateIntegerValue(0)).get(),
              schema.get(), NULL);
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(
                  Value::CreateDoubleValue(pow(2.0, DBL_MANT_DIG))).get(),
              schema.get(), NULL);
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(
                  Value::CreateDoubleValue(pow(-2.0, DBL_MANT_DIG))).get(),
              schema.get(), NULL);

  schema->SetString("type", "boolean");
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateBooleanValue(false)).get(),
              schema.get(), NULL);
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateBooleanValue(true)).get(),
              schema.get(), NULL);

  schema->SetString("type", "null");
  ExpectValid(TEST_SOURCE,
              scoped_ptr<Value>(Value::CreateNullValue()).get(),
              schema.get(), NULL);

  // not valid
  schema->SetString("type", "object");
  ExpectNotValid(TEST_SOURCE, scoped_ptr<Value>(new ListValue()).get(),
                 schema.get(), NULL, "",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "object", "array"));

  schema->SetString("type", "object");
  ExpectNotValid(TEST_SOURCE, scoped_ptr<Value>(Value::CreateNullValue()).get(),
                 schema.get(), NULL, "",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "object", "null"));

  schema->SetString("type", "array");
  ExpectNotValid(TEST_SOURCE,
                 scoped_ptr<Value>(Value::CreateIntegerValue(42)).get(),
                 schema.get(), NULL, "",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "array", "integer"));

  schema->SetString("type", "string");
  ExpectNotValid(TEST_SOURCE,
                 scoped_ptr<Value>(Value::CreateIntegerValue(42)).get(),
                 schema.get(), NULL, "",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "string", "integer"));

  schema->SetString("type", "number");
  ExpectNotValid(TEST_SOURCE,
                 scoped_ptr<Value>(Value::CreateStringValue("42")).get(),
                 schema.get(), NULL, "",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "number", "string"));

  schema->SetString("type", "integer");
  ExpectNotValid(TEST_SOURCE,
                 scoped_ptr<Value>(Value::CreateDoubleValue(88.8)).get(),
                 schema.get(), NULL, "",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "integer", "number"));

  schema->SetString("type", "integer");
  ExpectNotValid(TEST_SOURCE,
                 scoped_ptr<Value>(Value::CreateDoubleValue(88.8)).get(),
                 schema.get(), NULL, "",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "integer", "number"));

  schema->SetString("type", "boolean");
  ExpectNotValid(TEST_SOURCE,
                 scoped_ptr<Value>(Value::CreateIntegerValue(1)).get(),
                 schema.get(), NULL, "",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "boolean", "integer"));

  schema->SetString("type", "null");
  ExpectNotValid(TEST_SOURCE,
                 scoped_ptr<Value>(Value::CreateBooleanValue(false)).get(),
                 schema.get(), NULL, "",
                 JSONSchemaValidator::FormatErrorMessage(
                     JSONSchemaValidator::kInvalidType, "null", "boolean"));
}