Remove object schema type system

Since libweave no longer semantically parses command/state definitions
and validates commands and states, there is no need for this extra
code.

BUG: 25841230
Change-Id: I53dfab20db8c97b621c07fe234a7b3c6da7325bb
Reviewed-on: https://weave-review.googlesource.com/1660
Reviewed-by: Vitaly Buka <vitalybuka@google.com>
diff --git a/src/base_api_handler_unittest.cc b/src/base_api_handler_unittest.cc
index df78319..357385d 100644
--- a/src/base_api_handler_unittest.cc
+++ b/src/base_api_handler_unittest.cc
@@ -10,9 +10,9 @@
 #include <weave/provider/test/mock_config_store.h>
 #include <weave/provider/test/mock_http_client.h>
 #include <weave/test/mock_device.h>
+#include <weave/test/unittest_utils.h>
 
 #include "src/commands/command_manager.h"
-#include "src/commands/unittest_utils.h"
 #include "src/config.h"
 #include "src/device_registration_info.h"
 #include "src/states/mock_state_change_queue_interface.h"
diff --git a/src/commands/cloud_command_proxy.cc b/src/commands/cloud_command_proxy.cc
index 9ec3d3e..91db18c 100644
--- a/src/commands/cloud_command_proxy.cc
+++ b/src/commands/cloud_command_proxy.cc
@@ -9,8 +9,6 @@
 #include <weave/provider/task_runner.h>
 
 #include "src/commands/command_instance.h"
-#include "src/commands/prop_constraints.h"
-#include "src/commands/prop_types.h"
 #include "src/commands/schema_constants.h"
 #include "src/utils.h"
 
diff --git a/src/commands/cloud_command_proxy_unittest.cc b/src/commands/cloud_command_proxy_unittest.cc
index fdb22fc..a65a967 100644
--- a/src/commands/cloud_command_proxy_unittest.cc
+++ b/src/commands/cloud_command_proxy_unittest.cc
@@ -10,10 +10,10 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <weave/provider/test/fake_task_runner.h>
+#include <weave/test/unittest_utils.h>
 
 #include "src/commands/command_dictionary.h"
 #include "src/commands/command_instance.h"
-#include "src/commands/unittest_utils.h"
 #include "src/states/mock_state_change_queue_interface.h"
 
 using testing::_;
diff --git a/src/commands/command_definition_unittest.cc b/src/commands/command_definition_unittest.cc
index ecd6e54..867d48f 100644
--- a/src/commands/command_definition_unittest.cc
+++ b/src/commands/command_definition_unittest.cc
@@ -5,8 +5,7 @@
 #include "src/commands/command_definition.h"
 
 #include <gtest/gtest.h>
-
-#include "src/commands/unittest_utils.h"
+#include <weave/test/unittest_utils.h>
 
 namespace weave {
 
diff --git a/src/commands/command_dictionary_unittest.cc b/src/commands/command_dictionary_unittest.cc
index 7298363..adae4ec 100644
--- a/src/commands/command_dictionary_unittest.cc
+++ b/src/commands/command_dictionary_unittest.cc
@@ -5,8 +5,7 @@
 #include "src/commands/command_dictionary.h"
 
 #include <gtest/gtest.h>
-
-#include "src/commands/unittest_utils.h"
+#include <weave/test/unittest_utils.h>
 
 namespace weave {
 
diff --git a/src/commands/command_instance.h b/src/commands/command_instance.h
index 30ef907..e7a6eca 100644
--- a/src/commands/command_instance.h
+++ b/src/commands/command_instance.h
@@ -15,9 +15,6 @@
 #include <weave/error.h>
 #include <weave/command.h>
 
-#include "src/commands/prop_values.h"
-#include "src/commands/schema_utils.h"
-
 namespace base {
 class Value;
 }  // namespace base
diff --git a/src/commands/command_instance_unittest.cc b/src/commands/command_instance_unittest.cc
index cacb86c..1d32cd9 100644
--- a/src/commands/command_instance_unittest.cc
+++ b/src/commands/command_instance_unittest.cc
@@ -5,9 +5,9 @@
 #include "src/commands/command_instance.h"
 
 #include <gtest/gtest.h>
+#include <weave/test/unittest_utils.h>
 
 #include "src/commands/command_dictionary.h"
-#include "src/commands/unittest_utils.h"
 
 namespace weave {
 
diff --git a/src/commands/command_manager_unittest.cc b/src/commands/command_manager_unittest.cc
index 2b2b055..f0dc95d 100644
--- a/src/commands/command_manager_unittest.cc
+++ b/src/commands/command_manager_unittest.cc
@@ -9,9 +9,9 @@
 #include <base/json/json_writer.h>
 #include <gtest/gtest.h>
 #include <weave/provider/test/mock_config_store.h>
+#include <weave/test/unittest_utils.h>
 
 #include "src/bind_lambda.h"
-#include "src/commands/unittest_utils.h"
 
 using testing::Return;
 
diff --git a/src/commands/command_queue_unittest.cc b/src/commands/command_queue_unittest.cc
index 394ae20..dc7290a 100644
--- a/src/commands/command_queue_unittest.cc
+++ b/src/commands/command_queue_unittest.cc
@@ -13,7 +13,6 @@
 #include <gtest/gtest.h>
 
 #include "src/commands/command_definition.h"
-#include "src/commands/object_schema.h"
 #include "src/string_utils.h"
 
 namespace weave {
diff --git a/src/commands/object_schema.cc b/src/commands/object_schema.cc
deleted file mode 100644
index 6cfa5f4..0000000
--- a/src/commands/object_schema.cc
+++ /dev/null
@@ -1,379 +0,0 @@
-// Copyright 2015 The Weave 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 "src/commands/object_schema.h"
-
-#include <algorithm>
-#include <limits>
-
-#include <base/logging.h>
-#include <base/values.h>
-#include <weave/enum_to_string.h>
-
-#include "src/commands/prop_types.h"
-#include "src/commands/prop_values.h"
-#include "src/commands/schema_constants.h"
-#include "src/string_utils.h"
-
-namespace weave {
-
-namespace {
-
-// Helper function for to create a PropType based on type string.
-// Generates an error if the string identifies an unknown type.
-std::unique_ptr<PropType> CreatePropType(const std::string& type_name,
-                                         ErrorPtr* error) {
-  auto parts = SplitAtFirst(type_name, ".", false);
-  const std::string& primary_type = parts.first;
-  const std::string& array_type = parts.second;
-
-  std::unique_ptr<PropType> prop;
-  ValueType type;
-  if (PropType::GetTypeFromTypeString(primary_type, &type)) {
-    prop = PropType::Create(type);
-    if (prop && type == ValueType::Array && !array_type.empty()) {
-      auto items_type = CreatePropType(array_type, error);
-      if (items_type) {
-        prop->GetArray()->SetItemType(std::move(items_type));
-      } else {
-        prop.reset();
-        return prop;
-      }
-    }
-  }
-  if (!prop) {
-    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                       errors::commands::kUnknownType, "Unknown type %s",
-                       type_name.c_str());
-  }
-  return prop;
-}
-
-// Generates "no_type_info" error.
-void ErrorInvalidTypeInfo(ErrorPtr* error) {
-  Error::AddTo(error, FROM_HERE, errors::commands::kDomain,
-               errors::commands::kNoTypeInfo,
-               "Unable to determine parameter type");
-}
-
-// Helper function for PropFromJson to handle the case of parameter being
-// defined as a JSON string like this:
-//   "prop":"..."
-std::unique_ptr<PropType> PropFromJsonString(const base::Value& value,
-                                             const PropType* base_schema,
-                                             ErrorPtr* error) {
-  std::string type_name;
-  CHECK(value.GetAsString(&type_name)) << "Unable to get string value";
-  std::unique_ptr<PropType> prop = CreatePropType(type_name, error);
-  base::DictionaryValue empty;
-  if (prop && !prop->FromJson(&empty, base_schema, error))
-    prop.reset();
-
-  return prop;
-}
-
-// Detects a type based on JSON array. Inspects the first element of the array
-// to deduce the PropType from. Returns the string name of the type detected
-// or empty string is type detection failed.
-std::string DetectArrayType(const base::ListValue* list,
-                            const PropType* base_schema,
-                            bool allow_arrays) {
-  std::string type_name;
-  if (base_schema) {
-    type_name = base_schema->GetTypeAsString();
-  } else if (list->GetSize() > 0) {
-    const base::Value* first_element = nullptr;
-    if (list->Get(0, &first_element)) {
-      switch (first_element->GetType()) {
-        case base::Value::TYPE_BOOLEAN:
-          type_name = PropType::GetTypeStringFromType(ValueType::Boolean);
-          break;
-        case base::Value::TYPE_INTEGER:
-          type_name = PropType::GetTypeStringFromType(ValueType::Int);
-          break;
-        case base::Value::TYPE_DOUBLE:
-          type_name = PropType::GetTypeStringFromType(ValueType::Double);
-          break;
-        case base::Value::TYPE_STRING:
-          type_name = PropType::GetTypeStringFromType(ValueType::String);
-          break;
-        case base::Value::TYPE_DICTIONARY:
-          type_name = PropType::GetTypeStringFromType(ValueType::Object);
-          break;
-        case base::Value::TYPE_LIST: {
-          if (allow_arrays) {
-            type_name = PropType::GetTypeStringFromType(ValueType::Array);
-            const base::ListValue* first_element_list = nullptr;
-            if (first_element->GetAsList(&first_element_list)) {
-              // We do not allow arrays of arrays.
-              auto child_type =
-                  DetectArrayType(first_element_list, nullptr, false);
-              if (child_type.empty()) {
-                type_name.clear();
-              } else {
-                type_name += '.' + child_type;
-              }
-            }
-          }
-          break;
-        }
-        default:
-          // The rest are unsupported.
-          break;
-      }
-    }
-  }
-  return type_name;
-}
-
-// Helper function for PropFromJson to handle the case of parameter being
-// defined as a JSON array like this:
-//   "prop":[...]
-std::unique_ptr<PropType> PropFromJsonArray(const base::Value& value,
-                                            const PropType* base_schema,
-                                            ErrorPtr* error) {
-  std::unique_ptr<PropType> prop;
-  const base::ListValue* list = nullptr;
-  CHECK(value.GetAsList(&list)) << "Unable to get array value";
-  std::string type_name = DetectArrayType(list, base_schema, true);
-  if (type_name.empty()) {
-    ErrorInvalidTypeInfo(error);
-    return prop;
-  }
-  base::DictionaryValue array_object;
-  array_object.SetWithoutPathExpansion(commands::attributes::kOneOf_Enum,
-                                       list->DeepCopy());
-  prop = CreatePropType(type_name, error);
-  if (prop && !prop->FromJson(&array_object, base_schema, error))
-    prop.reset();
-
-  return prop;
-}
-
-// Detects a type based on JSON object definition of type. Looks at various
-// members such as minimum/maximum constraints, default and enum values to
-// try to deduce the underlying type of the element. Returns the string name of
-// the type detected or empty string is type detection failed.
-std::string DetectObjectType(const base::DictionaryValue* dict,
-                             const PropType* base_schema) {
-  bool has_min_max = dict->HasKey(commands::attributes::kNumeric_Min) ||
-                     dict->HasKey(commands::attributes::kNumeric_Max);
-
-  // Here we are trying to "detect the type and read in the object based on
-  // the deduced type". Later, we'll verify that this detected type matches
-  // the expectation of the base schema, if applicable, to make sure we are not
-  // changing the expected type. This makes the vendor-side (re)definition of
-  // standard and custom commands behave exactly the same.
-  // The only problem with this approach was the double-vs-int types.
-  // If the type is meant to be a double we want to allow its definition as
-  // "min:0, max:0" instead of just forcing it to be only "min:0.0, max:0.0".
-  // If we have "minimum" or "maximum", and we have a Double schema object,
-  // treat this object as a Double (even if both min and max are integers).
-  if (has_min_max && base_schema && base_schema->GetType() == ValueType::Double)
-    return PropType::GetTypeStringFromType(ValueType::Double);
-
-  // If we have at least one "minimum" or "maximum" that is Double,
-  // it's a Double.
-  const base::Value* value = nullptr;
-  if (dict->Get(commands::attributes::kNumeric_Min, &value) &&
-      value->IsType(base::Value::TYPE_DOUBLE))
-    return PropType::GetTypeStringFromType(ValueType::Double);
-  if (dict->Get(commands::attributes::kNumeric_Max, &value) &&
-      value->IsType(base::Value::TYPE_DOUBLE))
-    return PropType::GetTypeStringFromType(ValueType::Double);
-
-  // If we have "minimum" or "maximum", it's an Integer.
-  if (has_min_max)
-    return PropType::GetTypeStringFromType(ValueType::Int);
-
-  // If we have "minLength" or "maxLength", it's a String.
-  if (dict->HasKey(commands::attributes::kString_MinLength) ||
-      dict->HasKey(commands::attributes::kString_MaxLength))
-    return PropType::GetTypeStringFromType(ValueType::String);
-
-  // If we have "properties", it's an object.
-  if (dict->HasKey(commands::attributes::kObject_Properties))
-    return PropType::GetTypeStringFromType(ValueType::Object);
-
-  // If we have "items", it's an array.
-  if (dict->HasKey(commands::attributes::kItems))
-    return PropType::GetTypeStringFromType(ValueType::Array);
-
-  // If we have "enum", it's an array. Detect type from array elements.
-  const base::ListValue* list = nullptr;
-  if (dict->GetListWithoutPathExpansion(commands::attributes::kOneOf_Enum,
-                                        &list))
-    return DetectArrayType(list, base_schema, true);
-
-  // If we have "default", try to use it for type detection.
-  if (dict->Get(commands::attributes::kDefault, &value)) {
-    if (value->IsType(base::Value::TYPE_DOUBLE))
-      return PropType::GetTypeStringFromType(ValueType::Double);
-    if (value->IsType(base::Value::TYPE_INTEGER))
-      return PropType::GetTypeStringFromType(ValueType::Int);
-    if (value->IsType(base::Value::TYPE_BOOLEAN))
-      return PropType::GetTypeStringFromType(ValueType::Boolean);
-    if (value->IsType(base::Value::TYPE_STRING))
-      return PropType::GetTypeStringFromType(ValueType::String);
-    if (value->IsType(base::Value::TYPE_LIST)) {
-      CHECK(value->GetAsList(&list)) << "List value expected";
-      std::string child_type = DetectArrayType(list, base_schema, false);
-      if (!child_type.empty()) {
-        return PropType::GetTypeStringFromType(ValueType::Array) + '.' +
-               child_type;
-      }
-    }
-  }
-
-  return std::string{};
-}
-
-// Helper function for PropFromJson to handle the case of parameter being
-// defined as a JSON object like this:
-//   "prop":{...}
-std::unique_ptr<PropType> PropFromJsonObject(const base::Value& value,
-                                             const PropType* base_schema,
-                                             ErrorPtr* error) {
-  std::unique_ptr<PropType> prop;
-  const base::DictionaryValue* dict = nullptr;
-  CHECK(value.GetAsDictionary(&dict)) << "Unable to get dictionary value";
-  std::string type_name;
-  if (dict->HasKey(commands::attributes::kType)) {
-    if (!dict->GetString(commands::attributes::kType, &type_name)) {
-      ErrorInvalidTypeInfo(error);
-      return prop;
-    }
-  } else {
-    type_name = DetectObjectType(dict, base_schema);
-  }
-  if (type_name.empty()) {
-    if (!base_schema) {
-      ErrorInvalidTypeInfo(error);
-      return prop;
-    }
-    type_name = base_schema->GetTypeAsString();
-  }
-  prop = CreatePropType(type_name, error);
-  if (prop && !prop->FromJson(dict, base_schema, error))
-    prop.reset();
-
-  return prop;
-}
-
-const EnumToStringMap<base::Value::Type>::Map kMap[] = {
-    {base::Value::TYPE_NULL, "Null"},
-    {base::Value::TYPE_BOOLEAN, "Boolean"},
-    {base::Value::TYPE_INTEGER, "Integer"},
-    {base::Value::TYPE_DOUBLE, "Double"},
-    {base::Value::TYPE_STRING, "String"},
-    {base::Value::TYPE_BINARY, "Binary"},
-    {base::Value::TYPE_DICTIONARY, "Object"},
-    {base::Value::TYPE_LIST, "Array"},
-};
-
-}  // anonymous namespace
-
-template <>
-EnumToStringMap<base::Value::Type>::EnumToStringMap() : EnumToStringMap(kMap) {}
-
-ObjectSchema::ObjectSchema() {}
-ObjectSchema::~ObjectSchema() {}
-
-std::unique_ptr<ObjectSchema> ObjectSchema::Clone() const {
-  std::unique_ptr<ObjectSchema> cloned{new ObjectSchema};
-  for (const auto& pair : properties_) {
-    cloned->properties_.insert(
-        std::make_pair(pair.first, pair.second->Clone()));
-  }
-  cloned->extra_properties_allowed_ = extra_properties_allowed_;
-  return cloned;
-}
-
-void ObjectSchema::AddProp(const std::string& name,
-                           std::unique_ptr<PropType> prop) {
-  // Not using emplace() here to make sure we override existing properties.
-  properties_[name] = std::move(prop);
-}
-
-const PropType* ObjectSchema::GetProp(const std::string& name) const {
-  auto p = properties_.find(name);
-  return p != properties_.end() ? p->second.get() : nullptr;
-}
-
-bool ObjectSchema::MarkPropRequired(const std::string& name, ErrorPtr* error) {
-  auto p = properties_.find(name);
-  if (p == properties_.end()) {
-    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                       errors::commands::kUnknownProperty,
-                       "Unknown property '%s'", name.c_str());
-    return false;
-  }
-  p->second->MakeRequired(true);
-  return true;
-}
-
-std::unique_ptr<base::DictionaryValue> ObjectSchema::ToJson(
-    bool full_schema,
-    bool in_command_def) const {
-  std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
-  for (const auto& pair : properties_) {
-    auto prop_def = pair.second->ToJson(full_schema, in_command_def);
-    CHECK(prop_def);
-    value->SetWithoutPathExpansion(pair.first, prop_def.release());
-  }
-  return value;
-}
-
-bool ObjectSchema::FromJson(const base::DictionaryValue* value,
-                            const ObjectSchema* object_schema,
-                            ErrorPtr* error) {
-  Properties properties;
-  base::DictionaryValue::Iterator iter(*value);
-  while (!iter.IsAtEnd()) {
-    std::string name = iter.key();
-    const PropType* base_schema =
-        object_schema ? object_schema->GetProp(iter.key()) : nullptr;
-    auto prop_type = PropFromJson(iter.value(), base_schema, error);
-    if (prop_type) {
-      properties.insert(std::make_pair(iter.key(), std::move(prop_type)));
-    } else {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                         errors::commands::kInvalidPropDef,
-                         "Error in definition of property '%s'",
-                         iter.key().c_str());
-      return false;
-    }
-    iter.Advance();
-  }
-  properties_ = std::move(properties);
-  return true;
-}
-
-std::unique_ptr<PropType> ObjectSchema::PropFromJson(
-    const base::Value& value,
-    const PropType* base_schema,
-    ErrorPtr* error) {
-  if (value.IsType(base::Value::TYPE_STRING)) {
-    // A string value is a short-hand object specification and provides
-    // the parameter type.
-    return PropFromJsonString(value, base_schema, error);
-  } else if (value.IsType(base::Value::TYPE_LIST)) {
-    // One of the enumerated types.
-    return PropFromJsonArray(value, base_schema, error);
-  } else if (value.IsType(base::Value::TYPE_DICTIONARY)) {
-    // Full parameter definition.
-    return PropFromJsonObject(value, base_schema, error);
-  }
-  Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                     errors::commands::kUnknownType,
-                     "Unexpected JSON value type: %s",
-                     EnumToString(value.GetType()).c_str());
-  return nullptr;
-}
-
-std::unique_ptr<ObjectSchema> ObjectSchema::Create() {
-  return std::unique_ptr<ObjectSchema>{new ObjectSchema};
-}
-
-}  // namespace weave
diff --git a/src/commands/object_schema.h b/src/commands/object_schema.h
deleted file mode 100644
index 504e1dd..0000000
--- a/src/commands/object_schema.h
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2015 The Weave Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef LIBWEAVE_SRC_COMMANDS_OBJECT_SCHEMA_H_
-#define LIBWEAVE_SRC_COMMANDS_OBJECT_SCHEMA_H_
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include <weave/error.h>
-
-namespace base {
-class Value;
-class DictionaryValue;
-}  // namespace base
-
-namespace weave {
-
-class PropType;
-
-// ObjectSchema is a class representing an object definition in GCD command
-// schema. This could represent a GCD command definition, but also it can be
-// used when defining custom object types for command properties such as
-// output media type (paper) for print command. The schema definition for
-// these type of object description is the same.
-class ObjectSchema final {
- public:
-  // Do not inline the constructor/destructor to allow forward-declared type
-  // PropType to be part of |properties_| member.
-  ObjectSchema();
-  ~ObjectSchema();
-
-  // Properties is a string-to-PropType map representing a list of
-  // properties defined for a command/object. The key is the parameter
-  // name and the value is the parameter type definition object.
-  using Properties = std::map<std::string, std::unique_ptr<PropType>>;
-
-  // Makes a full copy of this object.
-  virtual std::unique_ptr<ObjectSchema> Clone() const;
-
-  // Add a new parameter definition.
-  void AddProp(const std::string& name, std::unique_ptr<PropType> prop);
-
-  // Finds parameter type definition by name. Returns nullptr if not found.
-  const PropType* GetProp(const std::string& name) const;
-
-  // Gets the list of all the properties defined.
-  const Properties& GetProps() const { return properties_; }
-
-  // Marks the property with given name as "required". If |name| specifies
-  // an unknown property, false is returned and |error| is set with detailed
-  // error message for the failure.
-  bool MarkPropRequired(const std::string& name, ErrorPtr* error);
-
-  // Specify whether extra properties are allowed on objects described by
-  // this schema. When validating a value of an object type, we can
-  // make sure that the value has only the properties explicitly defined by
-  // the schema and no other (custom) properties are allowed.
-  // This is to support JSON Schema's "additionalProperties" specification.
-  bool GetExtraPropertiesAllowed() const { return extra_properties_allowed_; }
-  void SetExtraPropertiesAllowed(bool allowed) {
-    extra_properties_allowed_ = allowed;
-  }
-
-  // Saves the object schema to JSON. When |full_schema| is set to true,
-  // then all properties and constraints are saved, otherwise, only
-  // the overridden (not inherited) ones are saved.
-  std::unique_ptr<base::DictionaryValue> ToJson(bool full_schema,
-                                                bool in_command_def) const;
-
-  // Loads the object schema from JSON. If |object_schema| is not nullptr, it is
-  // used as a base schema to inherit omitted properties and constraints from.
-  bool FromJson(const base::DictionaryValue* value,
-                const ObjectSchema* object_schema,
-                ErrorPtr* error);
-
-  // Helper factory method to create a new instance of ObjectSchema object.
-  static std::unique_ptr<ObjectSchema> Create();
-
-  // Helper method to load property type definitions from JSON.
-  static std::unique_ptr<PropType> PropFromJson(const base::Value& value,
-                                                const PropType* base_schema,
-                                                ErrorPtr* error);
-
- private:
-  // Internal parameter type definition map.
-  Properties properties_;
-  bool extra_properties_allowed_{false};
-};
-
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_COMMANDS_OBJECT_SCHEMA_H_
diff --git a/src/commands/object_schema_unittest.cc b/src/commands/object_schema_unittest.cc
deleted file mode 100644
index 7cdc16d..0000000
--- a/src/commands/object_schema_unittest.cc
+++ /dev/null
@@ -1,1697 +0,0 @@
-// Copyright 2015 The Weave 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 "src/commands/object_schema.h"
-
-#include <algorithm>
-#include <limits>
-#include <memory>
-#include <vector>
-
-#include <base/json/json_reader.h>
-#include <base/json/json_writer.h>
-#include <base/values.h>
-#include <gtest/gtest.h>
-
-#include "src/commands/prop_constraints.h"
-#include "src/commands/prop_types.h"
-#include "src/commands/schema_constants.h"
-#include "src/commands/unittest_utils.h"
-
-namespace weave {
-
-using test::CreateValue;
-using test::CreateDictionaryValue;
-
-namespace {
-
-template <typename T>
-std::vector<T> GetArrayValues(const ValueVector& arr) {
-  std::vector<T> values;
-  values.reserve(arr.size());
-  for (const auto& prop_value : arr) {
-    const auto& value = static_cast<const TypedValueBase<T>&>(*prop_value);
-    values.push_back(value.GetValue());
-  }
-  return values;
-}
-
-template <typename T>
-std::vector<T> GetOneOfValues(const PropType* prop_type) {
-  auto one_of = static_cast<const ConstraintOneOf*>(
-      prop_type->GetConstraint(ConstraintType::OneOf));
-  if (!one_of)
-    return {};
-
-  return GetArrayValues<T>(one_of->set_.value);
-}
-
-bool ValidateValue(const PropType& type,
-                   const base::Value& value,
-                   ErrorPtr* error) {
-  std::unique_ptr<PropValue> val = type.CreatePropValue(value, error);
-  return val != nullptr;
-}
-
-}  // anonymous namespace
-
-TEST(CommandSchema, IntPropType_Empty) {
-  IntPropType prop;
-  EXPECT_TRUE(prop.GetConstraints().empty());
-  EXPECT_FALSE(prop.HasOverriddenAttributes());
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  EXPECT_EQ(nullptr, prop.GetDefaultValue());
-}
-
-TEST(CommandSchema, IntPropType_Types) {
-  IntPropType prop;
-  EXPECT_EQ(nullptr, prop.GetBoolean());
-  EXPECT_EQ(&prop, prop.GetInt());
-  EXPECT_EQ(nullptr, prop.GetDouble());
-  EXPECT_EQ(nullptr, prop.GetString());
-  EXPECT_EQ(nullptr, prop.GetObject());
-  EXPECT_EQ(nullptr, prop.GetArray());
-}
-
-TEST(CommandSchema, IntPropType_ToJson) {
-  IntPropType prop;
-  EXPECT_JSON_EQ("'integer'", *prop.ToJson(false, false));
-  EXPECT_JSON_EQ("{'type':'integer'}", *prop.ToJson(true, false));
-  IntPropType param2;
-  param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_JSON_EQ("{}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'minimum':3}").get(), &prop, nullptr);
-  EXPECT_JSON_EQ("{'minimum':3}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'maximum':-7}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("{'maximum':-7}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'minimum':0,'maximum':5}").get(),
-                  &prop, nullptr);
-  EXPECT_JSON_EQ("{'maximum':5,'minimum':0}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'enum':[1,2,3]}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("[1,2,3]", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'default':123}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("{'default':123}", *param2.ToJson(false, false));
-}
-
-TEST(CommandSchema, IntPropType_FromJson) {
-  IntPropType prop;
-  prop.AddMinMaxConstraint(2, 8);
-  IntPropType param2;
-  param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_FALSE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_EQ(2, prop.GetMinValue());
-  EXPECT_EQ(8, prop.GetMaxValue());
-  prop.AddMinMaxConstraint(-2, 30);
-  param2.FromJson(CreateDictionaryValue("{'minimum':7}").get(), &prop, nullptr);
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_EQ(7, param2.GetMinValue());
-  EXPECT_EQ(30, param2.GetMaxValue());
-  param2.FromJson(CreateDictionaryValue("{'maximum':17}").get(), &prop,
-                  nullptr);
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_EQ(-2, param2.GetMinValue());
-  EXPECT_EQ(17, param2.GetMaxValue());
-
-  ASSERT_TRUE(param2.FromJson(CreateDictionaryValue("{'default':3}").get(),
-                              &prop, nullptr));
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  ASSERT_NE(nullptr, param2.GetDefaultValue());
-  EXPECT_EQ(3, param2.GetDefaultValue()->GetInt()->GetValue());
-}
-
-TEST(CommandSchema, IntPropType_Validate) {
-  IntPropType prop;
-  prop.AddMinMaxConstraint(2, 4);
-  ErrorPtr error;
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("-1"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("0"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("1"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("2"), &error));
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("3"), &error));
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("4"), &error));
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("5"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("true"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("3.0"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'3'"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-}
-
-TEST(CommandSchema, IntPropType_CreateValue) {
-  IntPropType prop;
-  ErrorPtr error;
-  auto val = prop.CreateValue(base::FundamentalValue{2}, &error);
-  ASSERT_NE(nullptr, val.get());
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_EQ(2, val->GetValue());
-
-  val = prop.CreateValue(base::StringValue{"blah"}, &error);
-  EXPECT_EQ(nullptr, val.get());
-  ASSERT_NE(nullptr, error.get());
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-TEST(CommandSchema, BoolPropType_Empty) {
-  BooleanPropType prop;
-  EXPECT_TRUE(prop.GetConstraints().empty());
-  EXPECT_FALSE(prop.HasOverriddenAttributes());
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  EXPECT_EQ(nullptr, prop.GetDefaultValue());
-  EXPECT_FALSE(prop.IsRequired());
-}
-
-TEST(CommandSchema, BoolPropType_Types) {
-  BooleanPropType prop;
-  EXPECT_EQ(nullptr, prop.GetInt());
-  EXPECT_EQ(&prop, prop.GetBoolean());
-  EXPECT_EQ(nullptr, prop.GetDouble());
-  EXPECT_EQ(nullptr, prop.GetString());
-  EXPECT_EQ(nullptr, prop.GetObject());
-  EXPECT_EQ(nullptr, prop.GetArray());
-}
-
-TEST(CommandSchema, BoolPropType_ToJson) {
-  BooleanPropType prop;
-  EXPECT_JSON_EQ("'boolean'", *prop.ToJson(false, false));
-  EXPECT_JSON_EQ("{'type':'boolean'}", *prop.ToJson(true, false));
-  BooleanPropType param2;
-  param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_JSON_EQ("{}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'enum':[true,false]}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("[true,false]", *param2.ToJson(false, false));
-  EXPECT_JSON_EQ("{'enum':[true,false],'type':'boolean'}",
-                 *param2.ToJson(true, false));
-  param2.FromJson(CreateDictionaryValue("{'default':true}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("{'default':true}", *param2.ToJson(false, false));
-}
-
-TEST(CommandSchema, BoolPropType_FromJson) {
-  BooleanPropType prop;
-  prop.FromJson(CreateDictionaryValue("{'enum':[true]}").get(), &prop, nullptr);
-  BooleanPropType param2;
-  param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_FALSE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_EQ(std::vector<bool>{true}, GetOneOfValues<bool>(&prop));
-
-  BooleanPropType prop_base;
-  BooleanPropType param3;
-  ASSERT_TRUE(param3.FromJson(CreateDictionaryValue("{'default':false}").get(),
-                              &prop_base, nullptr));
-  EXPECT_TRUE(param3.HasOverriddenAttributes());
-  ASSERT_NE(nullptr, param3.GetDefaultValue());
-  EXPECT_FALSE(param3.GetDefaultValue()->GetBoolean()->GetValue());
-}
-
-TEST(CommandSchema, BoolPropType_Validate) {
-  BooleanPropType prop;
-  prop.FromJson(CreateDictionaryValue("{'enum':[true]}").get(), &prop, nullptr);
-  ErrorPtr error;
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("false"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("true"), &error));
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("1"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("3.0"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'3'"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-}
-
-TEST(CommandSchema, BoolPropType_CreateValue) {
-  BooleanPropType prop;
-  ErrorPtr error;
-  auto val = prop.CreateValue(base::FundamentalValue{true}, &error);
-  ASSERT_NE(nullptr, val.get());
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_TRUE(val->GetValue());
-
-  val = prop.CreateValue(base::StringValue{"blah"}, &error);
-  EXPECT_EQ(nullptr, val.get());
-  ASSERT_NE(nullptr, error.get());
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-TEST(CommandSchema, DoublePropType_Empty) {
-  DoublePropType prop;
-  EXPECT_DOUBLE_EQ(std::numeric_limits<double>::lowest(), prop.GetMinValue());
-  EXPECT_DOUBLE_EQ((std::numeric_limits<double>::max)(), prop.GetMaxValue());
-  EXPECT_FALSE(prop.HasOverriddenAttributes());
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  EXPECT_EQ(nullptr, prop.GetDefaultValue());
-  EXPECT_FALSE(prop.IsRequired());
-}
-
-TEST(CommandSchema, DoublePropType_Types) {
-  DoublePropType prop;
-  EXPECT_EQ(nullptr, prop.GetInt());
-  EXPECT_EQ(nullptr, prop.GetBoolean());
-  EXPECT_EQ(&prop, prop.GetDouble());
-  EXPECT_EQ(nullptr, prop.GetString());
-  EXPECT_EQ(nullptr, prop.GetObject());
-  EXPECT_EQ(nullptr, prop.GetArray());
-}
-
-TEST(CommandSchema, DoublePropType_ToJson) {
-  DoublePropType prop;
-  EXPECT_JSON_EQ("'number'", *prop.ToJson(false, false));
-  EXPECT_JSON_EQ("{'type':'number'}", *prop.ToJson(true, false));
-  DoublePropType param2;
-  param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_JSON_EQ("{}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'minimum':3}").get(), &prop, nullptr);
-  EXPECT_JSON_EQ("{'minimum':3.0}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'maximum':-7}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("{'maximum':-7.0}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'minimum':0,'maximum':5}").get(),
-                  &prop, nullptr);
-  EXPECT_JSON_EQ("{'maximum':5.0,'minimum':0.0}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'default':12.3}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("{'default':12.3}", *param2.ToJson(false, false));
-}
-
-TEST(CommandSchema, DoublePropType_FromJson) {
-  DoublePropType prop;
-  prop.AddMinMaxConstraint(2.5, 8.7);
-  DoublePropType param2;
-  param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_FALSE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_DOUBLE_EQ(2.5, prop.GetMinValue());
-  EXPECT_DOUBLE_EQ(8.7, prop.GetMaxValue());
-  prop.AddMinMaxConstraint(-2.2, 30.4);
-  param2.FromJson(CreateDictionaryValue("{'minimum':7}").get(), &prop, nullptr);
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_DOUBLE_EQ(7.0, param2.GetMinValue());
-  EXPECT_DOUBLE_EQ(30.4, param2.GetMaxValue());
-  param2.FromJson(CreateDictionaryValue("{'maximum':17.2}").get(), &prop,
-                  nullptr);
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_DOUBLE_EQ(-2.2, param2.GetMinValue());
-  EXPECT_DOUBLE_EQ(17.2, param2.GetMaxValue());
-  param2.FromJson(CreateDictionaryValue("{'minimum':0,'maximum':6.1}").get(),
-                  &prop, nullptr);
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_DOUBLE_EQ(0.0, param2.GetMinValue());
-  EXPECT_DOUBLE_EQ(6.1, param2.GetMaxValue());
-
-  ASSERT_TRUE(param2.FromJson(CreateDictionaryValue("{'default':-1.234}").get(),
-                              &prop, nullptr));
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  ASSERT_NE(nullptr, param2.GetDefaultValue());
-  EXPECT_DOUBLE_EQ(-1.234, param2.GetDefaultValue()->GetDouble()->GetValue());
-}
-
-TEST(CommandSchema, DoublePropType_Validate) {
-  DoublePropType prop;
-  prop.AddMinMaxConstraint(-1.2, 1.3);
-  ErrorPtr error;
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("-2"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("-1.3"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("-1.2"), &error));
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("0.0"), &error));
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("1.3"), &error));
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("1.31"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("true"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'0.0'"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-}
-
-TEST(CommandSchema, DoublePropType_CreateValue) {
-  DoublePropType prop;
-  ErrorPtr error;
-  auto val = prop.CreateValue(base::FundamentalValue{2.0}, &error);
-  ASSERT_NE(nullptr, val.get());
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_DOUBLE_EQ(2.0, val->GetValue());
-
-  val = prop.CreateValue(base::StringValue{"blah"}, &error);
-  EXPECT_EQ(nullptr, val.get());
-  ASSERT_NE(nullptr, error.get());
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-TEST(CommandSchema, StringPropType_Empty) {
-  StringPropType prop;
-  EXPECT_EQ(0, prop.GetMinLength());
-  EXPECT_EQ((std::numeric_limits<int>::max)(), prop.GetMaxLength());
-  EXPECT_FALSE(prop.HasOverriddenAttributes());
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  EXPECT_EQ(nullptr, prop.GetDefaultValue());
-  EXPECT_FALSE(prop.IsRequired());
-}
-
-TEST(CommandSchema, StringPropType_Types) {
-  StringPropType prop;
-  EXPECT_EQ(nullptr, prop.GetInt());
-  EXPECT_EQ(nullptr, prop.GetBoolean());
-  EXPECT_EQ(nullptr, prop.GetDouble());
-  EXPECT_EQ(&prop, prop.GetString());
-  EXPECT_EQ(nullptr, prop.GetObject());
-  EXPECT_EQ(nullptr, prop.GetArray());
-}
-
-TEST(CommandSchema, StringPropType_ToJson) {
-  StringPropType prop;
-  EXPECT_JSON_EQ("'string'", *prop.ToJson(false, false));
-  EXPECT_JSON_EQ("{'type':'string'}", *prop.ToJson(true, false));
-  StringPropType param2;
-  param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_JSON_EQ("{}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'minLength':3}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("{'minLength':3}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'maxLength':7}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("{'maxLength':7}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'minLength':0,'maxLength':5}").get(),
-                  &prop, nullptr);
-  EXPECT_JSON_EQ("{'maxLength':5,'minLength':0}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'default':'abcd'}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("{'default':'abcd'}", *param2.ToJson(false, false));
-}
-
-TEST(CommandSchema, StringPropType_FromJson) {
-  StringPropType prop;
-  prop.AddLengthConstraint(2, 8);
-  StringPropType param2;
-  param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_FALSE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_EQ(2, prop.GetMinLength());
-  EXPECT_EQ(8, prop.GetMaxLength());
-  prop.AddLengthConstraint(3, 5);
-  param2.FromJson(CreateDictionaryValue("{'minLength':4}").get(), &prop,
-                  nullptr);
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_EQ(4, param2.GetMinLength());
-  EXPECT_EQ(5, param2.GetMaxLength());
-  param2.FromJson(CreateDictionaryValue("{'maxLength':8}").get(), &prop,
-                  nullptr);
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_EQ(3, param2.GetMinLength());
-  EXPECT_EQ(8, param2.GetMaxLength());
-  param2.FromJson(CreateDictionaryValue("{'minLength':1,'maxLength':7}").get(),
-                  &prop, nullptr);
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_EQ(1, param2.GetMinLength());
-  EXPECT_EQ(7, param2.GetMaxLength());
-
-  ASSERT_TRUE(param2.FromJson(CreateDictionaryValue("{'default':'foo'}").get(),
-                              &prop, nullptr));
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  ASSERT_NE(nullptr, param2.GetDefaultValue());
-  EXPECT_EQ("foo", param2.GetDefaultValue()->GetString()->GetValue());
-}
-
-TEST(CommandSchema, StringPropType_Validate) {
-  StringPropType prop;
-  prop.AddLengthConstraint(1, 3);
-  ErrorPtr error;
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("''"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  prop.AddLengthConstraint(2, 3);
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("''"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'a'"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("'ab'"), &error));
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("'abc'"), &error));
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'abcd'"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-
-  prop.FromJson(CreateDictionaryValue("{'enum':['abc','def','xyz!!']}").get(),
-                nullptr, &error);
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("'abc'"), &error));
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("'def'"), &error));
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("'xyz!!'"), &error));
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'xyz'"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchema, StringPropType_CreateValue) {
-  StringPropType prop;
-  ErrorPtr error;
-  auto val = prop.CreateValue(base::StringValue{"blah"}, &error);
-  ASSERT_NE(nullptr, val.get());
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_EQ("blah", val->GetValue());
-
-  val = prop.CreateValue(base::FundamentalValue{4}, &error);
-  EXPECT_EQ(nullptr, val.get());
-  ASSERT_NE(nullptr, error.get());
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-TEST(CommandSchema, ObjectPropType_Empty) {
-  ObjectPropType prop;
-  EXPECT_TRUE(prop.HasOverriddenAttributes());
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  EXPECT_EQ(nullptr, prop.GetDefaultValue());
-  EXPECT_FALSE(prop.IsRequired());
-}
-
-TEST(CommandSchema, ObjectPropType_Types) {
-  ObjectPropType prop;
-  EXPECT_EQ(nullptr, prop.GetInt());
-  EXPECT_EQ(nullptr, prop.GetBoolean());
-  EXPECT_EQ(nullptr, prop.GetDouble());
-  EXPECT_EQ(nullptr, prop.GetString());
-  EXPECT_EQ(&prop, prop.GetObject());
-  EXPECT_EQ(nullptr, prop.GetArray());
-}
-
-TEST(CommandSchema, ObjectPropType_ToJson) {
-  ObjectPropType prop;
-  EXPECT_JSON_EQ("{'additionalProperties':false,'properties':{}}",
-                 *prop.ToJson(false, false));
-  EXPECT_JSON_EQ(
-      "{'additionalProperties':false,'properties':{},'type':'object'}",
-      *prop.ToJson(true, false));
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  ObjectPropType prop2;
-  prop2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_JSON_EQ("{}", *prop2.ToJson(false, false));
-  EXPECT_TRUE(prop2.IsBasedOnSchema());
-
-  auto schema = ObjectSchema::Create();
-  schema->AddProp("expires", PropType::Create(ValueType::Int));
-  auto pw = PropType::Create(ValueType::String);
-  pw->GetString()->AddLengthConstraint(6, 100);
-  schema->AddProp("password", std::move(pw));
-  prop2.SetObjectSchema(std::move(schema));
-  auto expected = R"({
-    'additionalProperties': false,
-    'properties': {
-      'expires': 'integer',
-      'password': {
-        'maxLength': 100,
-        'minLength': 6
-      }
-    }
-  })";
-  EXPECT_JSON_EQ(expected, *prop2.ToJson(false, false));
-
-  expected = R"({
-    'additionalProperties': false,
-    'properties': {
-      'expires': {
-        'type': 'integer'
-      },
-      'password': {
-        'maxLength': 100,
-        'minLength': 6,
-        'type': 'string'
-      }
-    },
-    'type': 'object'
-  })";
-  EXPECT_JSON_EQ(expected, *prop2.ToJson(true, false));
-
-  ObjectPropType prop3;
-  ASSERT_TRUE(
-      prop3.FromJson(CreateDictionaryValue(
-                         "{'default':{'expires':3,'password':'abracadabra'}}")
-                         .get(),
-                     &prop2, nullptr));
-  expected = R"({
-    'default': {
-      'expires': 3,
-      'password': 'abracadabra'
-    }
-  })";
-  EXPECT_JSON_EQ(expected, *prop3.ToJson(false, false));
-
-  expected = R"({
-    'additionalProperties': false,
-    'default': {
-      'expires': 3,
-      'password': 'abracadabra'
-    },
-    'properties': {
-      'expires': {
-        'type': 'integer'
-      },
-      'password': {
-        'maxLength': 100,
-        'minLength': 6,
-        'type': 'string'
-      }
-    },
-    'type': 'object'
-  })";
-  EXPECT_JSON_EQ(expected, *prop3.ToJson(true, false));
-
-  ObjectPropType prop4;
-  ASSERT_TRUE(prop4.FromJson(
-      CreateDictionaryValue("{'additionalProperties':true,"
-                            "'default':{'expires':3,'password':'abracadabra'}}")
-          .get(),
-      &prop2, nullptr));
-  expected = R"({
-    'additionalProperties': true,
-    'default': {
-      'expires': 3,
-      'password': 'abracadabra'
-    },
-    'properties': {
-      'expires': 'integer',
-      'password': {
-        'maxLength': 100,
-        'minLength': 6
-      }
-    }
-  })";
-  EXPECT_JSON_EQ(expected, *prop4.ToJson(false, false));
-
-  expected = R"({
-    'additionalProperties': true,
-    'default': {
-      'expires': 3,
-      'password': 'abracadabra'
-    },
-    'properties': {
-      'expires': {
-        'type': 'integer'
-      },
-      'password': {
-        'maxLength': 100,
-        'minLength': 6,
-        'type': 'string'
-      }
-    },
-    'type': 'object'
-  })";
-  EXPECT_JSON_EQ(expected, *prop4.ToJson(true, false));
-}
-
-TEST(CommandSchema, ObjectPropType_FromJson) {
-  ObjectPropType base_prop;
-  EXPECT_TRUE(base_prop.FromJson(
-      CreateDictionaryValue("{'properties':{'name':'string','age':'integer'}}")
-          .get(),
-      nullptr, nullptr));
-  auto schema = base_prop.GetObject()->GetObjectSchemaPtr();
-  const PropType* prop = schema->GetProp("name");
-  EXPECT_EQ(ValueType::String, prop->GetType());
-  prop = schema->GetProp("age");
-  EXPECT_EQ(ValueType::Int, prop->GetType());
-
-  ObjectPropType prop2;
-  ASSERT_TRUE(prop2.FromJson(
-      CreateDictionaryValue("{'properties':{'name':'string','age':'integer'},"
-                            "'default':{'name':'Bob','age':33}}")
-          .get(),
-      nullptr, nullptr));
-  ASSERT_NE(nullptr, prop2.GetDefaultValue());
-  const ObjectValue* defval = prop2.GetDefaultValue()->GetObject();
-  ASSERT_NE(nullptr, defval);
-  ValueMap objval = defval->GetValue();
-  EXPECT_EQ("Bob", objval["name"]->GetString()->GetValue());
-  EXPECT_EQ(33, objval["age"]->GetInt()->GetValue());
-}
-
-TEST(CommandSchema, ObjectPropType_Validate) {
-  ObjectPropType prop;
-  prop.FromJson(
-      CreateDictionaryValue("{'properties':{'expires':'integer',"
-                            "'password':{'maxLength':100,'minLength':6}},"
-                            "'required':['expires','password']}")
-          .get(),
-      nullptr, nullptr);
-  ErrorPtr error;
-  EXPECT_TRUE(ValidateValue(
-      prop, *CreateValue("{'expires':10,'password':'abcdef'}"), &error));
-  error.reset();
-
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("{'expires':10}"), &error));
-  EXPECT_EQ("parameter_missing", error->GetCode());
-  error.reset();
-
-  EXPECT_FALSE(
-      ValidateValue(prop, *CreateValue("{'password':'abcdef'}"), &error));
-  EXPECT_EQ("parameter_missing", error->GetCode());
-  error.reset();
-
-  EXPECT_FALSE(ValidateValue(
-      prop, *CreateValue("{'expires':10,'password':'abcde'}"), &error));
-  EXPECT_EQ("out_of_range", error->GetFirstError()->GetCode());
-  error.reset();
-
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("2"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-  error.reset();
-
-  EXPECT_FALSE(ValidateValue(
-      prop, *CreateValue("{'expires':10,'password':'abcdef','retry':true}"),
-      &error));
-  EXPECT_EQ("unexpected_parameter", error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchema, ObjectPropType_Validate_Enum) {
-  ObjectPropType prop;
-  EXPECT_TRUE(prop.FromJson(
-      CreateDictionaryValue(
-          "{'properties':{'width':'integer','height':'integer'},"
-          "'enum':[{'width':10,'height':20},{'width':100,'height':200}]}")
-          .get(),
-      nullptr, nullptr));
-  ErrorPtr error;
-  EXPECT_TRUE(
-      ValidateValue(prop, *CreateValue("{'height':20,'width':10}"), &error));
-  error.reset();
-
-  EXPECT_TRUE(
-      ValidateValue(prop, *CreateValue("{'height':200,'width':100}"), &error));
-  error.reset();
-
-  EXPECT_FALSE(
-      ValidateValue(prop, *CreateValue("{'height':12,'width':10}"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchema, ObjectPropType_CreateValue) {
-  ObjectPropType prop;
-  IntPropType int_type;
-  ASSERT_TRUE(prop.FromJson(
-      CreateDictionaryValue(
-          "{'properties':{'width':'integer','height':'integer'},"
-          "'enum':[{'width':10,'height':20},{'width':100,'height':200}]}")
-          .get(),
-      nullptr, nullptr));
-  ValueMap obj{
-      {"width", int_type.CreateValue(base::FundamentalValue{10}, nullptr)},
-      {"height", int_type.CreateValue(base::FundamentalValue{20}, nullptr)},
-  };
-
-  ErrorPtr error;
-  auto val = prop.CreateValue(
-      *CreateDictionaryValue("{'width': 10, 'height': 20}"), &error);
-  ASSERT_NE(nullptr, val.get());
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_EQ(obj, val->GetValue());
-
-  val = prop.CreateValue(base::StringValue{"blah"}, &error);
-  EXPECT_EQ(nullptr, val.get());
-  ASSERT_NE(nullptr, error.get());
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-TEST(CommandSchema, ArrayPropType_Empty) {
-  ArrayPropType prop;
-  EXPECT_FALSE(prop.HasOverriddenAttributes());
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  EXPECT_EQ(nullptr, prop.GetDefaultValue());
-  EXPECT_EQ(nullptr, prop.GetItemTypePtr());
-  prop.SetItemType(PropType::Create(ValueType::Int));
-  EXPECT_TRUE(prop.HasOverriddenAttributes());
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  EXPECT_NE(nullptr, prop.GetItemTypePtr());
-  EXPECT_FALSE(prop.IsRequired());
-}
-
-TEST(CommandSchema, ArrayPropType_Types) {
-  ArrayPropType prop;
-  EXPECT_EQ(nullptr, prop.GetInt());
-  EXPECT_EQ(nullptr, prop.GetBoolean());
-  EXPECT_EQ(nullptr, prop.GetDouble());
-  EXPECT_EQ(nullptr, prop.GetString());
-  EXPECT_EQ(nullptr, prop.GetObject());
-  EXPECT_EQ(&prop, prop.GetArray());
-}
-
-TEST(CommandSchema, ArrayPropType_ToJson) {
-  ArrayPropType prop;
-  prop.SetItemType(PropType::Create(ValueType::Int));
-  EXPECT_JSON_EQ("{'items':'integer'}", *prop.ToJson(false, false));
-  EXPECT_JSON_EQ("{'items':{'type':'integer'},'type':'array'}",
-                 *prop.ToJson(true, false));
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  ArrayPropType prop2;
-  prop2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_JSON_EQ("{}", *prop2.ToJson(false, false));
-  EXPECT_TRUE(prop2.IsBasedOnSchema());
-  prop2.FromJson(CreateDictionaryValue("{'default':[1,2,3]}").get(), &prop,
-                 nullptr);
-  EXPECT_JSON_EQ("{'default':[1,2,3]}", *prop2.ToJson(false, false));
-  EXPECT_JSON_EQ(
-      "{'default':[1,2,3],'items':{'type':'integer'},'type':'array'}",
-      *prop2.ToJson(true, false));
-}
-
-TEST(CommandSchema, ArrayPropType_FromJson) {
-  ArrayPropType prop;
-  EXPECT_TRUE(prop.FromJson(CreateDictionaryValue("{'items':'integer'}").get(),
-                            nullptr, nullptr));
-  EXPECT_EQ(ValueType::Int, prop.GetItemTypePtr()->GetType());
-
-  ArrayPropType prop2;
-  ASSERT_TRUE(
-      prop2.FromJson(CreateDictionaryValue(
-                         "{'items':'string','default':['foo', 'bar', 'baz']}")
-                         .get(),
-                     nullptr, nullptr));
-  ASSERT_NE(nullptr, prop2.GetDefaultValue());
-  const ArrayValue* defval = prop2.GetDefaultValue()->GetArray();
-  ASSERT_NE(nullptr, defval);
-  EXPECT_EQ((std::vector<std::string>{"foo", "bar", "baz"}),
-            GetArrayValues<std::string>(defval->GetValue()));
-}
-
-TEST(CommandSchema, ArrayPropType_Validate) {
-  ArrayPropType prop;
-  prop.FromJson(
-      CreateDictionaryValue("{'items':{'minimum':2.3, 'maximum':10.5}}").get(),
-      nullptr, nullptr);
-
-  ErrorPtr error;
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("[3,4,10.5]"), &error));
-  error.reset();
-
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("[2]"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("[4, 5, 20]"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchema, ArrayPropType_Validate_Enum) {
-  ArrayPropType prop;
-  prop.FromJson(
-      CreateDictionaryValue("{'items':'integer', 'enum':[[1], [2,3], [4,5,6]]}")
-          .get(),
-      nullptr, nullptr);
-
-  ErrorPtr error;
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("[2,3]"), &error));
-  error.reset();
-
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("[2]"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("[2,3,4]"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchema, ArrayPropType_CreateValue) {
-  ArrayPropType prop;
-  ASSERT_TRUE(prop.FromJson(
-      CreateDictionaryValue(
-          "{'items':{'properties':{'width':'integer','height':'integer'}}}")
-          .get(),
-      nullptr, nullptr));
-
-  ErrorPtr error;
-  ValueVector arr;
-
-  auto val = prop.CreateValue(base::ListValue{}, &error);
-  ASSERT_NE(nullptr, val.get());
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_EQ(arr, val->GetValue());
-  EXPECT_JSON_EQ("[]", *val->ToJson());
-
-  val = prop.CreateValue(
-      *CreateValue("[{'height':20,'width':10},{'width':17, 'height':18}]"),
-      &error);
-  ASSERT_NE(nullptr, val.get());
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_JSON_EQ("[{'height':20,'width':10},{'height':18,'width':17}]",
-                 *val->ToJson());
-
-  val = prop.CreateValue(base::StringValue{"blah"}, &error);
-  EXPECT_EQ(nullptr, val.get());
-  ASSERT_NE(nullptr, error.get());
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-}
-
-TEST(CommandSchema, ArrayPropType_NestedArrays_NotSupported) {
-  ArrayPropType prop;
-  ErrorPtr error;
-  EXPECT_FALSE(prop.FromJson(
-      CreateDictionaryValue("{'items':{'items':'integer'}}").get(), nullptr,
-      &error));
-  EXPECT_EQ(errors::commands::kInvalidObjectSchema, error->GetCode());
-  error.reset();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-TEST(CommandSchema, ObjectSchema_FromJson_Shorthand_TypeName) {
-  ObjectSchema schema;
-  const char* schema_str =
-      "{"
-      "'param1':'integer',"
-      "'param2':'number',"
-      "'param3':'string'"
-      "}";
-  EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                              nullptr));
-  EXPECT_EQ(ValueType::Int, schema.GetProp("param1")->GetType());
-  EXPECT_EQ(ValueType::Double, schema.GetProp("param2")->GetType());
-  EXPECT_EQ(ValueType::String, schema.GetProp("param3")->GetType());
-  EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param2")->GetTypeAsString());
-  EXPECT_EQ("string", schema.GetProp("param3")->GetTypeAsString());
-  EXPECT_EQ(nullptr, schema.GetProp("param4"));
-
-  int min_int = (std::numeric_limits<int>::min)();
-  int max_int = (std::numeric_limits<int>::max)();
-  double min_dbl = (std::numeric_limits<double>::lowest)();
-  double max_dbl = (std::numeric_limits<double>::max)();
-  EXPECT_EQ(min_int, schema.GetProp("param1")->GetInt()->GetMinValue());
-  EXPECT_EQ(max_int, schema.GetProp("param1")->GetInt()->GetMaxValue());
-  EXPECT_EQ(min_dbl, schema.GetProp("param2")->GetDouble()->GetMinValue());
-  EXPECT_EQ(max_dbl, schema.GetProp("param2")->GetDouble()->GetMaxValue());
-  EXPECT_EQ(0, schema.GetProp("param3")->GetString()->GetMinLength());
-  EXPECT_EQ(max_int, schema.GetProp("param3")->GetString()->GetMaxLength());
-}
-
-TEST(CommandSchema, ObjectSchema_FromJson_Full_TypeName) {
-  ObjectSchema schema;
-  const char* schema_str =
-      "{"
-      "'param1':{'type':'integer'},"
-      "'param2':{'type':'number'},"
-      "'param3':{'type':'string'},"
-      "'param4':{'type':'array', 'items':'integer'},"
-      "'param5':{'type':'object', 'properties':{'p1':'integer'}}"
-      "}";
-  EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                              nullptr));
-  EXPECT_EQ(ValueType::Int, schema.GetProp("param1")->GetType());
-  EXPECT_EQ(ValueType::Double, schema.GetProp("param2")->GetType());
-  EXPECT_EQ(ValueType::String, schema.GetProp("param3")->GetType());
-  EXPECT_EQ(ValueType::Array, schema.GetProp("param4")->GetType());
-  EXPECT_EQ(ValueType::Object, schema.GetProp("param5")->GetType());
-  EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param2")->GetTypeAsString());
-  EXPECT_EQ("string", schema.GetProp("param3")->GetTypeAsString());
-  EXPECT_EQ("array", schema.GetProp("param4")->GetTypeAsString());
-  EXPECT_EQ("object", schema.GetProp("param5")->GetTypeAsString());
-  EXPECT_EQ(nullptr, schema.GetProp("param77"));
-
-  int min_int = (std::numeric_limits<int>::min)();
-  int max_int = (std::numeric_limits<int>::max)();
-  double min_dbl = (std::numeric_limits<double>::lowest)();
-  double max_dbl = (std::numeric_limits<double>::max)();
-  EXPECT_EQ(min_int, schema.GetProp("param1")->GetInt()->GetMinValue());
-  EXPECT_EQ(max_int, schema.GetProp("param1")->GetInt()->GetMaxValue());
-  EXPECT_EQ(min_dbl, schema.GetProp("param2")->GetDouble()->GetMinValue());
-  EXPECT_EQ(max_dbl, schema.GetProp("param2")->GetDouble()->GetMaxValue());
-  EXPECT_EQ(0, schema.GetProp("param3")->GetString()->GetMinLength());
-  EXPECT_EQ(max_int, schema.GetProp("param3")->GetString()->GetMaxLength());
-}
-
-TEST(CommandSchema, ObjectSchema_FromJson_Shorthand_TypeDeduction_Scalar) {
-  ObjectSchema schema;
-  const char* schema_str =
-      "{"
-      "'param1' :{'minimum':2},"
-      "'param2' :{'maximum':10},"
-      "'param3' :{'maximum':8, 'minimum':2},"
-      "'param4' :{'minimum':2.1},"
-      "'param5' :{'maximum':10.1},"
-      "'param6' :{'maximum':8.1, 'minimum':3.1},"
-      "'param7' :{'maximum':8, 'minimum':3.1},"
-      "'param8' :{'maximum':8.1, 'minimum':3},"
-      "'param9' :{'minLength':2},"
-      "'param10':{'maxLength':10},"
-      "'param11':{'maxLength':8, 'minLength':3},"
-      "'param12':{'default':12},"
-      "'param13':{'default':13.5},"
-      "'param14':{'default':true},"
-      "'param15':{'default':false},"
-      "'param16':{'default':'foobar'},"
-      "'param17':{'default':[1,2,3]},"
-      "'param18':{'items':'number', 'default':[]}"
-      "}";
-  EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                              nullptr));
-  EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString());
-  EXPECT_EQ("integer", schema.GetProp("param2")->GetTypeAsString());
-  EXPECT_EQ("integer", schema.GetProp("param3")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param4")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param5")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param6")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param7")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param8")->GetTypeAsString());
-  EXPECT_EQ("string", schema.GetProp("param9")->GetTypeAsString());
-  EXPECT_EQ("string", schema.GetProp("param10")->GetTypeAsString());
-  EXPECT_EQ("string", schema.GetProp("param11")->GetTypeAsString());
-  EXPECT_EQ("integer", schema.GetProp("param12")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param13")->GetTypeAsString());
-  EXPECT_EQ("boolean", schema.GetProp("param14")->GetTypeAsString());
-  EXPECT_EQ("boolean", schema.GetProp("param15")->GetTypeAsString());
-  EXPECT_EQ("string", schema.GetProp("param16")->GetTypeAsString());
-  EXPECT_EQ("array", schema.GetProp("param17")->GetTypeAsString());
-  auto prop17 = schema.GetProp("param17");
-  EXPECT_EQ("integer", prop17->GetArray()->GetItemTypePtr()->GetTypeAsString());
-  EXPECT_EQ("array", schema.GetProp("param18")->GetTypeAsString());
-  auto prop18 = schema.GetProp("param18");
-  EXPECT_EQ("number", prop18->GetArray()->GetItemTypePtr()->GetTypeAsString());
-
-  int min_int = (std::numeric_limits<int>::min)();
-  int max_int = (std::numeric_limits<int>::max)();
-  double min_dbl = (std::numeric_limits<double>::lowest)();
-  double max_dbl = (std::numeric_limits<double>::max)();
-  EXPECT_EQ(2, schema.GetProp("param1")->GetInt()->GetMinValue());
-  EXPECT_EQ(max_int, schema.GetProp("param1")->GetInt()->GetMaxValue());
-  EXPECT_EQ(min_int, schema.GetProp("param2")->GetInt()->GetMinValue());
-  EXPECT_EQ(10, schema.GetProp("param2")->GetInt()->GetMaxValue());
-  EXPECT_EQ(2, schema.GetProp("param3")->GetInt()->GetMinValue());
-  EXPECT_EQ(8, schema.GetProp("param3")->GetInt()->GetMaxValue());
-  EXPECT_DOUBLE_EQ(2.1, schema.GetProp("param4")->GetDouble()->GetMinValue());
-  EXPECT_DOUBLE_EQ(max_dbl,
-                   schema.GetProp("param4")->GetDouble()->GetMaxValue());
-  EXPECT_DOUBLE_EQ(min_dbl,
-                   schema.GetProp("param5")->GetDouble()->GetMinValue());
-  EXPECT_DOUBLE_EQ(10.1, schema.GetProp("param5")->GetDouble()->GetMaxValue());
-  EXPECT_DOUBLE_EQ(3.1, schema.GetProp("param6")->GetDouble()->GetMinValue());
-  EXPECT_DOUBLE_EQ(8.1, schema.GetProp("param6")->GetDouble()->GetMaxValue());
-  EXPECT_DOUBLE_EQ(3.1, schema.GetProp("param7")->GetDouble()->GetMinValue());
-  EXPECT_DOUBLE_EQ(8.0, schema.GetProp("param7")->GetDouble()->GetMaxValue());
-  EXPECT_DOUBLE_EQ(3.0, schema.GetProp("param8")->GetDouble()->GetMinValue());
-  EXPECT_DOUBLE_EQ(8.1, schema.GetProp("param8")->GetDouble()->GetMaxValue());
-  EXPECT_EQ(2, schema.GetProp("param9")->GetString()->GetMinLength());
-  EXPECT_EQ(max_int, schema.GetProp("param9")->GetString()->GetMaxLength());
-  EXPECT_EQ(0, schema.GetProp("param10")->GetString()->GetMinLength());
-  EXPECT_EQ(10, schema.GetProp("param10")->GetString()->GetMaxLength());
-  EXPECT_EQ(3, schema.GetProp("param11")->GetString()->GetMinLength());
-  EXPECT_EQ(8, schema.GetProp("param11")->GetString()->GetMaxLength());
-  const PropValue* val = schema.GetProp("param12")->GetDefaultValue();
-  EXPECT_EQ(12, val->GetInt()->GetValue());
-  val = schema.GetProp("param13")->GetDefaultValue();
-  EXPECT_DOUBLE_EQ(13.5, val->GetDouble()->GetValue());
-  val = schema.GetProp("param14")->GetDefaultValue();
-  EXPECT_TRUE(val->GetBoolean()->GetValue());
-  val = schema.GetProp("param15")->GetDefaultValue();
-  EXPECT_FALSE(val->GetBoolean()->GetValue());
-  val = schema.GetProp("param16")->GetDefaultValue();
-  EXPECT_EQ("foobar", val->GetString()->GetValue());
-  val = schema.GetProp("param17")->GetDefaultValue();
-  EXPECT_EQ((std::vector<int>{1, 2, 3}),
-            GetArrayValues<int>(val->GetArray()->GetValue()));
-  val = schema.GetProp("param18")->GetDefaultValue();
-  EXPECT_TRUE(val->GetArray()->GetValue().empty());
-}
-
-TEST(CommandSchema, ObjectSchema_FromJson_Shorthand_TypeDeduction_Array) {
-  ObjectSchema schema;
-  const char* schema_str =
-      "{"
-      "'param1' :[0,1,2,3],"
-      "'param2' :[0.0,1.1,2.2],"
-      "'param3' :['id1', 'id2'],"
-      "'param4' :{'enum':[1,2,3]},"
-      "'param5' :{'enum':[-1.1,2.2,3]},"
-      "'param6' :{'enum':['id0', 'id1']},"
-      "'param7' :{'type':'integer', 'enum':[1,2,3]},"
-      "'param8' :{'type':'number',  'enum':[1,2,3]},"
-      "'param9' :{'type':'number',  'enum':[]},"
-      "'param10':{'type':'integer', 'enum':[]},"
-      "'param11':[[0,1],[2,3]],"
-      "'param12':[['foo','bar']],"
-      "'param13':{'enum':[['id0', 'id1']]}"
-      "}";
-  EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                              nullptr));
-  EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param2")->GetTypeAsString());
-  EXPECT_EQ("string", schema.GetProp("param3")->GetTypeAsString());
-  EXPECT_EQ("integer", schema.GetProp("param4")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param5")->GetTypeAsString());
-  EXPECT_EQ("string", schema.GetProp("param6")->GetTypeAsString());
-  EXPECT_EQ("integer", schema.GetProp("param7")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param8")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param9")->GetTypeAsString());
-  EXPECT_EQ("integer", schema.GetProp("param10")->GetTypeAsString());
-
-  auto prop_type11 = schema.GetProp("param11");
-  EXPECT_EQ("array", prop_type11->GetTypeAsString());
-  EXPECT_EQ("integer",
-            prop_type11->GetArray()->GetItemTypePtr()->GetTypeAsString());
-
-  auto prop_type12 = schema.GetProp("param12");
-  EXPECT_EQ("array", prop_type12->GetTypeAsString());
-  EXPECT_EQ("string",
-            prop_type12->GetArray()->GetItemTypePtr()->GetTypeAsString());
-
-  auto prop_type13 = schema.GetProp("param13");
-  EXPECT_EQ("array", prop_type12->GetTypeAsString());
-  EXPECT_EQ("string",
-            prop_type13->GetArray()->GetItemTypePtr()->GetTypeAsString());
-
-  EXPECT_EQ((std::vector<int>{0, 1, 2, 3}),
-            GetOneOfValues<int>(schema.GetProp("param1")));
-  EXPECT_EQ((std::vector<double>{0.0, 1.1, 2.2}),
-            GetOneOfValues<double>(schema.GetProp("param2")));
-  EXPECT_EQ((std::vector<std::string>{"id1", "id2"}),
-            GetOneOfValues<std::string>(schema.GetProp("param3")));
-
-  EXPECT_EQ((std::vector<int>{1, 2, 3}),
-            GetOneOfValues<int>(schema.GetProp("param4")));
-  EXPECT_EQ((std::vector<double>{-1.1, 2.2, 3.0}),
-            GetOneOfValues<double>(schema.GetProp("param5")));
-  EXPECT_EQ((std::vector<std::string>{"id0", "id1"}),
-            GetOneOfValues<std::string>(schema.GetProp("param6")));
-  EXPECT_EQ((std::vector<int>{1, 2, 3}),
-            GetOneOfValues<int>(schema.GetProp("param7")));
-  EXPECT_EQ((std::vector<double>{1.0, 2.0, 3.0}),
-            GetOneOfValues<double>(schema.GetProp("param8")));
-  EXPECT_TRUE(GetOneOfValues<double>(schema.GetProp("param9")).empty());
-  EXPECT_TRUE(GetOneOfValues<int>(schema.GetProp("param10")).empty());
-}
-
-TEST(CommandSchema, ObjectSchema_FromJson_Inheritance) {
-  const char* base_schema_str =
-      "{"
-      "'param0' :{'minimum':1, 'maximum':5},"
-      "'param1' :{'minimum':1, 'maximum':5},"
-      "'param2' :{'minimum':1, 'maximum':5},"
-      "'param3' :{'minimum':1, 'maximum':5},"
-      "'param4' :{'minimum':1, 'maximum':5},"
-      "'param5' :{'minimum':1.1, 'maximum':5.5},"
-      "'param6' :{'minimum':1.1, 'maximum':5.5},"
-      "'param7' :{'minimum':1.1, 'maximum':5.5},"
-      "'param8' :{'minimum':1.1, 'maximum':5.5},"
-      "'param9' :{'minLength':1, 'maxLength':5},"
-      "'param10':{'minLength':1, 'maxLength':5},"
-      "'param11':{'minLength':1, 'maxLength':5},"
-      "'param12':{'minLength':1, 'maxLength':5},"
-      "'param13':[1,2,3],"
-      "'param14':[1,2,3],"
-      "'param15':[1.1,2.2,3.3],"
-      "'param16':[1.1,2.2,3.3],"
-      "'param17':['id1', 'id2'],"
-      "'param18':['id1', 'id2'],"
-      "'param19':{'minimum':1, 'maximum':5},"
-      "'param20':{'default':49},"
-      "'param21':{'default':49},"
-      "'param22':'integer'"
-      "}";
-  ObjectSchema base_schema;
-  EXPECT_TRUE(base_schema.FromJson(CreateDictionaryValue(base_schema_str).get(),
-                                   nullptr, nullptr));
-  const char* schema_str =
-      "{"
-      "'param1' :{},"
-      "'param2' :{'minimum':2},"
-      "'param3' :{'maximum':9},"
-      "'param4' :{'minimum':2, 'maximum':9},"
-      "'param5' :{},"
-      "'param6' :{'minimum':2.2},"
-      "'param7' :{'maximum':9.9},"
-      "'param8' :{'minimum':2.2, 'maximum':9.9},"
-      "'param9' :{},"
-      "'param10':{'minLength':3},"
-      "'param11':{'maxLength':8},"
-      "'param12':{'minLength':3, 'maxLength':8},"
-      "'param13':{},"
-      "'param14':[1,2,3,4],"
-      "'param15':{},"
-      "'param16':[1.1,2.2,3.3,4.4],"
-      "'param17':{},"
-      "'param18':['id1', 'id3'],"
-      "'param19':{},"
-      "'param20':{},"
-      "'param21':{'default':8},"
-      "'param22':{'default':123}"
-      "}";
-  ObjectSchema schema;
-  EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(),
-                              &base_schema, nullptr));
-  EXPECT_EQ(nullptr, schema.GetProp("param0"));
-  EXPECT_NE(nullptr, schema.GetProp("param1"));
-  EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString());
-  EXPECT_EQ(1, schema.GetProp("param1")->GetInt()->GetMinValue());
-  EXPECT_EQ(5, schema.GetProp("param1")->GetInt()->GetMaxValue());
-  EXPECT_EQ("integer", schema.GetProp("param2")->GetTypeAsString());
-  EXPECT_EQ(2, schema.GetProp("param2")->GetInt()->GetMinValue());
-  EXPECT_EQ(5, schema.GetProp("param2")->GetInt()->GetMaxValue());
-  EXPECT_EQ("integer", schema.GetProp("param3")->GetTypeAsString());
-  EXPECT_EQ(1, schema.GetProp("param3")->GetInt()->GetMinValue());
-  EXPECT_EQ(9, schema.GetProp("param3")->GetInt()->GetMaxValue());
-  EXPECT_EQ("integer", schema.GetProp("param4")->GetTypeAsString());
-  EXPECT_EQ(2, schema.GetProp("param4")->GetInt()->GetMinValue());
-  EXPECT_EQ(9, schema.GetProp("param4")->GetInt()->GetMaxValue());
-  EXPECT_EQ("number", schema.GetProp("param5")->GetTypeAsString());
-  EXPECT_EQ(1.1, schema.GetProp("param5")->GetDouble()->GetMinValue());
-  EXPECT_EQ(5.5, schema.GetProp("param5")->GetDouble()->GetMaxValue());
-  EXPECT_EQ("number", schema.GetProp("param6")->GetTypeAsString());
-  EXPECT_EQ(2.2, schema.GetProp("param6")->GetDouble()->GetMinValue());
-  EXPECT_EQ(5.5, schema.GetProp("param6")->GetDouble()->GetMaxValue());
-  EXPECT_EQ("number", schema.GetProp("param7")->GetTypeAsString());
-  EXPECT_EQ(1.1, schema.GetProp("param7")->GetDouble()->GetMinValue());
-  EXPECT_EQ(9.9, schema.GetProp("param7")->GetDouble()->GetMaxValue());
-  EXPECT_EQ("number", schema.GetProp("param8")->GetTypeAsString());
-  EXPECT_EQ(2.2, schema.GetProp("param8")->GetDouble()->GetMinValue());
-  EXPECT_EQ(9.9, schema.GetProp("param8")->GetDouble()->GetMaxValue());
-  EXPECT_EQ("string", schema.GetProp("param9")->GetTypeAsString());
-  EXPECT_EQ(1, schema.GetProp("param9")->GetString()->GetMinLength());
-  EXPECT_EQ(5, schema.GetProp("param9")->GetString()->GetMaxLength());
-  EXPECT_EQ("string", schema.GetProp("param10")->GetTypeAsString());
-  EXPECT_EQ(3, schema.GetProp("param10")->GetString()->GetMinLength());
-  EXPECT_EQ(5, schema.GetProp("param10")->GetString()->GetMaxLength());
-  EXPECT_EQ("string", schema.GetProp("param11")->GetTypeAsString());
-  EXPECT_EQ(1, schema.GetProp("param11")->GetString()->GetMinLength());
-  EXPECT_EQ(8, schema.GetProp("param11")->GetString()->GetMaxLength());
-  EXPECT_EQ("string", schema.GetProp("param12")->GetTypeAsString());
-  EXPECT_EQ(3, schema.GetProp("param12")->GetString()->GetMinLength());
-  EXPECT_EQ(8, schema.GetProp("param12")->GetString()->GetMaxLength());
-  EXPECT_EQ("integer", schema.GetProp("param13")->GetTypeAsString());
-  EXPECT_EQ((std::vector<int>{1, 2, 3}),
-            GetOneOfValues<int>(schema.GetProp("param13")));
-  EXPECT_EQ("integer", schema.GetProp("param14")->GetTypeAsString());
-  EXPECT_EQ((std::vector<int>{1, 2, 3, 4}),
-            GetOneOfValues<int>(schema.GetProp("param14")));
-  EXPECT_EQ("number", schema.GetProp("param15")->GetTypeAsString());
-  EXPECT_EQ((std::vector<double>{1.1, 2.2, 3.3}),
-            GetOneOfValues<double>(schema.GetProp("param15")));
-  EXPECT_EQ("number", schema.GetProp("param16")->GetTypeAsString());
-  EXPECT_EQ((std::vector<double>{1.1, 2.2, 3.3, 4.4}),
-            GetOneOfValues<double>(schema.GetProp("param16")));
-  EXPECT_EQ("string", schema.GetProp("param17")->GetTypeAsString());
-  EXPECT_EQ((std::vector<std::string>{"id1", "id2"}),
-            GetOneOfValues<std::string>(schema.GetProp("param17")));
-  EXPECT_EQ("string", schema.GetProp("param18")->GetTypeAsString());
-  EXPECT_EQ((std::vector<std::string>{"id1", "id3"}),
-            GetOneOfValues<std::string>(schema.GetProp("param18")));
-  EXPECT_EQ("integer", schema.GetProp("param19")->GetTypeAsString());
-  EXPECT_EQ(1, schema.GetProp("param19")->GetInt()->GetMinValue());
-  EXPECT_EQ(5, schema.GetProp("param19")->GetInt()->GetMaxValue());
-  EXPECT_EQ(49,
-            schema.GetProp("param20")->GetDefaultValue()->GetInt()->GetValue());
-  EXPECT_EQ(8,
-            schema.GetProp("param21")->GetDefaultValue()->GetInt()->GetValue());
-  EXPECT_EQ(123,
-            schema.GetProp("param22")->GetDefaultValue()->GetInt()->GetValue());
-}
-
-TEST(CommandSchema, ObjectSchema_UseDefaults) {
-  ObjectPropType prop;
-  const char* schema_str =
-      "{'properties':{"
-      "'param1':{'default':true},"
-      "'param2':{'default':2},"
-      "'param3':{'default':3.3},"
-      "'param4':{'default':'four'},"
-      "'param5':{'default':{'x':5,'y':6},"
-      "'properties':{'x':'integer','y':'integer'}},"
-      "'param6':{'default':[1,2,3]}"
-      "}}";
-  ASSERT_TRUE(
-      prop.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, nullptr));
-
-  // Omit all.
-  auto value =
-      prop.CreatePropValue(*CreateDictionaryValue("{}").get(), nullptr);
-  ASSERT_NE(nullptr, value);
-  ValueMap obj = value->GetObject()->GetValue();
-  EXPECT_TRUE(obj["param1"]->GetBoolean()->GetValue());
-  EXPECT_EQ(2, obj["param2"]->GetInt()->GetValue());
-  EXPECT_DOUBLE_EQ(3.3, obj["param3"]->GetDouble()->GetValue());
-  EXPECT_EQ("four", obj["param4"]->GetString()->GetValue());
-  ValueMap param5 = obj["param5"]->GetObject()->GetValue();
-  EXPECT_EQ(5, param5["x"]->GetInt()->GetValue());
-  EXPECT_EQ(6, param5["y"]->GetInt()->GetValue());
-  ValueVector param6 = obj["param6"]->GetArray()->GetValue();
-  EXPECT_EQ((std::vector<int>{1, 2, 3}), GetArrayValues<int>(param6));
-
-  // Specify some.
-  const char* val_json =
-      "{"
-      "'param1':false,"
-      "'param3':33.3,"
-      "'param5':{'x':-5,'y':-6}"
-      "}";
-  value = prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr);
-  ASSERT_NE(nullptr, value);
-  obj = value->GetObject()->GetValue();
-  EXPECT_FALSE(obj["param1"]->GetBoolean()->GetValue());
-  EXPECT_EQ(2, obj["param2"]->GetInt()->GetValue());
-  EXPECT_DOUBLE_EQ(33.3, obj["param3"]->GetDouble()->GetValue());
-  EXPECT_EQ("four", obj["param4"]->GetString()->GetValue());
-  param5 = obj["param5"]->GetObject()->GetValue();
-  EXPECT_EQ(-5, param5["x"]->GetInt()->GetValue());
-  EXPECT_EQ(-6, param5["y"]->GetInt()->GetValue());
-  param6 = obj["param6"]->GetArray()->GetValue();
-  EXPECT_EQ((std::vector<int>{1, 2, 3}), GetArrayValues<int>(param6));
-
-  // Specify all.
-  val_json =
-      "{"
-      "'param1':false,"
-      "'param2':22,"
-      "'param3':333.3,"
-      "'param4':'FOUR',"
-      "'param5':{'x':-55,'y':66},"
-      "'param6':[-1, 0]"
-      "}";
-  value = prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr);
-  ASSERT_NE(nullptr, value);
-  obj = value->GetObject()->GetValue();
-  EXPECT_FALSE(obj["param1"]->GetBoolean()->GetValue());
-  EXPECT_EQ(22, obj["param2"]->GetInt()->GetValue());
-  EXPECT_DOUBLE_EQ(333.3, obj["param3"]->GetDouble()->GetValue());
-  EXPECT_EQ("FOUR", obj["param4"]->GetString()->GetValue());
-  param5 = obj["param5"]->GetObject()->GetValue();
-  EXPECT_EQ(-55, param5["x"]->GetInt()->GetValue());
-  EXPECT_EQ(66, param5["y"]->GetInt()->GetValue());
-  param6 = obj["param6"]->GetArray()->GetValue();
-  EXPECT_EQ((std::vector<int>{-1, 0}), GetArrayValues<int>(param6));
-}
-
-TEST(CommandSchema, ObjectSchema_FromJson_BaseSchema_Failures) {
-  ObjectSchema schema;
-  ErrorPtr error;
-  const char* schema_str =
-      "{"
-      "'param1':{}"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':{'type':'foo'}"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("unknown_type", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':[]"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':{'minimum':'foo'}"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("type_mismatch", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':[1,2.2]"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("type_mismatch", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':{'minimum':1, 'enum':[1,2,3]}"  // can't have min/max & enum.
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("unexpected_parameter", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':{'maximum':1, 'blah':2}"  // 'blah' is unexpected.
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("unexpected_parameter", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':{'enum':[1,2,3],'default':5}"  // 'default' must be 1, 2, or 3.
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("out_of_range", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':[[1,2.3]]"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("type_mismatch", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':[[1,2],[3,4],['blah']]"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("type_mismatch", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':{'default':[]}"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':[[[1]],[[2]]]"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':{'enum':[[['foo']]]}"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':{'default':[[1],[2]]}"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchema, RequiredProperties_Integral) {
-  IntPropType prop;
-
-  prop.MakeRequired(false);
-  EXPECT_JSON_EQ("{'type':'integer'}", *prop.ToJson(true, false));
-  EXPECT_JSON_EQ("{'isRequired':false,'type':'integer'}",
-                 *prop.ToJson(true, true));
-
-  prop.MakeRequired(true);
-  EXPECT_JSON_EQ("{'type':'integer'}", *prop.ToJson(true, false));
-  EXPECT_JSON_EQ("{'isRequired':true,'type':'integer'}",
-                 *prop.ToJson(true, true));
-
-  IntPropType prop2;
-  EXPECT_TRUE(
-      prop2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr));
-  EXPECT_TRUE(prop2.IsRequired());
-
-  EXPECT_TRUE(prop2.FromJson(
-      CreateDictionaryValue("{'isRequired': false}").get(), &prop, nullptr));
-  EXPECT_FALSE(prop2.IsRequired());
-
-  EXPECT_JSON_EQ("{'type':'integer'}", *prop2.ToJson(true, false));
-  EXPECT_JSON_EQ("{'isRequired':false,'type':'integer'}",
-                 *prop2.ToJson(true, true));
-}
-
-TEST(CommandSchema, RequiredProperties_Object) {
-  ObjectPropType obj_type;
-  auto schema = ObjectSchema::Create();
-  auto type = PropType::Create(ValueType::Int);
-  type->MakeRequired(true);
-  schema->AddProp("prop1", std::move(type));
-  type = PropType::Create(ValueType::String);
-  type->MakeRequired(false);
-  schema->AddProp("prop2", std::move(type));
-  type = PropType::Create(ValueType::Boolean);
-  type->MakeRequired(true);
-  schema->AddProp("prop3", std::move(type));
-  type = PropType::Create(ValueType::Array);
-  type->GetArray()->SetItemType(PropType::Create(ValueType::String));
-  schema->AddProp("prop4", std::move(type));
-  auto expected1 = R"({
-    'prop1': 'integer',
-    'prop2': 'string',
-    'prop3': 'boolean',
-    'prop4': {'items': 'string'}
-  })";
-  EXPECT_JSON_EQ(expected1, *schema->ToJson(false, false));
-  auto expected2 = R"({
-    'prop1': {'type':'integer','isRequired': true},
-    'prop2': {'type':'string','isRequired': false},
-    'prop3': {'type':'boolean','isRequired': true},
-    'prop4': {'items': 'string'}
-  })";
-  EXPECT_JSON_EQ(expected2, *schema->ToJson(false, true));
-
-  obj_type.SetObjectSchema(std::move(schema));
-  auto expected3 = R"({
-    'additionalProperties': false,
-    'properties': {
-      'prop1': 'integer',
-      'prop2': 'string',
-      'prop3': 'boolean',
-      'prop4': {'items': 'string'}
-    },
-    'required': ['prop1','prop3']
-  })";
-  EXPECT_JSON_EQ(expected3, *obj_type.ToJson(false, false));
-  EXPECT_JSON_EQ(expected3, *obj_type.ToJson(false, true));
-}
-
-TEST(CommandSchema, RequiredProperties_Schema_FromJson) {
-  ObjectSchema schema;
-  auto schema_str = R"({
-    'prop1': {'type':'integer','isRequired': true},
-    'prop2': {'type':'string','isRequired': false},
-    'prop3': 'boolean'
-  })";
-  EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                              nullptr));
-  EXPECT_TRUE(schema.GetProp("prop1")->IsRequired());
-  EXPECT_FALSE(schema.GetProp("prop2")->IsRequired());
-  EXPECT_FALSE(schema.GetProp("prop3")->IsRequired());
-  EXPECT_JSON_EQ(schema_str, *schema.ToJson(false, true));
-}
-
-TEST(CommandSchema, RequiredProperties_Schema_FromJson_Inherit) {
-  ObjectSchema base_schema;
-  auto base_schema_str = R"({
-    'prop1': {'type':'integer','isRequired': true},
-    'prop2': {'type':'integer','isRequired': false},
-    'prop3': {'type':'integer','isRequired': true},
-    'prop4': {'type':'integer','isRequired': false}
-  })";
-  EXPECT_TRUE(base_schema.FromJson(CreateDictionaryValue(base_schema_str).get(),
-                                   nullptr, nullptr));
-  ObjectSchema schema;
-  auto schema_str = R"({
-    'prop1': {'isRequired': false},
-    'prop2': {'isRequired': true},
-    'prop3': {},
-    'prop4': 'integer'
-  })";
-  EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(),
-                              &base_schema, nullptr));
-  EXPECT_FALSE(schema.GetProp("prop1")->IsRequired());
-  EXPECT_TRUE(schema.GetProp("prop2")->IsRequired());
-  EXPECT_TRUE(schema.GetProp("prop3")->IsRequired());
-  EXPECT_FALSE(schema.GetProp("prop4")->IsRequired());
-  auto expected = R"({
-    'prop1': {'type':'integer','isRequired': false},
-    'prop2': {'type':'integer','isRequired': true},
-    'prop3': {},
-    'prop4': {}
-  })";
-  EXPECT_JSON_EQ(expected, *schema.ToJson(false, true));
-}
-
-TEST(CommandSchema, RequiredProperties_ObjectPropType_FromJson) {
-  ObjectPropType obj_type;
-  auto type_str = R"({
-    'properties': {
-      'prop1': 'integer',
-      'prop2': 'string',
-      'prop3': {'type':'boolean','isRequired':true},
-      'prop4': {'items': 'string','isRequired':false},
-      'prop5': {'type':'number','isRequired':true}
-    },
-    'required': ['prop1','prop3','prop4','prop5']
-  })";
-  EXPECT_TRUE(obj_type.FromJson(CreateDictionaryValue(type_str).get(), nullptr,
-                                nullptr));
-  EXPECT_TRUE(obj_type.GetObjectSchemaPtr()->GetProp("prop1")->IsRequired());
-  EXPECT_FALSE(obj_type.GetObjectSchemaPtr()->GetProp("prop2")->IsRequired());
-  EXPECT_TRUE(obj_type.GetObjectSchemaPtr()->GetProp("prop3")->IsRequired());
-  // 'required' takes precedence over 'isRequired'.
-  EXPECT_TRUE(obj_type.GetObjectSchemaPtr()->GetProp("prop4")->IsRequired());
-  EXPECT_TRUE(obj_type.GetObjectSchemaPtr()->GetProp("prop5")->IsRequired());
-}
-
-TEST(CommandSchema, RequiredProperties_Failures) {
-  ObjectPropType obj_type;
-  ErrorPtr error;
-
-  auto type_str = R"({
-    'properties': {
-      'prop1': 'integer',
-      'prop2': 'string'
-    },
-    'required': ['prop1','prop3','prop4']
-  })";
-  EXPECT_FALSE(obj_type.FromJson(CreateDictionaryValue(type_str).get(), nullptr,
-                                 &error));
-  EXPECT_EQ(errors::commands::kUnknownProperty, error->GetCode());
-  error.reset();
-
-  type_str = R"({
-    'properties': {
-      'prop1': 'integer',
-      'prop2': 'string'
-    },
-    'required': 'prop1'
-  })";
-  EXPECT_FALSE(obj_type.FromJson(CreateDictionaryValue(type_str).get(), nullptr,
-                                 &error));
-  EXPECT_EQ(errors::commands::kInvalidObjectSchema, error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchema, ObjectSchema_UseRequired) {
-  ObjectPropType prop;
-  auto schema_str = R"({
-    'properties':{
-      'param1':'integer',
-      'param2':'integer',
-      'param3':{'default':3},
-      'param4':{'default':4}
-    },
-    'required':['param1','param3']
-  })";
-  ASSERT_TRUE(
-      prop.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, nullptr));
-
-  auto val_json = R"({
-    'param1':10,
-    'param2':20,
-    'param3':30,
-    'param4':40
-  })";
-  auto value =
-      prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr);
-  ASSERT_NE(nullptr, value);
-  ValueMap obj = value->GetObject()->GetValue();
-  EXPECT_EQ(10, obj["param1"]->GetInt()->GetValue());
-  EXPECT_EQ(20, obj["param2"]->GetInt()->GetValue());
-  EXPECT_EQ(30, obj["param3"]->GetInt()->GetValue());
-  EXPECT_EQ(40, obj["param4"]->GetInt()->GetValue());
-
-  val_json = "{'param1':100}";
-  value = prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr);
-  ASSERT_NE(nullptr, value);
-  obj = value->GetObject()->GetValue();
-  EXPECT_EQ(3u, obj.size());
-
-  EXPECT_EQ(100, obj["param1"]->GetInt()->GetValue());
-  EXPECT_EQ(obj.end(), obj.find("param2"));
-  EXPECT_EQ(3, obj["param3"]->GetInt()->GetValue());
-  EXPECT_EQ(4, obj["param4"]->GetInt()->GetValue());
-}
-
-TEST(CommandSchema, ObjectSchema_UseRequired_Failure) {
-  ObjectPropType prop;
-  auto schema_str = R"({
-    'properties':{
-      'param1':'integer',
-      'param2':'integer',
-      'param3':{'default':3},
-      'param4':{'default':4}
-    },
-    'required':['param1','param3']
-  })";
-  ASSERT_TRUE(
-      prop.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, nullptr));
-
-  auto val_json = "{'param2':20}";
-  ErrorPtr error;
-  auto value =
-      prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), &error);
-  ASSERT_EQ(nullptr, value);
-  EXPECT_EQ(errors::commands::kPropertyMissing, error->GetCode());
-}
-
-}  // namespace weave
diff --git a/src/commands/prop_constraints.cc b/src/commands/prop_constraints.cc
deleted file mode 100644
index b7e9cf6..0000000
--- a/src/commands/prop_constraints.cc
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright 2015 The Weave 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 "src/commands/prop_constraints.h"
-
-#include <base/json/json_writer.h>
-#include <base/logging.h>
-
-#include "src/commands/prop_values.h"
-#include "src/commands/schema_constants.h"
-#include "src/string_utils.h"
-
-namespace weave {
-
-namespace {
-
-// Helper function to convert a property value to string, which is used for
-// error reporting.
-std::string PropValueToString(const PropValue& value) {
-  std::string result;
-  auto json = value.ToJson();
-  CHECK(json);
-  base::JSONWriter::Write(*json, &result);
-  return result;
-}
-
-}  // anonymous namespace
-
-// Constraint ----------------------------------------------------------------
-Constraint::~Constraint() {}
-
-bool Constraint::ReportErrorLessThan(ErrorPtr* error,
-                                     const std::string& val,
-                                     const std::string& limit) {
-  Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                     errors::commands::kOutOfRange,
-                     "Value %s is out of range. It must not be less than %s",
-                     val.c_str(), limit.c_str());
-  return false;
-}
-
-bool Constraint::ReportErrorGreaterThan(ErrorPtr* error,
-                                        const std::string& val,
-                                        const std::string& limit) {
-  Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                     errors::commands::kOutOfRange,
-                     "Value %s is out of range. It must not be greater than %s",
-                     val.c_str(), limit.c_str());
-  return false;
-}
-
-bool Constraint::ReportErrorNotOneOf(ErrorPtr* error,
-                                     const std::string& val,
-                                     const std::vector<std::string>& values) {
-  Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                     errors::commands::kOutOfRange,
-                     "Value %s is invalid. Expected one of [%s]", val.c_str(),
-                     Join(",", values).c_str());
-  return false;
-}
-
-void Constraint::AddToJsonDict(base::DictionaryValue* dict,
-                               bool overridden_only) const {
-  if (!overridden_only || HasOverriddenAttributes()) {
-    auto value = ToJson();
-    CHECK(value);
-    dict->SetWithoutPathExpansion(GetDictKey(), value.release());
-  }
-}
-
-// ConstraintStringLength -----------------------------------------------------
-ConstraintStringLength::ConstraintStringLength(
-    const InheritableAttribute<int>& limit)
-    : limit_(limit) {}
-ConstraintStringLength::ConstraintStringLength(int limit) : limit_(limit) {}
-
-bool ConstraintStringLength::HasOverriddenAttributes() const {
-  return !limit_.is_inherited;
-}
-
-std::unique_ptr<base::Value> ConstraintStringLength::ToJson() const {
-  return TypedValueToJson(limit_.value);
-}
-
-// ConstraintStringLengthMin --------------------------------------------------
-ConstraintStringLengthMin::ConstraintStringLengthMin(
-    const InheritableAttribute<int>& limit)
-    : ConstraintStringLength(limit) {}
-ConstraintStringLengthMin::ConstraintStringLengthMin(int limit)
-    : ConstraintStringLength(limit) {}
-
-bool ConstraintStringLengthMin::Validate(const PropValue& value,
-                                         ErrorPtr* error) const {
-  CHECK(value.GetString()) << "Expecting a string value for this constraint";
-  const std::string& str = value.GetString()->GetValue();
-  int length = static_cast<int>(str.size());
-  if (length < limit_.value) {
-    if (limit_.value == 1) {
-      Error::AddTo(error, FROM_HERE, errors::commands::kDomain,
-                   errors::commands::kOutOfRange, "String must not be empty");
-    } else {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                         errors::commands::kOutOfRange,
-                         "String must be at least %d characters long,"
-                         " actual length of string '%s' is %d",
-                         limit_.value, str.c_str(), length);
-    }
-    return false;
-  }
-  return true;
-}
-
-std::unique_ptr<Constraint> ConstraintStringLengthMin::Clone() const {
-  return std::unique_ptr<Constraint>{new ConstraintStringLengthMin{limit_}};
-}
-
-std::unique_ptr<Constraint> ConstraintStringLengthMin::CloneAsInherited()
-    const {
-  return std::unique_ptr<Constraint>{
-      new ConstraintStringLengthMin{limit_.value}};
-}
-
-// ConstraintStringLengthMax --------------------------------------------------
-ConstraintStringLengthMax::ConstraintStringLengthMax(
-    const InheritableAttribute<int>& limit)
-    : ConstraintStringLength(limit) {}
-ConstraintStringLengthMax::ConstraintStringLengthMax(int limit)
-    : ConstraintStringLength(limit) {}
-
-bool ConstraintStringLengthMax::Validate(const PropValue& value,
-                                         ErrorPtr* error) const {
-  CHECK(value.GetString()) << "Expecting a string value for this constraint";
-  const std::string& str = value.GetString()->GetValue();
-  int length = static_cast<int>(str.size());
-  if (length > limit_.value) {
-    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                       errors::commands::kOutOfRange,
-                       "String must be no more than %d character(s) "
-                       "long, actual length of string '%s' is %d",
-                       limit_.value, str.c_str(), length);
-    return false;
-  }
-  return true;
-}
-
-std::unique_ptr<Constraint> ConstraintStringLengthMax::Clone() const {
-  return std::unique_ptr<Constraint>{new ConstraintStringLengthMax{limit_}};
-}
-
-std::unique_ptr<Constraint> ConstraintStringLengthMax::CloneAsInherited()
-    const {
-  return std::unique_ptr<Constraint>{
-      new ConstraintStringLengthMax{limit_.value}};
-}
-
-// ConstraintOneOf --------------------------------------------------
-ConstraintOneOf::ConstraintOneOf(InheritableAttribute<ValueVector> set)
-    : set_(std::move(set)) {}
-ConstraintOneOf::ConstraintOneOf(ValueVector set) : set_(std::move(set)) {}
-
-bool ConstraintOneOf::Validate(const PropValue& value, ErrorPtr* error) const {
-  for (const auto& item : set_.value) {
-    if (value.IsEqual(item.get()))
-      return true;
-  }
-  std::vector<std::string> choice_list;
-  choice_list.reserve(set_.value.size());
-  for (const auto& item : set_.value) {
-    choice_list.push_back(PropValueToString(*item));
-  }
-  return ReportErrorNotOneOf(error, PropValueToString(value), choice_list);
-}
-
-std::unique_ptr<Constraint> ConstraintOneOf::Clone() const {
-  InheritableAttribute<ValueVector> attr;
-  attr.is_inherited = set_.is_inherited;
-  attr.value.reserve(set_.value.size());
-  for (const auto& prop_value : set_.value) {
-    attr.value.push_back(prop_value->Clone());
-  }
-  return std::unique_ptr<Constraint>{new ConstraintOneOf{std::move(attr)}};
-}
-
-std::unique_ptr<Constraint> ConstraintOneOf::CloneAsInherited() const {
-  ValueVector cloned;
-  cloned.reserve(set_.value.size());
-  for (const auto& prop_value : set_.value) {
-    cloned.push_back(prop_value->Clone());
-  }
-  return std::unique_ptr<Constraint>{new ConstraintOneOf{std::move(cloned)}};
-}
-
-std::unique_ptr<base::Value> ConstraintOneOf::ToJson() const {
-  return TypedValueToJson(set_.value);
-}
-
-const char* ConstraintOneOf::GetDictKey() const {
-  return commands::attributes::kOneOf_Enum;
-}
-
-}  // namespace weave
diff --git a/src/commands/prop_constraints.h b/src/commands/prop_constraints.h
deleted file mode 100644
index 53a4d93..0000000
--- a/src/commands/prop_constraints.h
+++ /dev/null
@@ -1,315 +0,0 @@
-// Copyright 2015 The Weave Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef LIBWEAVE_SRC_COMMANDS_PROP_CONSTRAINTS_H_
-#define LIBWEAVE_SRC_COMMANDS_PROP_CONSTRAINTS_H_
-
-#include <string>
-#include <type_traits>
-#include <vector>
-
-#include <base/macros.h>
-#include <base/values.h>
-#include <weave/error.h>
-
-#include "src/commands/prop_values.h"
-#include "src/commands/schema_constants.h"
-#include "src/commands/schema_utils.h"
-#include "src/string_utils.h"
-
-namespace weave {
-
-enum class ConstraintType { Min, Max, StringLengthMin, StringLengthMax, OneOf };
-
-// Abstract base class for all parameter constraints. Many constraints are
-// type-dependent. Thus, a numeric parameter could have "minimum" and/or
-// "maximum" constraints specified. Some constraints, such as "OneOf" apply to
-// any data type.
-class Constraint {
- public:
-  Constraint() = default;
-  virtual ~Constraint();
-
-  // Gets the constraint type.
-  virtual ConstraintType GetType() const = 0;
-
-  // Checks if any of the constraint properties/attributes are overridden
-  // from their base schema definition. If the constraint is inherited, then
-  // it will not be written to JSON when saving partial schema.
-  virtual bool HasOverriddenAttributes() const = 0;
-
-  // Validates a parameter against the constraint. Returns true if parameter
-  // value satisfies the constraint, otherwise fills the optional |error| with
-  // the details for the failure.
-  virtual bool Validate(const PropValue& value, ErrorPtr* error) const = 0;
-
-  // Makes a full copy of this Constraint instance.
-  virtual std::unique_ptr<Constraint> Clone() const = 0;
-
-  // Makes a copy of the constraint object, marking all the attributes
-  // as inherited from the original definition.
-  virtual std::unique_ptr<Constraint> CloneAsInherited() const = 0;
-
-  // Saves the constraint into the specified JSON |dict| object, representing
-  // the object schema. If |overridden_only| is set to true, then the
-  // inherited constraints will not be added to the schema object.
-  virtual void AddToJsonDict(base::DictionaryValue* dict,
-                             bool overridden_only) const;
-
-  // Saves the value of constraint to JSON value. E.g., if the numeric
-  // constraint was defined as {"minimum":20} this will create a JSON value
-  // of 20. The current design implies that each constraint has one value
-  // only. If this assumption changes, this interface needs to be updated
-  // accordingly.
-  virtual std::unique_ptr<base::Value> ToJson() const = 0;
-
-  // Overloaded by the concrete class implementation, it should return the
-  // JSON object property name to store the constraint's value as.
-  // E.g., if the numeric constraint was defined as {"minimum":20} this
-  // method should return "minimum".
-  virtual const char* GetDictKey() const = 0;
-
- protected:
-  // Static helper methods to format common constraint validation errors.
-  // They fill the |error| object with specific error message.
-  // Since these functions could be used by constraint objects for various
-  // data types, the values used in validation are expected to be
-  // send as strings already.
-  static bool ReportErrorLessThan(ErrorPtr* error,
-                                  const std::string& val,
-                                  const std::string& limit);
-  static bool ReportErrorGreaterThan(ErrorPtr* error,
-                                     const std::string& val,
-                                     const std::string& limit);
-
-  static bool ReportErrorNotOneOf(ErrorPtr* error,
-                                  const std::string& val,
-                                  const std::vector<std::string>& values);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(Constraint);
-};
-
-// ConstraintMinMaxBase is a base class for numeric Minimum and Maximum
-// constraints.
-template <typename T>
-class ConstraintMinMaxBase : public Constraint {
- public:
-  explicit ConstraintMinMaxBase(const InheritableAttribute<T>& limit)
-      : limit_(limit) {}
-  explicit ConstraintMinMaxBase(const T& limit) : limit_(limit) {}
-
-  // Implementation of Constraint::HasOverriddenAttributes().
-  bool HasOverriddenAttributes() const override { return !limit_.is_inherited; }
-
-  // Implementation of Constraint::ToJson().
-  std::unique_ptr<base::Value> ToJson() const override {
-    return TypedValueToJson(limit_.value);
-  }
-
-  // Stores the upper/lower value limit for maximum/minimum constraint.
-  // |limit_.is_inherited| indicates whether the constraint is inherited
-  // from base schema or overridden.
-  InheritableAttribute<T> limit_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ConstraintMinMaxBase);
-};
-
-// Implementation of Minimum value constraint for Integer/Double types.
-template <typename T>
-class ConstraintMin : public ConstraintMinMaxBase<T> {
- public:
-  explicit ConstraintMin(const InheritableAttribute<T>& limit)
-      : ConstraintMinMaxBase<T>(limit) {}
-  explicit ConstraintMin(const T& limit) : ConstraintMinMaxBase<T>(limit) {}
-
-  // Implementation of Constraint::GetType().
-  ConstraintType GetType() const override { return ConstraintType::Min; }
-
-  // Implementation of Constraint::Validate().
-  bool Validate(const PropValue& value, ErrorPtr* error) const override {
-    const T& v = static_cast<const TypedValueBase<T>&>(value).GetValue();
-    if (v < this->limit_.value) {
-      return this->ReportErrorLessThan(error, std::to_string(v),
-                                       std::to_string(this->limit_.value));
-    }
-    return true;
-  }
-
-  // Implementation of Constraint::Clone().
-  std::unique_ptr<Constraint> Clone() const override {
-    return std::unique_ptr<Constraint>{new ConstraintMin{this->limit_}};
-  }
-
-  // Implementation of Constraint::CloneAsInherited().
-  std::unique_ptr<Constraint> CloneAsInherited() const override {
-    return std::unique_ptr<Constraint>{new ConstraintMin{this->limit_.value}};
-  }
-
-  // Implementation of Constraint::GetDictKey().
-  const char* GetDictKey() const override {
-    return commands::attributes::kNumeric_Min;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ConstraintMin);
-};
-
-// Implementation of Maximum value constraint for Integer/Double types.
-template <typename T>
-class ConstraintMax : public ConstraintMinMaxBase<T> {
- public:
-  explicit ConstraintMax(const InheritableAttribute<T>& limit)
-      : ConstraintMinMaxBase<T>(limit) {}
-  explicit ConstraintMax(const T& limit) : ConstraintMinMaxBase<T>(limit) {}
-
-  // Implementation of Constraint::GetType().
-  ConstraintType GetType() const override { return ConstraintType::Max; }
-
-  // Implementation of Constraint::Validate().
-  bool Validate(const PropValue& value, ErrorPtr* error) const override {
-    const T& v = static_cast<const TypedValueBase<T>&>(value).GetValue();
-    if (v > this->limit_.value)
-      return this->ReportErrorGreaterThan(error, std::to_string(v),
-                                          std::to_string(this->limit_.value));
-    return true;
-  }
-
-  // Implementation of Constraint::Clone().
-  std::unique_ptr<Constraint> Clone() const override {
-    return std::unique_ptr<Constraint>{new ConstraintMax{this->limit_}};
-  }
-
-  // Implementation of Constraint::CloneAsInherited().
-  std::unique_ptr<Constraint> CloneAsInherited() const override {
-    return std::unique_ptr<Constraint>{new ConstraintMax{this->limit_.value}};
-  }
-
-  // Implementation of Constraint::GetDictKey().
-  const char* GetDictKey() const override {
-    return commands::attributes::kNumeric_Max;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ConstraintMax);
-};
-
-// ConstraintStringLength is a base class for Minimum/Maximum string length
-// constraints, similar to ConstraintMinMaxBase of numeric types.
-class ConstraintStringLength : public Constraint {
- public:
-  explicit ConstraintStringLength(const InheritableAttribute<int>& limit);
-  explicit ConstraintStringLength(int limit);
-
-  // Implementation of Constraint::HasOverriddenAttributes().
-  bool HasOverriddenAttributes() const override;
-  // Implementation of Constraint::ToJson().
-  std::unique_ptr<base::Value> ToJson() const override;
-
-  // Stores the upper/lower value limit for string length constraint.
-  // |limit_.is_inherited| indicates whether the constraint is inherited
-  // from base schema or overridden.
-  InheritableAttribute<int> limit_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ConstraintStringLength);
-};
-
-// Implementation of Minimum string length constraint.
-class ConstraintStringLengthMin : public ConstraintStringLength {
- public:
-  explicit ConstraintStringLengthMin(const InheritableAttribute<int>& limit);
-  explicit ConstraintStringLengthMin(int limit);
-
-  // Implementation of Constraint::GetType().
-  ConstraintType GetType() const override {
-    return ConstraintType::StringLengthMin;
-  }
-
-  // Implementation of Constraint::Validate().
-  bool Validate(const PropValue& value, ErrorPtr* error) const override;
-
-  // Implementation of Constraint::Clone().
-  std::unique_ptr<Constraint> Clone() const override;
-
-  // Implementation of Constraint::CloneAsInherited().
-  std::unique_ptr<Constraint> CloneAsInherited() const override;
-  // Implementation of Constraint::GetDictKey().
-  const char* GetDictKey() const override {
-    return commands::attributes::kString_MinLength;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ConstraintStringLengthMin);
-};
-
-// Implementation of Maximum string length constraint.
-class ConstraintStringLengthMax : public ConstraintStringLength {
- public:
-  explicit ConstraintStringLengthMax(const InheritableAttribute<int>& limit);
-  explicit ConstraintStringLengthMax(int limit);
-
-  // Implementation of Constraint::GetType().
-  ConstraintType GetType() const override {
-    return ConstraintType::StringLengthMax;
-  }
-
-  // Implementation of Constraint::Validate().
-  bool Validate(const PropValue& value, ErrorPtr* error) const override;
-
-  // Implementation of Constraint::Clone().
-  std::unique_ptr<Constraint> Clone() const override;
-
-  // Implementation of Constraint::CloneAsInherited().
-  std::unique_ptr<Constraint> CloneAsInherited() const override;
-
-  // Implementation of Constraint::GetDictKey().
-  const char* GetDictKey() const override {
-    return commands::attributes::kString_MaxLength;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ConstraintStringLengthMax);
-};
-
-// Implementation of OneOf constraint for different data types.
-class ConstraintOneOf : public Constraint {
- public:
-  explicit ConstraintOneOf(InheritableAttribute<ValueVector> set);
-  explicit ConstraintOneOf(ValueVector set);
-
-  // Implementation of Constraint::GetType().
-  ConstraintType GetType() const override { return ConstraintType::OneOf; }
-
-  // Implementation of Constraint::HasOverriddenAttributes().
-  bool HasOverriddenAttributes() const override { return !set_.is_inherited; }
-
-  // Implementation of Constraint::Validate().
-  bool Validate(const PropValue& value, ErrorPtr* error) const override;
-
-  // Implementation of Constraint::Clone().
-  std::unique_ptr<Constraint> Clone() const override;
-
-  // Implementation of Constraint::CloneAsInherited().
-  std::unique_ptr<Constraint> CloneAsInherited() const override;
-
-  // Implementation of Constraint::ToJson().
-  std::unique_ptr<base::Value> ToJson() const override;
-
-  // Implementation of Constraint::GetDictKey().
-  const char* GetDictKey() const override;
-
-  // Stores the list of acceptable values for the parameter.
-  // |set_.is_inherited| indicates whether the constraint is inherited
-  // from base schema or overridden.
-  InheritableAttribute<ValueVector> set_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ConstraintOneOf);
-};
-
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_COMMANDS_PROP_CONSTRAINTS_H_
diff --git a/src/commands/prop_types.cc b/src/commands/prop_types.cc
deleted file mode 100644
index def68b1..0000000
--- a/src/commands/prop_types.cc
+++ /dev/null
@@ -1,655 +0,0 @@
-// Copyright 2015 The Weave 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 "src/commands/prop_types.h"
-
-#include <algorithm>
-#include <limits>
-#include <set>
-
-#include <base/json/json_writer.h>
-#include <base/logging.h>
-#include <base/values.h>
-
-#include "src/commands/object_schema.h"
-#include "src/commands/prop_values.h"
-#include "src/commands/schema_constants.h"
-
-namespace weave {
-
-// PropType -------------------------------------------------------------------
-PropType::PropType() {}
-
-PropType::~PropType() {}
-
-std::string PropType::GetTypeAsString() const {
-  return GetTypeStringFromType(GetType());
-}
-
-bool PropType::HasOverriddenAttributes() const {
-  if (default_.value && !default_.is_inherited)
-    return true;
-
-  for (const auto& pair : constraints_) {
-    if (pair.second->HasOverriddenAttributes())
-      return true;
-  }
-  return false;
-}
-
-bool PropType::IsRequired() const {
-  return required_.value;
-}
-
-void PropType::MakeRequired(bool required) {
-  required_.value = required;
-  required_.is_inherited = false;
-}
-
-std::unique_ptr<base::Value> PropType::ToJson(bool full_schema,
-                                              bool in_command_def) const {
-  // Determine if we need to output "isRequired" attribute.
-  const bool include_required = in_command_def && !required_.is_inherited;
-
-  // If we must include "isRequired" attribute, then treat this as "full schema"
-  // request because there could be cases where we have just this attribute and
-  // won't be able to infer the type from the constraints only.
-  if (include_required)
-    full_schema = true;
-
-  if (!full_schema && !HasOverriddenAttributes()) {
-    if (based_on_schema_)
-      return std::unique_ptr<base::Value>(new base::DictionaryValue);
-    return TypedValueToJson(GetTypeAsString());
-  }
-
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
-  if (full_schema) {
-    // If we are asked for full_schema definition, then we need to output every
-    // parameter property, including the "type", and any constraints.
-    // So, we write the "type" only if asked for full schema.
-    // Otherwise we will be able to infer the parameter type based on
-    // the constraints and their types.
-    // That is, the following JSONs could possibly define a parameter:
-    // {'type':'integer'} -> explicit "integer" with no constraints
-    // {'minimum':10} -> no type specified, but since we have "minimum"
-    //                   and 10 is integer, than this is an integer
-    //                   parameter with min constraint.
-    // {'enum':[1,2,3]} -> integer with OneOf constraint.
-    // And so is this: [1,2,3] -> an array of ints assume it's an "int" enum.
-    dict->SetString(commands::attributes::kType, GetTypeAsString());
-  }
-
-  if (!full_schema && constraints_.size() == 1) {
-    // If we are not asked for full schema, and we have only one constraint
-    // which is OneOf, we short-circuit the whole thing and return just
-    // the array [1,2,3] instead of an object with "enum" property like:
-    // {'enum':[1,2,3]}
-    auto p = constraints_.find(ConstraintType::OneOf);
-    if (p != constraints_.end()) {
-      return p->second->ToJson();
-    }
-  }
-
-  for (const auto& pair : constraints_)
-    pair.second->AddToJsonDict(dict.get(), !full_schema);
-
-  if (default_.value && (full_schema || !default_.is_inherited)) {
-    auto def_val = default_.value->ToJson();
-    CHECK(def_val);
-    dict->Set(commands::attributes::kDefault, def_val.release());
-  }
-
-  if (include_required)
-    dict->SetBoolean(commands::attributes::kIsRequired, required_.value);
-  return std::unique_ptr<base::Value>(dict.release());
-}
-
-std::unique_ptr<PropType> PropType::Clone() const {
-  auto cloned = PropType::Create(GetType());
-  cloned->based_on_schema_ = based_on_schema_;
-  for (const auto& pair : constraints_) {
-    cloned->constraints_.insert(
-        std::make_pair(pair.first, pair.second->Clone()));
-  }
-  cloned->default_.is_inherited = default_.is_inherited;
-  if (default_.value)
-    cloned->default_.value = default_.value->Clone();
-  cloned->required_ = required_;
-  return cloned;
-}
-
-bool PropType::FromJson(const base::DictionaryValue* value,
-                        const PropType* base_schema,
-                        ErrorPtr* error) {
-  if (base_schema && base_schema->GetType() != GetType()) {
-    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                       errors::commands::kPropTypeChanged,
-                       "Redefining a property of type %s as %s",
-                       base_schema->GetTypeAsString().c_str(),
-                       GetTypeAsString().c_str());
-    return false;
-  }
-  based_on_schema_ = (base_schema != nullptr);
-  constraints_.clear();
-  // Add the well-known object properties first (like "type", "displayName",
-  // "default") to the list of "processed" keys so we do not complain about them
-  // when we check for unknown/unexpected keys below.
-  std::set<std::string> processed_keys{
-      commands::attributes::kType, commands::attributes::kDisplayName,
-      commands::attributes::kDefault, commands::attributes::kIsRequired,
-  };
-  if (!ObjectSchemaFromJson(value, base_schema, &processed_keys, error))
-    return false;
-  if (base_schema) {
-    for (const auto& pair : base_schema->GetConstraints()) {
-      constraints_.insert(
-          std::make_pair(pair.first, pair.second->CloneAsInherited()));
-    }
-  }
-  if (!ConstraintsFromJson(value, &processed_keys, error))
-    return false;
-
-  // Now make sure there are no unexpected/unknown keys in the property schema
-  // definition object.
-  base::DictionaryValue::Iterator iter(*value);
-  while (!iter.IsAtEnd()) {
-    std::string key = iter.key();
-    if (processed_keys.find(key) == processed_keys.end()) {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                         errors::commands::kUnknownProperty,
-                         "Unexpected property '%s'", key.c_str());
-      return false;
-    }
-    iter.Advance();
-  }
-
-  // Read the "isRequired" attribute, if specified.
-  bool required = false;
-  if (value->GetBoolean(commands::attributes::kIsRequired, &required)) {
-    required_.value = required;
-    required_.is_inherited = false;
-  } else if (base_schema) {
-    // If we have the base schema, inherit the type's required value from it.
-    if (base_schema->required_.value)
-      required_.value = base_schema->required_.value;
-    required_.is_inherited = true;
-  }
-
-  // Read the default value, if specified.
-  // We need to do this last since the current type definition must be complete,
-  // so we can parse and validate the value of the default.
-  const base::Value* defval = nullptr;  // Owned by value
-  if (value->GetWithoutPathExpansion(commands::attributes::kDefault, &defval)) {
-    std::unique_ptr<PropValue> prop_value = CreatePropValue(*defval, error);
-    if (!prop_value) {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                         errors::commands::kInvalidPropValue,
-                         "Invalid value for property '%s'",
-                         commands::attributes::kDefault);
-      return false;
-    }
-    default_.value = std::move(prop_value);
-    default_.is_inherited = false;
-  } else if (base_schema) {
-    // If we have the base schema, inherit the type's default value from it.
-    // It doesn't matter if the base schema actually has a default value
-    // specified or not. If it doesn't, then the current type definition will
-    // have no default value set either (|default_.value| is a unique_ptr to
-    // PropValue, which can be set to nullptr).
-    if (base_schema->default_.value)
-      default_.value = base_schema->default_.value->Clone();
-    default_.is_inherited = true;
-  }
-  return true;
-}
-
-void PropType::AddConstraint(std::unique_ptr<Constraint> constraint) {
-  constraints_[constraint->GetType()] = std::move(constraint);
-}
-
-void PropType::RemoveConstraint(ConstraintType constraint_type) {
-  constraints_.erase(constraint_type);
-}
-
-void PropType::RemoveAllConstraints() {
-  constraints_.clear();
-}
-
-const Constraint* PropType::GetConstraint(
-    ConstraintType constraint_type) const {
-  auto p = constraints_.find(constraint_type);
-  return p != constraints_.end() ? p->second.get() : nullptr;
-}
-
-Constraint* PropType::GetConstraint(ConstraintType constraint_type) {
-  auto p = constraints_.find(constraint_type);
-  return p != constraints_.end() ? p->second.get() : nullptr;
-}
-
-bool PropType::ValidateConstraints(const PropValue& value,
-                                   ErrorPtr* error) const {
-  for (const auto& pair : constraints_) {
-    if (!pair.second->Validate(value, error))
-      return false;
-  }
-  return true;
-}
-
-const PropType::TypeMap& PropType::GetTypeMap() {
-  static TypeMap map = {
-      {ValueType::Int, "integer"},   {ValueType::Double, "number"},
-      {ValueType::String, "string"}, {ValueType::Boolean, "boolean"},
-      {ValueType::Object, "object"}, {ValueType::Array, "array"},
-  };
-  return map;
-}
-
-std::string PropType::GetTypeStringFromType(ValueType type) {
-  for (const auto& pair : GetTypeMap()) {
-    if (pair.first == type)
-      return pair.second;
-  }
-  LOG(FATAL) << "Type map is missing a type";
-  return std::string();
-}
-
-bool PropType::GetTypeFromTypeString(const std::string& name, ValueType* type) {
-  for (const auto& pair : GetTypeMap()) {
-    if (pair.second == name) {
-      *type = pair.first;
-      return true;
-    }
-  }
-  return false;
-}
-
-std::unique_ptr<PropType> PropType::Create(ValueType type) {
-  PropType* prop = nullptr;
-  switch (type) {
-    case ValueType::Int:
-      prop = new IntPropType;
-      break;
-    case ValueType::Double:
-      prop = new DoublePropType;
-      break;
-    case ValueType::String:
-      prop = new StringPropType;
-      break;
-    case ValueType::Boolean:
-      prop = new BooleanPropType;
-      break;
-    case ValueType::Object:
-      prop = new ObjectPropType;
-      break;
-    case ValueType::Array:
-      prop = new ArrayPropType;
-      break;
-  }
-  return std::unique_ptr<PropType>(prop);
-}
-
-template <typename T>
-static std::unique_ptr<Constraint> LoadOneOfConstraint(
-    const base::DictionaryValue* value,
-    const PropType* prop_type,
-    ErrorPtr* error) {
-  std::unique_ptr<Constraint> constraint;
-  const base::Value* list = nullptr;  // Owned by |value|
-  CHECK(value->Get(commands::attributes::kOneOf_Enum, &list))
-      << "'enum' property missing in JSON dictionary";
-  ValueVector choice_list;
-  ArrayPropType array_type;
-  array_type.SetItemType(prop_type->Clone());
-  if (!TypedValueFromJson(list, &array_type, &choice_list, error))
-    return constraint;
-  InheritableAttribute<ValueVector> val(std::move(choice_list), false);
-  constraint.reset(new ConstraintOneOf{std::move(val)});
-  return constraint;
-}
-
-template <class ConstraintClass, typename T>
-static std::unique_ptr<Constraint> LoadMinMaxConstraint(
-    const char* dict_key,
-    const base::DictionaryValue* value,
-    ErrorPtr* error) {
-  std::unique_ptr<Constraint> constraint;
-  InheritableAttribute<T> limit;
-
-  const base::Value* src_val = nullptr;
-  CHECK(value->Get(dict_key, &src_val)) << "Unable to get min/max constraints";
-  if (!TypedValueFromJson(src_val, nullptr, &limit.value, error))
-    return constraint;
-  limit.is_inherited = false;
-
-  constraint.reset(new ConstraintClass{limit});
-  return constraint;
-}
-
-// PropTypeBase ----------------------------------------------------------------
-
-template <class Derived, class Value, typename T>
-bool PropTypeBase<Derived, Value, T>::ConstraintsFromJson(
-    const base::DictionaryValue* value,
-    std::set<std::string>* processed_keys,
-    ErrorPtr* error) {
-  if (!PropType::ConstraintsFromJson(value, processed_keys, error))
-    return false;
-
-  if (value->HasKey(commands::attributes::kOneOf_Enum)) {
-    auto type = Clone();
-    type->RemoveAllConstraints();
-    auto constraint = LoadOneOfConstraint<T>(value, type.get(), error);
-    if (!constraint)
-      return false;
-    this->AddConstraint(std::move(constraint));
-    this->RemoveConstraint(ConstraintType::Min);
-    this->RemoveConstraint(ConstraintType::Max);
-    processed_keys->insert(commands::attributes::kOneOf_Enum);
-  }
-
-  return true;
-}
-
-// NumericPropTypeBase ---------------------------------------------------------
-
-template <class Derived, class Value, typename T>
-bool NumericPropTypeBase<Derived, Value, T>::ConstraintsFromJson(
-    const base::DictionaryValue* value,
-    std::set<std::string>* processed_keys,
-    ErrorPtr* error) {
-  if (!Base::ConstraintsFromJson(value, processed_keys, error))
-    return false;
-
-  if (processed_keys->find(commands::attributes::kOneOf_Enum) ==
-      processed_keys->end()) {
-    // Process min/max constraints only if "enum" constraint wasn't already
-    // specified.
-    if (value->HasKey(commands::attributes::kNumeric_Min)) {
-      auto constraint = LoadMinMaxConstraint<ConstraintMin<T>, T>(
-          commands::attributes::kNumeric_Min, value, error);
-      if (!constraint)
-        return false;
-      this->AddConstraint(std::move(constraint));
-      this->RemoveConstraint(ConstraintType::OneOf);
-      processed_keys->insert(commands::attributes::kNumeric_Min);
-    }
-    if (value->HasKey(commands::attributes::kNumeric_Max)) {
-      auto constraint = LoadMinMaxConstraint<ConstraintMax<T>, T>(
-          commands::attributes::kNumeric_Max, value, error);
-      if (!constraint)
-        return false;
-      this->AddConstraint(std::move(constraint));
-      this->RemoveConstraint(ConstraintType::OneOf);
-      processed_keys->insert(commands::attributes::kNumeric_Max);
-    }
-  }
-
-  return true;
-}
-
-// StringPropType -------------------------------------------------------------
-
-bool StringPropType::ConstraintsFromJson(const base::DictionaryValue* value,
-                                         std::set<std::string>* processed_keys,
-                                         ErrorPtr* error) {
-  if (!Base::ConstraintsFromJson(value, processed_keys, error))
-    return false;
-
-  if (processed_keys->find(commands::attributes::kOneOf_Enum) ==
-      processed_keys->end()) {
-    // Process min/max constraints only if "enum" constraint wasn't already
-    // specified.
-    if (value->HasKey(commands::attributes::kString_MinLength)) {
-      auto constraint = LoadMinMaxConstraint<ConstraintStringLengthMin, int>(
-          commands::attributes::kString_MinLength, value, error);
-      if (!constraint)
-        return false;
-      AddConstraint(std::move(constraint));
-      RemoveConstraint(ConstraintType::OneOf);
-      processed_keys->insert(commands::attributes::kString_MinLength);
-    }
-    if (value->HasKey(commands::attributes::kString_MaxLength)) {
-      auto constraint = LoadMinMaxConstraint<ConstraintStringLengthMax, int>(
-          commands::attributes::kString_MaxLength, value, error);
-      if (!constraint)
-        return false;
-      AddConstraint(std::move(constraint));
-      RemoveConstraint(ConstraintType::OneOf);
-      processed_keys->insert(commands::attributes::kString_MaxLength);
-    }
-  }
-  return true;
-}
-
-void StringPropType::AddLengthConstraint(int min_len, int max_len) {
-  InheritableAttribute<int> min_attr(min_len, false);
-  InheritableAttribute<int> max_attr(max_len, false);
-  AddConstraint(std::unique_ptr<ConstraintStringLengthMin>{
-      new ConstraintStringLengthMin{min_attr}});
-  AddConstraint(std::unique_ptr<ConstraintStringLengthMax>{
-      new ConstraintStringLengthMax{max_attr}});
-}
-
-int StringPropType::GetMinLength() const {
-  auto slc = static_cast<const ConstraintStringLength*>(
-      GetConstraint(ConstraintType::StringLengthMin));
-  return slc ? slc->limit_.value : 0;
-}
-
-int StringPropType::GetMaxLength() const {
-  auto slc = static_cast<const ConstraintStringLength*>(
-      GetConstraint(ConstraintType::StringLengthMax));
-  return slc ? slc->limit_.value : std::numeric_limits<int>::max();
-}
-
-// ObjectPropType -------------------------------------------------------------
-
-ObjectPropType::ObjectPropType()
-    : object_schema_{ObjectSchema::Create(), false} {}
-
-bool ObjectPropType::HasOverriddenAttributes() const {
-  return PropType::HasOverriddenAttributes() || !object_schema_.is_inherited;
-}
-
-std::unique_ptr<PropType> ObjectPropType::Clone() const {
-  auto cloned = Base::Clone();
-
-  cloned->GetObject()->object_schema_.is_inherited =
-      object_schema_.is_inherited;
-  cloned->GetObject()->object_schema_.value = object_schema_.value->Clone();
-  return cloned;
-}
-
-std::unique_ptr<base::Value> ObjectPropType::ToJson(bool full_schema,
-                                                    bool in_command_def) const {
-  std::unique_ptr<base::Value> value =
-      PropType::ToJson(full_schema, in_command_def);
-  CHECK(value);
-  base::DictionaryValue* dict = nullptr;
-  CHECK(value->GetAsDictionary(&dict)) << "Expecting a JSON object";
-  if (!object_schema_.is_inherited || full_schema) {
-    auto object_schema = object_schema_.value->ToJson(full_schema, false);
-    CHECK(object_schema);
-
-    dict->SetWithoutPathExpansion(commands::attributes::kObject_Properties,
-                                  object_schema.release());
-    dict->SetBooleanWithoutPathExpansion(
-        commands::attributes::kObject_AdditionalProperties,
-        object_schema_.value->GetExtraPropertiesAllowed());
-    std::unique_ptr<base::ListValue> required{new base::ListValue};
-    for (const auto& pair : object_schema_.value->GetProps()) {
-      if (pair.second->IsRequired())
-        required->AppendString(pair.first);
-    }
-    if (required->GetSize() > 0) {
-      dict->Set(commands::attributes::kObject_Required, required.release());
-    }
-  }
-  return value;
-}
-
-bool ObjectPropType::ObjectSchemaFromJson(const base::DictionaryValue* value,
-                                          const PropType* base_schema,
-                                          std::set<std::string>* processed_keys,
-                                          ErrorPtr* error) {
-  if (!Base::ObjectSchemaFromJson(value, base_schema, processed_keys, error))
-    return false;
-
-  using commands::attributes::kObject_Properties;
-  using commands::attributes::kObject_AdditionalProperties;
-
-  const ObjectSchema* base_object_schema = nullptr;
-  if (base_schema)
-    base_object_schema = base_schema->GetObject()->GetObjectSchemaPtr();
-
-  const base::DictionaryValue* props = nullptr;
-  std::unique_ptr<ObjectSchema> object_schema;
-  bool inherited = false;
-  if (value->GetDictionaryWithoutPathExpansion(kObject_Properties, &props)) {
-    processed_keys->insert(kObject_Properties);
-    object_schema.reset(new ObjectSchema);
-    if (!object_schema->FromJson(props, base_object_schema, error)) {
-      Error::AddTo(error, FROM_HERE, errors::commands::kDomain,
-                   errors::commands::kInvalidObjectSchema,
-                   "Error parsing object property schema");
-      return false;
-    }
-  } else if (base_object_schema) {
-    object_schema = base_object_schema->Clone();
-    inherited = true;
-  } else {
-    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                       errors::commands::kInvalidObjectSchema,
-                       "Object type definition must include the "
-                       "object schema ('%s' field not found)",
-                       kObject_Properties);
-    return false;
-  }
-  bool extra_properties_allowed = false;
-  if (value->GetBooleanWithoutPathExpansion(kObject_AdditionalProperties,
-                                            &extra_properties_allowed)) {
-    processed_keys->insert(kObject_AdditionalProperties);
-    object_schema->SetExtraPropertiesAllowed(extra_properties_allowed);
-    inherited = false;
-  }
-  const base::Value* required = nullptr;
-  if (value->Get(commands::attributes::kObject_Required, &required)) {
-    processed_keys->insert(commands::attributes::kObject_Required);
-    const base::ListValue* required_list = nullptr;
-    if (!required->GetAsList(&required_list)) {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                         errors::commands::kInvalidObjectSchema,
-                         "Property '%s' must be an array",
-                         commands::attributes::kObject_Required);
-      return false;
-    }
-    for (const base::Value* value : *required_list) {
-      std::string name;
-      if (!value->GetAsString(&name)) {
-        std::string json_value;
-        CHECK(base::JSONWriter::Write(*value, &json_value));
-        Error::AddToPrintf(
-            error, FROM_HERE, errors::commands::kDomain,
-            errors::commands::kInvalidObjectSchema,
-            "Property '%s' contains invalid element (%s). String expected",
-            commands::attributes::kObject_Required, json_value.c_str());
-        return false;
-      }
-      if (!object_schema->MarkPropRequired(name, error))
-        return false;
-      inherited = false;
-    }
-  }
-  object_schema_.value = std::move(object_schema);
-  object_schema_.is_inherited = inherited;
-
-  return true;
-}
-
-void ObjectPropType::SetObjectSchema(
-    std::unique_ptr<const ObjectSchema> schema) {
-  object_schema_.value = std::move(schema);
-  object_schema_.is_inherited = false;
-}
-
-// ArrayPropType -------------------------------------------------------------
-
-ArrayPropType::ArrayPropType() {}
-
-bool ArrayPropType::HasOverriddenAttributes() const {
-  return PropType::HasOverriddenAttributes() || !item_type_.is_inherited;
-}
-
-std::unique_ptr<PropType> ArrayPropType::Clone() const {
-  auto cloned = Base::Clone();
-
-  cloned->GetArray()->item_type_.is_inherited = item_type_.is_inherited;
-  cloned->GetArray()->item_type_.value = item_type_.value->Clone();
-  return cloned;
-}
-
-std::unique_ptr<base::Value> ArrayPropType::ToJson(bool full_schema,
-                                                   bool in_command_def) const {
-  std::unique_ptr<base::Value> value =
-      PropType::ToJson(full_schema, in_command_def);
-  CHECK(value);
-  if (!item_type_.is_inherited || full_schema) {
-    base::DictionaryValue* dict = nullptr;
-    CHECK(value->GetAsDictionary(&dict)) << "Expecting a JSON object";
-    auto type = item_type_.value->ToJson(full_schema, false);
-    CHECK(type);
-    dict->SetWithoutPathExpansion(commands::attributes::kItems, type.release());
-  }
-  return value;
-}
-
-bool ArrayPropType::ObjectSchemaFromJson(const base::DictionaryValue* value,
-                                         const PropType* base_schema,
-                                         std::set<std::string>* processed_keys,
-                                         ErrorPtr* error) {
-  if (!Base::ObjectSchemaFromJson(value, base_schema, processed_keys, error))
-    return false;
-
-  using commands::attributes::kItems;
-
-  const PropType* base_type = nullptr;
-  if (base_schema)
-    base_type = base_schema->GetArray()->GetItemTypePtr();
-
-  const base::Value* type_value = nullptr;
-  if (value->GetWithoutPathExpansion(kItems, &type_value)) {
-    processed_keys->insert(kItems);
-    auto item_type = ObjectSchema::PropFromJson(*type_value, base_type, error);
-    if (!item_type)
-      return false;
-    if (item_type->GetType() == ValueType::Array) {
-      Error::AddTo(error, FROM_HERE, errors::commands::kDomain,
-                   errors::commands::kInvalidObjectSchema,
-                   "Arrays of arrays are not supported");
-      return false;
-    }
-    SetItemType(std::move(item_type));
-  } else if (!item_type_.value) {
-    if (base_type) {
-      item_type_.value = base_type->Clone();
-      item_type_.is_inherited = true;
-    } else {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                         errors::commands::kInvalidObjectSchema,
-                         "Array type definition must include the "
-                         "array item type ('%s' field not found)",
-                         kItems);
-      return false;
-    }
-  }
-  return true;
-}
-
-void ArrayPropType::SetItemType(std::unique_ptr<const PropType> item_type) {
-  item_type_.value = std::move(item_type);
-  item_type_.is_inherited = false;
-}
-
-}  // namespace weave
diff --git a/src/commands/prop_types.h b/src/commands/prop_types.h
deleted file mode 100644
index f784eb6..0000000
--- a/src/commands/prop_types.h
+++ /dev/null
@@ -1,352 +0,0 @@
-// Copyright 2015 The Weave Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef LIBWEAVE_SRC_COMMANDS_PROP_TYPES_H_
-#define LIBWEAVE_SRC_COMMANDS_PROP_TYPES_H_
-
-#include <limits>
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <weave/error.h>
-
-#include "src/commands/prop_constraints.h"
-#include "src/commands/prop_values.h"
-
-namespace weave {
-
-class IntPropType;
-class DoublePropType;
-class StringPropType;
-class BooleanPropType;
-class ObjectPropType;
-class ArrayPropType;
-
-// PropType is a base class for all parameter type definition objects.
-// Property definitions of a particular type will derive from this class and
-// provide type-specific implementations.
-class PropType {
- public:
-  // ConstraintMap is a type alias for a map containing parameter
-  // constraints. It is implemented as a map for fast look-ups of constraints
-  // of particular type. Also it is expected to have at most one constraint
-  // of each type (e.g. it makes no sense to impose two "minimum" constraints
-  // onto a numeric parameter).
-  using ConstraintMap = std::map<ConstraintType, std::unique_ptr<Constraint>>;
-
-  PropType();
-  virtual ~PropType();
-
-  // Gets the parameter type as an enumeration.
-  virtual ValueType GetType() const = 0;
-  // Gets the parameter type as a string.
-  std::string GetTypeAsString() const;
-  // Returns true if this parameter definition inherits a type
-  // definition from a base object schema.
-  bool IsBasedOnSchema() const { return based_on_schema_; }
-  // Returns a default value specified for the type, or nullptr if no default
-  // is available.
-  const PropValue* GetDefaultValue() const { return default_.value.get(); }
-  // Gets the constraints specified for the parameter, if any.
-  const ConstraintMap& GetConstraints() const { return constraints_; }
-  // Returns true if this value is required. Properties are marked as required
-  // by using "isRequired" attribute or listed in "required" array.
-  bool IsRequired() const;
-  // Sets the required attribute to the value of |required| and marks it as
-  // overridden (not-inherited).
-  void MakeRequired(bool required);
-  // Checks if any of the type attributes were overridden from the base
-  // schema definition. If this type does not inherit from a base schema,
-  // this method returns true.
-  // An attribute could be the value of any of the constraints, default
-  // value of a parameter or any other data that may be specified in
-  // parameter type definition in and can be inherited from the base schema.
-  virtual bool HasOverriddenAttributes() const;
-
-  // Type conversion methods. Used in lieu of RTTI and dynamic_cast<>.
-  virtual IntPropType* GetInt() { return nullptr; }
-  virtual IntPropType const* GetInt() const { return nullptr; }
-  virtual DoublePropType* GetDouble() { return nullptr; }
-  virtual DoublePropType const* GetDouble() const { return nullptr; }
-  virtual StringPropType* GetString() { return nullptr; }
-  virtual StringPropType const* GetString() const { return nullptr; }
-  virtual BooleanPropType* GetBoolean() { return nullptr; }
-  virtual BooleanPropType const* GetBoolean() const { return nullptr; }
-  virtual ObjectPropType* GetObject() { return nullptr; }
-  virtual ObjectPropType const* GetObject() const { return nullptr; }
-  virtual ArrayPropType* GetArray() { return nullptr; }
-  virtual ArrayPropType const* GetArray() const { return nullptr; }
-
-  // Makes a full copy of this type definition.
-  virtual std::unique_ptr<PropType> Clone() const;
-
-  // Creates an instance of associated value object.
-  virtual std::unique_ptr<PropValue> CreatePropValue(const base::Value& value,
-                                                     ErrorPtr* error) const = 0;
-
-  // Saves the parameter type definition as a JSON object.
-  // If |full_schema| is set to true, the full type definition is saved,
-  // otherwise only the overridden properties and attributes from the base
-  // schema is saved. That is, inherited properties and attributes are not
-  // saved.
-  // If it fails, returns "nullptr" and fills in the |error| with additional
-  // error information.
-  // |in_command_def| is set to true if the property type describes a
-  // GCD command parameter, otherwise it is for an object property.
-  // Command definitions handle required parameters differently (using
-  // "isRequired" property as opposed to "required" list for object properties).
-  virtual std::unique_ptr<base::Value> ToJson(bool full_schema,
-                                              bool in_command_def) const;
-  // Parses an JSON parameter type definition. Optional |base_schema| may
-  // specify the base schema type definition this type should be based upon.
-  // If not specified (nullptr), the parameter type is assumed to be a full
-  // definition and any omitted required properties are treated as an error.
-  // Returns true on success, otherwise fills in the |error| with additional
-  // error information.
-  virtual bool FromJson(const base::DictionaryValue* value,
-                        const PropType* base_schema,
-                        ErrorPtr* error);
-  // Helper function to load object schema from JSON.
-  virtual bool ObjectSchemaFromJson(const base::DictionaryValue* value,
-                                    const PropType* base_schema,
-                                    std::set<std::string>* processed_keys,
-                                    ErrorPtr* error) {
-    return true;
-  }
-  // Helper function to load type-specific constraints from JSON.
-  virtual bool ConstraintsFromJson(const base::DictionaryValue* value,
-                                   std::set<std::string>* processed_keys,
-                                   ErrorPtr* error) {
-    return true;
-  }
-
-  // Additional helper static methods to help with converting a type enum
-  // value into a string and back.
-  using TypeMap = std::vector<std::pair<ValueType, std::string>>;
-  // Returns a list of value types and corresponding type names.
-  static const TypeMap& GetTypeMap();
-  // Gets the type name string for the given type.
-  static std::string GetTypeStringFromType(ValueType type);
-  // Finds the type for the given type name. Returns true on success.
-  static bool GetTypeFromTypeString(const std::string& name, ValueType* type);
-
-  // Creates an instance of PropType-derived class for the specified
-  // parameter type.
-  static std::unique_ptr<PropType> Create(ValueType type);
-
-  // Adds a constraint to the type definition.
-  void AddConstraint(std::unique_ptr<Constraint> constraint);
-  // Removes a constraint of given type, if it exists.
-  void RemoveConstraint(ConstraintType constraint_type);
-  // Removes all constraints.
-  void RemoveAllConstraints();
-
-  // Finds a constraint of given type. Returns nullptr if not found.
-  const Constraint* GetConstraint(ConstraintType constraint_type) const;
-  Constraint* GetConstraint(ConstraintType constraint_type);
-
-  // Validates the given value against all the constraints.
-  bool ValidateConstraints(const PropValue& value, ErrorPtr* error) const;
-
- protected:
-  friend class StatePackage;
-  virtual std::unique_ptr<PropValue> CreateDefaultValue() const = 0;
-
-  // Specifies if this parameter definition is derived from a base
-  // object schema.
-  bool based_on_schema_ = false;
-  // A list of constraints specified for the parameter.
-  ConstraintMap constraints_;
-  // The default value specified for the parameter, if any. If the default
-  // value is present, the parameter is treated as optional and the default
-  // value is used if the parameter value is omitted when sending a command.
-  // Otherwise the parameter is treated as required and, if it is omitted,
-  // this is treated as an error.
-  InheritableAttribute<std::unique_ptr<PropValue>> default_;
-  // Specifies whether the parameter/property is required and must be specified
-  // (either directly, or by the default value being provided in the schema).
-  // Non-required parameters can be omitted completely and their values will not
-  // be present in the object instance.
-  InheritableAttribute<bool> required_;
-};
-
-// Base class for all the derived concrete implementations of property
-// type classes. Provides implementations for common methods of PropType base.
-template <class Derived, class Value, typename T>
-class PropTypeBase : public PropType {
- public:
-  // Overrides from PropType.
-  ValueType GetType() const override { return GetValueType<T>(); }
-
-  std::unique_ptr<PropValue> CreatePropValue(const base::Value& value,
-                                             ErrorPtr* error) const override {
-    return CreateValue(value, error);
-  }
-
-  std::unique_ptr<Value> CreateValue(const base::Value& value,
-                                     ErrorPtr* error) const {
-    return Value::CreateFromJson(value, *this, error);
-  }
-
-  bool ConstraintsFromJson(const base::DictionaryValue* value,
-                           std::set<std::string>* processed_keys,
-                           ErrorPtr* error) override;
-
- protected:
-  std::unique_ptr<PropValue> CreateDefaultValue() const override {
-    if (GetDefaultValue())
-      return GetDefaultValue()->Clone();
-    return std::unique_ptr<PropValue>{new Value{*this}};
-  }
-};
-
-// Helper base class for Int and Double parameter types.
-template <class Derived, class Value, typename T>
-class NumericPropTypeBase : public PropTypeBase<Derived, Value, T> {
- public:
-  using Base = PropTypeBase<Derived, Value, T>;
-  bool ConstraintsFromJson(const base::DictionaryValue* value,
-                           std::set<std::string>* processed_keys,
-                           ErrorPtr* error) override;
-
-  // Helper method to set and obtain a min/max constraint values.
-  // Used mostly for unit testing.
-  void AddMinMaxConstraint(T min_value, T max_value) {
-    InheritableAttribute<T> min_attr(min_value, false);
-    InheritableAttribute<T> max_attr(max_value, false);
-    this->AddConstraint(
-        std::unique_ptr<ConstraintMin<T>>{new ConstraintMin<T>{min_attr}});
-    this->AddConstraint(
-        std::unique_ptr<ConstraintMax<T>>{new ConstraintMax<T>{max_attr}});
-  }
-  T GetMinValue() const {
-    auto mmc = static_cast<const ConstraintMin<T>*>(
-        this->GetConstraint(ConstraintType::Min));
-    return mmc ? mmc->limit_.value : std::numeric_limits<T>::lowest();
-  }
-  T GetMaxValue() const {
-    auto mmc = static_cast<const ConstraintMax<T>*>(
-        this->GetConstraint(ConstraintType::Max));
-    return mmc ? mmc->limit_.value : (std::numeric_limits<T>::max)();
-  }
-};
-
-// Property definition of Integer type.
-class IntPropType : public NumericPropTypeBase<IntPropType, IntValue, int> {
- public:
-  // Overrides from the PropType base class.
-  IntPropType* GetInt() override { return this; }
-  IntPropType const* GetInt() const override { return this; }
-};
-
-// Property definition of Number type.
-class DoublePropType
-    : public NumericPropTypeBase<DoublePropType, DoubleValue, double> {
- public:
-  // Overrides from the PropType base class.
-  DoublePropType* GetDouble() override { return this; }
-  DoublePropType const* GetDouble() const override { return this; }
-};
-
-// Property definition of String type.
-class StringPropType
-    : public PropTypeBase<StringPropType, StringValue, std::string> {
- public:
-  using Base = PropTypeBase<StringPropType, StringValue, std::string>;
-  // Overrides from the PropType base class.
-  StringPropType* GetString() override { return this; }
-  StringPropType const* GetString() const override { return this; }
-
-  bool ConstraintsFromJson(const base::DictionaryValue* value,
-                           std::set<std::string>* processed_keys,
-                           ErrorPtr* error) override;
-
-  // Helper methods to add and inspect simple constraints.
-  // Used mostly for unit testing.
-  void AddLengthConstraint(int min_len, int max_len);
-  int GetMinLength() const;
-  int GetMaxLength() const;
-};
-
-// Property definition of Boolean type.
-class BooleanPropType
-    : public PropTypeBase<BooleanPropType, BooleanValue, bool> {
- public:
-  // Overrides from the PropType base class.
-  BooleanPropType* GetBoolean() override { return this; }
-  BooleanPropType const* GetBoolean() const override { return this; }
-};
-
-// Parameter definition of Object type.
-class ObjectPropType
-    : public PropTypeBase<ObjectPropType, ObjectValue, ValueMap> {
- public:
-  using Base = PropTypeBase<ObjectPropType, ObjectValue, ValueMap>;
-  ObjectPropType();
-
-  // Overrides from the ParamType base class.
-  bool HasOverriddenAttributes() const override;
-
-  ObjectPropType* GetObject() override { return this; }
-  ObjectPropType const* GetObject() const override { return this; }
-
-  std::unique_ptr<PropType> Clone() const override;
-
-  std::unique_ptr<base::Value> ToJson(bool full_schema,
-                                      bool in_command_def) const override;
-  bool ObjectSchemaFromJson(const base::DictionaryValue* value,
-                            const PropType* base_schema,
-                            std::set<std::string>* processed_keys,
-                            ErrorPtr* error) override;
-
-  // Returns a schema for Object-type parameter.
-  inline const ObjectSchema* GetObjectSchemaPtr() const {
-    return object_schema_.value.get();
-  }
-  void SetObjectSchema(std::unique_ptr<const ObjectSchema> schema);
-
- private:
-  InheritableAttribute<std::unique_ptr<const ObjectSchema>> object_schema_;
-};
-
-// Parameter definition of Array type.
-class ArrayPropType
-    : public PropTypeBase<ArrayPropType, ArrayValue, ValueVector> {
- public:
-  using Base = PropTypeBase<ArrayPropType, ArrayValue, ValueVector>;
-  ArrayPropType();
-
-  // Overrides from the ParamType base class.
-  bool HasOverriddenAttributes() const override;
-
-  ArrayPropType* GetArray() override { return this; }
-  ArrayPropType const* GetArray() const override { return this; }
-
-  std::unique_ptr<PropType> Clone() const override;
-
-  std::unique_ptr<base::Value> ToJson(bool full_schema,
-                                      bool in_command_def) const override;
-
-  bool ObjectSchemaFromJson(const base::DictionaryValue* value,
-                            const PropType* base_schema,
-                            std::set<std::string>* processed_keys,
-                            ErrorPtr* error) override;
-
-  // Returns a type for Array elements.
-  inline const PropType* GetItemTypePtr() const {
-    return item_type_.value.get();
-  }
-  void SetItemType(std::unique_ptr<const PropType> item_type);
-
- private:
-  InheritableAttribute<std::unique_ptr<const PropType>> item_type_;
-};
-
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_COMMANDS_PROP_TYPES_H_
diff --git a/src/commands/prop_values.cc b/src/commands/prop_values.cc
deleted file mode 100644
index 6f30bee..0000000
--- a/src/commands/prop_values.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2015 The Weave 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 "src/commands/prop_values.h"
-
-#include "src/commands/prop_types.h"
-
-namespace weave {
-
-PropValue::PropValue(const PropType& type) : type_{type.Clone()} {}
-
-PropValue::PropValue(const PropValue& other) : PropValue{*other.type_} {}
-
-PropValue::~PropValue() {}
-
-}  // namespace weave
diff --git a/src/commands/prop_values.h b/src/commands/prop_values.h
deleted file mode 100644
index 960802b..0000000
--- a/src/commands/prop_values.h
+++ /dev/null
@@ -1,266 +0,0 @@
-// Copyright 2015 The Weave Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef LIBWEAVE_SRC_COMMANDS_PROP_VALUES_H_
-#define LIBWEAVE_SRC_COMMANDS_PROP_VALUES_H_
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include <weave/error.h>
-
-#include "src/commands/schema_utils.h"
-
-namespace base {
-class Value;
-class DictionaryValue;
-}  // namespace base
-
-namespace weave {
-
-// Enumeration to indicate supported command parameter types.
-enum class ValueType {
-  Int,
-  Double,
-  String,
-  Boolean,
-  Object,
-  Array,
-};
-
-class PropValue;
-class IntValue;
-class DoubleValue;
-class StringValue;
-class BooleanValue;
-class ObjectValue;
-class ArrayValue;
-
-class PropType;
-
-// Helper methods to get the parameter type enum value for the given
-// native C++ data representation.
-// The generic GetValueType<T>() is undefined, however particular
-// type specializations return the appropriate ValueType.
-template <typename T>
-ValueType GetValueType();  // Undefined.
-template <>
-inline ValueType GetValueType<int>() {
-  return ValueType::Int;
-}
-template <>
-inline ValueType GetValueType<double>() {
-  return ValueType::Double;
-}
-template <>
-inline ValueType GetValueType<std::string>() {
-  return ValueType::String;
-}
-template <>
-inline ValueType GetValueType<bool>() {
-  return ValueType::Boolean;
-}
-template <>
-inline ValueType GetValueType<ValueMap>() {
-  return ValueType::Object;
-}
-template <>
-inline ValueType GetValueType<ValueVector>() {
-  return ValueType::Array;
-}
-
-// The base class for property values.
-// Concrete value classes of various types will be derived from this base.
-// A property value is the actual command parameter value (or a concrete value
-// that can be used in constraints and presets). The PropValue is mostly
-// just parsed content of base::Value when a command is dispatched, however
-// it does have some additional functionality:
-//   - it has a reference to the type definition (PropType) which is used
-//     when validating the value, especially for "object" types.
-//   - it can be compared with another instances of values of the same type.
-//     This is used to validate the values against "enum"/"one of" constraints.
-class PropValue {
- public:
-  // Only CreateDefaultValue should use this constructor.
-  explicit PropValue(const PropType& type);
-  virtual ~PropValue();
-
-  // Gets the type of the value.
-  virtual ValueType GetType() const = 0;
-
-  // Type conversion methods. Used in lieu of RTTI and dynamic_cast<>.
-  virtual IntValue* GetInt() { return nullptr; }
-  virtual IntValue const* GetInt() const { return nullptr; }
-  virtual DoubleValue* GetDouble() { return nullptr; }
-  virtual DoubleValue const* GetDouble() const { return nullptr; }
-  virtual StringValue* GetString() { return nullptr; }
-  virtual StringValue const* GetString() const { return nullptr; }
-  virtual BooleanValue* GetBoolean() { return nullptr; }
-  virtual BooleanValue const* GetBoolean() const { return nullptr; }
-  virtual ObjectValue* GetObject() { return nullptr; }
-  virtual ObjectValue const* GetObject() const { return nullptr; }
-  virtual ArrayValue* GetArray() { return nullptr; }
-  virtual ArrayValue const* GetArray() const { return nullptr; }
-
-  // Makes a full copy of this value class.
-  virtual std::unique_ptr<PropValue> Clone() const = 0;
-
-  // Saves the value as a JSON object. Never fails.
-  virtual std::unique_ptr<base::Value> ToJson() const = 0;
-
-  // Return the type definition of this value.
-  const PropType* GetPropType() const { return type_.get(); }
-  // Compares two values and returns true if they are equal.
-  virtual bool IsEqual(const PropValue* value) const = 0;
-
- protected:
-  // Special out-of-line constructor to help implement PropValue::Clone().
-  // That method needs to clone the underlying type but can't do this in this
-  // header file since PropType is just forward-declared (it needs PropValue
-  // fully defined in its own inner workings).
-  explicit PropValue(const PropValue& other);
-
- private:
-  std::unique_ptr<const PropType> type_;
-};
-
-// A helper template base class for implementing value classes.
-template <typename T>
-class TypedValueBase : public PropValue {
- public:
-  TypedValueBase(const PropType& type) : PropValue(type) {}
-
-  // Overrides from PropValue base class.
-  ValueType GetType() const override { return GetValueType<T>(); }
-
-  std::unique_ptr<base::Value> ToJson() const override {
-    return TypedValueToJson(value_);
-  }
-
-  bool IsEqual(const PropValue* value) const override {
-    if (GetType() != value->GetType())
-      return false;
-    return CompareValue(GetValue(),
-                        static_cast<const TypedValueBase*>(value)->GetValue());
-  }
-
-  // Helper methods to get and set the C++ representation of the value.
-  const T& GetValue() const { return value_; }
-
- protected:
-  explicit TypedValueBase(const TypedValueBase& other)
-      : PropValue(other), value_(other.value_) {}
-
-  TypedValueBase(const PropType& type, T value)
-      : PropValue(type), value_(value) {}
-
- private:
-  T value_{};  // The value of the parameter in C++ data representation.
-};
-
-// A helper template base class for implementing value classes.
-template <typename Derived, typename T>
-class TypedValueWithClone : public TypedValueBase<T> {
- public:
-  TypedValueWithClone(const PropType& type) : TypedValueBase<T>(type) {}
-  TypedValueWithClone(const PropType& type, const T& value)
-      : TypedValueBase<T>(type, value) {}
-
-  std::unique_ptr<PropValue> Clone() const override {
-    return std::unique_ptr<PropValue>{
-        new Derived{*static_cast<const Derived*>(this)}};
-  }
-
-  static std::unique_ptr<Derived> CreateFromJson(const base::Value& value,
-                                                 const PropType& type,
-                                                 ErrorPtr* error) {
-    T tmp_value;
-    if (!TypedValueFromJson(&value, &type, &tmp_value, error))
-      return nullptr;
-
-    // Only place where invalid value can exist.
-    std::unique_ptr<Derived> result(new Derived(type, tmp_value));
-    if (!result->GetPropType()->ValidateConstraints(*result, error))
-      return nullptr;
-
-    return result;
-  }
-};
-
-// Value of type Integer.
-class IntValue final : public TypedValueWithClone<IntValue, int> {
- public:
-  explicit IntValue(const PropType& type)
-      : TypedValueWithClone<IntValue, int>(type) {}
-  IntValue(const PropType& type, int value)
-      : TypedValueWithClone<IntValue, int>(type, value) {}
-
-  IntValue* GetInt() override { return this; }
-  IntValue const* GetInt() const override { return this; }
-};
-
-// Value of type Number.
-class DoubleValue final : public TypedValueWithClone<DoubleValue, double> {
- public:
-  explicit DoubleValue(const PropType& type)
-      : TypedValueWithClone<DoubleValue, double>(type) {}
-  DoubleValue(const PropType& type, double value)
-      : TypedValueWithClone<DoubleValue, double>(type, value) {}
-
-  DoubleValue* GetDouble() override { return this; }
-  DoubleValue const* GetDouble() const override { return this; }
-};
-
-// Value of type String.
-class StringValue final : public TypedValueWithClone<StringValue, std::string> {
- public:
-  explicit StringValue(const PropType& type)
-      : TypedValueWithClone<StringValue, std::string>(type) {}
-  StringValue(const PropType& type, std::string value)
-      : TypedValueWithClone<StringValue, std::string>(type, value) {}
-
-  StringValue* GetString() override { return this; }
-  StringValue const* GetString() const override { return this; }
-};
-
-// Value of type Boolean.
-class BooleanValue final : public TypedValueWithClone<BooleanValue, bool> {
- public:
-  explicit BooleanValue(const PropType& type)
-      : TypedValueWithClone<BooleanValue, bool>(type) {}
-  BooleanValue(const PropType& type, bool value)
-      : TypedValueWithClone<BooleanValue, bool>(type, value) {}
-
-  BooleanValue* GetBoolean() override { return this; }
-  BooleanValue const* GetBoolean() const override { return this; }
-};
-
-// Value of type Object.
-class ObjectValue final : public TypedValueWithClone<ObjectValue, ValueMap> {
- public:
-  explicit ObjectValue(const PropType& type)
-      : TypedValueWithClone<ObjectValue, ValueMap>(type) {}
-  ObjectValue(const PropType& type, ValueMap value)
-      : TypedValueWithClone<ObjectValue, ValueMap>(type, value) {}
-
-  ObjectValue* GetObject() override { return this; }
-  ObjectValue const* GetObject() const override { return this; }
-};
-
-// Value of type Array.
-class ArrayValue final : public TypedValueWithClone<ArrayValue, ValueVector> {
- public:
-  explicit ArrayValue(const PropType& type)
-      : TypedValueWithClone<ArrayValue, ValueVector>(type) {}
-  ArrayValue(const PropType& type, ValueVector value)
-      : TypedValueWithClone<ArrayValue, ValueVector>(type, value) {}
-
-  ArrayValue* GetArray() override { return this; }
-  ArrayValue const* GetArray() const override { return this; }
-};
-
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_COMMANDS_PROP_VALUES_H_
diff --git a/src/commands/schema_constants.cc b/src/commands/schema_constants.cc
index 7f8431f..cd8bf84 100644
--- a/src/commands/schema_constants.cc
+++ b/src/commands/schema_constants.cc
@@ -10,17 +10,9 @@
 namespace commands {
 const char kDomain[] = "command_schema";
 
-const char kOutOfRange[] = "out_of_range";
 const char kTypeMismatch[] = "type_mismatch";
-const char kPropTypeChanged[] = "param_type_changed";
-const char kUnknownType[] = "unknown_type";
-const char kInvalidPropDef[] = "invalid_parameter_definition";
 const char kInvalidPropValue[] = "invalid_parameter_value";
-const char kNoTypeInfo[] = "no_type_info";
 const char kPropertyMissing[] = "parameter_missing";
-const char kUnknownProperty[] = "unexpected_parameter";
-const char kInvalidObjectSchema[] = "invalid_object_schema";
-const char kDuplicateCommandDef[] = "duplicate_command_definition";
 const char kInvalidCommandName[] = "invalid_command_name";
 const char kCommandFailed[] = "command_failed";
 const char kInvalidMinimalRole[] = "invalid_minimal_role";
@@ -32,25 +24,6 @@
 namespace commands {
 namespace attributes {
 
-const char kType[] = "type";
-const char kDisplayName[] = "displayName";
-const char kDefault[] = "default";
-const char kItems[] = "items";
-const char kIsRequired[] = "isRequired";
-
-const char kNumeric_Min[] = "minimum";
-const char kNumeric_Max[] = "maximum";
-
-const char kString_MinLength[] = "minLength";
-const char kString_MaxLength[] = "maxLength";
-
-const char kOneOf_Enum[] = "enum";
-const char kOneOf_Metadata[] = "metadata";
-
-const char kObject_Properties[] = "properties";
-const char kObject_AdditionalProperties[] = "additionalProperties";
-const char kObject_Required[] = "required";
-
 const char kCommand_Id[] = "id";
 const char kCommand_Name[] = "name";
 const char kCommand_Parameters[] = "parameters";
diff --git a/src/commands/schema_constants.h b/src/commands/schema_constants.h
index ea59f17..57766e6 100644
--- a/src/commands/schema_constants.h
+++ b/src/commands/schema_constants.h
@@ -13,17 +13,9 @@
 extern const char kDomain[];
 
 // Common command definition error codes.
-extern const char kOutOfRange[];
 extern const char kTypeMismatch[];
-extern const char kPropTypeChanged[];
-extern const char kUnknownType[];
-extern const char kInvalidPropDef[];
 extern const char kInvalidPropValue[];
-extern const char kNoTypeInfo[];
 extern const char kPropertyMissing[];
-extern const char kUnknownProperty[];
-extern const char kInvalidObjectSchema[];
-extern const char kDuplicateCommandDef[];
 extern const char kInvalidCommandName[];
 extern const char kCommandFailed[];
 extern const char kInvalidMinimalRole[];
@@ -35,25 +27,6 @@
 namespace commands {
 namespace attributes {
 // Command description JSON schema attributes.
-extern const char kType[];
-extern const char kDisplayName[];
-extern const char kDefault[];
-extern const char kItems[];
-extern const char kIsRequired[];
-
-extern const char kNumeric_Min[];
-extern const char kNumeric_Max[];
-
-extern const char kString_MinLength[];
-extern const char kString_MaxLength[];
-
-extern const char kOneOf_Enum[];
-extern const char kOneOf_Metadata[];
-
-extern const char kObject_Properties[];
-extern const char kObject_AdditionalProperties[];
-extern const char kObject_Required[];
-
 extern const char kCommand_Id[];
 extern const char kCommand_Name[];
 extern const char kCommand_Parameters[];
diff --git a/src/commands/schema_utils.cc b/src/commands/schema_utils.cc
deleted file mode 100644
index be6cb16..0000000
--- a/src/commands/schema_utils.cc
+++ /dev/null
@@ -1,266 +0,0 @@
-// Copyright 2015 The Weave 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 "src/commands/schema_utils.h"
-
-#include <algorithm>
-#include <set>
-#include <string>
-
-#include <base/json/json_writer.h>
-#include <base/logging.h>
-
-#include "src/commands/object_schema.h"
-#include "src/commands/prop_types.h"
-#include "src/commands/prop_values.h"
-
-namespace weave {
-namespace {
-// Helper function to report "type mismatch" errors when parsing JSON.
-void ReportJsonTypeMismatch(const tracked_objects::Location& location,
-                            const base::Value* value_in,
-                            const std::string& expected_type,
-                            ErrorPtr* error) {
-  std::string value_as_string;
-  base::JSONWriter::Write(*value_in, &value_as_string);
-  Error::AddToPrintf(error, location, errors::commands::kDomain,
-                     errors::commands::kTypeMismatch,
-                     "Unable to convert value %s into %s",
-                     value_as_string.c_str(), expected_type.c_str());
-}
-
-// Template version of ReportJsonTypeMismatch that deduces the type of expected
-// data from the value_out parameter passed to particular overload of
-// TypedValueFromJson() function. Always returns false.
-template <typename T>
-bool ReportUnexpectedJson(const tracked_objects::Location& location,
-                          const base::Value* value_in,
-                          T*,
-                          ErrorPtr* error) {
-  ReportJsonTypeMismatch(location, value_in,
-                         PropType::GetTypeStringFromType(GetValueType<T>()),
-                         error);
-  return false;
-}
-
-bool ErrorMissingProperty(ErrorPtr* error,
-                          const tracked_objects::Location& location,
-                          const char* param_name) {
-  Error::AddToPrintf(error, location, errors::commands::kDomain,
-                     errors::commands::kPropertyMissing,
-                     "Required parameter missing: %s", param_name);
-  return false;
-}
-
-}  // namespace
-
-// Specializations of TypedValueToJson<T>() for supported C++ types.
-std::unique_ptr<base::FundamentalValue> TypedValueToJson(bool value) {
-  return std::unique_ptr<base::FundamentalValue>(
-      new base::FundamentalValue(value));
-}
-
-std::unique_ptr<base::FundamentalValue> TypedValueToJson(int value) {
-  return std::unique_ptr<base::FundamentalValue>(
-      new base::FundamentalValue(value));
-}
-
-std::unique_ptr<base::FundamentalValue> TypedValueToJson(double value) {
-  return std::unique_ptr<base::FundamentalValue>(
-      new base::FundamentalValue(value));
-}
-
-std::unique_ptr<base::StringValue> TypedValueToJson(const std::string& value) {
-  return std::unique_ptr<base::StringValue>(new base::StringValue(value));
-}
-
-std::unique_ptr<base::DictionaryValue> TypedValueToJson(const ValueMap& value) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
-  for (const auto& pair : value) {
-    auto prop_value = pair.second->ToJson();
-    CHECK(prop_value);
-    dict->SetWithoutPathExpansion(pair.first, prop_value.release());
-  }
-  return dict;
-}
-
-std::unique_ptr<base::ListValue> TypedValueToJson(const ValueVector& value) {
-  std::unique_ptr<base::ListValue> list(new base::ListValue);
-  for (const auto& item : value) {
-    auto json = item->ToJson();
-    CHECK(json);
-    list->Append(json.release());
-  }
-  return list;
-}
-
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        bool* value_out,
-                        ErrorPtr* error) {
-  return value_in->GetAsBoolean(value_out) ||
-         ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
-}
-
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        int* value_out,
-                        ErrorPtr* error) {
-  return value_in->GetAsInteger(value_out) ||
-         ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
-}
-
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        double* value_out,
-                        ErrorPtr* error) {
-  return value_in->GetAsDouble(value_out) ||
-         ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
-}
-
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        std::string* value_out,
-                        ErrorPtr* error) {
-  return value_in->GetAsString(value_out) ||
-         ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
-}
-
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        ValueMap* value_out,
-                        ErrorPtr* error) {
-  const base::DictionaryValue* dict = nullptr;
-  if (!value_in->GetAsDictionary(&dict))
-    return ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
-
-  CHECK(type) << "Object definition must be provided";
-  CHECK(ValueType::Object == type->GetType()) << "Type must be Object";
-
-  const ObjectSchema* object_schema = type->GetObject()->GetObjectSchemaPtr();
-  std::set<std::string> keys_processed;
-  value_out->clear();  // Clear possible default values already in |value_out|.
-  for (const auto& pair : object_schema->GetProps()) {
-    const PropValue* def_value = pair.second->GetDefaultValue();
-    if (dict->HasKey(pair.first)) {
-      const base::Value* param_value = nullptr;
-      CHECK(dict->GetWithoutPathExpansion(pair.first, &param_value))
-          << "Unable to get parameter";
-      auto value = pair.second->CreatePropValue(*param_value, error);
-      if (!value) {
-        Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                           errors::commands::kInvalidPropValue,
-                           "Invalid value for property '%s'",
-                           pair.first.c_str());
-        return false;
-      }
-      value_out->insert(std::make_pair(pair.first, std::move(value)));
-      keys_processed.insert(pair.first);
-    } else if (def_value) {
-      value_out->insert(std::make_pair(pair.first, def_value->Clone()));
-      keys_processed.insert(pair.first);
-    } else if (pair.second->IsRequired()) {
-      return ErrorMissingProperty(error, FROM_HERE, pair.first.c_str());
-    }
-  }
-
-  // Just for sanity, make sure that we processed all the necessary properties
-  // and there weren't any extra (unknown) ones specified. If so, ignore
-  // them, but log as warnings...
-  base::DictionaryValue::Iterator iter(*dict);
-  while (!iter.IsAtEnd()) {
-    std::string key = iter.key();
-    if (keys_processed.find(key) == keys_processed.end() &&
-        !object_schema->GetExtraPropertiesAllowed()) {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                         errors::commands::kUnknownProperty,
-                         "Unrecognized parameter '%s'", key.c_str());
-      return false;
-    }
-    iter.Advance();
-  }
-
-  // Now go over all property values and validate them.
-  for (const auto& pair : *value_out) {
-    const PropType* prop_type = pair.second->GetPropType();
-    CHECK(prop_type) << "Value property type must be available";
-    if (!prop_type->ValidateConstraints(*pair.second, error)) {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                         errors::commands::kInvalidPropValue,
-                         "Invalid value for property '%s'", pair.first.c_str());
-      return false;
-    }
-  }
-  return true;
-}
-
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        ValueVector* value_out,
-                        ErrorPtr* error) {
-  const base::ListValue* list = nullptr;
-  if (!value_in->GetAsList(&list))
-    return ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
-
-  CHECK(type) << "Array type definition must be provided";
-  CHECK(ValueType::Array == type->GetType()) << "Type must be Array";
-  const PropType* item_type = type->GetArray()->GetItemTypePtr();
-  CHECK(item_type) << "Incomplete array type definition";
-
-  // This value might already contain values from the type defaults.
-  // Clear them first before proceeding.
-  value_out->clear();
-  value_out->reserve(list->GetSize());
-  for (const base::Value* item : *list) {
-    std::unique_ptr<PropValue> prop_value =
-        item_type->CreatePropValue(*item, error);
-    if (!prop_value)
-      return false;
-    value_out->push_back(std::move(prop_value));
-  }
-  return true;
-}
-
-// Compares two sets of key-value pairs from two Objects.
-static bool obj_cmp(const ValueMap::value_type& v1,
-                    const ValueMap::value_type& v2) {
-  return (v1.first == v2.first) && v1.second->IsEqual(v2.second.get());
-}
-
-bool operator==(const ValueMap& obj1, const ValueMap& obj2) {
-  if (obj1.size() != obj2.size())
-    return false;
-
-  auto pair = std::mismatch(obj1.begin(), obj1.end(), obj2.begin(), obj_cmp);
-  return pair == std::make_pair(obj1.end(), obj2.end());
-}
-
-bool operator==(const ValueVector& arr1, const ValueVector& arr2) {
-  if (arr1.size() != arr2.size())
-    return false;
-
-  using Type = const ValueVector::value_type;
-  // Compare two array items.
-  auto arr_cmp = [](const Type& v1, const Type& v2) {
-    return v1->IsEqual(v2.get());
-  };
-  auto pair = std::mismatch(arr1.begin(), arr1.end(), arr2.begin(), arr_cmp);
-  return pair == std::make_pair(arr1.end(), arr2.end());
-}
-
-std::string ToString(const ValueMap& obj) {
-  auto val = TypedValueToJson(obj);
-  std::string str;
-  base::JSONWriter::Write(*val, &str);
-  return str;
-}
-
-std::string ToString(const ValueVector& arr) {
-  auto val = TypedValueToJson(arr);
-  std::string str;
-  base::JSONWriter::Write(*val, &str);
-  return str;
-}
-
-}  // namespace weave
diff --git a/src/commands/schema_utils.h b/src/commands/schema_utils.h
deleted file mode 100644
index 0c1d1b3..0000000
--- a/src/commands/schema_utils.h
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2015 The Weave Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef LIBWEAVE_SRC_COMMANDS_SCHEMA_UTILS_H_
-#define LIBWEAVE_SRC_COMMANDS_SCHEMA_UTILS_H_
-
-#include <cmath>
-#include <limits>
-#include <map>
-#include <memory>
-#include <string>
-#include <type_traits>
-#include <vector>
-
-#include <base/logging.h>
-#include <base/values.h>
-#include <weave/error.h>
-
-namespace weave {
-
-class PropType;
-class PropValue;
-class ObjectSchema;
-class ObjectValue;
-
-// C++ representation of object values.
-using ValueMap = std::map<std::string, std::shared_ptr<const PropValue>>;
-
-// C++ representation of array of values.
-using ValueVector = std::vector<std::shared_ptr<const PropValue>>;
-
-// Converts an object to string.
-std::string ToString(const ValueMap& obj);
-
-// Converts an array to string.
-std::string ToString(const ValueVector& arr);
-
-// InheritableAttribute class is used for specifying various command parameter
-// attributes that can be inherited from a base (parent) schema.
-// The |value| still specifies the actual attribute values, whether it
-// is inherited or overridden, while |is_inherited| can be used to identify
-// if the attribute was inherited (true) or overridden (false).
-template <typename T>
-class InheritableAttribute final {
- public:
-  InheritableAttribute() = default;
-  explicit InheritableAttribute(T val)
-      : value(std::move(val)), is_inherited(true) {}
-  InheritableAttribute(T val, bool inherited)
-      : value(std::move(val)), is_inherited(inherited) {}
-  T value{};
-  bool is_inherited{true};
-};
-
-// A bunch of helper function to create base::Value for specific C++ classes,
-// including vectors of types. These are used in template classes below
-// to simplify specialization logic.
-std::unique_ptr<base::FundamentalValue> TypedValueToJson(bool value);
-std::unique_ptr<base::FundamentalValue> TypedValueToJson(int value);
-std::unique_ptr<base::FundamentalValue> TypedValueToJson(double value);
-std::unique_ptr<base::StringValue> TypedValueToJson(const std::string& value);
-std::unique_ptr<base::DictionaryValue> TypedValueToJson(const ValueMap& value);
-std::unique_ptr<base::ListValue> TypedValueToJson(const ValueVector& value);
-template <typename T>
-std::unique_ptr<base::ListValue> TypedValueToJson(
-    const std::vector<T>& values) {
-  std::unique_ptr<base::ListValue> list(new base::ListValue);
-  for (const auto& v : values) {
-    auto json = TypedValueToJson(v);
-    CHECK(json);
-    list->Append(json.release());
-  }
-  return list;
-}
-
-// Similarly to TypedValueToJson() function above, the following overloaded
-// helper methods allow to extract specific C++ data types from base::Value.
-// Also used in template classes below to simplify specialization logic.
-// TODO(vitalybuka): Fix this. Interface is misleading. Seeing PropType internal
-// type validation is expected. In reality only ValueMap and ValueVector do
-// validation.
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        bool* value_out,
-                        ErrorPtr* error);
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        int* value_out,
-                        ErrorPtr* error);
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        double* value_out,
-                        ErrorPtr* error);
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        std::string* value_out,
-                        ErrorPtr* error);
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        ValueMap* value_out,
-                        ErrorPtr* error);
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        ValueVector* value_out,
-                        ErrorPtr* error);
-
-bool operator==(const ValueMap& obj1, const ValueMap& obj2);
-bool operator==(const ValueVector& arr1, const ValueVector& arr2);
-
-// CompareValue is a helper function to help with implementing EqualsTo operator
-// for various data types. For most scalar types it is using operator==(),
-// however, for floating point values, rounding errors in binary representation
-// of IEEE floats/doubles can cause straight == comparison to fail for seemingly
-// equivalent values. For these, use approximate comparison with the error
-// margin equal to the epsilon value defined for the corresponding data type.
-// This is used when looking up values for implementation of OneOf constraints
-// which should work reliably for floating points also ("number" type).
-
-// Compare exact types using ==.
-template <typename T>
-inline typename std::enable_if<!std::is_floating_point<T>::value, bool>::type
-CompareValue(const T& v1, const T& v2) {
-  return v1 == v2;
-}
-
-// Compare non-exact types (such as double) using precision margin (epsilon).
-template <typename T>
-inline typename std::enable_if<std::is_floating_point<T>::value, bool>::type
-CompareValue(const T& v1, const T& v2) {
-  return std::abs(v1 - v2) <= std::numeric_limits<T>::epsilon();
-}
-
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_COMMANDS_SCHEMA_UTILS_H_
diff --git a/src/commands/schema_utils_unittest.cc b/src/commands/schema_utils_unittest.cc
deleted file mode 100644
index 1ea4c90..0000000
--- a/src/commands/schema_utils_unittest.cc
+++ /dev/null
@@ -1,212 +0,0 @@
-// Copyright 2015 The Weave 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 "src/commands/schema_utils.h"
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <base/values.h>
-#include <gtest/gtest.h>
-
-#include "src/commands/object_schema.h"
-#include "src/commands/prop_types.h"
-#include "src/commands/prop_values.h"
-#include "src/commands/schema_constants.h"
-#include "src/commands/unittest_utils.h"
-
-namespace weave {
-
-using test::CreateDictionaryValue;
-using test::CreateValue;
-
-TEST(CommandSchemaUtils, TypedValueToJson_Scalar) {
-  EXPECT_JSON_EQ("true", *TypedValueToJson(true));
-  EXPECT_JSON_EQ("false", *TypedValueToJson(false));
-
-  EXPECT_JSON_EQ("0", *TypedValueToJson(0));
-  EXPECT_JSON_EQ("-10", *TypedValueToJson(-10));
-  EXPECT_JSON_EQ("20", *TypedValueToJson(20));
-
-  EXPECT_JSON_EQ("0.0", *TypedValueToJson(0.0));
-  EXPECT_JSON_EQ("1.2", *TypedValueToJson(1.2));
-
-  EXPECT_JSON_EQ("'abc'", *TypedValueToJson(std::string("abc")));
-
-  std::vector<bool> bool_array{true, false};
-  EXPECT_JSON_EQ("[true,false]", *TypedValueToJson(bool_array));
-
-  std::vector<int> int_array{1, 2, 5};
-  EXPECT_JSON_EQ("[1,2,5]", *TypedValueToJson(int_array));
-
-  std::vector<double> dbl_array{1.1, 2.2};
-  EXPECT_JSON_EQ("[1.1,2.2]", *TypedValueToJson(dbl_array));
-
-  std::vector<std::string> str_array{"a", "bc"};
-  EXPECT_JSON_EQ("['a','bc']", *TypedValueToJson(str_array));
-}
-
-TEST(CommandSchemaUtils, TypedValueToJson_Object) {
-  IntPropType int_type;
-  ValueMap object;
-
-  object.insert(std::make_pair(
-      "width", int_type.CreateValue(base::FundamentalValue{640}, nullptr)));
-  object.insert(std::make_pair(
-      "height", int_type.CreateValue(base::FundamentalValue{480}, nullptr)));
-  EXPECT_JSON_EQ("{'height':480,'width':640}", *TypedValueToJson(object));
-}
-
-TEST(CommandSchemaUtils, TypedValueToJson_Array) {
-  IntPropType int_type;
-  ValueVector arr;
-
-  arr.push_back(int_type.CreateValue(base::FundamentalValue{640}, nullptr));
-  arr.push_back(int_type.CreateValue(base::FundamentalValue{480}, nullptr));
-  EXPECT_JSON_EQ("[640,480]", *TypedValueToJson(arr));
-}
-
-TEST(CommandSchemaUtils, TypedValueFromJson_Bool) {
-  bool value;
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("true").get(), nullptr, &value, nullptr));
-  EXPECT_TRUE(value);
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("false").get(), nullptr, &value, nullptr));
-  EXPECT_FALSE(value);
-
-  ErrorPtr error;
-  EXPECT_FALSE(
-      TypedValueFromJson(CreateValue("0").get(), nullptr, &value, &error));
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchemaUtils, TypedValueFromJson_Int) {
-  int value;
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("0").get(), nullptr, &value, nullptr));
-  EXPECT_EQ(0, value);
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("23").get(), nullptr, &value, nullptr));
-  EXPECT_EQ(23, value);
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("-1234").get(), nullptr, &value, nullptr));
-  EXPECT_EQ(-1234, value);
-
-  ErrorPtr error;
-  EXPECT_FALSE(
-      TypedValueFromJson(CreateValue("'abc'").get(), nullptr, &value, &error));
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchemaUtils, TypedValueFromJson_Double) {
-  double value;
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("0").get(), nullptr, &value, nullptr));
-  EXPECT_DOUBLE_EQ(0.0, value);
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("0.0").get(), nullptr, &value, nullptr));
-  EXPECT_DOUBLE_EQ(0.0, value);
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("23").get(), nullptr, &value, nullptr));
-  EXPECT_EQ(23.0, value);
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("23.1").get(), nullptr, &value, nullptr));
-  EXPECT_EQ(23.1, value);
-
-  EXPECT_TRUE(TypedValueFromJson(CreateValue("-1.23E+02").get(), nullptr,
-                                 &value, nullptr));
-  EXPECT_EQ(-123.0, value);
-
-  ErrorPtr error;
-  EXPECT_FALSE(
-      TypedValueFromJson(CreateValue("'abc'").get(), nullptr, &value, &error));
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchemaUtils, TypedValueFromJson_String) {
-  std::string value;
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("''").get(), nullptr, &value, nullptr));
-  EXPECT_EQ("", value);
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("'23'").get(), nullptr, &value, nullptr));
-  EXPECT_EQ("23", value);
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("'abc'").get(), nullptr, &value, nullptr));
-  EXPECT_EQ("abc", value);
-
-  ErrorPtr error;
-  EXPECT_FALSE(
-      TypedValueFromJson(CreateValue("12").get(), nullptr, &value, &error));
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchemaUtils, TypedValueFromJson_Object) {
-  ValueMap value;
-  std::unique_ptr<ObjectSchema> schema{new ObjectSchema};
-
-  IntPropType age_prop;
-  age_prop.AddMinMaxConstraint(0, 150);
-  schema->AddProp("age", age_prop.Clone());
-
-  StringPropType name_prop;
-  name_prop.AddLengthConstraint(1, 30);
-  schema->AddProp("name", name_prop.Clone());
-
-  ObjectPropType type;
-  type.SetObjectSchema(std::move(schema));
-  EXPECT_TRUE(TypedValueFromJson(CreateValue("{'age':20,'name':'Bob'}").get(),
-                                 &type, &value, nullptr));
-  ValueMap value2;
-  value2.insert(std::make_pair(
-      "age", age_prop.CreateValue(base::FundamentalValue{20}, nullptr)));
-  value2.insert(std::make_pair(
-      "name", name_prop.CreateValue(base::StringValue("Bob"), nullptr)));
-  EXPECT_EQ(value2, value);
-
-  ErrorPtr error;
-  EXPECT_FALSE(
-      TypedValueFromJson(CreateValue("'abc'").get(), nullptr, &value, &error));
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchemaUtils, TypedValueFromJson_Array) {
-  ValueVector arr;
-  StringPropType str_type;
-  str_type.AddLengthConstraint(3, 100);
-  ArrayPropType type;
-  type.SetItemType(str_type.Clone());
-
-  EXPECT_TRUE(TypedValueFromJson(CreateValue("['foo', 'bar']").get(), &type,
-                                 &arr, nullptr));
-  ValueVector arr2;
-  arr2.push_back(str_type.CreateValue(base::StringValue{"foo"}, nullptr));
-  arr2.push_back(str_type.CreateValue(base::StringValue{"bar"}, nullptr));
-  EXPECT_EQ(arr2, arr);
-
-  ErrorPtr error;
-  EXPECT_FALSE(TypedValueFromJson(CreateValue("['baz', 'ab']").get(), &type,
-                                  &arr, &error));
-  EXPECT_EQ(errors::commands::kOutOfRange, error->GetCode());
-  error.reset();
-}
-
-}  // namespace weave
diff --git a/src/commands/unittest_utils.h b/src/commands/unittest_utils.h
deleted file mode 100644
index 25392ee..0000000
--- a/src/commands/unittest_utils.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2015 The Weave Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef LIBWEAVE_SRC_COMMANDS_UNITTEST_UTILS_H_
-#define LIBWEAVE_SRC_COMMANDS_UNITTEST_UTILS_H_
-
-#include <memory>
-#include <string>
-
-#include <base/values.h>
-#include <gtest/gtest.h>
-#include <weave/test/unittest_utils.h>
-
-#include "src/commands/prop_types.h"
-#include "src/commands/prop_values.h"
-
-namespace weave {
-namespace test {
-
-template <typename T>
-std::unique_ptr<const PropValue> make_prop_value(const base::Value& value) {
-  auto prop_type = PropType::Create(GetValueType<T>());
-  return prop_type->CreatePropValue(value, nullptr);
-}
-
-inline std::unique_ptr<const PropValue> make_int_prop_value(int value) {
-  return make_prop_value<int>(base::FundamentalValue{value});
-}
-
-inline std::unique_ptr<const PropValue> make_double_prop_value(double value) {
-  return make_prop_value<double>(base::FundamentalValue{value});
-}
-
-inline std::unique_ptr<const PropValue> make_bool_prop_value(bool value) {
-  return make_prop_value<bool>(base::FundamentalValue{value});
-}
-
-inline std::unique_ptr<const PropValue> make_string_prop_value(
-    const std::string& value) {
-  return make_prop_value<std::string>(base::StringValue{value});
-}
-
-}  // namespace test
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_COMMANDS_UNITTEST_UTILS_H_
diff --git a/src/config_unittest.cc b/src/config_unittest.cc
index 10ed07e..c36bb25 100644
--- a/src/config_unittest.cc
+++ b/src/config_unittest.cc
@@ -10,8 +10,7 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <weave/provider/test/mock_config_store.h>
-
-#include "src/commands/unittest_utils.h"
+#include <weave/test/unittest_utils.h>
 
 using testing::_;
 using testing::Invoke;
diff --git a/src/device_registration_info_unittest.cc b/src/device_registration_info_unittest.cc
index 9b53872..f2fe4c4 100644
--- a/src/device_registration_info_unittest.cc
+++ b/src/device_registration_info_unittest.cc
@@ -11,10 +11,10 @@
 #include <weave/provider/test/fake_task_runner.h>
 #include <weave/provider/test/mock_config_store.h>
 #include <weave/provider/test/mock_http_client.h>
+#include <weave/test/unittest_utils.h>
 
 #include "src/bind_lambda.h"
 #include "src/commands/command_manager.h"
-#include "src/commands/unittest_utils.h"
 #include "src/http_constants.h"
 #include "src/states/mock_state_change_queue_interface.h"
 #include "src/states/state_manager.h"
diff --git a/src/notification/notification_parser_unittest.cc b/src/notification/notification_parser_unittest.cc
index d0b43fe..6b3916d 100644
--- a/src/notification/notification_parser_unittest.cc
+++ b/src/notification/notification_parser_unittest.cc
@@ -6,8 +6,7 @@
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-
-#include "src/commands/unittest_utils.h"
+#include <weave/test/unittest_utils.h>
 
 using testing::SaveArg;
 using testing::Invoke;
diff --git a/src/states/state_change_queue.cc b/src/states/state_change_queue.cc
index 87cc18d..ba7bf65 100644
--- a/src/states/state_change_queue.cc
+++ b/src/states/state_change_queue.cc
@@ -5,6 +5,7 @@
 #include "src/states/state_change_queue.h"
 
 #include <base/logging.h>
+#include <base/values.h>
 
 namespace weave {
 
diff --git a/src/states/state_change_queue_interface.h b/src/states/state_change_queue_interface.h
index 81a2cfc..631a127 100644
--- a/src/states/state_change_queue_interface.h
+++ b/src/states/state_change_queue_interface.h
@@ -10,7 +10,9 @@
 #include <base/callback_list.h>
 #include <base/time/time.h>
 
-#include "src/commands/schema_utils.h"
+namespace base {
+class DictionaryValue;
+}  // namespace base
 
 namespace weave {
 
diff --git a/src/states/state_change_queue_unittest.cc b/src/states/state_change_queue_unittest.cc
index 865336c..bb2947a 100644
--- a/src/states/state_change_queue_unittest.cc
+++ b/src/states/state_change_queue_unittest.cc
@@ -5,9 +5,9 @@
 #include "src/states/state_change_queue.h"
 
 #include <gtest/gtest.h>
+#include <weave/test/unittest_utils.h>
 
 #include "src/bind_lambda.h"
-#include "src/commands/unittest_utils.h"
 
 namespace weave {
 
diff --git a/src/states/state_manager_unittest.cc b/src/states/state_manager_unittest.cc
index f336d42..f655e84 100644
--- a/src/states/state_manager_unittest.cc
+++ b/src/states/state_manager_unittest.cc
@@ -12,9 +12,9 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <weave/provider/test/mock_config_store.h>
+#include <weave/test/unittest_utils.h>
 
 #include "src/commands/schema_constants.h"
-#include "src/commands/unittest_utils.h"
 #include "src/states/error_codes.h"
 #include "src/states/mock_state_change_queue_interface.h"
 
diff --git a/src/states/state_package_unittest.cc b/src/states/state_package_unittest.cc
index 0da005b..c116b37 100644
--- a/src/states/state_package_unittest.cc
+++ b/src/states/state_package_unittest.cc
@@ -9,9 +9,9 @@
 
 #include <base/values.h>
 #include <gtest/gtest.h>
+#include <weave/test/unittest_utils.h>
 
 #include "src/commands/schema_constants.h"
-#include "src/commands/unittest_utils.h"
 #include "src/states/error_codes.h"
 
 namespace weave {
diff --git a/src/test/mock_command.cc b/src/test/mock_command.cc
index a8564c4..65c2bab 100644
--- a/src/test/mock_command.cc
+++ b/src/test/mock_command.cc
@@ -8,8 +8,7 @@
 #include <string>
 
 #include <base/values.h>
-
-#include "src/commands/unittest_utils.h"
+#include <weave/test/unittest_utils.h>
 
 namespace weave {
 namespace test {
diff --git a/src/test/unittest_utils.cc b/src/test/unittest_utils.cc
index b9858e4..df8ccc0 100644
--- a/src/test/unittest_utils.cc
+++ b/src/test/unittest_utils.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "src/commands/unittest_utils.h"
+#include <weave/test/unittest_utils.h>
 
 #include <base/json/json_reader.h>
 #include <base/json/json_writer.h>