Apr 9 14

Runtime signal connection errors when using the new qt signal style

by mandel

In the ubuntu download manager we are using the new connection style syntax so that if there are errors in the signal connections we will be notified at compile time. However, in recent versions of udm we have noticed that the udm tests that ensure that the qt signals are emitted correctly have started failing randomly in the build servers.

As it can be seen in the following build logs the compilation does finish with no errors but the tests raise errors at runtime (an assert was added for each of the connect calls in the project):

Some of the errors between the diff archs are the same but this feels like a coincidence. The unity-scope-click package project has had the same issue and has solved it in the following way:

123
124
125
126
127
128
129
130
131
132
 
    // NOTE: using SIGNAL/SLOT macros here because new-style
    // connections are flaky on ARM.
    c = connect(impl->systemDownloadManager.data(), SIGNAL(downloadCreated(Download*)),
                this, SLOT(handleDownloadCreated(Download*)));
 
    if (!c) {
        qDebug() << "failed to connect to systemDownloadManager::downloadCreated";
 
    }

I am not the only one that have encoutered this bug within canonical (check out this bug). Apprently -Bsymbolic breaks PMF (Pointer to Member Function) comparison under ARM as it was reported in linaro. As it is explained in the Linaro mailing list a workaround to this (since the correct way would be to fix the linker) is to build with PIE support. The Qt guys have decided to drop -Bsymbolic* on anything but x86 and x86-64. I hope all this info help others that might find the same problem.

Mar 10 14

How to verify Google Mock expectations in QTest

by mandel

This is a small tip for those thos want to use QTest and Google Mock. To ensure that the expectations are check at the end of the execution of the test function and that errors are correctly reported you have to check the expectaions manually and pass the results to a QVERIFY macro. The following examples should be good to get you started:

1
2
3
4
5
6
7
8
9
10
11
void
TestBaseDownload::testStartQueued() {
    QScopedPointer<MockDownload> down(
        new MockDownload(_id, _path, _isConfined, _rootPath, _url,
            _metadata, _headers));
    down->start();
    EXPECT_CALL(*down.data(), startDownload())
            .Times(0);
 
    QVERIFY(Mock::VerifyAndClearExpectations(down.data()));
}

The important line to look at in the example is the following:

1
QVERIFY(Mock::VerifyAndClearExpectations(down.data()));

There we are passing the result of Mock::VerifyAndClearExpectations, where VerifyAndClearExpectations verifies and removes the expectations on a mocked object and returls a bool if it was successful. This way if the expectations are not met the QTest will fail.

For those interested in the error output it would be somthing of the following stype:

home/mandel/Canonical/udm/upload-interface/ubuntu-download-manager-tests/test_base_download.cpp:135: Failure
Mock function called more times than expected - returning directly.
    Function call: cancelDownload()
         Expected: to be never called
           Actual: called once - over-saturated and active
FAIL!  : TestBaseDownload::testCancelNotQueued() 'Mock::VerifyAndClearExpectations(down.data())' returned FALSE. ()
   Loc: [/home/mandel/Canonical/udm/upload-interface/ubuntu-download-manager-tests/test_base_download.cpp(139)]
Jan 9 14

QDBus daemon crashing with a dbus-message.c assert

by mandel

You might have had the following error in your dbus daemon at some point and said to yoursefl WTF???

process 1288: arguments to dbus_message_set_error_name() were incorrect, assertion "error_name == NULL
   || _dbus_check_is_valid_error_name (error_name)" failed in file dbus-message.c line 2900.

Well, you are not the only one and I might be able to point you to the correct direction, your code is probably returnning a QDBusError that you created using the QDBusError::Other enum. The problem here is that the enum value your are using only indicates that the error name is not known and therefore cannot be match to a value in the QDBusError enum. When you use that enumerator the message created does have an incorrect name as follows:

QDBusMessage(type=Error, service="", error name="other", error message="msg", signature="", contents=() ) 

And “other” is, of course, not a valid DBus name and thefore the app crashes. Easies way to solve it, create a correct DBusError ;)

Nov 19 13

How to connect overloaded signals with the new syntax in Qt5

by mandel

Ok, imaging that you are working with Qt 5 and using the new way to connect signals, lets for example say we are working with QNetworkReply and we want to have a slot for the QNetworkReply::error signals that takes a QNetworkReply::NetworkError, the way to do it is the following:

1
2
3
4
 
connect(_reply, static_cast<void(QNetworkReply::*)
    (QNetworkReply::NetworkError)>(&QNetworkReply::error),
        this, &MyClass::onNetworkError)

The static_cast is helping the compiler know what method (the signals or the actual method) you are talking about. I know, it is not nice at all but works at compile time better than getting a qWarning at runtime.

The problem is that without the help the compiler does not know what method error you are talking about :-/

Oct 27 13

What is the use case for the Ubuntu Download Manager

by mandel

I the last few months I have been working on the Ubuntu Download Manager, one of The Big Rocks of August. The u-d-m provide a dbus service that allows applications to request downloads to be performed, to such download requests it adds some nice features that a user on a mobile phone, and probably a desktop, is interested to have. Some of those features are:

  • Apparmor isolation per download. That means that only you application can interact with its downloads.
  • Pause/Resume downloads
  • Autodetect network connection.
  • WIFI only downloads.
  • Hash check support.
  • Allow downloads to be performed while an application has been paused or killed.
  • Group downloads, where a bunch of files are provided and the different downloads are performed as a single atomic operation.

A download might seem a simple action to perform, right? Well, as soon as you start supporting all the above a single download operation becomes a fairly complicated matter. The following is a state machine that identifies the states of a download that would support such features:

Download

As you can see, it is a complicated matter and all these has to be tested and check by the QA team. By providing u-d-m (and later a client library to use approach in C and in the Ubuntu SDK, I’m terribly sorry but I did not have the time to finish it on time for the release) we are helping developers to perform simple downloads with robust code and do not worry about all the corner cases. Performing a download is as simple as requesting it and listen to the different signals. This kind of service is also provided by FirefoxOs, WEbOs and Tizan (but no in IOS or SailFish) but I believe we are doing a better job at exposing a richer API. Of course all this is open source and at least our friend at Jolla (and I really mean friends, I think they are doing an awesome work!!! and competition + collaboration is great).

In the following days I’ll be posting on hot to use the API via C, C++ and DBus.

Oct 22 13

Long time no posts!

by mandel

I have not updating this page lately for a very simple reason: TO MUCH TO CODE.

After a crazy amount of work to push the Ubuntu Download Manager to be the centralized daemon used to perform downloads in Ubuntu Touch I have some more time to write. In the following weeks I’m going to be focusing on explaining some issues and patterns I have found using Qt while developing u-d-m (short of ubuntu download manager from now on).

PS: Is quite embarrasing that my last post was the ‘She got me dancing’ videoclip… you should take a look nevertheless :)

Feb 4 13

She’s got me dancing

by mandel

One of the funniest videos I have seen so far

Tommy Sparks “She’s Got Me Dancing” from Eric Wareheim on Vimeo.

Jan 10 13

How to test the DBus serialization of your structures

by mandel

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!

Jan 3 13

How to send a {sv} over DBus with Qt

by mandel

I have recently been doing some work with Qt and DBus and I got stuck a little on how to correctly send a {sv} over DBus. Either my google-fu is terrible or there are not many examples on how to do this, therefore here is a small static method that will return a {sv} that can be send via DBus without getting a wrong parameters error:

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
 
#ifndef DBUS_HELPER_H
#define DBUS_HELPER_H
 
#include 
#include 
#include 
#include 
 
typedef QHash DBusStringHash;
class DBusHelper : public QObject
{
    Q_OBJECT
public:
    static int DBUS_STRING_MAP_ID;
 
    explicit DBusHelper(QObject *parent = 0);
 
    static class _init
    {
        public:
            _init()
            {
                // diff actions to init
                qRegisterMetaType("DBusStringHash");
                qDBusRegisterMetaType();
                DBUS_STRING_MAP_ID = QMetaType::type("DBusStringHash");
            }
    } _initializer;
 
    static QVariant getVariant(DBusStringHash hash);
 
};
 
Q_DECLARE_METATYPE(DBusStringHash)
#endif // DBUS_HELPER_H
1
2
3
4
5
6
7
8
9
10
11
12
13
// required for the init
int DBusHelper::DBUS_STRING_MAP_ID = 0;
DBusHelper::_init DBusHelper::_initializer;
 
DBusHelper::DBusHelper(QObject *parent) :
    QObject(parent)
{
}
 
QVariant DBusHelper::getVariant(DBusStringHash hash)
{
    return QVariant(DBUS_STRING_MAP_ID, &amp;hash);
}

I added the init trick so that there is no need to manually register the types. I hope it helps!

Jan 3 13

Moved this blog away from godaddy

by mandel

When I started this blog I did not have the time to research for which host company I should used, I simply picked godaddy because I had a coupon from the long dead diggnation… One of the propositions for this year was to move away from godaddy for several reasons:

  • It is expensive, specially for the use I was given to my virtual server.
  • The UI to manage the domains is crazy bad.
  • Is slow.

At the moment the blog is running on a EC2 instance hosted in the EU zone witch is more than I need. Please let me know if you see any possible errors in blogs posts (all of the should be there as the used to be) or with the RSS feeds, next step, move away from wordpress to a python/django solution.