Merge lp:~xavi-garcia-mena/keeper/tests-with-propertieschanged-signal into lp:keeper/devel
- tests-with-propertieschanged-signal
- Merge into devel
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 |
Related bugs: |
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.
unity-api-1-bot (unity-api-1-bot) wrote : | # |
Charles Kerr (charlesk) wrote : | # |
Clean and straightforward. Overall, looks very good!
Several questions and minor/trivial suggestions inline, one borderline needs-fixing.
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.
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Continuous integration, rev:101
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Charles Kerr (charlesk) wrote : | # |
LGTM. Thanks for the changes!
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Preview Diff
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 ¤t) |
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> |
FAILED: Continuous integration, rev:100 /jenkins. canonical. com/unity- api-1/job/ lp-keeper- ci/36/ /jenkins. canonical. com/unity- api-1/job/ build/553/ console /jenkins. canonical. com/unity- api-1/job/ build-0- fetch/559/ console
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /jenkins. canonical. com/unity- api-1/job/ lp-keeper- ci/36/rebuild
https:/