buffet: Raw string literals for some JSON constants in tests

Added EXPECT_JSON_EQ() macro to help compare JSON values without being
influenced by string formatting and whitespace. This allows us to use
raw literal strings in conditions which improves readability of the
tests.

BUG=None
TEST=FEATURE=test emerge-gizmo buffet

Change-Id: Ibc6642a053dd3e6f3b667de31ceefa21f03e85bf
Reviewed-on: https://chromium-review.googlesource.com/268961
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Vitaly Buka <vitalybuka@chromium.org>
Tested-by: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/buffet/commands/command_dictionary_unittest.cc b/buffet/commands/command_dictionary_unittest.cc
index 6a2c8d7..e31f8cc 100644
--- a/buffet/commands/command_dictionary_unittest.cc
+++ b/buffet/commands/command_dictionary_unittest.cc
@@ -11,6 +11,7 @@
 namespace buffet {
 
 using unittests::CreateDictionaryValue;
+using unittests::IsEqualValue;
 
 TEST(CommandDictionary, Empty) {
   CommandDictionary dict;
@@ -206,7 +207,7 @@
   CommandDictionary base_dict;
   base_dict.LoadCommands(*json_base, "base", nullptr, nullptr);
 
-  auto json = unittests::CreateDictionaryValue(R"({
+  auto json = CreateDictionaryValue(R"({
     'base': {
       'reboot': {
         'parameters': {'delay': {'minimum': 10}},
@@ -225,24 +226,51 @@
 
   json = dict.GetCommandsAsJson(
       [](const CommandDefinition* def) { return true; }, false, nullptr);
-  EXPECT_NE(nullptr, json.get());
-  EXPECT_EQ(
-      "{'base':{'reboot':{'parameters':{'delay':{'minimum':10}}}},"
-      "'robot':{'_jump':{'parameters':{'_height':'integer'}}}}",
-      unittests::ValueToString(json.get()));
+  ASSERT_NE(nullptr, json.get());
+  auto expected = R"({
+    'base': {
+      'reboot': {
+        'parameters': {'delay': {'minimum': 10}}
+      }
+    },
+    'robot': {
+      '_jump': {
+        'parameters': {'_height': 'integer'}
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(expected, *json);
 
   json = dict.GetCommandsAsJson(
       [](const CommandDefinition* def) { return true; }, true, nullptr);
-  EXPECT_NE(nullptr, json.get());
-  EXPECT_EQ(
-      "{'base':{'reboot':{'parameters':{'delay':{"
-      "'maximum':100,'minimum':10,'type':'integer'}}}},"
-      "'robot':{'_jump':{'parameters':{'_height':{'type':'integer'}}}}}",
-      unittests::ValueToString(json.get()));
+  ASSERT_NE(nullptr, json.get());
+  expected = R"({
+    'base': {
+      'reboot': {
+        'parameters': {
+          'delay': {
+            'maximum': 100,
+            'minimum': 10,
+            'type': 'integer'
+          }
+        }
+      }
+    },
+    'robot': {
+      '_jump': {
+        'parameters': {
+          '_height': {
+           'type': 'integer'
+          }
+        }
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(expected, *json);
 }
 
 TEST(CommandDictionary, GetCommandsAsJsonWithVisibility) {
-  auto json = unittests::CreateDictionaryValue(R"({
+  auto json = CreateDictionaryValue(R"({
     'test': {
       'command1': {
         'parameters': {},
@@ -292,56 +320,60 @@
   json = dict.GetCommandsAsJson(
       [](const CommandDefinition* def) { return true; }, false, nullptr);
   ASSERT_NE(nullptr, json.get());
-  EXPECT_EQ(
-      "{'test':{"
-      "'command1':{'parameters':{}},"
-      "'command2':{'parameters':{}},"
-      "'command3':{'parameters':{}},"
-      "'command4':{'parameters':{}},"
-      "'command5':{'parameters':{}},"
-      "'command6':{'parameters':{}},"
-      "'command7':{'parameters':{}},"
-      "'command8':{'parameters':{}}"
-      "}}",
-      unittests::ValueToString(json.get()));
+  auto expected = R"({
+    'test': {
+      'command1': {'parameters': {}},
+      'command2': {'parameters': {}},
+      'command3': {'parameters': {}},
+      'command4': {'parameters': {}},
+      'command5': {'parameters': {}},
+      'command6': {'parameters': {}},
+      'command7': {'parameters': {}},
+      'command8': {'parameters': {}}
+    }
+  })";
+  EXPECT_JSON_EQ(expected, *json);
 
   json = dict.GetCommandsAsJson(
       [](const CommandDefinition* def) { return def->GetVisibility().local; },
       false, nullptr);
   ASSERT_NE(nullptr, json.get());
-  EXPECT_EQ(
-      "{'test':{"
-      "'command2':{'parameters':{}},"
-      "'command4':{'parameters':{}},"
-      "'command6':{'parameters':{}},"
-      "'command8':{'parameters':{}}"
-      "}}",
-      unittests::ValueToString(json.get()));
+  expected = R"({
+    'test': {
+      'command2': {'parameters': {}},
+      'command4': {'parameters': {}},
+      'command6': {'parameters': {}},
+      'command8': {'parameters': {}}
+    }
+  })";
+  EXPECT_JSON_EQ(expected, *json);
 
   json = dict.GetCommandsAsJson(
       [](const CommandDefinition* def) { return def->GetVisibility().cloud; },
       false, nullptr);
   ASSERT_NE(nullptr, json.get());
-  EXPECT_EQ(
-      "{'test':{"
-      "'command3':{'parameters':{}},"
-      "'command4':{'parameters':{}},"
-      "'command7':{'parameters':{}},"
-      "'command8':{'parameters':{}}"
-      "}}",
-      unittests::ValueToString(json.get()));
+  expected = R"({
+    'test': {
+      'command3': {'parameters': {}},
+      'command4': {'parameters': {}},
+      'command7': {'parameters': {}},
+      'command8': {'parameters': {}}
+    }
+  })";
+  EXPECT_JSON_EQ(expected, *json);
 
   json = dict.GetCommandsAsJson(
     [](const CommandDefinition* def) {
       return def->GetVisibility().local && def->GetVisibility().cloud;
     }, false, nullptr);
   ASSERT_NE(nullptr, json.get());
-  EXPECT_EQ(
-      "{'test':{"
-      "'command4':{'parameters':{}},"
-      "'command8':{'parameters':{}}"
-      "}}",
-      unittests::ValueToString(json.get()));
+  expected = R"({
+    'test': {
+      'command4': {'parameters': {}},
+      'command8': {'parameters': {}}
+    }
+  })";
+  EXPECT_JSON_EQ(expected, *json);
 }
 
 TEST(CommandDictionary, LoadCommandsWithVisibility) {
diff --git a/buffet/commands/object_schema_unittest.cc b/buffet/commands/object_schema_unittest.cc
index 06d2ca7..1330099 100644
--- a/buffet/commands/object_schema_unittest.cc
+++ b/buffet/commands/object_schema_unittest.cc
@@ -23,7 +23,6 @@
 
 using unittests::CreateValue;
 using unittests::CreateDictionaryValue;
-using unittests::ValueToString;
 
 namespace {
 
@@ -69,32 +68,26 @@
 
 TEST(CommandSchema, IntPropType_ToJson) {
   IntPropType prop;
-  EXPECT_EQ("'integer'", ValueToString(prop.ToJson(false, nullptr).get()));
-  EXPECT_EQ("{'type':'integer'}",
-            ValueToString(prop.ToJson(true, nullptr).get()));
+  EXPECT_JSON_EQ("'integer'", *prop.ToJson(false, nullptr));
+  EXPECT_JSON_EQ("{'type':'integer'}", *prop.ToJson(true, nullptr));
   IntPropType param2;
   param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_EQ("{}", ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{}", *param2.ToJson(false, nullptr));
   param2.FromJson(CreateDictionaryValue("{'minimum':3}").get(),
                   &prop, nullptr);
-  EXPECT_EQ("{'minimum':3}",
-            ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{'minimum':3}", *param2.ToJson(false, nullptr));
   param2.FromJson(CreateDictionaryValue("{'maximum':-7}").get(),
                   &prop, nullptr);
-  EXPECT_EQ("{'maximum':-7}",
-            ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{'maximum':-7}", *param2.ToJson(false, nullptr));
   param2.FromJson(CreateDictionaryValue("{'minimum':0,'maximum':5}").get(),
                   &prop, nullptr);
-  EXPECT_EQ("{'maximum':5,'minimum':0}",
-            ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{'maximum':5,'minimum':0}", *param2.ToJson(false, nullptr));
   param2.FromJson(CreateDictionaryValue("{'enum':[1,2,3]}").get(), &prop,
                   nullptr);
-  EXPECT_EQ("[1,2,3]",
-            ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("[1,2,3]", *param2.ToJson(false, nullptr));
   param2.FromJson(CreateDictionaryValue("{'default':123}").get(),
                   &prop, nullptr);
-  EXPECT_EQ("{'default':123}",
-            ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{'default':123}", *param2.ToJson(false, nullptr));
 }
 
 TEST(CommandSchema, IntPropType_FromJson) {
@@ -195,21 +188,19 @@
 
 TEST(CommandSchema, BoolPropType_ToJson) {
   BooleanPropType prop;
-  EXPECT_EQ("'boolean'", ValueToString(prop.ToJson(false, nullptr).get()));
-  EXPECT_EQ("{'type':'boolean'}",
-            ValueToString(prop.ToJson(true, nullptr).get()));
+  EXPECT_JSON_EQ("'boolean'", *prop.ToJson(false, nullptr));
+  EXPECT_JSON_EQ("{'type':'boolean'}", *prop.ToJson(true, nullptr));
   BooleanPropType param2;
   param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_EQ("{}", ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{}", *param2.ToJson(false, nullptr));
   param2.FromJson(CreateDictionaryValue("{'enum':[true,false]}").get(), &prop,
                   nullptr);
-  EXPECT_EQ("[true,false]", ValueToString(param2.ToJson(false, nullptr).get()));
-  EXPECT_EQ("{'enum':[true,false],'type':'boolean'}",
-            ValueToString(param2.ToJson(true, nullptr).get()));
+  EXPECT_JSON_EQ("[true,false]", *param2.ToJson(false, nullptr));
+  EXPECT_JSON_EQ("{'enum':[true,false],'type':'boolean'}",
+                 *param2.ToJson(true, nullptr));
   param2.FromJson(CreateDictionaryValue("{'default':true}").get(),
                   &prop, nullptr);
-  EXPECT_EQ("{'default':true}",
-            ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{'default':true}", *param2.ToJson(false, nullptr));
 }
 
 TEST(CommandSchema, BoolPropType_FromJson) {
@@ -288,28 +279,24 @@
 
 TEST(CommandSchema, DoublePropType_ToJson) {
   DoublePropType prop;
-  EXPECT_EQ("'number'", ValueToString(prop.ToJson(false, nullptr).get()));
-  EXPECT_EQ("{'type':'number'}",
-            ValueToString(prop.ToJson(true, nullptr).get()));
+  EXPECT_JSON_EQ("'number'", *prop.ToJson(false, nullptr));
+  EXPECT_JSON_EQ("{'type':'number'}", *prop.ToJson(true, nullptr));
   DoublePropType param2;
   param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_EQ("{}", ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{}", *param2.ToJson(false, nullptr));
   param2.FromJson(CreateDictionaryValue("{'minimum':3}").get(), &prop,
                   nullptr);
-  EXPECT_EQ("{'minimum':3.0}",
-            ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{'minimum':3.0}", *param2.ToJson(false, nullptr));
   param2.FromJson(CreateDictionaryValue("{'maximum':-7}").get(), &prop,
                   nullptr);
-  EXPECT_EQ("{'maximum':-7.0}",
-            ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{'maximum':-7.0}", *param2.ToJson(false, nullptr));
   param2.FromJson(CreateDictionaryValue("{'minimum':0,'maximum':5}").get(),
                   &prop, nullptr);
-  EXPECT_EQ("{'maximum':5.0,'minimum':0.0}",
-            ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{'maximum':5.0,'minimum':0.0}",
+                 *param2.ToJson(false, nullptr));
   param2.FromJson(CreateDictionaryValue("{'default':12.3}").get(),
                   &prop, nullptr);
-  EXPECT_EQ("{'default':12.3}",
-            ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{'default':12.3}", *param2.ToJson(false, nullptr));
 }
 
 TEST(CommandSchema, DoublePropType_FromJson) {
@@ -411,28 +398,24 @@
 
 TEST(CommandSchema, StringPropType_ToJson) {
   StringPropType prop;
-  EXPECT_EQ("'string'", ValueToString(prop.ToJson(false, nullptr).get()));
-  EXPECT_EQ("{'type':'string'}",
-            ValueToString(prop.ToJson(true, nullptr).get()));
+  EXPECT_JSON_EQ("'string'", *prop.ToJson(false, nullptr));
+  EXPECT_JSON_EQ("{'type':'string'}", *prop.ToJson(true, nullptr));
   StringPropType param2;
   param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_EQ("{}", ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{}", *param2.ToJson(false, nullptr));
   param2.FromJson(CreateDictionaryValue("{'minLength':3}").get(), &prop,
                   nullptr);
-  EXPECT_EQ("{'minLength':3}",
-            ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{'minLength':3}", *param2.ToJson(false, nullptr));
   param2.FromJson(CreateDictionaryValue("{'maxLength':7}").get(), &prop,
                   nullptr);
-  EXPECT_EQ("{'maxLength':7}",
-            ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{'maxLength':7}", *param2.ToJson(false, nullptr));
   param2.FromJson(CreateDictionaryValue("{'minLength':0,'maxLength':5}").get(),
                   &prop, nullptr);
-  EXPECT_EQ("{'maxLength':5,'minLength':0}",
-            ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{'maxLength':5,'minLength':0}",
+                 *param2.ToJson(false, nullptr));
   param2.FromJson(CreateDictionaryValue("{'default':'abcd'}").get(),
                   &prop, nullptr);
-  EXPECT_EQ("{'default':'abcd'}",
-            ValueToString(param2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{'default':'abcd'}", *param2.ToJson(false, nullptr));
 }
 
 TEST(CommandSchema, StringPropType_FromJson) {
@@ -538,14 +521,15 @@
 
 TEST(CommandSchema, ObjectPropType_ToJson) {
   ObjectPropType prop;
-  EXPECT_EQ("{'additionalProperties':false,'properties':{}}",
-            ValueToString(prop.ToJson(false, nullptr).get()));
-  EXPECT_EQ("{'additionalProperties':false,'properties':{},'type':'object'}",
-            ValueToString(prop.ToJson(true, nullptr).get()));
+  EXPECT_JSON_EQ("{'additionalProperties':false,'properties':{}}",
+                 *prop.ToJson(false, nullptr));
+  EXPECT_JSON_EQ(
+      "{'additionalProperties':false,'properties':{},'type':'object'}",
+      *prop.ToJson(true, nullptr));
   EXPECT_FALSE(prop.IsBasedOnSchema());
   ObjectPropType prop2;
   prop2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_EQ("{}", ValueToString(prop2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{}", *prop2.ToJson(false, nullptr));
   EXPECT_TRUE(prop2.IsBasedOnSchema());
 
   auto schema = ObjectSchema::Create();
@@ -554,44 +538,106 @@
   pw->GetString()->AddLengthConstraint(6, 100);
   schema->AddProp("password", std::move(pw));
   prop2.SetObjectSchema(std::move(schema));
-  EXPECT_EQ("{'additionalProperties':false,'properties':{'expires':'integer',"
-            "'password':{'maxLength':100,'minLength':6}}}",
-            ValueToString(prop2.ToJson(false, nullptr).get()));
-  EXPECT_EQ("{'additionalProperties':false,'properties':{"
-            "'expires':{'type':'integer'},"
-            "'password':{'maxLength':100,'minLength':6,'type':'string'}},"
-            "'type':'object'}",
-            ValueToString(prop2.ToJson(true, nullptr).get()));
+  auto expected = R"({
+    'additionalProperties': false,
+    'properties': {
+      'expires': 'integer',
+      'password': {
+        'maxLength': 100,
+        'minLength': 6
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(expected, *prop2.ToJson(false, nullptr));
+
+  expected = R"({
+    'additionalProperties': false,
+    'properties': {
+      'expires': {
+        'type': 'integer'
+      },
+      'password': {
+        'maxLength': 100,
+        'minLength': 6,
+        'type': 'string'
+      }
+    },
+    'type': 'object'
+  })";
+  EXPECT_JSON_EQ(expected, *prop2.ToJson(true, nullptr));
 
   ObjectPropType prop3;
   ASSERT_TRUE(prop3.FromJson(CreateDictionaryValue(
       "{'default':{'expires':3,'password':'abracadabra'}}").get(), &prop2,
       nullptr));
-  EXPECT_EQ("{'default':{'expires':3,'password':'abracadabra'}}",
-            ValueToString(prop3.ToJson(false, nullptr).get()));
-  EXPECT_EQ("{'additionalProperties':false,"
-            "'default':{'expires':3,'password':'abracadabra'},"
-            "'properties':{'expires':{'type':'integer'},"
-            "'password':{'maxLength':100,'minLength':6,'type':'string'}},"
-            "'type':'object'}",
-            ValueToString(prop3.ToJson(true, nullptr).get()));
+  expected = R"({
+    'default': {
+      'expires': 3,
+      'password': 'abracadabra'
+    }
+  })";
+  EXPECT_JSON_EQ(expected, *prop3.ToJson(false, nullptr));
+
+  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, nullptr));
 
   ObjectPropType prop4;
   ASSERT_TRUE(prop4.FromJson(CreateDictionaryValue(
       "{'additionalProperties':true,"
       "'default':{'expires':3,'password':'abracadabra'}}").get(), &prop2,
       nullptr));
-  EXPECT_EQ("{'additionalProperties':true,"
-            "'default':{'expires':3,'password':'abracadabra'},"
-            "'properties':{'expires':'integer',"
-            "'password':{'maxLength':100,'minLength':6}}}",
-            ValueToString(prop4.ToJson(false, nullptr).get()));
-  EXPECT_EQ("{'additionalProperties':true,"
-            "'default':{'expires':3,'password':'abracadabra'},"
-            "'properties':{'expires':{'type':'integer'},"
-            "'password':{'maxLength':100,'minLength':6,'type':'string'}},"
-            "'type':'object'}",
-            ValueToString(prop4.ToJson(true, nullptr).get()));
+  expected = R"({
+    'additionalProperties': true,
+    'default': {
+      'expires': 3,
+      'password': 'abracadabra'
+    },
+    'properties': {
+      'expires': 'integer',
+      'password': {
+        'maxLength': 100,
+        'minLength': 6
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(expected, *prop4.ToJson(false, nullptr));
+
+  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, nullptr));
 }
 
 TEST(CommandSchema, ObjectPropType_FromJson) {
@@ -725,21 +771,20 @@
 TEST(CommandSchema, ArrayPropType_ToJson) {
   ArrayPropType prop;
   prop.SetItemType(PropType::Create(ValueType::Int));
-  EXPECT_EQ("{'items':'integer'}",
-            ValueToString(prop.ToJson(false, nullptr).get()));
-  EXPECT_EQ("{'items':{'type':'integer'},'type':'array'}",
-            ValueToString(prop.ToJson(true, nullptr).get()));
+  EXPECT_JSON_EQ("{'items':'integer'}", *prop.ToJson(false, nullptr));
+  EXPECT_JSON_EQ("{'items':{'type':'integer'},'type':'array'}",
+                 *prop.ToJson(true, nullptr));
   EXPECT_FALSE(prop.IsBasedOnSchema());
   ArrayPropType prop2;
   prop2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_EQ("{}", ValueToString(prop2.ToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("{}", *prop2.ToJson(false, nullptr));
   EXPECT_TRUE(prop2.IsBasedOnSchema());
   prop2.FromJson(CreateDictionaryValue("{'default':[1,2,3]}").get(),
                  &prop, nullptr);
-  EXPECT_EQ("{'default':[1,2,3]}",
-            ValueToString(prop2.ToJson(false, nullptr).get()));
-  EXPECT_EQ("{'default':[1,2,3],'items':{'type':'integer'},'type':'array'}",
-            ValueToString(prop2.ToJson(true, nullptr).get()));
+  EXPECT_JSON_EQ("{'default':[1,2,3]}", *prop2.ToJson(false, nullptr));
+  EXPECT_JSON_EQ(
+      "{'default':[1,2,3],'items':{'type':'integer'},'type':'array'}",
+      *prop2.ToJson(true, nullptr));
 }
 
 TEST(CommandSchema, ArrayPropType_FromJson) {
@@ -816,7 +861,7 @@
   ASSERT_NE(nullptr, val.get());
   EXPECT_EQ(nullptr, error.get());
   EXPECT_EQ(arr, val->GetValueAsAny().Get<native_types::Array>());
-  EXPECT_EQ("[]", ValueToString(val->ToJson(nullptr).get()));
+  EXPECT_JSON_EQ("[]", *val->ToJson(nullptr));
 
   IntPropType int_type;
   ObjectPropType obj_type;
@@ -840,8 +885,8 @@
   ASSERT_NE(nullptr, val.get());
   EXPECT_EQ(nullptr, error.get());
   EXPECT_EQ(arr, val->GetValueAsAny().Get<native_types::Array>());
-  EXPECT_EQ("[{'height':20,'width':10},{'height':18,'width':17}]",
-            ValueToString(val->ToJson(nullptr).get()));
+  EXPECT_JSON_EQ("[{'height':20,'width':10},{'height':18,'width':17}]",
+                 *val->ToJson(nullptr));
 
   val = prop.CreateValue("blah", &error);
   EXPECT_EQ(nullptr, val.get());
diff --git a/buffet/commands/schema_utils_unittest.cc b/buffet/commands/schema_utils_unittest.cc
index 9698317..d872995 100644
--- a/buffet/commands/schema_utils_unittest.cc
+++ b/buffet/commands/schema_utils_unittest.cc
@@ -22,38 +22,32 @@
 
 using unittests::CreateDictionaryValue;
 using unittests::CreateValue;
-using unittests::ValueToString;
 using chromeos::VariantDictionary;
 
 TEST(CommandSchemaUtils, TypedValueToJson_Scalar) {
-  EXPECT_EQ("true", ValueToString(TypedValueToJson(true, nullptr).get()));
-  EXPECT_EQ("false", ValueToString(TypedValueToJson(false, nullptr).get()));
+  EXPECT_JSON_EQ("true", *TypedValueToJson(true, nullptr));
+  EXPECT_JSON_EQ("false", *TypedValueToJson(false, nullptr));
 
-  EXPECT_EQ("0", ValueToString(TypedValueToJson(0, nullptr).get()));
-  EXPECT_EQ("-10", ValueToString(TypedValueToJson(-10, nullptr).get()));
-  EXPECT_EQ("20", ValueToString(TypedValueToJson(20, nullptr).get()));
+  EXPECT_JSON_EQ("0", *TypedValueToJson(0, nullptr));
+  EXPECT_JSON_EQ("-10", *TypedValueToJson(-10, nullptr));
+  EXPECT_JSON_EQ("20", *TypedValueToJson(20, nullptr));
 
-  EXPECT_EQ("0.0", ValueToString(TypedValueToJson(0.0, nullptr).get()));
-  EXPECT_EQ("1.2", ValueToString(TypedValueToJson(1.2, nullptr).get()));
+  EXPECT_JSON_EQ("0.0", *TypedValueToJson(0.0, nullptr));
+  EXPECT_JSON_EQ("1.2", *TypedValueToJson(1.2, nullptr));
 
-  EXPECT_EQ("'abc'",
-            ValueToString(TypedValueToJson(std::string("abc"), nullptr).get()));
+  EXPECT_JSON_EQ("'abc'", *TypedValueToJson(std::string("abc"), nullptr));
 
   std::vector<bool> bool_array{true, false};
-  EXPECT_EQ("[true,false]",
-            ValueToString(TypedValueToJson(bool_array, nullptr).get()));
+  EXPECT_JSON_EQ("[true,false]", *TypedValueToJson(bool_array, nullptr));
 
   std::vector<int> int_array{1, 2, 5};
-  EXPECT_EQ("[1,2,5]",
-            ValueToString(TypedValueToJson(int_array, nullptr).get()));
+  EXPECT_JSON_EQ("[1,2,5]", *TypedValueToJson(int_array, nullptr));
 
   std::vector<double> dbl_array{1.1, 2.2};
-  EXPECT_EQ("[1.1,2.2]",
-            ValueToString(TypedValueToJson(dbl_array, nullptr).get()));
+  EXPECT_JSON_EQ("[1.1,2.2]", *TypedValueToJson(dbl_array, nullptr));
 
   std::vector<std::string> str_array{"a", "bc"};
-  EXPECT_EQ("['a','bc']",
-            ValueToString(TypedValueToJson(str_array, nullptr).get()));
+  EXPECT_JSON_EQ("['a','bc']", *TypedValueToJson(str_array, nullptr));
 }
 
 TEST(CommandSchemaUtils, TypedValueToJson_Object) {
@@ -62,8 +56,8 @@
 
   object.insert(std::make_pair("width", int_type.CreateValue(640, nullptr)));
   object.insert(std::make_pair("height", int_type.CreateValue(480, nullptr)));
-  EXPECT_EQ("{'height':480,'width':640}",
-            ValueToString(TypedValueToJson(object, nullptr).get()));
+  EXPECT_JSON_EQ("{'height':480,'width':640}",
+                 *TypedValueToJson(object, nullptr));
 }
 
 TEST(CommandSchemaUtils, TypedValueToJson_Array) {
@@ -72,7 +66,7 @@
 
   arr.push_back(int_type.CreateValue(640, nullptr));
   arr.push_back(int_type.CreateValue(480, nullptr));
-  EXPECT_EQ("[640,480]", ValueToString(TypedValueToJson(arr, nullptr).get()));
+  EXPECT_JSON_EQ("[640,480]", *TypedValueToJson(arr, nullptr));
 }
 
 TEST(CommandSchemaUtils, TypedValueFromJson_Bool) {
diff --git a/buffet/commands/unittest_utils.cc b/buffet/commands/unittest_utils.cc
index db8aa4a..be4d4e7 100644
--- a/buffet/commands/unittest_utils.cc
+++ b/buffet/commands/unittest_utils.cc
@@ -14,25 +14,22 @@
   std::string json2(json);
   // Convert apostrophes to double-quotes so JSONReader can parse the string.
   std::replace(json2.begin(), json2.end(), '\'', '"');
-  return std::unique_ptr<base::Value>(base::JSONReader::Read(json2));
+  int error = 0;
+  std::string message;
+  std::unique_ptr<base::Value> value{base::JSONReader::ReadAndReturnError(
+      json2, base::JSON_PARSE_RFC, &error, &message)};
+  CHECK(value) << "Failed to load JSON: " << message << ", " << json;
+  return value;
 }
 
 std::unique_ptr<base::DictionaryValue> CreateDictionaryValue(const char* json) {
-  std::string json2(json);
-  std::replace(json2.begin(), json2.end(), '\'', '"');
-  base::Value* value = base::JSONReader::Read(json2);
-  CHECK(value) << "Failed to load JSON: " << json2;
+  std::unique_ptr<base::Value> value = CreateValue(json);
   base::DictionaryValue* dict = nullptr;
   value->GetAsDictionary(&dict);
+  CHECK(dict) << "Value is not dictionary: " << json;
+  value.release();
   return std::unique_ptr<base::DictionaryValue>(dict);
 }
 
-std::string ValueToString(const base::Value* value) {
-  std::string json;
-  base::JSONWriter::Write(value, &json);
-  std::replace(json.begin(), json.end(), '"', '\'');
-  return json;
-}
-
 }  // namespace unittests
 }  // namespace buffet
diff --git a/buffet/commands/unittest_utils.h b/buffet/commands/unittest_utils.h
index 25db6b2..3a9dcc1 100644
--- a/buffet/commands/unittest_utils.h
+++ b/buffet/commands/unittest_utils.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include <base/values.h>
+#include <gtest/gtest.h>
 
 #include "buffet/commands/prop_types.h"
 #include "buffet/commands/prop_values.h"
@@ -24,9 +25,9 @@
 // Helper method to create a JSON dictionary object from a string.
 std::unique_ptr<base::DictionaryValue> CreateDictionaryValue(const char* json);
 
-// Converts a JSON value to a string. It also converts double-quotes to
-// apostrophes for easy comparisons in C++ source code.
-std::string ValueToString(const base::Value* value);
+inline bool IsEqualValue(const base::Value& val1, const base::Value& val2) {
+  return val1.Equals(&val2);
+}
 
 template <typename PropVal, typename T>
 std::unique_ptr<const PropVal> make_prop_value(const T& value) {
@@ -56,4 +57,8 @@
 }  // namespace unittests
 }  // namespace buffet
 
+#define EXPECT_JSON_EQ(expected, actual) \
+  EXPECT_PRED2(buffet::unittests::IsEqualValue, \
+               *buffet::unittests::CreateValue(expected), actual)
+
 #endif  // BUFFET_COMMANDS_UNITTEST_UTILS_H_
diff --git a/buffet/device_registration_info_unittest.cc b/buffet/device_registration_info_unittest.cc
index f82a39b..1800da1 100644
--- a/buffet/device_registration_info_unittest.cc
+++ b/buffet/device_registration_info_unittest.cc
@@ -400,12 +400,29 @@
     base::DictionaryValue* commandDefs = nullptr;
     EXPECT_TRUE(json->GetDictionary("deviceDraft.commandDefs", &commandDefs));
     EXPECT_FALSE(commandDefs->empty());
-    EXPECT_EQ(
-        "{'base':{'reboot':{'parameters':{"
-        "'delay':{'minimum':10,'type':'integer'}}}},"
-        "'robot':{'_jump':{'parameters':{"
-        "'_height':{'type':'integer'}}}}}",
-        unittests::ValueToString(commandDefs));
+
+    auto expected = R"({
+      'base': {
+        'reboot': {
+          'parameters': {
+            'delay': {
+              'minimum': 10,
+              'type': 'integer'
+            }
+          }
+        }
+      },
+      'robot': {
+        '_jump': {
+          'parameters': {
+            '_height': {
+              'type': 'integer'
+            }
+          }
+        }
+      }
+    })";
+    EXPECT_JSON_EQ(expected, *commandDefs);
 
     base::DictionaryValue json_resp;
     json_resp.SetString("id", test_data::kClaimTicketId);
diff --git a/buffet/states/state_manager_unittest.cc b/buffet/states/state_manager_unittest.cc
index 12d3845..d71ebb6 100644
--- a/buffet/states/state_manager_unittest.cc
+++ b/buffet/states/state_manager_unittest.cc
@@ -21,7 +21,6 @@
 using testing::_;
 using testing::Return;
 using unittests::CreateDictionaryValue;
-using unittests::ValueToString;
 
 namespace {
 std::unique_ptr<base::DictionaryValue> GetTestSchema() {
@@ -53,16 +52,14 @@
     // Initial expectations.
     EXPECT_CALL(mock_state_change_queue_, IsEmpty()).Times(0);
     EXPECT_CALL(mock_state_change_queue_, NotifyPropertiesUpdated(_, _))
-      .Times(0);
+        .Times(0);
     EXPECT_CALL(mock_state_change_queue_, GetAndClearRecordedStateChanges())
-      .Times(0);
+        .Times(0);
     mgr_.reset(new StateManager(&mock_state_change_queue_));
     LoadStateDefinition(GetTestSchema().get(), "default", nullptr);
     ASSERT_TRUE(mgr_->LoadStateDefaults(*GetTestValues().get(), nullptr));
   }
-  void TearDown() override {
-    mgr_.reset();
-  }
+  void TearDown() override { mgr_.reset(); }
 
   void LoadStateDefinition(const base::DictionaryValue* json,
                            const std::string& category,
@@ -82,9 +79,16 @@
 
 TEST_F(StateManagerTest, Initialized) {
   EXPECT_EQ(std::set<std::string>{"default"}, mgr_->GetCategories());
-  EXPECT_EQ("{'base':{'manufacturer':'Skynet','serialNumber':'T1000'},"
-            "'terminator':{'target':''}}",
-            ValueToString(mgr_->GetStateValuesAsJson(nullptr).get()));
+  auto expected = R"({
+    'base': {
+      'manufacturer': 'Skynet',
+      'serialNumber': 'T1000'
+    },
+    'terminator': {
+      'target': ''
+    }
+  })";
+  EXPECT_JSON_EQ(expected, *mgr_->GetStateValuesAsJson(nullptr));
 }
 
 TEST_F(StateManagerTest, LoadStateDefinition) {
@@ -96,27 +100,42 @@
   LoadStateDefinition(dict.get(), "powerd", nullptr);
   EXPECT_EQ((std::set<std::string>{"default", "powerd"}),
             mgr_->GetCategories());
-  EXPECT_EQ("{'base':{'manufacturer':'Skynet','serialNumber':'T1000'},"
-            "'power':{'battery_level':0},"
-            "'terminator':{'target':''}}",
-            ValueToString(mgr_->GetStateValuesAsJson(nullptr).get()));
+
+  auto expected = R"({
+    'base': {
+      'manufacturer': 'Skynet',
+      'serialNumber': 'T1000'
+    },
+    'power': {
+      'battery_level': 0
+    },
+    'terminator': {
+      'target': ''
+    }
+  })";
+  EXPECT_JSON_EQ(expected, *mgr_->GetStateValuesAsJson(nullptr));
 }
 
 TEST_F(StateManagerTest, SetPropertyValue) {
   native_types::Object expected_prop_set{
-    {"terminator.target", unittests::make_string_prop_value("John Connor")},
+      {"terminator.target", unittests::make_string_prop_value("John Connor")},
   };
   base::Time timestamp = base::Time::Now();
   EXPECT_CALL(mock_state_change_queue_,
               NotifyPropertiesUpdated(timestamp, expected_prop_set))
       .WillOnce(Return(true));
-  ASSERT_TRUE(mgr_->SetPropertyValue("terminator.target",
-                                     std::string{"John Connor"},
-                                     timestamp,
-                                     nullptr));
-  EXPECT_EQ("{'base':{'manufacturer':'Skynet','serialNumber':'T1000'},"
-            "'terminator':{'target':'John Connor'}}",
-            ValueToString(mgr_->GetStateValuesAsJson(nullptr).get()));
+  ASSERT_TRUE(mgr_->SetPropertyValue(
+      "terminator.target", std::string{"John Connor"}, timestamp, nullptr));
+  auto expected = R"({
+    'base': {
+      'manufacturer': 'Skynet',
+      'serialNumber': 'T1000'
+    },
+    'terminator': {
+      'target': 'John Connor'
+    }
+  })";
+  EXPECT_JSON_EQ(expected, *mgr_->GetStateValuesAsJson(nullptr));
 }
 
 TEST_F(StateManagerTest, SetPropertyValue_Error_NoName) {
@@ -129,8 +148,8 @@
 
 TEST_F(StateManagerTest, SetPropertyValue_Error_NoPackage) {
   chromeos::ErrorPtr error;
-  ASSERT_FALSE(mgr_->SetPropertyValue("target", int{0}, base::Time::Now(),
-                                      &error));
+  ASSERT_FALSE(
+      mgr_->SetPropertyValue("target", int{0}, base::Time::Now(), &error));
   EXPECT_EQ(errors::state::kDomain, error->GetDomain());
   EXPECT_EQ(errors::state::kPackageNameMissing, error->GetCode());
   EXPECT_EQ("Package name is missing in the property name",
@@ -139,8 +158,8 @@
 
 TEST_F(StateManagerTest, SetPropertyValue_Error_UnknownPackage) {
   chromeos::ErrorPtr error;
-  ASSERT_FALSE(mgr_->SetPropertyValue("power.level", int{0}, base::Time::Now(),
-                                      &error));
+  ASSERT_FALSE(
+      mgr_->SetPropertyValue("power.level", int{0}, base::Time::Now(), &error));
   EXPECT_EQ(errors::state::kDomain, error->GetDomain());
   EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode());
   EXPECT_EQ("Unknown state property package 'power'", error->GetMessage());
@@ -148,8 +167,8 @@
 
 TEST_F(StateManagerTest, SetPropertyValue_Error_UnknownProperty) {
   chromeos::ErrorPtr error;
-  ASSERT_FALSE(mgr_->SetPropertyValue("base.level", int{0}, base::Time::Now(),
-                                      &error));
+  ASSERT_FALSE(
+      mgr_->SetPropertyValue("base.level", int{0}, base::Time::Now(), &error));
   EXPECT_EQ(errors::state::kDomain, error->GetDomain());
   EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode());
   EXPECT_EQ("State property 'base.level' is not defined", error->GetMessage());
@@ -159,10 +178,8 @@
   base::Time timestamp = base::Time::Now();
   EXPECT_CALL(mock_state_change_queue_, NotifyPropertiesUpdated(timestamp, _))
       .WillOnce(Return(true));
-  ASSERT_TRUE(mgr_->SetPropertyValue("terminator.target",
-                                     std::string{"John Connor"},
-                                     timestamp,
-                                     nullptr));
+  ASSERT_TRUE(mgr_->SetPropertyValue(
+      "terminator.target", std::string{"John Connor"}, timestamp, nullptr));
   std::vector<StateChange> expected_val;
   expected_val.emplace_back(
       timestamp,
diff --git a/buffet/states/state_package_unittest.cc b/buffet/states/state_package_unittest.cc
index 7856531..179d206 100644
--- a/buffet/states/state_package_unittest.cc
+++ b/buffet/states/state_package_unittest.cc
@@ -18,7 +18,6 @@
 namespace buffet {
 
 using unittests::CreateDictionaryValue;
-using unittests::ValueToString;
 
 class StatePackageTestHelper {
  public:
@@ -86,16 +85,41 @@
   ASSERT_TRUE(package.AddSchemaFromJson(GetTestSchema().get(), nullptr));
   EXPECT_EQ(4, GetTypes(package).GetProps().size());
   EXPECT_EQ(4, GetValues(package).size());
-  EXPECT_EQ("{'color':{'type':'string'},"
-            "'direction':{'additionalProperties':false,'properties':{"
-              "'altitude':{'maximum':90.0,'type':'number'},"
-              "'azimuth':{'type':'number'}},"
-              "'type':'object'},"
-            "'iso':{'enum':[50,100,200,400],'type':'integer'},"
-            "'light':{'type':'boolean'}}",
-            ValueToString(GetTypes(package).ToJson(true, nullptr).get()));
-  EXPECT_EQ("{'color':'','direction':{},'iso':0,'light':false}",
-            ValueToString(package.GetValuesAsJson(nullptr).get()));
+
+  auto expected = R"({
+    'color': {
+      'type': 'string'
+    },
+    'direction': {
+      'additionalProperties': false,
+      'properties': {
+        'altitude': {
+          'maximum': 90.0,
+          'type': 'number'
+        },
+        'azimuth': {
+          'type': 'number'
+        }
+      },
+      'type': 'object'
+    },
+    'iso': {
+      'enum': [50, 100, 200, 400],
+      'type': 'integer'
+    },
+    'light': {
+      'type': 'boolean'
+    }
+  })";
+  EXPECT_JSON_EQ(expected, *GetTypes(package).ToJson(true, nullptr));
+
+  expected = R"({
+    'color': '',
+    'direction': {},
+    'iso': 0,
+    'light': false
+  })";
+  EXPECT_JSON_EQ(expected, *package.GetValuesAsJson(nullptr));
 }
 
 TEST(StatePackage, AddValuesFromJson_OnEmpty) {
@@ -103,11 +127,16 @@
   ASSERT_TRUE(package.AddSchemaFromJson(GetTestSchema().get(), nullptr));
   ASSERT_TRUE(package.AddValuesFromJson(GetTestValues().get(), nullptr));
   EXPECT_EQ(4, GetValues(package).size());
-  EXPECT_EQ("{'color':'white',"
-            "'direction':{'altitude':89.9,'azimuth':57.2957795},"
-            "'iso':200,"
-            "'light':true}",
-            ValueToString(package.GetValuesAsJson(nullptr).get()));
+  auto expected = R"({
+    'color': 'white',
+    'direction': {
+      'altitude': 89.9,
+      'azimuth': 57.2957795
+    },
+    'iso': 200,
+    'light': true
+  })";
+  EXPECT_JSON_EQ(expected, *package.GetValuesAsJson(nullptr));
 }
 
 TEST_F(StatePackageTest, AddSchemaFromJson_AddMore) {
@@ -115,21 +144,48 @@
   ASSERT_TRUE(package_->AddSchemaFromJson(dict.get(), nullptr));
   EXPECT_EQ(5, GetTypes(*package_).GetProps().size());
   EXPECT_EQ(5, GetValues(*package_).size());
-  EXPECT_EQ("{'brightness':{'enum':['low','medium','high'],'type':'string'},"
-            "'color':{'type':'string'},"
-            "'direction':{'additionalProperties':false,'properties':{"
-              "'altitude':{'maximum':90.0,'type':'number'},"
-              "'azimuth':{'type':'number'}},"
-              "'type':'object'},"
-            "'iso':{'enum':[50,100,200,400],'type':'integer'},"
-            "'light':{'type':'boolean'}}",
-            ValueToString(GetTypes(*package_).ToJson(true, nullptr).get()));
-  EXPECT_EQ("{'brightness':'',"
-            "'color':'white',"
-            "'direction':{'altitude':89.9,'azimuth':57.2957795},"
-            "'iso':200,"
-            "'light':true}",
-            ValueToString(package_->GetValuesAsJson(nullptr).get()));
+  auto expected = R"({
+    'brightness': {
+      'enum': ['low', 'medium', 'high'],
+      'type': 'string'
+    },
+    'color': {
+      'type': 'string'
+    },
+    'direction': {
+      'additionalProperties': false,
+      'properties': {
+        'altitude': {
+          'maximum': 90.0,
+          'type': 'number'
+        },
+        'azimuth': {
+          'type': 'number'
+        }
+      },
+      'type': 'object'
+    },
+    'iso': {
+      'enum': [50, 100, 200, 400],
+      'type': 'integer'
+    },
+    'light': {
+      'type': 'boolean'
+    }
+  })";
+  EXPECT_JSON_EQ(expected, *GetTypes(*package_).ToJson(true, nullptr));
+
+  expected = R"({
+    'brightness': '',
+    'color': 'white',
+    'direction': {
+      'altitude': 89.9,
+      'azimuth': 57.2957795
+    },
+    'iso': 200,
+    'light': true
+  })";
+  EXPECT_JSON_EQ(expected, *package_->GetValuesAsJson(nullptr));
 }
 
 TEST_F(StatePackageTest, AddValuesFromJson_AddMore) {
@@ -138,12 +194,17 @@
   dict = CreateDictionaryValue("{'brightness':'medium'}");
   ASSERT_TRUE(package_->AddValuesFromJson(dict.get(), nullptr));
   EXPECT_EQ(5, GetValues(*package_).size());
-  EXPECT_EQ("{'brightness':'medium',"
-            "'color':'white',"
-            "'direction':{'altitude':89.9,'azimuth':57.2957795},"
-            "'iso':200,"
-            "'light':true}",
-            ValueToString(package_->GetValuesAsJson(nullptr).get()));
+  auto expected = R"({
+    'brightness': 'medium',
+    'color': 'white',
+    'direction': {
+      'altitude': 89.9,
+      'azimuth': 57.2957795
+    },
+    'iso': 200,
+    'light': true
+  })";
+  EXPECT_JSON_EQ(expected, *package_->GetValuesAsJson(nullptr));
 }
 
 TEST_F(StatePackageTest, AddSchemaFromJson_Error_Redefined) {
@@ -214,11 +275,17 @@
     {"azimuth", double{15.0}},
   };
   EXPECT_TRUE(package_->SetPropertyValue("direction", direction, nullptr));
-  EXPECT_EQ("{'color':'white',"
-            "'direction':{'altitude':45.0,'azimuth':15.0},"
-            "'iso':200,"
-            "'light':true}",
-            ValueToString(package_->GetValuesAsJson(nullptr).get()));
+
+  auto expected = R"({
+    'color': 'white',
+    'direction': {
+      'altitude': 45.0,
+      'azimuth': 15.0
+    },
+    'iso': 200,
+    'light': true
+  })";
+  EXPECT_JSON_EQ(expected, *package_->GetValuesAsJson(nullptr));
 }
 
 TEST_F(StatePackageTest, SetPropertyValue_Error_TypeMismatch) {