buffet: Added error handling to PropType::CreateValue()

PropType::CreateValue(Any) used to abort if Any didn't contain
the data of expected type. Now that we are adding more and more
reliance on D-Bus transport for GCD data (commands and state),
we can no longer guarantee that the variant data passed to
CreateValue contains the expected values (since it may be not
under our control). Add the ability for CreateValue to gracefully
fail and provide necessary error information to the caller.

BUG=chromium:415364
TEST=FEATURES=test emerge-link buffet

Change-Id: I097d674073fc84a69184a78edf2b7381608c000f
Reviewed-on: https://chromium-review.googlesource.com/219135
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/commands/object_schema_unittest.cc b/buffet/commands/object_schema_unittest.cc
index e2e32e1..e802a67 100644
--- a/buffet/commands/object_schema_unittest.cc
+++ b/buffet/commands/object_schema_unittest.cc
@@ -14,6 +14,7 @@
 
 #include "buffet/commands/object_schema.h"
 #include "buffet/commands/prop_types.h"
+#include "buffet/commands/schema_constants.h"
 #include "buffet/commands/unittest_utils.h"
 
 using buffet::unittests::CreateValue;
@@ -124,6 +125,20 @@
   EXPECT_EQ("type_mismatch", error->GetCode());
 }
 
+TEST(CommandSchema, IntPropType_CreateValue) {
+  buffet::IntPropType prop;
+  chromeos::ErrorPtr error;
+  auto val = prop.CreateValue(2, &error);
+  ASSERT_NE(nullptr, val.get());
+  EXPECT_EQ(nullptr, error.get());
+  EXPECT_EQ(2, val->GetValueAsAny().Get<int>());
+
+  val = prop.CreateValue("blah", &error);
+  EXPECT_EQ(nullptr, val.get());
+  ASSERT_NE(nullptr, error.get());
+  EXPECT_EQ(buffet::errors::commands::kTypeMismatch, error->GetCode());
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 TEST(CommandSchema, BoolPropType_Empty) {
@@ -188,6 +203,20 @@
   EXPECT_EQ("type_mismatch", error->GetCode());
 }
 
+TEST(CommandSchema, BoolPropType_CreateValue) {
+  buffet::BooleanPropType prop;
+  chromeos::ErrorPtr error;
+  auto val = prop.CreateValue(true, &error);
+  ASSERT_NE(nullptr, val.get());
+  EXPECT_EQ(nullptr, error.get());
+  EXPECT_TRUE(val->GetValueAsAny().Get<bool>());
+
+  val = prop.CreateValue("blah", &error);
+  EXPECT_EQ(nullptr, val.get());
+  ASSERT_NE(nullptr, error.get());
+  EXPECT_EQ(buffet::errors::commands::kTypeMismatch, error->GetCode());
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 TEST(CommandSchema, DoublePropType_Empty) {
@@ -285,6 +314,20 @@
   EXPECT_EQ("type_mismatch", error->GetCode());
 }
 
+TEST(CommandSchema, DoublePropType_CreateValue) {
+  buffet::DoublePropType prop;
+  chromeos::ErrorPtr error;
+  auto val = prop.CreateValue(2.0, &error);
+  ASSERT_NE(nullptr, val.get());
+  EXPECT_EQ(nullptr, error.get());
+  EXPECT_DOUBLE_EQ(2.0, val->GetValueAsAny().Get<double>());
+
+  val = prop.CreateValue("blah", &error);
+  EXPECT_EQ(nullptr, val.get());
+  ASSERT_NE(nullptr, error.get());
+  EXPECT_EQ(buffet::errors::commands::kTypeMismatch, error->GetCode());
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 TEST(CommandSchema, StringPropType_Empty) {
@@ -388,6 +431,20 @@
   error.reset();
 }
 
+TEST(CommandSchema, StringPropType_CreateValue) {
+  buffet::StringPropType prop;
+  chromeos::ErrorPtr error;
+  auto val = prop.CreateValue(std::string{"blah"}, &error);
+  ASSERT_NE(nullptr, val.get());
+  EXPECT_EQ(nullptr, error.get());
+  EXPECT_EQ("blah", val->GetValueAsAny().Get<std::string>());
+
+  val = prop.CreateValue(4, &error);
+  EXPECT_EQ(nullptr, val.get());
+  ASSERT_NE(nullptr, error.get());
+  EXPECT_EQ(buffet::errors::commands::kTypeMismatch, error->GetCode());
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 TEST(CommandSchema, ObjectPropType_Empty) {
@@ -501,6 +558,30 @@
   error.reset();
 }
 
+TEST(CommandSchema, ObjectPropType_CreateValue) {
+  buffet::ObjectPropType prop;
+  buffet::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));
+  buffet::native_types::Object obj{
+    {"width", int_type.CreateValue(10, nullptr)},
+    {"height", int_type.CreateValue(20, nullptr)},
+  };
+
+  chromeos::ErrorPtr error;
+  auto val = prop.CreateValue(obj, &error);
+  ASSERT_NE(nullptr, val.get());
+  EXPECT_EQ(nullptr, error.get());
+  EXPECT_EQ(obj, val->GetValueAsAny().Get<buffet::native_types::Object>());
+
+  val = prop.CreateValue("blah", &error);
+  EXPECT_EQ(nullptr, val.get());
+  ASSERT_NE(nullptr, error.get());
+  EXPECT_EQ(buffet::errors::commands::kTypeMismatch, error->GetCode());
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 TEST(CommandSchema, ObjectSchema_FromJson_Shorthand_TypeName) {