Merge lp:~xavi-garcia-mena/keeper/tests-with-propertieschanged-signal into lp:keeper/devel

Proposed by Xavi Garcia
Status: Merged
Merge reported by: Xavi Garcia
Merged at revision: not available
Proposed branch: lp:~xavi-garcia-mena/keeper/tests-with-propertieschanged-signal
Merge into: lp:keeper/devel
Diff against target: 819 lines (+550/-23)
14 files modified
src/service/keeper-task.cpp (+24/-1)
src/service/keeper-task.h (+2/-0)
src/service/private/keeper-task_p.h (+2/-0)
src/service/task-manager.cpp (+30/-16)
src/service/task-manager.h (+5/-0)
src/util/dbus-utils.cpp (+6/-1)
tests/CMakeLists.txt (+1/-0)
tests/integration/helpers/CMakeLists.txt (+3/-0)
tests/integration/helpers/helpers-test-failure.cpp (+16/-1)
tests/integration/helpers/helpers-test.cc (+14/-0)
tests/integration/helpers/test-helpers-base.cpp (+360/-3)
tests/integration/helpers/test-helpers-base.h (+7/-1)
tests/qdbus-stubs/CMakeLists.txt (+53/-0)
tests/qdbus-stubs/org.freedesktop.DBus.Properties.xml (+27/-0)
To merge this branch: bzr merge lp:~xavi-garcia-mena/keeper/tests-with-propertieschanged-signal
Reviewer Review Type Date Requested Status
unity-api-1-bot continuous-integration Needs Fixing
Charles Kerr (community) Approve
Review via email: mp+304928@code.launchpad.net

Commit message

Improve integration tests to listen to the PropertiesChanged signal from keeper.User dbus interface.

Description of the change

Improve integration tests to listen to the PropertiesChanged signal from keeper.User dbus interface.

The tests now check that the signals progress is correct. It records all the signals until completion of all tasks and checks that things like "action" state makes sense of "percent-done" is always greater or equal than the previous signal.

It also adds some code modifications to emit the PropertiesChanged signal as now the state lives in the TaskManager class.

To post a comment you must log in.
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) wrote :

Clean and straightforward. Overall, looks very good!

Several questions and minor/trivial suggestions inline, one borderline needs-fixing.

review: Needs Information
Revision history for this message
Xavi Garcia (xavi-garcia-mena) wrote :

Thanks for the review, Charles. I've added some comments inline.

When fixing the issues you found and suggested I realized that we were not sending a lot of PropertiesChanges signal at the beginning because when I did the TaskManager change I forgot to add them.

To fix this I've created a static method in the KeeperTask class that returns an initial state.

The idea would be to set that initial state for all tasks (which should appear as queued for the user) and then emit the PropertiesSignal to notify the state of all tasks all at once.

I also added a new change to detect when the previous state of a task is equal to the current one. In that case we don't emit the PropertiesChanged signal.

We have a big TODO here, though, and it's about i18n as stated in the code... I've created a Trello card for that.

Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :

FAILED: Continuous integration, rev:101
https://jenkins.canonical.com/unity-api-1/job/lp-keeper-ci/40/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build/561/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/567
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/395
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/395/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/395
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/395/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/395/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/395
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/395/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/395
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/395/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/395/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/395
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/395/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/395
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/395/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/395/console

Click here to trigger a rebuild:
https://jenkins.canonical.com/unity-api-1/job/lp-keeper-ci/40/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) wrote :

LGTM. Thanks for the changes!

review: Approve
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :

FAILED: Autolanding.
More details in the following jenkins job:
https://jenkins.canonical.com/unity-api-1/job/lp-keeper-autoland/5/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build/568/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/574
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/402
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/402/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/402
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/402/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/402/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/402
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/402/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/402
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/402/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/402/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/402
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/402/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/402
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/402/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/402/console

review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/service/keeper-task.cpp'
2--- src/service/keeper-task.cpp 2016-08-31 14:40:50 +0000
3+++ src/service/keeper-task.cpp 2016-09-06 13:08:57 +0000
4@@ -114,7 +114,7 @@
5
6 void KeeperTaskPrivate::on_helper_percent_done_changed(float /*percent_done*/)
7 {
8- calculate_task_state();
9+ calculate_and_notify_state(helper_->state());
10 }
11
12 QVariantMap KeeperTaskPrivate::calculate_task_state()
13@@ -149,6 +149,24 @@
14 Q_EMIT(q_ptr->task_state_changed(state));
15 }
16
17+QVariantMap KeeperTaskPrivate::get_initial_state(KeeperTask::TaskData const &td)
18+{
19+ QVariantMap ret;
20+
21+ auto const uuid = td.metadata.uuid();
22+
23+ ret.insert(QStringLiteral("action"), td.action);
24+
25+ // TODO review this when we add the restore tasks.
26+ // TODO we maybe have different fields
27+ ret.insert(QStringLiteral("display-name"), td.metadata.display_name());
28+ ret.insert(QStringLiteral("speed"), 0);
29+ ret.insert(QStringLiteral("percent-done"), double(0.0));
30+ ret.insert(QStringLiteral("uuid"), uuid);
31+
32+ return ret;
33+}
34+
35 KeeperTask::KeeperTask(TaskData const & task_data,
36 QSharedPointer<HelperRegistry> const & helper_registry,
37 QSharedPointer<StorageFrameworkClient> const & storage,
38@@ -178,3 +196,8 @@
39 Q_D(const KeeperTask);
40 return d->state();
41 }
42+
43+QVariantMap KeeperTask::get_initial_state(KeeperTask::TaskData const &td)
44+{
45+ return KeeperTaskPrivate::get_initial_state(td);
46+}
47
48=== modified file 'src/service/keeper-task.h'
49--- src/service/keeper-task.h 2016-08-24 11:04:47 +0000
50+++ src/service/keeper-task.h 2016-09-06 13:08:57 +0000
51@@ -59,6 +59,8 @@
52 bool start();
53 QVariantMap state() const;
54
55+ static QVariantMap get_initial_state(KeeperTask::TaskData const &td);
56+
57 Q_SIGNALS:
58 void task_state_changed(Helper::State state);
59 void task_socket_ready(int socket_descriptor);
60
61=== modified file 'src/service/private/keeper-task_p.h'
62--- src/service/private/keeper-task_p.h 2016-08-24 11:04:47 +0000
63+++ src/service/private/keeper-task_p.h 2016-09-06 13:08:57 +0000
64@@ -36,6 +36,8 @@
65 QVariantMap state() const;
66 void ask_for_storage_framework_socket(quint64 n_bytes);
67
68+ static QVariantMap get_initial_state(KeeperTask::TaskData const &td);
69+
70 protected:
71 void set_current_task_action(QString const& action);
72 void on_helper_percent_done_changed(float percent_done);
73
74=== modified file 'src/service/task-manager.cpp'
75--- src/service/task-manager.cpp 2016-08-31 14:56:18 +0000
76+++ src/service/task-manager.cpp 2016-09-06 13:08:57 +0000
77@@ -79,8 +79,10 @@
78 td.type = type;
79 td.action = QStringLiteral("queued"); // TODO i18n
80
81-// update_task_state(td);
82+ set_initial_task_state(td);
83 }
84+ // notify the initial state once for all tasks
85+ notify_state_changed();
86
87 start_next_task();
88 }
89@@ -222,20 +224,13 @@
90 update_task_state(it.value());
91 }
92
93- void update_task_state(KeeperTask::KeeperTask::TaskData& td)
94- {
95- state_[td.metadata.uuid()] = task_->state();
96-
97-#if 0
98- // FIXME: we don't need this to work correctly for the sprint because Marcus is polling in a loop
99- // but we will need this in order for him to stop doing that
100-
101- // TODO: compare old and new and decide if it's worth emitting a PropertyChanged signal;
102- // eg don't contribute to dbus noise for minor speed fluctuations
103-
104- // TODO: this function is called inside a loop when initializing the state
105- // after start_tasks(), so also ensure we don't have a notify flood here
106-
107+ void set_initial_task_state(KeeperTask::KeeperTask::TaskData& td)
108+ {
109+ state_[td.metadata.uuid()] = KeeperTask::get_initial_state(td);
110+ }
111+
112+ void notify_state_changed()
113+ {
114 DBusUtils::notifyPropertyChanged(
115 QDBusConnection::sessionBus(),
116 *q_ptr,
117@@ -243,7 +238,26 @@
118 DBusTypes::KEEPER_USER_INTERFACE,
119 QStringList(QStringLiteral("State"))
120 );
121-#endif
122+
123+ Q_EMIT(q_ptr->state_changed());
124+ }
125+
126+ void update_task_state(KeeperTask::KeeperTask::TaskData& td)
127+ {
128+ auto task_state = task_->state();
129+
130+ // avoid sending repeated states to minimize the use of the bus
131+ if (task_state != state_[td.metadata.uuid()] && !task_state.isEmpty())
132+ {
133+ state_[td.metadata.uuid()] = task_state;
134+
135+ // FIXME: we don't need this to work correctly for the sprint because Marcus is polling in a loop
136+ // but we will need this in order for him to stop doing that
137+
138+ // TODO: compare old and new and decide if it's worth emitting a PropertyChanged signal;
139+ // eg don't contribute to dbus noise for minor speed fluctuations
140+ notify_state_changed();
141+ }
142 }
143
144 void set_current_task_action(QString const& action)
145
146=== modified file 'src/service/task-manager.h'
147--- src/service/task-manager.h 2016-08-24 11:04:47 +0000
148+++ src/service/task-manager.h 2016-09-06 13:08:57 +0000
149@@ -46,6 +46,10 @@
150
151 Q_DISABLE_COPY(TaskManager)
152
153+ Q_PROPERTY(QVariantDictMap State
154+ READ get_state
155+ NOTIFY state_changed)
156+
157 void start_tasks(QStringList const & task_uuids);
158
159 QVariantDictMap get_state() const;
160@@ -54,6 +58,7 @@
161
162 Q_SIGNALS:
163 void socket_ready(int reply);
164+ void state_changed();
165
166 private:
167 QScopedPointer<TaskManagerPrivate> const d_ptr;
168
169=== modified file 'src/util/dbus-utils.cpp'
170--- src/util/dbus-utils.cpp 2016-05-26 21:33:18 +0000
171+++ src/util/dbus-utils.cpp 2016-09-06 13:08:57 +0000
172@@ -65,7 +65,12 @@
173 signal << it.key().second;
174 // Changed properties (name, value)
175 signal << it.value().second;
176- signal << QStringList();
177+ QStringList properties_changed;
178+ for (auto iter_props = it.value().second.begin(); iter_props != it.value().second.end(); ++iter_props)
179+ {
180+ properties_changed << iter_props.key();
181+ }
182+ signal << properties_changed;
183 // Connection
184 it.value().first.send(signal);
185 }
186
187=== modified file 'tests/CMakeLists.txt'
188--- tests/CMakeLists.txt 2016-08-31 14:40:50 +0000
189+++ tests/CMakeLists.txt 2016-09-06 13:08:57 +0000
190@@ -99,6 +99,7 @@
191 add_subdirectory(utils)
192 add_subdirectory(fakes)
193 add_subdirectory(integration)
194+add_subdirectory(qdbus-stubs)
195
196 set(
197 COVERAGE_TEST_TARGETS
198
199=== modified file 'tests/integration/helpers/CMakeLists.txt'
200--- tests/integration/helpers/CMakeLists.txt 2016-08-30 12:50:46 +0000
201+++ tests/integration/helpers/CMakeLists.txt 2016-09-06 13:08:57 +0000
202@@ -23,6 +23,7 @@
203 ${UAL_INCLUDE_DIRS}
204 ${PROP-CPP_INCLUDE_DIRS}
205 ${HELPERS_TEST_DEPS_INCLUDE_DIRS}
206+ "${CMAKE_BINARY_DIR}/tests/qdbus-stubs"
207 )
208
209 set(
210@@ -48,6 +49,7 @@
211 ${HELPERS_TEST}
212 ${HELPERS_TEST_DEPS_LDFLAGS}
213 ${INTEGRATION_TEST_LIBRARIES}
214+ qdbus-stubs-tests
215 Qt5::Core
216 Qt5::DBus
217 Qt5::Test
218@@ -113,6 +115,7 @@
219 ${HELPERS_TEST_FAILURE}
220 ${HELPERS_TEST_DEPS_LDFLAGS}
221 ${INTEGRATION_TEST_LIBRARIES}
222+ qdbus-stubs-tests
223 Qt5::Core
224 Qt5::DBus
225 Qt5::Test
226
227=== modified file 'tests/integration/helpers/helpers-test-failure.cpp'
228--- tests/integration/helpers/helpers-test-failure.cpp 2016-09-01 12:55:19 +0000
229+++ tests/integration/helpers/helpers-test-failure.cpp 2016-09-06 13:08:57 +0000
230@@ -67,11 +67,26 @@
231 QFile helper_mark(SIMPLE_HELPER_MARK_FILE_PATH);
232 qDebug() << "Helper mark exists before calling StartBackup..." << helper_mark.exists();
233
234+ QSharedPointer<DBusPropertiesInterface> properties_interface(new DBusPropertiesInterface(
235+ DBusTypes::KEEPER_SERVICE,
236+ DBusTypes::KEEPER_USER_PATH,
237+ dbus_test_runner.sessionConnection()
238+ ) );
239+
240+ ASSERT_TRUE(properties_interface->isValid()) << qPrintable(QDBusConnection::sessionBus().lastError().message());
241+
242+ QSignalSpy spy(properties_interface.data(),&DBusPropertiesInterface::PropertiesChanged);
243+
244 // Now we know the music folder uuid, let's start the backup for it.
245 QDBusReply<void> backup_reply = user_iface->call("StartBackup", QStringList{user_folder_uuid});
246 ASSERT_TRUE(backup_reply.isValid()) << qPrintable(dbus_test_runner.sessionConnection().lastError().message());
247
248- // wait until all the tasks have the action state "failed"
249+ // waits until all tasks are complete, recording PropertiesChanged signals
250+ // and checks all the recorded values
251+ EXPECT_TRUE(capture_and_check_state_until_all_tasks_complete(spy, {user_folder_uuid}, "failed"));
252+
253+ // wait until all the tasks have the action state "complete"
254+ // this one uses pooling so it should just call Get once
255 EXPECT_TRUE(wait_for_all_tasks_have_action_state({user_folder_uuid}, "failed", user_iface));
256
257 // check that the content of the file is the expected
258
259=== modified file 'tests/integration/helpers/helpers-test.cc'
260--- tests/integration/helpers/helpers-test.cc 2016-09-01 12:55:19 +0000
261+++ tests/integration/helpers/helpers-test.cc 2016-09-06 13:08:57 +0000
262@@ -104,12 +104,26 @@
263 ASSERT_FALSE(user_folder_uuid_2.isEmpty());
264 qDebug() << "User folder 2 UUID is:" << user_folder_uuid_2;
265
266+ QSharedPointer<DBusPropertiesInterface> properties_interface(new DBusPropertiesInterface(
267+ DBusTypes::KEEPER_SERVICE,
268+ DBusTypes::KEEPER_USER_PATH,
269+ dbus_test_runner.sessionConnection()
270+ ) );
271+
272+ ASSERT_TRUE(properties_interface->isValid()) << qPrintable(QDBusConnection::sessionBus().lastError().message());
273+
274+ QSignalSpy spy(properties_interface.data(),&DBusPropertiesInterface::PropertiesChanged);
275
276 // Now we know the music folder uuid, let's start the backup for it.
277 QDBusReply<void> backup_reply = user_iface->call("StartBackup", QStringList{user_folder_uuid, user_folder_uuid_2});
278 ASSERT_TRUE(backup_reply.isValid()) << qPrintable(dbus_test_runner.sessionConnection().lastError().message());
279
280+ // waits until all tasks are complete, recording PropertiesChanged signals
281+ // and checks all the recorded values
282+ EXPECT_TRUE(capture_and_check_state_until_all_tasks_complete(spy, {user_folder_uuid, user_folder_uuid_2}, "complete"));
283+
284 // wait until all the tasks have the action state "complete"
285+ // this one uses pooling so it should just call Get once
286 EXPECT_TRUE(wait_for_all_tasks_have_action_state({user_folder_uuid, user_folder_uuid_2}, "complete", user_iface));
287
288 // check that the content of the file is the expected
289
290=== modified file 'tests/integration/helpers/test-helpers-base.cpp'
291--- tests/integration/helpers/test-helpers-base.cpp 2016-08-31 14:48:23 +0000
292+++ tests/integration/helpers/test-helpers-base.cpp 2016-09-06 13:08:57 +0000
293@@ -26,6 +26,242 @@
294 using namespace QtDBusTest;
295 using namespace QtDBusMock;
296
297+///
298+/// State helpers
299+//
300+
301+bool qvariant_to_map(QVariant const& variant, QVariantMap& map)
302+{
303+ if (variant.type() == QMetaType::QVariantMap)
304+ {
305+ map = variant.toMap();
306+ return true;
307+ }
308+ qWarning() << Q_FUNC_INFO << ": Could not convert variant to QVariantMap. Variant received has type " << variant.typeName();
309+
310+ return false;
311+}
312+
313+bool qdbus_argument_to_variant_dict_map(QVariant const& variant, QVariantDictMap& map)
314+{
315+ if (variant.canConvert<QDBusArgument>())
316+ {
317+ QDBusArgument value(variant.value<QDBusArgument>());
318+ if (value.currentType() == QDBusArgument::MapType)
319+ {
320+ value >> map;
321+ return true;
322+ }
323+ else
324+ {
325+ qWarning() << Q_FUNC_INFO << ": Could not convert variant to QVariantDictMap. Variant received has type " << value.currentType();
326+ }
327+ }
328+ else
329+ {
330+ qWarning() << Q_FUNC_INFO << ": Could not convert variant to QDBusArgument.";
331+ }
332+ return false;
333+}
334+
335+bool get_property_qvariant_dict_map(QString const & property, QVariant const &variant, QVariantDictMap & map)
336+{
337+ QVariantMap properties_map;
338+ if (!qvariant_to_map(variant, properties_map))
339+ {
340+ qWarning() << Q_FUNC_INFO << ": Error converting variant in PropertiesChanged signal to QVariantMap";
341+ return false;
342+ }
343+
344+ auto iter = properties_map.find(property);
345+ if (iter == properties_map.end())
346+ {
347+ qWarning() << Q_FUNC_INFO << ": Property [" << property << "] was not found in PropertiesChanged";
348+ return false;
349+ }
350+
351+ if(!qdbus_argument_to_variant_dict_map((*iter), map))
352+ {
353+ qWarning() << Q_FUNC_INFO << ": Error converting property [" << property << "] to QVariantDictMap";
354+ return false;
355+ }
356+
357+ return true;
358+}
359+
360+bool all_tasks_has_state(QMap<QString, QString> const & tasks_state, QString const & state)
361+{
362+ for (auto iter = tasks_state.begin(); iter != tasks_state.end(); ++iter )
363+ {
364+ if ((*iter) != state)
365+ {
366+ return false;
367+ }
368+ }
369+ return true;
370+}
371+
372+bool get_task_property(QString const &property, QVariantMap const &values, QVariant &value)
373+{
374+ auto iter = values.find(property);
375+ if (iter == values.end())
376+ {
377+ qWarning() << Q_FUNC_INFO << ": Property [" << property << "] was not found.";
378+ return false;
379+ }
380+ value = (*iter);
381+ return true;
382+}
383+
384+bool analyze_task_percentage_values(QString const & uuid, QList<QVariantMap> const & recorded_values)
385+{
386+ double previous_percentage = -1.0;
387+ for (auto iter = recorded_values.begin(); iter != recorded_values.end(); ++iter)
388+ {
389+ QVariant percentage;
390+ if (!get_task_property("percent-done", (*iter), percentage))
391+ {
392+ qWarning() << Q_FUNC_INFO << ": Percentage was not found for task: " << uuid;
393+ return false;
394+ }
395+ bool ok_double;
396+ auto percentage_double = percentage.toDouble(&ok_double);
397+ if (!ok_double)
398+ {
399+ qWarning() << Q_FUNC_INFO << ": Error converting percent-done to double for uuid: " << uuid << ". State: " << (*iter);
400+ return false;
401+ }
402+ if (percentage_double < previous_percentage)
403+ {
404+ qWarning() << Q_FUNC_INFO << ": Eror, current percentage is less than previous: current=" << percentage_double << ", previous=" << previous_percentage;
405+ return false;
406+ }
407+ previous_percentage = percentage_double;
408+ }
409+ return true;
410+}
411+
412+bool check_valid_action_state_step(QString const &previous, QString const &current)
413+{
414+ if (current == "queued")
415+ {
416+ return previous == "none" || previous == "queued";
417+ }
418+ else if (current == "saving")
419+ {
420+ return previous == "saving" || previous == "queued";
421+ }
422+ else if (current == "finishing")
423+ {
424+ return previous == "finishing" || previous == "saving";
425+ }
426+ else if (current == "complete")
427+ {
428+ // we may pass from "saving" to "complete" if we don't have enough time
429+ // to emit the "finishing" state change
430+ return previous == "complete" || previous == "finishing" || previous == "saving";
431+ }
432+ else if (current == "failed")
433+ {
434+ // we can get to failed from any state except complete
435+ return previous != "complete";
436+ }
437+ else
438+ {
439+ // for possible new states, please add your code here
440+ qWarning() << "Unhandled state: " << current;
441+ return false;
442+ }
443+}
444+
445+bool analyze_task_action_values(QString const & uuid, QList<QVariantMap> const & recorded_values)
446+{
447+ QString previous_action = "none";
448+ for (auto iter = recorded_values.begin(); iter != recorded_values.end(); ++iter)
449+ {
450+ QVariant action;
451+ if (!get_task_property("action", (*iter), action))
452+ {
453+ qWarning() << Q_FUNC_INFO << ": Action was not found for task: " << uuid;
454+ return false;
455+ }
456+ auto current_action = action.toString();
457+
458+ if (!check_valid_action_state_step(previous_action, action.toString()))
459+ {
460+ qWarning() << Q_FUNC_INFO << ": Bad action state step: previous state=" << previous_action << ", current=" << action.toString();
461+ return false;
462+ }
463+ previous_action = action.toString();
464+ }
465+ return true;
466+}
467+
468+bool analyze_task_display_name_values(QString const & uuid, QList<QVariantMap> const & recorded_values)
469+{
470+ QString previous_name;
471+ // check that the display name never changes between recorded states
472+ for (auto iter = recorded_values.begin(); iter != recorded_values.end(); ++iter)
473+ {
474+ QVariant name;
475+ if (!get_task_property("display-name", (*iter), name))
476+ {
477+ qWarning() << Q_FUNC_INFO << ": display-name was not found for task: " << uuid;
478+ return false;
479+ }
480+ auto current_name = name.toString();
481+
482+ if (previous_name.isEmpty())
483+ {
484+ previous_name = name.toString();
485+ }
486+ else if(name.toString() != previous_name)
487+ {
488+ qWarning() << Q_FUNC_INFO << "ERROR: display-name for uuid: " << uuid << " changed from " << previous_name << " to " << name.toString();
489+ return false;
490+ }
491+ }
492+ return true;
493+}
494+
495+bool analyze_tasks_values(QMap<QString, QList<QVariantMap>> const &uuids_state)
496+{
497+ for (auto iter = uuids_state.begin(); iter != uuids_state.end(); ++iter)
498+ {
499+ if(!analyze_task_display_name_values(iter.key(), (*iter)))
500+ return false;
501+ if(!analyze_task_action_values(iter.key(), (*iter)))
502+ return false;
503+ if(!analyze_task_percentage_values(iter.key(), (*iter)))
504+ return false;
505+ }
506+ return true;
507+}
508+
509+bool verify_signal_interface_and_invalidated_properties(QVariant const &interface,
510+ QVariant const &invalidated_properties,
511+ QString const & expected_interface,
512+ QString const & expected_property)
513+{
514+ auto props_list = invalidated_properties.toStringList();
515+ if (!props_list.contains(expected_property))
516+ {
517+ qWarning() << Q_FUNC_INFO << "ERROR: PropertiesChanged signal did not include the property: " << expected_property << " as the ones invalidated";
518+ return false;
519+ }
520+
521+ if (interface.toString() != expected_interface)
522+ {
523+ qWarning() << Q_FUNC_INFO << "ERROR: Interface: [" << interface.toString() << "] is not valid. Expecting: [" << expected_interface << "]";
524+ return false;
525+ }
526+ return true;
527+}
528+
529+///
530+///
531+///
532+
533 TestHelpersBase::TestHelpersBase()
534 :dbus_mock(dbus_test_runner)
535 {
536@@ -63,7 +299,13 @@
537 throw;
538 }
539
540- system("dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames");
541+#if 0
542+ // Enable this to get extra dbus information.
543+ if (!start_dbus_monitor())
544+ {
545+ throw std::logic_error("Error starting dbus-monitor process.");
546+ }
547+#endif
548 }
549
550 void TestHelpersBase::SetUp()
551@@ -243,12 +485,12 @@
552 : -1;
553 }
554
555-bool TestHelpersBase::wait_for_all_tasks_have_action_state(QStringList const & uuids, QString const & action_state, QSharedPointer<DBusInterfaceKeeperUser> const & keeper_user_iface, int max_timeout)
556+bool TestHelpersBase::wait_for_all_tasks_have_action_state(QStringList const & uuids, QString const & action_state, QSharedPointer<DBusInterfaceKeeperUser> const & keeper_user_iface, int max_timeout_msec)
557 {
558 QElapsedTimer timer;
559 timer.start();
560 bool finished = false;
561- while (!timer.hasExpired(max_timeout) && !finished)
562+ while (!timer.hasExpired(max_timeout_msec) && !finished)
563 {
564 auto state = keeper_user_iface->state();
565 auto all_helpers_finished = true;
566@@ -278,6 +520,98 @@
567 return (*iter_props).toString() == action_state;
568 }
569
570+bool TestHelpersBase::capture_and_check_state_until_all_tasks_complete(QSignalSpy & spy, QStringList const & uuids, QString const & action_state, int max_timeout_msec)
571+{
572+ QMap<QString, QList<QVariantMap>> uuids_state;
573+ QMap<QString, QString> uuids_current_state;
574+
575+ // initialize the current state map to wait for all expected uuids
576+ for (auto const& uuid : uuids)
577+ {
578+ uuids_current_state[uuid] = "none";
579+ uuids_state[uuid] = QList<QVariantMap>();
580+ }
581+
582+ QElapsedTimer timer;
583+ timer.start();
584+ bool finished = false;
585+ while (!timer.hasExpired(max_timeout_msec) && !finished)
586+ {
587+ spy.wait();
588+
589+ qDebug() << "PropertiesChanged SIGNALS RECEIVED: " << spy.count();
590+ while (spy.count())
591+ {
592+ auto arguments = spy.takeFirst();
593+
594+ if (arguments.size() != 3)
595+ {
596+ qWarning() << "Bad number of arguments in PropertiesChanged signal";
597+ return false;
598+ }
599+
600+ // verify interface and invalidated_properties arguments
601+ if(!verify_signal_interface_and_invalidated_properties(arguments.at(0), arguments.at(2), DBusTypes::KEEPER_USER_INTERFACE, "State"))
602+ {
603+ return false;
604+ }
605+ QVariantDictMap keeper_state;
606+ if (!get_property_qvariant_dict_map("State", arguments.at(1), keeper_state))
607+ {
608+ return false;
609+ }
610+ for (auto iter = keeper_state.begin(); iter != keeper_state.end(); ++iter )
611+ {
612+ // check for unexpected uuids
613+ if (!uuids.contains(iter.key()))
614+ {
615+ qWarning() << "State contains unexpected uuid: " << iter.key();
616+ return false;
617+ }
618+
619+ QVariantMap task_state;
620+ if (!qvariant_to_map((*iter), task_state))
621+ {
622+ qWarning() << "Error converting second argument in PropertiesChanged signal to QVariantMap for uuid: " << iter.key();
623+ return false;
624+ }
625+ qDebug() << "State for uuid: " << iter.key() << " : " << task_state;
626+
627+ QVariant action;
628+ if (get_task_property("action", task_state, action))
629+ {
630+ if (action.type() != QVariant::String)
631+ {
632+ qWarning() << "Property [action] is not a string";
633+ return false;
634+ }
635+ // store the current action state
636+ uuids_current_state[iter.key()] = action.toString();
637+
638+ bool store = true;
639+ if (action.toString() == action_state)
640+ {
641+ // check if it worths storing this new event
642+ store = uuids_state[iter.key()].last() != task_state;
643+ }
644+ if (store)
645+ {
646+ // store the current state for later inspection
647+ uuids_state[iter.key()].push_back(task_state);
648+ }
649+ }
650+ }
651+ }
652+ finished = all_tasks_has_state(uuids_current_state, action_state);
653+ if (finished)
654+ {
655+ qDebug() << "ALL TASKS FINISHED =========================================";
656+ }
657+ }
658+ // check for the recorded values
659+ return analyze_tasks_values(uuids_state);
660+}
661+
662 QString TestHelpersBase::get_uuid_for_xdg_folder_path(QString const &path, QVariantDictMap const & choices) const
663 {
664 for(auto iter = choices.begin(); iter != choices.end(); ++iter)
665@@ -296,3 +630,26 @@
666
667 return QString();
668 }
669+
670+bool TestHelpersBase::start_dbus_monitor()
671+{
672+ if (!dbus_monitor_process)
673+ {
674+ dbus_monitor_process.reset(new QProcess());
675+
676+ dbus_monitor_process->setProcessChannelMode(QProcess::ForwardedChannels);
677+
678+ dbus_monitor_process->start("dbus-monitor", QStringList() << "--session");
679+ if (!dbus_monitor_process->waitForStarted())
680+ {
681+ qWarning() << "Error, starting dbus-monitor: " << dbus_monitor_process->errorString();
682+ return false;
683+ }
684+ }
685+ else
686+ {
687+ qWarning() << "Error, dbus-monitor should be called one time per test.";
688+ return false;
689+ }
690+ return true;
691+}
692
693=== modified file 'tests/integration/helpers/test-helpers-base.h'
694--- tests/integration/helpers/test-helpers-base.h 2016-08-30 12:50:46 +0000
695+++ tests/integration/helpers/test-helpers-base.h 2016-09-06 13:08:57 +0000
696@@ -22,6 +22,7 @@
697
698 #include <helper/backup-helper.h>
699 #include <qdbus-stubs/dbus-types.h>
700+#include "DBusPropertiesInterface.h"
701 #include "qdbus-stubs/keeper_user_interface.h"
702
703 #include "tests/fakes/fake-backup-helper.h"
704@@ -73,6 +74,7 @@
705 QtDBusMock::DBusMock dbus_mock;
706 QSharedPointer<QtDBusTest::QProcessDBusService> keeper_service;
707 QSharedPointer<QtDBusTest::QProcessDBusService> upstart_service;
708+ QScopedPointer<QProcess> dbus_monitor_process;
709
710 protected:
711 void start_tasks();
712@@ -93,11 +95,15 @@
713
714 QString get_last_storage_framework_file();
715
716- bool wait_for_all_tasks_have_action_state(QStringList const & uuids, QString const & action_state, QSharedPointer<DBusInterfaceKeeperUser> const & keeper_user_iface, int max_timeout = 15000);
717+ bool wait_for_all_tasks_have_action_state(QStringList const & uuids, QString const & action_state, QSharedPointer<DBusInterfaceKeeperUser> const & keeper_user_iface, int max_timeout_msec = 15000);
718
719 bool check_task_has_action_state(QVariantDictMap const & state, QString const & uuid, QString const & action_state);
720
721+ bool capture_and_check_state_until_all_tasks_complete(QSignalSpy & spy, QStringList const & uuids, QString const & action_state, int max_timeout_msec = 15000);
722+
723 QString get_uuid_for_xdg_folder_path(QString const &path, QVariantDictMap const & choices) const;
724+
725+ bool start_dbus_monitor();
726 };
727
728 #define EXPECT_ENV(expected, envvars, key) EXPECT_EQ(expected, get_env(envvars, key)) << "for key " << key
729
730=== added directory 'tests/qdbus-stubs'
731=== added file 'tests/qdbus-stubs/CMakeLists.txt'
732--- tests/qdbus-stubs/CMakeLists.txt 1970-01-01 00:00:00 +0000
733+++ tests/qdbus-stubs/CMakeLists.txt 2016-09-06 13:08:57 +0000
734@@ -0,0 +1,53 @@
735+# Copyright © 2015 Canonical Ltd.
736+#
737+# This program is free software: you can redistribute it and/or modify it
738+# under the terms of the GNU Lesser General Public License version 3,
739+# as published by the Free Software Foundation.
740+#
741+# This program is distributed in the hope that it will be useful,
742+# but WITHOUT ANY WARRANTY; without even the implied warranty of
743+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
744+# GNU Lesser General Public License for more details.
745+#
746+# You should have received a copy of the GNU Lesser General Public License
747+# along with this program. If not, see <http://www.gnu.org/licenses/>.
748+#
749+# Authors:
750+# Xavi Garcia <marcus.tomlinson@canonical.com>
751+
752+# disable compilier warnings in machine-generated code
753+add_compile_options(-w)
754+
755+# XML
756+
757+set(
758+ properties_xml
759+ "org.freedesktop.DBus.Properties.xml"
760+)
761+
762+set_source_files_properties(
763+ "${properties_xml}"
764+ PROPERTIES
765+ NO_NAMESPACE YES
766+ CLASSNAME DBusPropertiesInterface
767+)
768+
769+qt5_add_dbus_interface(
770+ interface_files
771+ ${properties_xml}
772+ DBusPropertiesInterface
773+)
774+
775+#
776+#
777+
778+set(
779+ STUBS_LIB
780+ qdbus-stubs-tests
781+)
782+
783+add_library(
784+ ${STUBS_LIB}
785+ STATIC
786+ ${interface_files}
787+)
788
789=== added file 'tests/qdbus-stubs/org.freedesktop.DBus.Properties.xml'
790--- tests/qdbus-stubs/org.freedesktop.DBus.Properties.xml 1970-01-01 00:00:00 +0000
791+++ tests/qdbus-stubs/org.freedesktop.DBus.Properties.xml 2016-09-06 13:08:57 +0000
792@@ -0,0 +1,27 @@
793+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
794+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
795+<node>
796+ <interface name="org.freedesktop.DBus.Properties">
797+ <method name="Get">
798+ <arg type="s" name="interface_name" direction="in"/>
799+ <arg type="s" name="property_name" direction="in"/>
800+ <arg type="v" name="value" direction="out"/>
801+ </method>
802+ <method name="GetAll">
803+ <arg type="s" name="interface_name" direction="in"/>
804+ <arg type="a{sv}" name="properties" direction="out"/>
805+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
806+ </method>
807+ <method name="Set">
808+ <arg type="s" name="interface_name" direction="in"/>
809+ <arg type="s" name="property_name" direction="in"/>
810+ <arg type="v" name="value" direction="in"/>
811+ </method>
812+ <signal name="PropertiesChanged">
813+ <arg type="s" name="interface_name"/>
814+ <arg type="a{sv}" name="changed_properties"/>
815+ <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/>
816+ <arg type="as" name="invalidated_properties"/>
817+ </signal>
818+ </interface>
819+</node>

Subscribers

People subscribed via source and target branches

to all changes: