dbus_send: Fixed a bug with _build_arg_string()

The function would incorrectly error when a numeric dbus.* type object
was passed in (e.g. dbus.Int16) due to a 'continue' statement within a
nested loop. This was bypassed by refactoring the loop body.

BUG=chromium:726432
TEST=dbus_send_unittest.py

Change-Id: Ic8028746fe2e4cc5ae459c91bea28208d675bacf
Reviewed-on: https://chromium-review.googlesource.com/514409
Commit-Ready: Alexander Curtiss <curtissa@google.com>
Tested-by: Kirtika Ruchandani <kirtika@chromium.org>
Tested-by: Alexander Curtiss <curtissa@google.com>
Reviewed-by: Eric Caruso <ejcaruso@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
diff --git a/client/common_lib/cros/dbus_send.py b/client/common_lib/cros/dbus_send.py
index 1b9c924..36d7e09 100644
--- a/client/common_lib/cros/dbus_send.py
+++ b/client/common_lib/cros/dbus_send.py
@@ -116,9 +116,9 @@
     # For backward compatibility, match old dbus-send output too.
     # TODO: drop this when dbus is upgraded to 1.10.12+.
     if match is None:
-      match = re.match(r'method return sender=(%s) -> dest=(%s) '
-                       r'reply_serial=\d+' %
-                       (dbus_address_pattern, dbus_address_pattern), header)
+        match = re.match(r'method return sender=(%s) -> dest=(%s) '
+                         r'reply_serial=\d+' %
+                         (dbus_address_pattern, dbus_address_pattern), header)
 
     if match is None:
         raise error.TestError('Could not parse dbus-send header: %s' % header)
@@ -132,14 +132,13 @@
     return DBusSendResult(sender=sender, responder=responder, response=ret_val)
 
 
-def _build_arg_string(raw_args):
-    """Construct a string of arguments to a DBus method as dbus-send expects.
+def _dbus2string(raw_arg):
+    """Turn a dbus.* type object into a string that dbus-send expects.
 
-    @param raw_args list of dbus.* type objects to seriallize.
+    @param raw_dbus dbus.* type object to stringify.
     @return string suitable for dbus-send.
 
     """
-    dbus.Boolean
     int_map = {
             dbus.Int16: 'int16:',
             dbus.Int32: 'int32:',
@@ -150,25 +149,31 @@
             dbus.Double: 'double:',
             dbus.Byte: 'byte:',
     }
-    arg_list = []
-    for arg in raw_args:
-        if isinstance(arg, dbus.String):
-            arg_list.append(pipes.quote('string:%s' %
-                                        arg.replace('"', r'\"')))
-            continue
-        if isinstance(arg, dbus.Boolean):
-            if arg:
-                arg_list.append('boolean:true')
-            else:
-                arg_list.append('boolean:false')
-            continue
-        for prim_type, prefix in int_map.iteritems():
-            if isinstance(arg, prim_type):
-                arg_list.append(prefix + str(arg))
-                continue
 
-        raise error.TestError('No support for serializing %r' % arg)
-    return ' '.join(arg_list)
+    if isinstance(raw_arg, dbus.String):
+        return pipes.quote('string:%s' % raw_arg.replace('"', r'\"'))
+
+    if isinstance(raw_arg, dbus.Boolean):
+        if raw_arg:
+            return 'boolean:true'
+        else:
+            return 'boolean:false'
+
+    for prim_type, prefix in int_map.iteritems():
+        if isinstance(raw_arg, prim_type):
+            return prefix + str(raw_arg)
+
+    raise error.TestError('No support for serializing %r' % raw_arg)
+
+
+def _build_arg_string(raw_args):
+    """Construct a string of arguments to a DBus method as dbus-send expects.
+
+    @param raw_args list of dbus.* type objects to seriallize.
+    @return string suitable for dbus-send.
+
+    """
+    return ' '.join([_dbus2string(arg) for arg in raw_args])
 
 
 def dbus_send(bus_name, interface, object_path, method_name, args=None,
diff --git a/client/common_lib/cros/dbus_send_unittest.py b/client/common_lib/cros/dbus_send_unittest.py
index 79e0ffa..0f8991e 100755
--- a/client/common_lib/cros/dbus_send_unittest.py
+++ b/client/common_lib/cros/dbus_send_unittest.py
@@ -7,6 +7,7 @@
 import unittest
 
 import common
+import dbus
 from autotest_lib.client.common_lib.cros import dbus_send
 
 EXAMPLE_SHILL_GET_PROPERTIES_OUTPUT = \
@@ -210,6 +211,18 @@
         assert len(result.response) == 0, (
             'Got extra response: %r' % result.response)
 
+    def testBuildArgString(self):
+        """Test that we correctly form argument strings from dbus.* types."""
+        self.assertEquals(dbus_send._build_arg_string(
+            [dbus.Int16(42)]),
+            'int16:42')
+        self.assertEquals(dbus_send._build_arg_string(
+            [dbus.Int16(42), dbus.Boolean(True)]),
+            'int16:42 boolean:true')
+        self.assertEquals(dbus_send._build_arg_string(
+            [dbus.Int16(42), dbus.Boolean(True), dbus.String("foo")]),
+            'int16:42 boolean:true string:foo')
+
 
 if __name__ == "__main__":
     unittest.main()