chromeos-dbus-bindings: Add support for sending signals from the adaptor.

This patch creates helper methods in the Adaptor to send the defined
signals with the right signature.

BUG=chromium:426531
TEST=Unittests

Change-Id: Ib9b9c2bc125a3edbff368d3a2bccb80ba06ba089
Reviewed-on: https://chromium-review.googlesource.com/225306
Reviewed-by: Paul Stewart <pstew@chromium.org>
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Tested-by: Alex Deymo <deymo@chromium.org>
Commit-Queue: Alex Deymo <deymo@chromium.org>
diff --git a/chromeos-dbus-bindings/adaptor_generator.cc b/chromeos-dbus-bindings/adaptor_generator.cc
index 02c751e..53f7e79 100644
--- a/chromeos-dbus-bindings/adaptor_generator.cc
+++ b/chromeos-dbus-bindings/adaptor_generator.cc
@@ -55,6 +55,7 @@
   text.PushOffset(kBlockOffset);
   AddMethodInterface(interface, &text);
   AddConstructor(interface, adaptor_name, &text);
+  AddSendSignalMethods(interface, &text);
   text.AddLine(StringPrintf("virtual ~%s() = default;", adaptor_name.c_str()));
   text.AddLine("virtual void OnRegisterComplete(bool success) {}");
   text.PopOffset();
@@ -75,6 +76,7 @@
   text.PushOffset(kBlockOffset);
   text.AddLine("MethodInterface* interface_;  // Owned by caller.");
   text.AddLine("chromeos::dbus_utils::DBusObject dbus_object_;");
+  AddSignalDataMembers(interface, &text);
   text.AddLine("// Owned by |dbus_object_|.");
   text.AddLine("chromeos::dbus_utils::DBusInterface* dbus_interface_;");
   text.AddLine(StringPrintf(
@@ -111,6 +113,17 @@
   block.AddLine("bus,");
   block.AddLine("dbus::ObjectPath(object_path)),");
   block.PopOffset();
+
+  // Signal constructors.
+  for (const auto& signal : interface.signals) {
+    block.AddLine(StringPrintf("signal_%s_(", signal.name.c_str()));
+    block.PushOffset(kLineContinuationOffset);
+    block.AddLine("&dbus_object_,");
+    block.AddLine(StringPrintf("\"%s\",", interface.name.c_str()));
+    block.AddLine(StringPrintf("\"%s\"),", signal.name.c_str()));
+    block.PopOffset();
+  }
+
   block.AddLine("dbus_interface_(");
   block.PushOffset(kLineContinuationOffset);
   block.AddLine(StringPrintf(
@@ -184,4 +197,67 @@
   text->AddBlock(block);
 }
 
+// static
+void AdaptorGenerator::AddSendSignalMethods(const Interface& interface,
+                                            IndentedText *text) {
+  IndentedText block;
+  DbusSignature signature;
+
+  for (const auto& signal : interface.signals) {
+    block.AddLine(StringPrintf("void Send%sSignal(", signal.name.c_str()));
+    block.PushOffset(kLineContinuationOffset);
+    string last_argument;
+    int unnamed_args = 0;
+    string call_arguments;  // The arguments to pass to the Send() call.
+    for (const auto& argument : signal.arguments) {
+      if (!last_argument.empty())
+        block.AddLine(last_argument + ",");
+      CHECK(signature.Parse(argument.type, &last_argument));
+      if (!IsIntegralType(last_argument))
+        last_argument = StringPrintf("const %s&", last_argument.c_str());
+      string argument_name = argument.name;
+      if (argument.name.empty())
+        argument_name = StringPrintf("_arg_%d", ++unnamed_args);
+      last_argument.append(" " + argument_name);
+      if (!call_arguments.empty())
+        call_arguments.append(", ");
+      call_arguments.append(argument_name);
+    }
+    block.AddLine(last_argument + ") {");
+    block.PopOffset();
+    block.PushOffset(kBlockOffset);
+    block.AddLine(StringPrintf(
+        "signal_%s_.Send(%s);", signal.name.c_str(), call_arguments.c_str()));
+    block.PopOffset();
+    block.AddLine("}");
+  }
+  text->AddBlock(block);
+}
+
+// static
+void AdaptorGenerator::AddSignalDataMembers(const Interface& interface,
+                                            IndentedText *text) {
+  IndentedText block;
+  DbusSignature signature;
+
+  for (const auto& signal : interface.signals) {
+    block.AddLine("chromeos::dbus_utils::DBusSignal<");
+    block.PushOffset(kLineContinuationOffset);
+    string last_argument;
+    for (const auto& argument : signal.arguments) {
+      if (!last_argument.empty())
+        block.AddLine(last_argument + ",");
+      CHECK(signature.Parse(argument.type, &last_argument));
+      if (!argument.name.empty())
+        last_argument.append(StringPrintf(" /* %s */", argument.name.c_str()));
+    }
+    block.AddLine(StringPrintf(
+        "%s> signal_%s_;", last_argument.c_str(), signal.name.c_str()));
+    block.PopOffset();
+  }
+  text->AddBlock(block);
+}
+
+
+
 }  // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/adaptor_generator.h b/chromeos-dbus-bindings/adaptor_generator.h
index fe32409..689d5bf 100644
--- a/chromeos-dbus-bindings/adaptor_generator.h
+++ b/chromeos-dbus-bindings/adaptor_generator.h
@@ -41,6 +41,14 @@
   static void AddMethodInterface(const Interface& interface,
                                  IndentedText *text);
 
+  // Generates adaptor methods to send the signals.
+  static void AddSendSignalMethods(const Interface& interface,
+                                   IndentedText *text);
+
+  // Generates DBusSignal data members for the signals.
+  static void AddSignalDataMembers(const Interface& interface,
+                                   IndentedText *text);
+
   DISALLOW_COPY_AND_ASSIGN(AdaptorGenerator);
 };
 
diff --git a/chromeos-dbus-bindings/adaptor_generator_unittest.cc b/chromeos-dbus-bindings/adaptor_generator_unittest.cc
index 993091a..62d25d8 100644
--- a/chromeos-dbus-bindings/adaptor_generator_unittest.cc
+++ b/chromeos-dbus-bindings/adaptor_generator_unittest.cc
@@ -35,6 +35,12 @@
 const char kMethod3Name[] = "Kiyoko";
 const char kMethod3Return0[] = "x";
 const char kMethod3Return1[] = "s";
+const char kSignal0Name[] = "Update";
+const char kSignal1Name[] = "Mapping";
+const char kSignal1Argument0[] = "s";
+const char kSignal1ArgumentName0[] = "key";
+const char kSignal1Argument1[] = "ao";
+
 const char kInterfaceName[] = "org.chromium.TestInterface";
 const char kExpectedContent[] = R"literal_string(
 #include <string>
@@ -74,6 +80,14 @@
             object_manager,
             bus,
             dbus::ObjectPath(object_path)),
+        signal_Update_(
+            &dbus_object_,
+            "org.chromium.TestInterface",
+            "Update"),
+        signal_Mapping_(
+            &dbus_object_,
+            "org.chromium.TestInterface",
+            "Mapping"),
         dbus_interface_(
             dbus_object_.AddOrGetInterface("org.chromium.TestInterface")) {
     dbus_interface_->AddMethodHandler(
@@ -91,6 +105,15 @@
     dbus_object_.RegisterAsync(base::Bind(
         &TestInterfaceAdaptor::OnRegisterComplete, base::Unretained(this)));
   }
+  void SendUpdateSignal(
+      ) {
+    signal_Update_.Send();
+  }
+  void SendMappingSignal(
+      const std::string& key,
+      const std::vector<dbus::ObjectPath>& _arg_1) {
+    signal_Mapping_.Send(key, _arg_1);
+  }
   virtual ~TestInterfaceAdaptor() = default;
   virtual void OnRegisterComplete(bool success) {}
 
@@ -102,6 +125,11 @@
  private:
   MethodInterface* interface_;  // Owned by caller.
   chromeos::dbus_utils::DBusObject dbus_object_;
+  chromeos::dbus_utils::DBusSignal<
+      > signal_Update_;
+  chromeos::dbus_utils::DBusSignal<
+      std::string /* key */,
+      std::vector<dbus::ObjectPath>> signal_Mapping_;
   // Owned by |dbus_object_|.
   chromeos::dbus_utils::DBusInterface* dbus_interface_;
   DISALLOW_COPY_AND_ASSIGN(TestInterfaceAdaptor);
@@ -151,6 +179,16 @@
       vector<Interface::Argument>{
           {"", kMethod3Return0},
           {"", kMethod3Return1}});
+  // Signals generate helper methods to send them.
+  interface.signals.emplace_back(
+      kSignal0Name,
+      vector<Interface::Argument>{});
+  interface.signals.emplace_back(
+      kSignal1Name,
+      vector<Interface::Argument>{
+          {kSignal1ArgumentName0, kSignal1Argument0},
+          {"", kSignal1Argument1}});
+
   base::FilePath output_path = temp_dir_.path().Append("output.h");
   EXPECT_TRUE(AdaptorGenerator::GenerateAdaptor(interface, output_path));
   string contents;