[gjs/wip/gdbus-2: 10/13] GDBus: introduce new convenience wrappers



commit 6a9b8bbe8e4304b960d75271792849272dbeaa4b
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Thu Jun 14 22:25:26 2012 +0200

    GDBus: introduce new convenience wrappers
    
    Using the new metaclass system, introduce Gio.DBusProxyClass and
    Gio.DBusImplementerClass, that allow declaring specialized proxies/
    implementations for specific interfaces.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=669350

 modules/overrides/Gio.js |  201 ++++++++++++++++-
 test/js/testGDBus2.js    |  563 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 759 insertions(+), 5 deletions(-)
---
diff --git a/modules/overrides/Gio.js b/modules/overrides/Gio.js
index 6251f91..0c157dd 100644
--- a/modules/overrides/Gio.js
+++ b/modules/overrides/Gio.js
@@ -54,10 +54,10 @@ function _proxyInvoker(methodName, sync, inSignature, arg_array) {
 
     if (arg_array.length < minNumberArgs) {
         throw new Error("Not enough arguments passed for method: " + methodName +
-                       ". Expected " + minNumberArgs + ", got " + arg_array.length);
+                        ". Expected " + minNumberArgs + ", got " + arg_array.length);
     } else if (arg_array.length > maxNumberArgs) {
         throw new Error("Too many arguments passed for method: " + methodName +
-                       ". Maximum is " + maxNumberArgs +
+                        ". Maximum is " + maxNumberArgs +
                         " + one callback and/or flags");
     }
 
@@ -109,7 +109,7 @@ function _logReply(result, exc) {
     }
 }
 
-function _makeProxyMethod(method, sync) {
+function _makeProxyMethod(method, sync, insideClass) {
     var i;
     var name = method.name;
     var inArgs = method.in_args;
@@ -117,9 +117,14 @@ function _makeProxyMethod(method, sync) {
     for (i = 0; i < inArgs.length; i++)
         inSignature.push(inArgs[i].signature);
 
-    return function() {
+    var f = function() {
         return _proxyInvoker.call(this, name, sync, inSignature, arguments);
-    };
+    }
+
+    if (insideClass)
+        return this.wrapFunction(method, f);
+    else
+        return f;
 }
 
 function _convertToNativeSignal(proxy, sender_name, signal_name, parameters) {
@@ -151,6 +156,10 @@ function _propertySetter(value, name, signature) {
 }
 
 function _addDBusConvenience() {
+    // Check if this is actually using new-style bindings
+    if (this.constructor instanceof DBusProxyClass)
+        return;
+
     let info = this.g_interface_info;
     if (!info)
         return;
@@ -175,7 +184,100 @@ function _addDBusConvenience() {
     }
 }
 
+const DBusProxyClass = new Lang.Class({
+    Name: 'DBusProxyClass',
+    Extends: GObject.Class,
+
+    _construct: function(params) {
+        params.Extends = Gio.DBusProxy;
+
+        return this.parent(params);
+    },
+
+    _init: function(classParams) {
+        if (!classParams.Interface)
+            throw new TypeError('Interface must be specified in the declaration of a DBusProxyClass');
+        if (!(classParams.Interface instanceof Gio.DBusInterfaceInfo))
+            classParams.Interface = _newInterfaceInfo(classParams.Interface);
+
+        classParams._init = function(params) {
+            let klass = this.constructor;
+            if (!params)
+                params = { };
+            params.g_interface_name = this.Interface.name;
+            params.g_interface_info = this.Interface;
+
+            let asyncCallback, cancellable = null;
+            if ('g_async_callback' in params) {
+                asyncCallback = params.g_async_callback;
+                delete params.g_async_callback;
+            }
+            if ('g_cancellable' in params) {
+                cancellable = params.g_cancellable;
+                delete params.g_cancellable;
+            }
+
+            this.parent(params);
+
+            if (asyncCallback) {
+                this.init_async(GLib.PRIORITY_DEFAULT, cancellable, function(initable, result) {
+                    try {
+                        initable.init_finish(result);
+                        asyncCallback(initable, null);
+                    } catch(e) {
+                        asyncCallback(null, e);
+                    }
+                });
+            } else {
+                this.init(cancellable);
+            }
+
+            this.connect('g-signal', _convertToNativeSignal);
+        }
+
+        // build the actual class
+        this.parent(classParams);
+
+        // add convenience methods
+        this._addDBusConvenience(classParams);
+    },
+
+    _addDBusConvenience: function(classParams) {
+        let info = classParams.Interface;
+
+        let i, methods = info.methods;
+        for (i = 0; i < methods.length; i++) {
+            var method = methods[i];
+            this.prototype[method.name + 'Remote'] = _makeProxyMethod.call(this, methods[i], false, true);
+            this.prototype[method.name + 'Sync'] = _makeProxyMethod.call(this, methods[i], true, true);
+        }
+
+        let properties = info.properties;
+        for (i = 0; i < properties.length; i++) {
+            let name = properties[i].name;
+            let signature = properties[i].signature;
+            let flags = properties[i].flags;
+            let getter = undefined, setter = undefined;
+
+            if (flags & Gio.DBusPropertyInfoFlags.READABLE) {
+                getter = function() {
+                    return _propertyGetter.call(this, name);
+                };
+            }
+            if (flags & Gio.DBusPropertyInfoFlags.WRITABLE) {
+                setter = function(val) {
+                    return _propertySetter.call(this, name, val, signature);
+                };
+            }
+
+            Lang.defineAccessorProperty(this.prototype, name, getter, setter);
+        }
+    }
+});
+
 function _makeProxyWrapper(interfaceXml) {
+    log ('makeProxyWrapper is deprecated. Use Gio.DBusProxyClass instead');
+
     var info = _newInterfaceInfo(interfaceXml);
     var iname = info.name;
     return function(bus, name, object, asyncCallback, cancellable) {
@@ -329,6 +431,91 @@ function _handlePropertySet(info, impl, property_name, new_value) {
     this[property_name] = new_value.deep_unpack();
 }
 
+const DBusImplementerBase = new Lang.Class({
+    Name: 'DBusImplementerBase',
+
+    _init: function() {
+        this._dbusImpl = new GjsPrivate.DBusImplementation({ g_interface_info: this.constructor.Interface });
+
+        this._dbusImpl.connect('handle-method-call', Lang.bind(this, this._handleMethodCall));
+        this._dbusImpl.connect('handle-property-get', Lang.bind(this, this._handlePropertyGet));
+        this._dbusImpl.connect('handle-property-set', Lang.bind(this, this._handlePropertySet));
+
+        let klass = this.constructor;
+        if (klass.ObjectPath && klass.BusType)
+            this.export(Gio.bus_get_sync(klass.BusType, null), klass.ObjectPath);
+    },
+
+    _handleMethodCall: function(impl, method_name, parameters, invocation) {
+        return _handleMethodCall.call(this, this.constructor.Interface, impl, method_name, parameters, invocation);
+    },
+
+    _handlePropertyGet: function(impl, property_name) {
+        return _handlePropertyGet.call(this, this.constructor.Interface, impl, property_name);
+    },
+
+    _handlePropertySet: function(impl, property_name, value) {
+        return _handlePropertySet.call(this, this.constructor.Interface, impl, property_name, value);
+    },
+
+    export: function(bus, path) {
+        this._dbusImpl.export(bus, path);
+    },
+
+    unexport: function() {
+        this._dbusImpl.unexport();
+    },
+
+    emit_signal: function(signal_name) {
+        let klass = this.constructor;
+
+        let signalInfo = klass.Interface.lookup_signal(signal_name);
+        let signalType = _makeOutSignature(signalInfo.args);
+
+        let argArray = Array.prototype.slice.call(arguments);
+        argArray.shift();
+
+        if (argArray.length == 0)
+            this._dbusImpl.emit_signal(signal_name, null);
+        else
+            this._dbusImpl.emit_signal(signal_name, GLib.Variant.new(signalType, argArray));
+    },
+
+    emit_property_changed: function(property_name, new_value) {
+        let klass = this.constructor;
+
+        let propertyInfo = klass.Inteface.lookup_property(property_name);
+        if (new_value != undefined)
+            new_value = GLib.Variant.new(propertyInfo.signature, new_value);
+
+        this._dbusImpl.emit_property_changed(property_name, new_value);
+    },
+});
+
+const DBusImplementerClass = new Lang.Class({
+    Name: 'DBusImplementerClass',
+    Extends: Lang.Class,
+
+    _construct: function(params) {
+        params.Extends = DBusImplementerBase;
+
+        return this.parent(params);
+    },
+
+    _init: function(params) {
+        if (!params.Interface)
+            throw new TypeError('Interface must be specified in the declaration of a DBusImplementerClass');
+        if (!(params.Interface instanceof Gio.DBusInterfaceInfo))
+            params.Interface = _newInterfaceInfo(params.Interface);
+        params.Interface.cache_build();
+
+        this.Interface = params.Interface;
+        delete params.Interface;
+
+        this.parent(params);
+    }
+});
+
 function _wrapJSObject(interfaceInfo, jsObj) {
     var info;
     if (interfaceInfo instanceof Gio.DBusInterfaceInfo)
@@ -391,6 +578,9 @@ function _init() {
 
     _injectToMethod(Gio.DBusProxy.prototype, 'init', _addDBusConvenience);
     _injectToMethod(Gio.DBusProxy.prototype, 'init_async', _addDBusConvenience);
+
+    Gio.DBusProxyClass = DBusProxyClass;
+    Gio.DBusProxy.prototype.__metaclass__ = DBusProxyClass;
     Gio.DBusProxy.prototype.connectSignal = Signals._connect;
     Gio.DBusProxy.prototype.disconnectSignal = Signals._disconnect;
 
@@ -400,6 +590,7 @@ function _init() {
     _wrapFunction(Gio.DBusNodeInfo, 'new_for_xml', _newNodeInfo);
     Gio.DBusInterfaceInfo.new_for_xml = _newInterfaceInfo;
 
+    Gio.DBusImplementerClass = DBusImplementerClass;
     Gio.DBusExportedObject = GjsPrivate.DBusImplementation;
     Gio.DBusExportedObject.wrapJSObject = _wrapJSObject;
 }
diff --git a/test/js/testGDBus2.js b/test/js/testGDBus2.js
new file mode 100644
index 0000000..7255285
--- /dev/null
+++ b/test/js/testGDBus2.js
@@ -0,0 +1,563 @@
+// application/javascript;version=1.8
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+
+/* The methods list with their signatures.
+ *
+ * *** NOTE: If you add stuff here, you need to update testIntrospectReal
+ */
+var TestIface = <interface name="org.gnome.gjs.Test">
+<method name="nonJsonFrobateStuff">
+    <arg type="i" direction="in"/>
+    <arg type="s" direction="out"/>
+</method>
+<method name="frobateStuff">
+    <arg type="a{sv}" direction="in"/>
+    <arg type="a{sv}" direction="out"/>
+</method>
+<method name="alwaysThrowException">
+    <arg type="a{sv}" direction="in"/>
+    <arg type="a{sv}" direction="out"/>
+</method>
+<method name="noInParameter">
+    <arg type="s" direction="out"/>
+</method>
+<method name="multipleInArgs">
+    <arg type="i" direction="in"/>
+    <arg type="i" direction="in"/>
+    <arg type="i" direction="in"/>
+    <arg type="i" direction="in"/>
+    <arg type="i" direction="in"/>
+    <arg type="s" direction="out"/>
+</method>
+<method name="noReturnValue"/>
+<method name="emitSignal"/>
+<method name="multipleOutValues">
+    <arg type="s" direction="out"/>
+    <arg type="s" direction="out"/>
+    <arg type="s" direction="out"/>
+</method>
+<method name="oneArrayOut">
+    <arg type="as" direction="out"/>
+</method>
+<method name="arrayOfArrayOut">
+    <arg type="aas" direction="out"/>
+</method>
+<method name="multipleArrayOut">
+    <arg type="as" direction="out"/>
+    <arg type="as" direction="out"/>
+</method>
+<method name="arrayOutBadSig">
+    <arg type="i" direction="out"/>
+</method>
+<method name="byteArrayEcho">
+    <arg type="ay" direction="in"/>
+    <arg type="ay" direction="out"/>
+</method>
+<method name="byteEcho">
+    <arg type="y" direction="in"/>
+    <arg type="y" direction="out"/>
+</method>
+<method name="dictEcho">
+    <arg type="a{sv}" direction="in"/>
+    <arg type="a{sv}" direction="out"/>
+</method>
+<method name="echo">
+    <arg type="s" direction="in"/>
+    <arg type="i" direction="in"/>
+    <arg type="s" direction="out"/>
+    <arg type="i" direction="out"/>
+</method>
+<method name="structArray">
+    <arg type="a(ii)" direction="out"/>
+</method>
+<signal name="signalFoo">
+    <arg type="s" direction="out"/>
+</signal>
+<property name="PropReadOnly" type="b" access="read" />
+<property name="PropWriteOnly" type="s" access="write" />
+<property name="PropReadWrite" type="v" access="readwrite" />
+</interface>
+
+const PROP_READ_WRITE_INITIAL_VALUE = 58;
+const PROP_WRITE_ONLY_INITIAL_VALUE = "Initial value";
+
+const Test = new Gio.DBusImplementerClass({
+    Name: 'Test',
+    Interface: TestIface,
+
+    _init: function() {
+	this.parent();
+
+        this._propWriteOnly = PROP_WRITE_ONLY_INITIAL_VALUE;
+        this._propReadWrite = PROP_READ_WRITE_INITIAL_VALUE;
+    },
+
+    frobateStuff: function(args) {
+        return { hello: GLib.Variant.new('s', 'world') };
+    },
+
+    nonJsonFrobateStuff: function(i) {
+        if (i == 42) {
+            return "42 it is!";
+        } else {
+            return "Oops";
+        }
+    },
+
+    alwaysThrowException: function() {
+        throw Error("Exception!");
+    },
+
+    noInParameter: function() {
+        return "Yes!";
+    },
+
+    multipleInArgs: function(a, b, c, d, e) {
+        return a + " " + b + " " + c + " " + d + " " + e;
+    },
+
+    emitSignal: function() {
+        this.emit_signal('signalFoo', "foobar");
+    },
+
+    noReturnValue: function() {
+        /* Empty! */
+    },
+
+    /* The following two functions have identical return values
+     * in JS, but the bus message will be different.
+     * multipleOutValues is "sss", while oneArrayOut is "as"
+     */
+    multipleOutValues: function() {
+        return [ "Hello", "World", "!" ];
+    },
+
+    oneArrayOut: function() {
+        return [ "Hello", "World", "!" ];
+    },
+
+    /* Same thing again. In this case multipleArrayOut is "asas",
+     * while arrayOfArrayOut is "aas".
+     */
+    multipleArrayOut: function() {
+        return [[ "Hello", "World" ], [ "World", "Hello" ]];
+    },
+
+    arrayOfArrayOut: function() {
+        return [[ "Hello", "World" ], [ "World", "Hello" ]];
+    },
+
+    arrayOutBadSig: function() {
+        return [ "Hello", "World", "!" ];
+    },
+
+    byteArrayEcho: function(binaryString) {
+        return binaryString;
+    },
+
+    byteEcho: function(aByte) {
+        return aByte;
+    },
+
+    dictEcho: function(dict) {
+        return dict;
+    },
+
+    /* This one is implemented asynchronously. Returns
+     * the input arguments */
+    echoAsync: function(parameters, invocation) {
+        var [someString, someInt] = parameters;
+        Mainloop.idle_add(function() {
+            invocation.return_value(GLib.Variant.new('(si)', [someString, someInt]));
+            return false;
+        });
+    },
+
+    // boolean
+    get PropReadOnly() {
+        return true;
+    },
+
+    // string
+    set PropWriteOnly(value) {
+        this._propWriteOnly = value;
+    },
+
+    // variant
+    get PropReadWrite() {
+        return GLib.Variant.new('u', this._propReadWrite);
+    },
+
+    set PropReadWrite(value) {
+        this._propReadWrite = value.deep_unpack();
+    },
+
+    structArray: function () {
+        return [[128, 123456], [42, 654321]];
+    }
+});
+
+var own_name_id;
+
+const TestProxy = new Lang.Class({
+    Name: 'TestProxy',
+    Extends: Gio.DBusProxy,
+    Interface: TestIface,
+});
+
+var proxy, exporter;
+
+function testExportStuff() {
+    exporter = new Test();
+    exporter.export(Gio.DBus.session, '/org/gnome/gjs/Test');
+
+    own_name_id = Gio.DBus.session.own_name('org.gnome.gjs.Test',
+                                            Gio.BusNameOwnerFlags.NONE,
+                                            function(connection, name) {
+                                                log("Acquired name " + name);
+
+                                                Mainloop.quit('testGDBus');
+                                            },
+                                            function(connection, name) {
+                                                log("Lost name " + name);
+                                            });
+
+    Mainloop.run('testGDBus');
+}
+
+function testInitStuff() {
+    var theError;
+    proxy = new TestProxy({ g_connection: Gio.DBus.session,
+                            g_name: 'org.gnome.gjs.Test',
+                            g_object_path: '/org/gnome/gjs/Test',
+                            g_async_callback: function (obj, error) {
+                                theError = error;
+                                proxy = obj;
+
+                                Mainloop.quit('testGDBus');
+                            } });
+
+    log(typeof(proxy._init) + " " + typeof(proxy._construct) + " " + typeof(proxy.frobateStuffRemote));
+
+    Mainloop.run('testGDBus');
+
+    assertNull(theError);
+    assertNotNull(proxy);
+}
+
+function testFrobateStuff() {
+    let theResult, theExcp;
+    proxy.frobateStuffRemote({}, function(result, excp) {
+        theResult = result;
+        theExcp = excp;
+        Mainloop.quit('testGDBus');
+    });
+
+    Mainloop.run('testGDBus');
+
+    assertNull(theExcp);
+    assertEquals("world", theResult[0].hello.deep_unpack());
+}
+
+/* excp must be exactly the exception thrown by the remote method
+   (more or less) */
+function testThrowException() {
+    let theResult, theExcp;
+    proxy.alwaysThrowExceptionRemote({}, function(result, excp) {
+        theResult = result;
+        theExcp = excp;
+        Mainloop.quit('testGDBus');
+    });
+
+    Mainloop.run('testGDBus');
+
+    assertNull(theResult);
+    assertNotNull(theExcp);
+}
+
+function testNonJsonFrobateStuff() {
+    let theResult, theExcp;
+    proxy.nonJsonFrobateStuffRemote(42, function(result, excp) {
+        [theResult] = result;
+        theExcp = excp;
+        Mainloop.quit('testGDBus');
+    });
+
+    Mainloop.run('testGDBus');
+
+    assertEquals("42 it is!", theResult);
+    assertNull(theExcp);
+}
+
+function testNoInParameter() {
+    let theResult, theExcp;
+    proxy.noInParameterRemote(function(result, excp) {
+        [theResult] = result;
+        theExcp = excp;
+        Mainloop.quit('testGDBus');
+    });
+
+    Mainloop.run('testGDBus');
+
+    assertEquals("Yes!", theResult);
+    assertNull(theExcp);
+}
+
+function testMultipleInArgs() {
+    let theResult, theExcp;
+    proxy.multipleInArgsRemote(1, 2, 3, 4, 5, function(result, excp) {
+        [theResult] = result;
+        theExcp = excp;
+        Mainloop.quit('testGDBus');
+    });
+
+    Mainloop.run('testGDBus');
+
+    assertEquals("1 2 3 4 5", theResult);
+    assertNull(theExcp);
+}
+
+function testNoReturnValue() {
+    let theResult, theExcp;
+    proxy.noReturnValueRemote(function(result, excp) {
+        [theResult] = result;
+        theExcp = excp;
+        Mainloop.quit('testGDBus');
+    });
+
+    Mainloop.run('testGDBus');
+
+    assertEquals(undefined, theResult);
+    assertNull(theExcp);
+}
+
+function testEmitSignal() {
+    let theResult, theExcp;
+    let signalReceived = 0;
+    let signalArgument = null;
+    let id = proxy.connectSignal('signalFoo',
+                                 function(emitter, senderName, parameters) {
+                                     signalReceived ++;
+                                     [signalArgument] = parameters;
+
+                                     proxy.disconnectSignal(id);
+                                 });
+    proxy.emitSignalRemote(function(result, excp) {
+        [theResult] = result;
+        theExcp = excp;
+        if (excp)
+            log("Signal emission exception: " + excp);
+        Mainloop.quit('testGDBus');
+    });
+
+    Mainloop.run('testGDBus');
+
+    assertUndefined('result should be undefined', theResult);
+    assertNull('no exception set', theExcp);
+    assertEquals('number of signals received', signalReceived, 1);
+    assertEquals('signal argument', signalArgument, "foobar");
+
+}
+
+function testMultipleOutValues() {
+    let theResult, theExcp;
+    proxy.multipleOutValuesRemote(function(result, excp) {
+        theResult = result;
+        theExcp = excp;
+        Mainloop.quit('testGDBus');
+    });
+
+    Mainloop.run('testGDBus');
+
+    assertEquals("Hello", theResult[0]);
+    assertEquals("World", theResult[1]);
+    assertEquals("!", theResult[2]);
+    assertNull(theExcp);
+}
+
+function testOneArrayOut() {
+    let theResult, theExcp;
+    proxy.oneArrayOutRemote(function(result, excp) {
+        [theResult] = result;
+        theExcp = excp;
+        Mainloop.quit('testGDBus');
+    });
+
+    Mainloop.run('testGDBus');
+
+    assertEquals("Hello", theResult[0]);
+    assertEquals("World", theResult[1]);
+    assertEquals("!", theResult[2]);
+    assertNull(theExcp);
+}
+
+function testArrayOfArrayOut() {
+    let theResult, theExcp;
+    proxy.arrayOfArrayOutRemote(function(result, excp) {
+        [theResult] = result;
+        theExcp = excp;
+        Mainloop.quit('testGDBus');
+    });
+
+    Mainloop.run('testGDBus');
+
+    let a1 = theResult[0];
+    let a2 = theResult[1];
+
+    assertEquals("Hello", a1[0]);
+    assertEquals("World", a1[1]);
+
+    assertEquals("World", a2[0]);
+    assertEquals("Hello", a2[1]);;
+
+    assertNull(theExcp);
+}
+
+function testMultipleArrayOut() {
+    let theResult, theExcp;
+    proxy.multipleArrayOutRemote(function(result, excp) {
+        theResult = result;
+        theExcp = excp;
+        Mainloop.quit('testGDBus');
+    });
+
+    Mainloop.run('testGDBus');
+
+    let a1 = theResult[0];
+    let a2 = theResult[1];
+
+    assertEquals("Hello", a1[0]);
+    assertEquals("World", a1[1]);
+
+    assertEquals("World", a2[0]);
+    assertEquals("Hello", a2[1]);;
+
+    assertNull(theExcp);
+}
+
+/* We are returning an array but the signature says it's an integer,
+ * so this should fail
+ */
+function testArrayOutBadSig() {
+    let theResult, theExcp;
+    proxy.arrayOutBadSigRemote(function(result, excp) {
+        theResult = result;
+        theExcp = excp;
+        Mainloop.quit('testGDBus');
+    });
+
+    Mainloop.run('testGDBus');
+    assertNull(theResult);
+    assertNotNull(theExcp);
+}
+
+function testAsyncImplementation() {
+    let someString = "Hello world!";
+    let someInt = 42;
+    let theResult, theExcp;
+    proxy.echoRemote(someString, someInt,
+                     function(result, excp) {
+                         theResult = result;
+                         theExcp = excp;
+                         Mainloop.quit('testGDBus');
+                     });
+
+    Mainloop.run('testGDBus');
+    assertNull(theExcp);
+    assertNotNull(theResult);
+    assertEquals(theResult[0], someString);
+    assertEquals(theResult[1], someInt);
+}
+
+function testBytes() {
+    let someBytes = [ 0, 63, 234 ];
+    let theResult, theExcp;
+    for (let i = 0; i < someBytes.length; ++i) {
+        theResult = null;
+        theExcp = null;
+        proxy.byteEchoRemote(someBytes[i], function(result, excp) {
+            [theResult] = result;
+            theExcp = excp;
+            Mainloop.quit('testGDBus');
+        });
+
+        Mainloop.run('testGDBus');
+        assertNull(theExcp);
+        assertNotNull(theResult);
+        assertEquals(someBytes[i], theResult);
+    }
+}
+
+function testStructArray() {
+    let theResult, theExcp;
+    proxy.structArrayRemote(function(result, excp) {
+        [theResult] = result;
+        theExcp = excp;
+        Mainloop.quit('testGDBus');
+    });
+    Mainloop.run('testGDBus');
+    assertNull(theExcp);
+    assertNotNull(theResult);
+    assertEquals(theResult[0][0], 128);
+    assertEquals(theResult[0][1], 123456);
+    assertEquals(theResult[1][0], 42);
+    assertEquals(theResult[1][1], 654321);
+}
+
+function testDictSignatures() {
+    let someDict = {
+        aDouble: GLib.Variant.new('d', 10),
+        // should be an integer after round trip
+        anInteger: GLib.Variant.new('i', 10.5),
+        // should remain a double
+        aDoubleBeforeAndAfter: GLib.Variant.new('d', 10.5),
+    };
+    let theResult, theExcp;
+    proxy.dictEchoRemote(someDict, function(result, excp) {
+        [theResult] = result;
+        theExcp = excp;
+        Mainloop.quit('testGDBus');
+    });
+
+    Mainloop.run('testGDBus');
+    assertNull(theExcp);
+    assertNotNull(theResult);
+
+    // verify the fractional part was dropped off int
+    assertEquals(11, theResult['anInteger'].deep_unpack());
+
+    // and not dropped off a double
+    assertEquals(10.5, theResult['aDoubleBeforeAndAfter'].deep_unpack());
+
+    // this assertion is useless, it will work
+    // anyway if the result is really an int,
+    // but it at least checks we didn't lose data
+    assertEquals(10.0, theResult['aDouble'].deep_unpack());
+}
+
+function testProperties() {
+    let readonly = proxy.PropReadOnly;
+    assertEquals(true, readonly);
+
+    let readwrite = proxy.PropReadWrite;
+    assertTrue(readwrite instanceof GLib.Variant);
+    assertEquals(PROP_READ_WRITE_INITIAL_VALUE, readwrite.deep_unpack());
+
+    // we cannot test property sets, as those happen asynchronously
+}
+
+function testFinalize() {
+    // clean everything up, before we destroy the context
+    // (otherwise, gio will report a name owner changed signal
+    // in idle, which will be called in some other test)
+    Gio.DBus.session.unown_name(own_name_id);
+
+    proxy = exporter = null;
+    context = GLib.MainContext.default();
+    while (context.iteration(false));
+}
+
+gjstestRun();



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]