How to test the DBus serialization of your structures

by mandel on January 10th, 2013

When defining structure in Qt that you want to send via DBus you need to define the operator<< and operator>> to a QDBusArgument, but how do you test it? The problem is that the QDBusArument constructor creates a no writable argument so that you cannot do:

1
2
3
4
5
6
7
MyStructure structure = MyStructure("first arg", "second arg");
MyStrcuture result;
QDBusArgument arg;
 
arg << structure;
arg >> result;
QCOMPARE(structure, result);

If you add the above test you will get result as an empty structure because the arg is not writable. A good way to test the serialization is to start a DBus interface and test the serialization and deserialization using the real dbus, for example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/**
*
* Copyright (c) 2012 mandel
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
 
#ifndef TEST_SECRET_H
#define TEST_SECRET_H
 
#include <QObject>
#include <QDBusInterface>
#include "test_runner.h"
#include "keyring/secret.h"
 
namespace tori
{
 
namespace keyring
{
 
class TypesInterface: public QDBusAbstractAdaptor
{
    Q_OBJECT
    Q_CLASSINFO("D-Bus Interface", "org.saruneko.tori.keyring.TypesInterface")
public:
    TypesInterface(QObject *parent)
        : QDBusAbstractAdaptor(parent)
    { }
 
    // make it secret so that we can acess it
    Secret secret;
 
public slots:
 
    Secret retrieveSecret()
    {
        return secret;
    }
};
 
class TestSecret : public QObject
{
    Q_OBJECT
public:
    explicit TestSecret(QObject *parent = 0);
 
private slots:
    void testSerialize();
 
private:
    QDBusInterface *iface;
    TypesInterface *adaptor;
};
 
DECLARE_TEST(TestSecret)
 
} // keyring
 
} // tori
 
#endif // TEST_SECRET_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
*
* Copyright (c) 2012 mandel
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
 
#include "test_secret.h"
 
namespace tori
{
 
namespace keyring
{
 
TestSecret::TestSecret(QObject *parent) :
    QObject(parent)
{
    adaptor = new TypesInterface(this);
    QDBusConnection::sessionBus().registerObject("/", this);
 
    iface = new QDBusInterface(QDBusConnection::sessionBus().baseService(), "/",
                               "org.saruneko.tori.keyring.TypesInterface",
                                QDBusConnection::sessionBus(),
                                this);
}
 
void TestSecret::testSerialize()
{
    QDBusObjectPath session("/path/to/session");
    QByteArray params = "the parameters";
    QByteArray value = "the value";
    QString contentType = "password";
 
    // test serializing and deserializing
    Secret secret(session, params, value, contentType);
    adaptor->secret = secret;
 
    QDBusReply<Secret> result = iface->call(QDBus::BlockWithGui, "retrieveSecret");
    QVERIFY(result.isValid());
    QCOMPARE(result.value().getSession(), secret.getSession());
    QCOMPARE(result.value().getParameters(), secret.getParameters());
    QCOMPARE(result.value().getValue(), secret.getValue());
    QCOMPARE(result.value().getContentType(), secret.getContentType());
}
 
} //keyring
 
} // tori

Where secret is defined as:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/**
*
* Copyright (c) 2012 Manuel de la Pena <mandel@themacaque.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
 
#ifndef SECRET_H
#define SECRET_H
 
#include <QtDBus>
#include <QByteArray>
#include <QHash>
#include <QString>
#include "dbus/dbus_helper.h"
 
class Secret
{
    Q_PROPERTY(QString session READ getSession)
    Q_PROPERTY(QByteArray parameters READ getParameters)
    Q_PROPERTY(QByteArray value READ getValue)
    Q_PROPERTY(QString contentType READ getContentType)
 
public:
    Secret();
    Secret(QDBusObjectPath session, QByteArray parameters, QByteArray value, QString contentType);
    Secret(const Secret& other);
    Secret& operator=(const Secret& other);
    ~Secret();
 
    friend QDBusArgument &operator<<(QDBusArgument &argument, const Secret& secret);
    friend const QDBusArgument &operator>>(const QDBusArgument &argument, Secret &secret);
 
    //register Secret with the Qt type system
    static void registerMetaType();
 
    // property getters
    QString getSession() const;
    QByteArray getParameters() const;
    QByteArray getValue() const;
    QString getContentType() const;
 
private:
    QDBusObjectPath _session;
    QByteArray _parameters;
    QByteArray _value;
    QString _contentType;
};
 
typedef QHash<QDBusObjectPath, Secret> DBusSecretHash;
 
Q_DECLARE_METATYPE(DBusSecretHash)
Q_DECLARE_METATYPE(Secret)
 
#endif // SECRET_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/**
*
* Copyright (c) 2012 Manuel de la Pena <mandel@themacaque.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
 
#include "secret.h"
 
 
Secret::Secret() :
    _session(),
    _parameters(),
    _value(),
    _contentType()
{
}
 
Secret::Secret(QDBusObjectPath session, QByteArray parameters, QByteArray value, QString contentType) :
    _session(session),
    _parameters(parameters),
    _value(value),
    _contentType(contentType)
{
}
 
Secret::Secret(const Secret& other) :
    _session(other._session),
    _parameters(other._parameters),
    _value(other._value),
    _contentType(other._contentType)
{
}
 
Secret& Secret::operator=(const Secret& other)
{
    _session = other._session;
    _parameters = other._parameters;
    _value = other._value;
    _contentType = other._contentType;
 
    return *this;
}
 
Secret::~Secret()
{
}
 
void Secret::registerMetaType()
{
    qRegisterMetaType<Secret>("Secret");
    qDBusRegisterMetaType<Secret>();
}
 
QDBusArgument &operator<<(QDBusArgument &argument, const Secret& secret)
{
    argument.beginStructure();
    argument << secret._session;
    argument << secret._parameters;
    argument << secret._value;
    argument << secret._contentType;
    argument.endStructure();
 
    return argument;
}
 
const QDBusArgument &operator>>(const QDBusArgument &argument, Secret &secret)
{
    argument.beginStructure();
    argument >> secret._session;
    argument >> secret._parameters;
    argument >> secret._value;
    argument >> secret._contentType;
    argument.endStructure();
 
    return argument;
}
 
QString Secret::getSession() const
{
    return _session.path();
}
 
QByteArray Secret::getParameters() const
{
    return _parameters;
}
 
QByteArray Secret::getValue() const
{
    return _value;
}
 
QString Secret::getContentType() const
{
    return _contentType;
}

This will ensure that the serialization and deserialization are correctly implemented without to much effort. Hope it helps!

From Canonical, Qt