Merge lp:~autopilot/autopilot-qt/experimental into lp:autopilot-qt
- experimental
- Merge into trunk
Proposed by
Thomi Richards
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Thomi Richards | ||||||||
Approved revision: | 85 | ||||||||
Merged at revision: | 71 | ||||||||
Proposed branch: | lp:~autopilot/autopilot-qt/experimental | ||||||||
Merge into: | lp:autopilot-qt | ||||||||
Diff against target: |
1194 lines (+610/-161) 15 files modified
debian/changelog (+7/-0) debian/control (+4/-2) driver/autopilot_types.h (+26/-0) driver/dbus_adaptor.cpp (+1/-1) driver/dbus_object.cpp (+4/-2) driver/driver.pro (+2/-1) driver/introspection.cpp (+62/-25) driver/introspection.h (+1/-1) driver/qtnode.cpp (+125/-50) driver/qtnode.h (+29/-8) driver/qttestability.cpp (+3/-0) driver/rootnode.cpp (+15/-26) driver/rootnode.h (+2/-4) tests/unittests/tst_introspection.cpp (+326/-39) tests/unittests/unittests.pro (+3/-2) |
||||||||
To merge this branch: | bzr merge lp:~autopilot/autopilot-qt/experimental | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Approve | |
Autopilot Hackers | Pending | ||
Review via email: mp+185843@code.launchpad.net |
Commit message
1.4 wire protocol changes.
Description of the change
Merge 1.4 changes.
To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : | # |
review:
Needs Fixing
(continuous-integration)
- 85. By Thomi Richards
-
Add Breaks clause on binary package.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:85
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
review:
Approve
(continuous-integration)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'debian/changelog' | |||
2 | --- debian/changelog 2013-08-14 02:04:35 +0000 | |||
3 | +++ debian/changelog 2013-09-16 18:00:39 +0000 | |||
4 | @@ -1,3 +1,10 @@ | |||
5 | 1 | autopilot-qt (1.4-0ubuntu1) saucy; urgency=low | ||
6 | 2 | |||
7 | 3 | [ Thomi Richards ] | ||
8 | 4 | * Update to new xpathselect 1.4 API. Version bump to 1.4. | ||
9 | 5 | |||
10 | 6 | -- Thomi Richards <thomi.richards@canonical.com> Thu, 15 Aug 2013 11:23:33 +1200 | ||
11 | 7 | |||
12 | 1 | autopilot-qt (1.3+13.10.20130814-0ubuntu1) saucy; urgency=low | 8 | autopilot-qt (1.3+13.10.20130814-0ubuntu1) saucy; urgency=low |
13 | 2 | 9 | ||
14 | 3 | [ Robert Bruce Park ] | 10 | [ Robert Bruce Park ] |
15 | 4 | 11 | ||
16 | === modified file 'debian/control' | |||
17 | --- debian/control 2013-08-13 22:39:44 +0000 | |||
18 | +++ debian/control 2013-09-16 18:00:39 +0000 | |||
19 | @@ -6,7 +6,7 @@ | |||
20 | 6 | libgl1-mesa-dev, | 6 | libgl1-mesa-dev, |
21 | 7 | libgles2-mesa-dev, | 7 | libgles2-mesa-dev, |
22 | 8 | libqt4-dev, | 8 | libqt4-dev, |
24 | 9 | libxpathselect-dev (>= 1.3), | 9 | libxpathselect-dev (>= 1.4), |
25 | 10 | mesa-common-dev, | 10 | mesa-common-dev, |
26 | 11 | pkg-config, | 11 | pkg-config, |
27 | 12 | qt4-qmake, | 12 | qt4-qmake, |
28 | @@ -27,9 +27,11 @@ | |||
29 | 27 | Package: libautopilot-qt | 27 | Package: libautopilot-qt |
30 | 28 | Section: libs | 28 | Section: libs |
31 | 29 | Architecture: any | 29 | Architecture: any |
33 | 30 | Depends: libxpathselect1.3, | 30 | Depends: libxpathselect1.4, |
34 | 31 | ${misc:Depends}, | 31 | ${misc:Depends}, |
35 | 32 | ${shlibs:Depends}, | 32 | ${shlibs:Depends}, |
36 | 33 | Breaks: python-autopilot (<< 1.4), | ||
37 | 34 | python3-autopilot (<< 1.4) | ||
38 | 33 | Replaces: autopilot-qt, | 35 | Replaces: autopilot-qt, |
39 | 34 | autopilot-qt5, | 36 | autopilot-qt5, |
40 | 35 | Conflicts: autopilot-qt, | 37 | Conflicts: autopilot-qt, |
41 | 36 | 38 | ||
42 | === added file 'driver/autopilot_types.h' | |||
43 | --- driver/autopilot_types.h 1970-01-01 00:00:00 +0000 | |||
44 | +++ driver/autopilot_types.h 2013-09-16 18:00:39 +0000 | |||
45 | @@ -0,0 +1,26 @@ | |||
46 | 1 | /* | ||
47 | 2 | Copyright 2012 Canonical | ||
48 | 3 | |||
49 | 4 | This program is free software: you can redistribute it and/or modify it | ||
50 | 5 | under the terms of the GNU General Public License version 3, as published | ||
51 | 6 | by the Free Software Foundation. | ||
52 | 7 | */ | ||
53 | 8 | |||
54 | 9 | |||
55 | 10 | #ifndef AUTOPILOT_TYPES_H | ||
56 | 11 | #define AUTOPILOT_TYPES_H | ||
57 | 12 | |||
58 | 13 | /// IMPORTANT: THese constants are taken from the autopilot XPathSelect protocol document. | ||
59 | 14 | /// Only add options here if the support has been added for them in autopilot itself. | ||
60 | 15 | enum autopilot_type_id | ||
61 | 16 | { | ||
62 | 17 | TYPE_PLAIN = 0, | ||
63 | 18 | TYPE_RECT = 1, | ||
64 | 19 | TYPE_POINT = 2, | ||
65 | 20 | TYPE_SIZE = 3, | ||
66 | 21 | TYPE_COLOR = 4, | ||
67 | 22 | TYPE_DATETIME = 5, | ||
68 | 23 | TYPE_TIME = 6, | ||
69 | 24 | }; | ||
70 | 25 | |||
71 | 26 | #endif | ||
72 | 0 | 27 | ||
73 | === modified file 'driver/dbus_adaptor.cpp' | |||
74 | --- driver/dbus_adaptor.cpp 2013-05-07 23:46:49 +0000 | |||
75 | +++ driver/dbus_adaptor.cpp 2013-09-16 18:00:39 +0000 | |||
76 | @@ -51,7 +51,7 @@ | |||
77 | 51 | void AutopilotAdaptor::GetVersion(const QDBusMessage &message) | 51 | void AutopilotAdaptor::GetVersion(const QDBusMessage &message) |
78 | 52 | { | 52 | { |
79 | 53 | QDBusMessage reply = message.createReply(); | 53 | QDBusMessage reply = message.createReply(); |
81 | 54 | reply << QVariant(QString("1.3")); | 54 | reply << QVariant(QString("1.4")); |
82 | 55 | QDBusConnection::sessionBus().send(reply); | 55 | QDBusConnection::sessionBus().send(reply); |
83 | 56 | } | 56 | } |
84 | 57 | 57 | ||
85 | 58 | 58 | ||
86 | === modified file 'driver/dbus_object.cpp' | |||
87 | --- driver/dbus_object.cpp 2013-06-27 16:27:11 +0000 | |||
88 | +++ driver/dbus_object.cpp 2013-09-16 18:00:39 +0000 | |||
89 | @@ -294,10 +294,12 @@ | |||
90 | 294 | void DBusObject::ProcessQuery() | 294 | void DBusObject::ProcessQuery() |
91 | 295 | { | 295 | { |
92 | 296 | Query query = _queries.takeFirst(); | 296 | Query query = _queries.takeFirst(); |
94 | 297 | QList<QVariant> state = Introspect(query.first); | 297 | QList<NodeIntrospectionData> state = Introspect(query.first); |
95 | 298 | 298 | ||
96 | 299 | QDBusMessage msg = query.second; | 299 | QDBusMessage msg = query.second; |
98 | 300 | msg << QVariant(state); | 300 | QVariant var; |
99 | 301 | var.setValue(state); | ||
100 | 302 | msg << var; | ||
101 | 301 | 303 | ||
102 | 302 | QDBusConnection::sessionBus().send(msg); | 304 | QDBusConnection::sessionBus().send(msg); |
103 | 303 | } | 305 | } |
104 | 304 | 306 | ||
105 | === modified file 'driver/driver.pro' | |||
106 | --- driver/driver.pro 2013-01-17 20:20:44 +0000 | |||
107 | +++ driver/driver.pro 2013-09-16 18:00:39 +0000 | |||
108 | @@ -29,7 +29,8 @@ | |||
109 | 29 | rootnode.h \ | 29 | rootnode.h \ |
110 | 30 | qtnode.h \ | 30 | qtnode.h \ |
111 | 31 | introspection.h \ | 31 | introspection.h \ |
113 | 32 | dbus_adaptor_qt.h | 32 | dbus_adaptor_qt.h \ |
114 | 33 | autopilot_types.h | ||
115 | 33 | 34 | ||
116 | 34 | target.file = libtestability* | 35 | target.file = libtestability* |
117 | 35 | 36 | ||
118 | 36 | 37 | ||
119 | === modified file 'driver/introspection.cpp' | |||
120 | --- driver/introspection.cpp 2013-06-26 09:00:13 +0000 | |||
121 | +++ driver/introspection.cpp 2013-09-16 18:00:39 +0000 | |||
122 | @@ -37,6 +37,7 @@ | |||
123 | 37 | #include <QUrl> | 37 | #include <QUrl> |
124 | 38 | #include <QDateTime> | 38 | #include <QDateTime> |
125 | 39 | 39 | ||
126 | 40 | #include "autopilot_types.h" | ||
127 | 40 | #include "introspection.h" | 41 | #include "introspection.h" |
128 | 41 | #include "qtnode.h" | 42 | #include "qtnode.h" |
129 | 42 | #include "rootnode.h" | 43 | #include "rootnode.h" |
130 | @@ -47,13 +48,13 @@ | |||
131 | 47 | QStringList GetNodeChildNames(QObject* obj); | 48 | QStringList GetNodeChildNames(QObject* obj); |
132 | 48 | void AddCustomProperties(QObject* obj, QVariantMap& properties); | 49 | void AddCustomProperties(QObject* obj, QVariantMap& properties); |
133 | 49 | 50 | ||
135 | 50 | QList<QVariant> Introspect(QString const& query_string) | 51 | QList<NodeIntrospectionData> Introspect(QString const& query_string) |
136 | 51 | { | 52 | { |
138 | 52 | QList<QVariant> state; | 53 | QList<NodeIntrospectionData> state; |
139 | 53 | QList<QtNode::Ptr> node_list = GetNodesThatMatchQuery(query_string); | 54 | QList<QtNode::Ptr> node_list = GetNodesThatMatchQuery(query_string); |
140 | 54 | foreach (QtNode::Ptr obj, node_list) | 55 | foreach (QtNode::Ptr obj, node_list) |
141 | 55 | { | 56 | { |
143 | 56 | state.append(obj->IntrospectNode()); | 57 | state.append(obj->GetIntrospectionData()); |
144 | 57 | } | 58 | } |
145 | 58 | 59 | ||
146 | 59 | return state; | 60 | return state; |
147 | @@ -85,11 +86,11 @@ | |||
148 | 85 | #endif | 86 | #endif |
149 | 86 | QList<QtNode::Ptr> node_list; | 87 | QList<QtNode::Ptr> node_list; |
150 | 87 | 88 | ||
152 | 88 | xpathselect::NodeList list = xpathselect::SelectNodes(root, query_string.toStdString()); | 89 | xpathselect::NodeVector list = xpathselect::SelectNodes(root, query_string.toStdString()); |
153 | 89 | for (auto node : list) | 90 | for (auto node : list) |
154 | 90 | { | 91 | { |
155 | 91 | // node may be our root node wrapper *or* an ordinary qobject wrapper | 92 | // node may be our root node wrapper *or* an ordinary qobject wrapper |
157 | 92 | auto object_ptr = std::static_pointer_cast<QtNode>(node); | 93 | auto object_ptr = std::static_pointer_cast<const QtNode>(node); |
158 | 93 | if (object_ptr) | 94 | if (object_ptr) |
159 | 94 | { | 95 | { |
160 | 95 | node_list.append(object_ptr); | 96 | node_list.append(object_ptr); |
161 | @@ -154,7 +155,7 @@ | |||
162 | 154 | // add the 'Children' pseudo-property: | 155 | // add the 'Children' pseudo-property: |
163 | 155 | QStringList children = GetNodeChildNames(obj); | 156 | QStringList children = GetNodeChildNames(obj); |
164 | 156 | if (!children.empty()) | 157 | if (!children.empty()) |
166 | 157 | object_properties["Children"] = children; | 158 | object_properties["Children"] = PackProperty(children); |
167 | 158 | 159 | ||
168 | 159 | return object_properties; | 160 | return object_properties; |
169 | 160 | } | 161 | } |
170 | @@ -211,69 +212,105 @@ | |||
171 | 211 | case QVariant::StringList: | 212 | case QVariant::StringList: |
172 | 212 | case QVariant::Double: | 213 | case QVariant::Double: |
173 | 213 | { | 214 | { |
175 | 214 | return prop; | 215 | return QList<QVariant> { |
176 | 216 | QVariant(TYPE_PLAIN), | ||
177 | 217 | prop | ||
178 | 218 | }; | ||
179 | 215 | } | 219 | } |
180 | 216 | 220 | ||
181 | 217 | case QVariant::ByteArray: | 221 | case QVariant::ByteArray: |
182 | 218 | { | 222 | { |
184 | 219 | return QVariant(QString(qvariant_cast<QByteArray>(prop))); | 223 | return QList<QVariant> { |
185 | 224 | QVariant(TYPE_PLAIN), | ||
186 | 225 | QVariant(QString(qvariant_cast<QByteArray>(prop))) | ||
187 | 226 | }; | ||
188 | 220 | } | 227 | } |
189 | 221 | 228 | ||
190 | 222 | case QVariant::Point: | 229 | case QVariant::Point: |
191 | 223 | { | 230 | { |
192 | 224 | QPoint p = qvariant_cast<QPoint>(prop); | 231 | QPoint p = qvariant_cast<QPoint>(prop); |
195 | 225 | QList<QVariant> l = {QVariant(p.x()), QVariant(p.y())}; | 232 | return QList<QVariant> { |
196 | 226 | return QVariant(l); | 233 | QVariant(TYPE_POINT), |
197 | 234 | QVariant(p.x()), | ||
198 | 235 | QVariant(p.y()) | ||
199 | 236 | }; | ||
200 | 227 | } | 237 | } |
201 | 228 | 238 | ||
202 | 229 | case QVariant::Rect: | 239 | case QVariant::Rect: |
203 | 230 | { | 240 | { |
204 | 231 | QRect r = qvariant_cast<QRect>(prop); | 241 | QRect r = qvariant_cast<QRect>(prop); |
206 | 232 | QList<QVariant> l = { | 242 | return QList<QVariant> { |
207 | 243 | QVariant(TYPE_RECT), | ||
208 | 233 | QVariant(r.x()), | 244 | QVariant(r.x()), |
209 | 234 | QVariant(r.y()), | 245 | QVariant(r.y()), |
210 | 235 | QVariant(r.width()), | 246 | QVariant(r.width()), |
211 | 236 | QVariant(r.height()) }; | 247 | QVariant(r.height()) }; |
212 | 237 | return QVariant(l); | ||
213 | 238 | } | 248 | } |
214 | 239 | 249 | ||
215 | 240 | case QVariant::Size: | 250 | case QVariant::Size: |
216 | 241 | { | 251 | { |
217 | 242 | QSize s = qvariant_cast<QSize>(prop); | 252 | QSize s = qvariant_cast<QSize>(prop); |
220 | 243 | QList<QVariant> l = { QVariant(s.width()), QVariant(s.height()) }; | 253 | return QList<QVariant> { |
221 | 244 | return QVariant(l); | 254 | QVariant(TYPE_SIZE), |
222 | 255 | QVariant(s.width()), | ||
223 | 256 | QVariant(s.height()) | ||
224 | 257 | }; | ||
225 | 245 | } | 258 | } |
226 | 246 | 259 | ||
227 | 247 | case QVariant::Color: | 260 | case QVariant::Color: |
228 | 248 | { | 261 | { |
229 | 249 | QColor color = qvariant_cast<QColor>(prop).toRgb(); | 262 | QColor color = qvariant_cast<QColor>(prop).toRgb(); |
236 | 250 | QList<QVariant> l = { QVariant(color.red()), | 263 | return QList<QVariant> { |
237 | 251 | QVariant(color.green()), | 264 | QVariant(TYPE_COLOR), |
238 | 252 | QVariant(color.blue()), | 265 | QVariant(color.red()), |
239 | 253 | QVariant(color.alpha()) | 266 | QVariant(color.green()), |
240 | 254 | }; | 267 | QVariant(color.blue()), |
241 | 255 | return QVariant(l); | 268 | QVariant(color.alpha()) |
242 | 269 | }; | ||
243 | 256 | } | 270 | } |
244 | 257 | 271 | ||
245 | 258 | case QVariant::Url: | 272 | case QVariant::Url: |
246 | 259 | { | 273 | { |
248 | 260 | return QVariant(prop.toUrl().toString()); | 274 | return QList<QVariant> { |
249 | 275 | QVariant(TYPE_PLAIN), | ||
250 | 276 | QVariant(prop.toUrl().toString()) | ||
251 | 277 | }; | ||
252 | 261 | } | 278 | } |
253 | 262 | 279 | ||
254 | 263 | // Depending on the architecture, floating points might be of type QMetaType::Float instead of QVariant::Double | 280 | // Depending on the architecture, floating points might be of type QMetaType::Float instead of QVariant::Double |
255 | 264 | // QDBus however, can only carry QVariant types, so lets convert it to QVariant::Double | 281 | // QDBus however, can only carry QVariant types, so lets convert it to QVariant::Double |
256 | 265 | case QMetaType::Float: | 282 | case QMetaType::Float: |
257 | 266 | { | 283 | { |
259 | 267 | return QVariant(prop.toDouble()); | 284 | return QList<QVariant> { |
260 | 285 | QVariant(TYPE_PLAIN), | ||
261 | 286 | QVariant(prop.toDouble()) | ||
262 | 287 | }; | ||
263 | 268 | } | 288 | } |
264 | 289 | |||
265 | 269 | case QVariant::Date: | 290 | case QVariant::Date: |
266 | 270 | case QVariant::DateTime: | 291 | case QVariant::DateTime: |
268 | 271 | return QVariant(prop.toDateTime().toTime_t()); | 292 | { |
269 | 293 | return QList<QVariant> { | ||
270 | 294 | QVariant(TYPE_DATETIME), | ||
271 | 295 | QVariant(prop.toDateTime().toTime_t()) | ||
272 | 296 | }; | ||
273 | 297 | } | ||
274 | 298 | |||
275 | 272 | case QVariant::Time: | 299 | case QVariant::Time: |
277 | 273 | return QVariant(prop.toTime().toString("hh:mm:ss")); | 300 | { |
278 | 301 | QTime t = qvariant_cast<QTime>(prop); | ||
279 | 302 | return QList<QVariant> { | ||
280 | 303 | QVariant(TYPE_TIME), | ||
281 | 304 | QVariant(t.hour()), | ||
282 | 305 | QVariant(t.minute()), | ||
283 | 306 | QVariant(t.second()), | ||
284 | 307 | QVariant(t.msec()) | ||
285 | 308 | }; | ||
286 | 309 | } | ||
287 | 310 | |||
288 | 274 | default: | 311 | default: |
289 | 275 | { | 312 | { |
291 | 276 | return QVariant(); | 313 | return QVariant(); // unsupported type, will not be sent to the client. |
292 | 277 | } | 314 | } |
293 | 278 | } | 315 | } |
294 | 279 | } | 316 | } |
295 | 280 | 317 | ||
296 | === modified file 'driver/introspection.h' | |||
297 | --- driver/introspection.h 2012-08-23 22:01:03 +0000 | |||
298 | +++ driver/introspection.h 2013-09-16 18:00:39 +0000 | |||
299 | @@ -14,7 +14,7 @@ | |||
300 | 14 | #include <QVariantMap> | 14 | #include <QVariantMap> |
301 | 15 | 15 | ||
302 | 16 | /// Introspect 'obj' and return it's properties in a QVariantMap. | 16 | /// Introspect 'obj' and return it's properties in a QVariantMap. |
304 | 17 | QList<QVariant> Introspect(const QString& query_string); | 17 | QList<NodeIntrospectionData> Introspect(const QString& query_string); |
305 | 18 | 18 | ||
306 | 19 | /// Get a list of QtNode pointers that match the given query. | 19 | /// Get a list of QtNode pointers that match the given query. |
307 | 20 | QList<QtNode::Ptr> GetNodesThatMatchQuery(QString const& query_string); | 20 | QList<QtNode::Ptr> GetNodesThatMatchQuery(QString const& query_string); |
308 | 21 | 21 | ||
309 | === modified file 'driver/qtnode.cpp' | |||
310 | --- driver/qtnode.cpp 2013-06-19 10:56:21 +0000 | |||
311 | +++ driver/qtnode.cpp 2013-09-16 18:00:39 +0000 | |||
312 | @@ -15,41 +15,54 @@ | |||
313 | 15 | #include <QGraphicsScene> | 15 | #include <QGraphicsScene> |
314 | 16 | #include <QGraphicsObject> | 16 | #include <QGraphicsObject> |
315 | 17 | #endif | 17 | #endif |
316 | 18 | #include <QDBusArgument> | ||
317 | 19 | |||
318 | 20 | // Marshall the NodeIntrospectionData data into a D-Bus argument | ||
319 | 21 | QDBusArgument &operator<<(QDBusArgument &argument, const NodeIntrospectionData &node_data) | ||
320 | 22 | { | ||
321 | 23 | argument.beginStructure(); | ||
322 | 24 | argument << node_data.object_path << node_data.state; | ||
323 | 25 | argument.endStructure(); | ||
324 | 26 | return argument; | ||
325 | 27 | } | ||
326 | 28 | |||
327 | 29 | // Retrieve the NodeIntrospectionData data from the D-Bus argument | ||
328 | 30 | const QDBusArgument &operator>>(const QDBusArgument &argument, NodeIntrospectionData &node_data) | ||
329 | 31 | { | ||
330 | 32 | argument.beginStructure(); | ||
331 | 33 | argument >> node_data.object_path >> node_data.state; | ||
332 | 34 | argument.endStructure(); | ||
333 | 35 | return argument; | ||
334 | 36 | } | ||
335 | 18 | 37 | ||
336 | 19 | const QByteArray AP_ID_NAME("_autopilot_id"); | 38 | const QByteArray AP_ID_NAME("_autopilot_id"); |
337 | 20 | 39 | ||
340 | 21 | QtNode::QtNode(QObject *obj, std::string const& parent_path) | 40 | QtNode::QtNode(QObject *obj, QtNode::Ptr parent) |
341 | 22 | : object_(obj) | 41 | : object_(obj) |
342 | 42 | , parent_(parent) | ||
343 | 23 | { | 43 | { |
344 | 44 | std::string parent_path = parent ? parent->GetPath() : ""; | ||
345 | 24 | full_path_ = parent_path + "/" + GetName(); | 45 | full_path_ = parent_path + "/" + GetName(); |
346 | 25 | } | 46 | } |
347 | 26 | 47 | ||
348 | 48 | QtNode::QtNode(QObject* obj) | ||
349 | 49 | : object_(obj) | ||
350 | 50 | { | ||
351 | 51 | full_path_ = "/" + GetName(); | ||
352 | 52 | } | ||
353 | 53 | |||
354 | 27 | QObject* QtNode::getWrappedObject() const | 54 | QObject* QtNode::getWrappedObject() const |
355 | 28 | { | 55 | { |
356 | 29 | return object_; | 56 | return object_; |
357 | 30 | } | 57 | } |
358 | 31 | 58 | ||
380 | 32 | QVariant QtNode::IntrospectNode() const | 59 | NodeIntrospectionData QtNode::GetIntrospectionData() const |
381 | 33 | { | 60 | { |
382 | 34 | // return must be (name, state_map) | 61 | NodeIntrospectionData data; |
383 | 35 | QString object_name = QString::fromStdString(GetPath()); | 62 | data.object_path = QString::fromStdString(GetPath()); |
384 | 36 | QVariantMap object_properties = GetNodeProperties(object_); | 63 | data.state = GetNodeProperties(object_); |
385 | 37 | object_properties["id"] = GetObjectId(); | 64 | data.state["id"] = PackProperty(GetId()); |
386 | 38 | QList<QVariant> object_tuple = { QVariant(object_name), QVariant(object_properties) }; | 65 | return data; |
366 | 39 | return QVariant(object_tuple); | ||
367 | 40 | } | ||
368 | 41 | |||
369 | 42 | qint64 QtNode::GetObjectId() const | ||
370 | 43 | { | ||
371 | 44 | // Note: This starts at 1 for a reason - 1 is reserved for the pseudo root node, and | ||
372 | 45 | // so must never be allocated to a regular object. | ||
373 | 46 | static qint64 next_id=1; | ||
374 | 47 | |||
375 | 48 | QList<QByteArray> property_names = object_->dynamicPropertyNames(); | ||
376 | 49 | if (!property_names.contains(AP_ID_NAME)) | ||
377 | 50 | object_->setProperty(AP_ID_NAME, QVariant(++next_id)); | ||
378 | 51 | return object_->property(AP_ID_NAME).toLongLong(); | ||
379 | 52 | |||
387 | 53 | } | 66 | } |
388 | 54 | 67 | ||
389 | 55 | std::string QtNode::GetName() const | 68 | std::string QtNode::GetName() const |
390 | @@ -68,30 +81,86 @@ | |||
391 | 68 | return full_path_; | 81 | return full_path_; |
392 | 69 | } | 82 | } |
393 | 70 | 83 | ||
395 | 71 | bool QtNode::MatchProperty(const std::string& name, const std::string& value) const | 84 | int32_t QtNode::GetId() const |
396 | 85 | { | ||
397 | 86 | // Note: This method is used to assign ids to both the root node (with a QApplication object) and | ||
398 | 87 | // child nodes. This used to be separate code, but now that we export QApplication properties, | ||
399 | 88 | // we can use this one method everywhere. | ||
400 | 89 | static int32_t next_id=0; | ||
401 | 90 | |||
402 | 91 | QList<QByteArray> property_names = object_->dynamicPropertyNames(); | ||
403 | 92 | if (!property_names.contains(AP_ID_NAME)) | ||
404 | 93 | { | ||
405 | 94 | int32_t new_id = ++next_id; | ||
406 | 95 | object_->setProperty(AP_ID_NAME, QVariant(new_id)); | ||
407 | 96 | } | ||
408 | 97 | return qvariant_cast<int32_t>(object_->property(AP_ID_NAME)); | ||
409 | 98 | } | ||
410 | 99 | |||
411 | 100 | bool QtNode::MatchStringProperty(const std::string& name, const std::string& value) const | ||
412 | 101 | { | ||
413 | 102 | QVariantMap properties = GetNodeProperties(object_); | ||
414 | 103 | |||
415 | 104 | QString qname = QString::fromStdString(name); | ||
416 | 105 | if (! properties.contains(qname)) | ||
417 | 106 | return false; | ||
418 | 107 | |||
419 | 108 | QVariant object_value = qvariant_cast<QVariantList>(properties[qname]).at(1); | ||
420 | 109 | QVariant check_value(QString::fromStdString(value)); | ||
421 | 110 | if (check_value.canConvert(object_value.type())) | ||
422 | 111 | { | ||
423 | 112 | check_value.convert(object_value.type()); | ||
424 | 113 | return check_value == object_value; | ||
425 | 114 | } | ||
426 | 115 | |||
427 | 116 | return false; | ||
428 | 117 | } | ||
429 | 118 | |||
430 | 119 | bool QtNode::MatchIntegerProperty(const std::string& name, int32_t value) const | ||
431 | 72 | { | 120 | { |
432 | 73 | if (name == "id") | 121 | if (name == "id") |
454 | 74 | return QString::fromStdString(value).toLongLong() == GetObjectId(); | 122 | return value == GetId(); |
455 | 75 | QVariantMap properties = GetNodeProperties(object_); | 123 | |
456 | 76 | 124 | QVariantMap properties = GetNodeProperties(object_); | |
457 | 77 | QString qname = QString::fromStdString(name); | 125 | |
458 | 78 | if (! properties.contains(qname)) | 126 | QString qname = QString::fromStdString(name); |
459 | 79 | return false; | 127 | if (! properties.contains(qname)) |
460 | 80 | 128 | return false; | |
461 | 81 | QVariant object_value = properties[qname]; | 129 | |
462 | 82 | QVariant check_value(QString::fromStdString(value)); | 130 | QVariant object_value = qvariant_cast<QVariantList>(properties[qname]).at(1); |
463 | 83 | if (check_value.canConvert(object_value.type())) | 131 | QVariant check_value(value); |
464 | 84 | { | 132 | if (check_value.canConvert(object_value.type())) |
465 | 85 | check_value.convert(object_value.type()); | 133 | { |
466 | 86 | return check_value == object_value; | 134 | check_value.convert(object_value.type()); |
467 | 87 | } | 135 | return check_value == object_value; |
468 | 88 | 136 | } | |
469 | 89 | return false; | 137 | |
470 | 90 | } | 138 | return false; |
471 | 91 | 139 | } | |
472 | 92 | xpathselect::NodeList QtNode::Children() const | 140 | |
473 | 93 | { | 141 | bool QtNode::MatchBooleanProperty(const std::string& name, bool value) const |
474 | 94 | xpathselect::NodeList children; | 142 | { |
475 | 143 | QVariantMap properties = GetNodeProperties(object_); | ||
476 | 144 | |||
477 | 145 | QString qname = QString::fromStdString(name); | ||
478 | 146 | if (! properties.contains(qname)) | ||
479 | 147 | return false; | ||
480 | 148 | |||
481 | 149 | QVariant object_value = qvariant_cast<QVariantList>(properties[qname]).at(1); | ||
482 | 150 | QVariant check_value(value); | ||
483 | 151 | |||
484 | 152 | if (check_value.canConvert(object_value.type())) | ||
485 | 153 | { | ||
486 | 154 | check_value.convert(object_value.type()); | ||
487 | 155 | return check_value == object_value; | ||
488 | 156 | } | ||
489 | 157 | |||
490 | 158 | return false; | ||
491 | 159 | } | ||
492 | 160 | |||
493 | 161 | xpathselect::NodeVector QtNode::Children() const | ||
494 | 162 | { | ||
495 | 163 | xpathselect::NodeVector children; | ||
496 | 95 | 164 | ||
497 | 96 | #ifdef QT5_SUPPORT | 165 | #ifdef QT5_SUPPORT |
498 | 97 | // Qt5's hierarchy for QML has changed a bit: | 166 | // Qt5's hierarchy for QML has changed a bit: |
499 | @@ -101,21 +170,21 @@ | |||
500 | 101 | 170 | ||
501 | 102 | QQuickView *view = qobject_cast<QQuickView*>(object_); | 171 | QQuickView *view = qobject_cast<QQuickView*>(object_); |
502 | 103 | if (view && view->rootObject() != 0) { | 172 | if (view && view->rootObject() != 0) { |
504 | 104 | children.push_back(std::make_shared<QtNode>(view->rootObject(), GetPath())); | 173 | children.push_back(std::make_shared<QtNode>(view->rootObject(), shared_from_this())); |
505 | 105 | } | 174 | } |
506 | 106 | 175 | ||
507 | 107 | QQuickItem* item = qobject_cast<QQuickItem*>(object_); | 176 | QQuickItem* item = qobject_cast<QQuickItem*>(object_); |
508 | 108 | if (item) { | 177 | if (item) { |
509 | 109 | foreach (QQuickItem *childItem, item->childItems()) { | 178 | foreach (QQuickItem *childItem, item->childItems()) { |
510 | 110 | if (childItem->parentItem() == item) { | 179 | if (childItem->parentItem() == item) { |
512 | 111 | children.push_back(std::make_shared<QtNode>(childItem, GetPath())); | 180 | children.push_back(std::make_shared<QtNode>(childItem, shared_from_this())); |
513 | 112 | } | 181 | } |
514 | 113 | } | 182 | } |
515 | 114 | } else { | 183 | } else { |
516 | 115 | foreach (QObject *child, object_->children()) | 184 | foreach (QObject *child, object_->children()) |
517 | 116 | { | 185 | { |
518 | 117 | if (child->parent() == object_) | 186 | if (child->parent() == object_) |
520 | 118 | children.push_back(std::make_shared<QtNode>(child, GetPath())); | 187 | children.push_back(std::make_shared<QtNode>(child, shared_from_this())); |
521 | 119 | } | 188 | } |
522 | 120 | } | 189 | } |
523 | 121 | 190 | ||
524 | @@ -123,7 +192,7 @@ | |||
525 | 123 | foreach (QObject *child, object_->children()) | 192 | foreach (QObject *child, object_->children()) |
526 | 124 | { | 193 | { |
527 | 125 | if (child->parent() == object_) | 194 | if (child->parent() == object_) |
529 | 126 | children.push_back(std::make_shared<QtNode>(child, GetPath())); | 195 | children.push_back(std::make_shared<QtNode>(child, shared_from_this())); |
530 | 127 | } | 196 | } |
531 | 128 | 197 | ||
532 | 129 | // If our wrapped object is a QGraphicsScene, we need to explicitly grab any child graphics | 198 | // If our wrapped object is a QGraphicsScene, we need to explicitly grab any child graphics |
533 | @@ -137,10 +206,16 @@ | |||
534 | 137 | { | 206 | { |
535 | 138 | QGraphicsObject *obj = item->toGraphicsObject(); | 207 | QGraphicsObject *obj = item->toGraphicsObject(); |
536 | 139 | if (obj && ! obj->parent()) | 208 | if (obj && ! obj->parent()) |
538 | 140 | children.push_back(std::make_shared<QtNode>(obj, GetPath())); | 209 | children.push_back(std::make_shared<QtNode>(obj, shared_from_this())); |
539 | 141 | } | 210 | } |
540 | 142 | } | 211 | } |
541 | 143 | #endif | 212 | #endif |
542 | 144 | 213 | ||
543 | 145 | return children; | 214 | return children; |
544 | 146 | } | 215 | } |
545 | 216 | |||
546 | 217 | |||
547 | 218 | xpathselect::Node::Ptr QtNode::GetParent() const | ||
548 | 219 | { | ||
549 | 220 | return parent_; | ||
550 | 221 | } | ||
551 | 147 | 222 | ||
552 | === modified file 'driver/qtnode.h' | |||
553 | --- driver/qtnode.h 2013-04-18 03:44:31 +0000 | |||
554 | +++ driver/qtnode.h 2013-09-16 18:00:39 +0000 | |||
555 | @@ -1,33 +1,54 @@ | |||
556 | 1 | #ifndef QTNODE_H | 1 | #ifndef QTNODE_H |
557 | 2 | #define QTNODE_H | 2 | #define QTNODE_H |
558 | 3 | 3 | ||
559 | 4 | #include <cstdint> | ||
560 | 4 | #include <QVariant> | 5 | #include <QVariant> |
561 | 6 | #include <QDBusArgument> | ||
562 | 5 | #include <xpathselect/node.h> | 7 | #include <xpathselect/node.h> |
563 | 6 | 8 | ||
564 | 9 | /// A simple data structure representing the state of a single node: | ||
565 | 10 | struct NodeIntrospectionData | ||
566 | 11 | { | ||
567 | 12 | QString object_path; | ||
568 | 13 | QVariantMap state; | ||
569 | 14 | }; | ||
570 | 15 | |||
571 | 16 | Q_DECLARE_METATYPE(NodeIntrospectionData); | ||
572 | 17 | Q_DECLARE_METATYPE(QList<NodeIntrospectionData>); | ||
573 | 18 | |||
574 | 19 | QDBusArgument &operator<<(QDBusArgument &argument, const NodeIntrospectionData &node_data); | ||
575 | 20 | const QDBusArgument &operator>>(const QDBusArgument &argument, NodeIntrospectionData &node_data); | ||
576 | 21 | |||
577 | 7 | /// Base class for all Qt-based object nodes. | 22 | /// Base class for all Qt-based object nodes. |
578 | 8 | /// | 23 | /// |
579 | 9 | /// QtNode wraps a single QObject pointer. It derives from xpathselect::Node and, | 24 | /// QtNode wraps a single QObject pointer. It derives from xpathselect::Node and, |
580 | 10 | /// like that class, is designed to be allocated on the heap and stored in a | 25 | /// like that class, is designed to be allocated on the heap and stored in a |
581 | 11 | /// std::shared_ptr. | 26 | /// std::shared_ptr. |
583 | 12 | class QtNode: public xpathselect::Node | 27 | class QtNode: public xpathselect::Node, public std::enable_shared_from_this<QtNode> |
584 | 13 | { | 28 | { |
585 | 14 | public: | 29 | public: |
587 | 15 | typedef std::shared_ptr<QtNode> Ptr; | 30 | typedef std::shared_ptr<const QtNode> Ptr; |
588 | 16 | 31 | ||
590 | 17 | QtNode(QObject* object, std::string const& parent_path); | 32 | QtNode(QObject* object, Ptr parent); |
591 | 33 | explicit QtNode(QObject* object); | ||
592 | 18 | 34 | ||
593 | 19 | QObject* getWrappedObject() const; | 35 | QObject* getWrappedObject() const; |
597 | 20 | 36 | xpathselect::Node::Ptr GetParent() const; | |
598 | 21 | virtual QVariant IntrospectNode() const; | 37 | |
599 | 22 | virtual qint64 GetObjectId() const; | 38 | virtual NodeIntrospectionData GetIntrospectionData() const; |
600 | 39 | |||
601 | 23 | 40 | ||
602 | 24 | virtual std::string GetName() const; | 41 | virtual std::string GetName() const; |
603 | 25 | virtual std::string GetPath() const; | 42 | virtual std::string GetPath() const; |
606 | 26 | virtual bool MatchProperty(const std::string& name, const std::string& value) const; | 43 | virtual int32_t GetId() const; |
607 | 27 | virtual xpathselect::NodeList Children() const; | 44 | virtual bool MatchStringProperty(const std::string& name, const std::string& value) const; |
608 | 45 | virtual bool MatchIntegerProperty(const std::string& name, int32_t value) const; | ||
609 | 46 | virtual bool MatchBooleanProperty(const std::string& name, bool value) const; | ||
610 | 47 | virtual xpathselect::NodeVector Children() const; | ||
611 | 28 | private: | 48 | private: |
612 | 29 | QObject *object_; | 49 | QObject *object_; |
613 | 30 | std::string full_path_; | 50 | std::string full_path_; |
614 | 51 | Ptr parent_; | ||
615 | 31 | }; | 52 | }; |
616 | 32 | 53 | ||
617 | 33 | #endif // QTNODE_H | 54 | #endif // QTNODE_H |
618 | 34 | 55 | ||
619 | === modified file 'driver/qttestability.cpp' | |||
620 | --- driver/qttestability.cpp 2012-12-19 12:27:25 +0000 | |||
621 | +++ driver/qttestability.cpp 2013-09-16 18:00:39 +0000 | |||
622 | @@ -10,6 +10,7 @@ | |||
623 | 10 | #include "dbus_adaptor.h" | 10 | #include "dbus_adaptor.h" |
624 | 11 | #include "dbus_adaptor_qt.h" | 11 | #include "dbus_adaptor_qt.h" |
625 | 12 | #include "dbus_object.h" | 12 | #include "dbus_object.h" |
626 | 13 | #include "qtnode.h" | ||
627 | 13 | 14 | ||
628 | 14 | #include <QCoreApplication> | 15 | #include <QCoreApplication> |
629 | 15 | #include <QDebug> | 16 | #include <QDebug> |
630 | @@ -20,6 +21,8 @@ | |||
631 | 20 | void qt_testability_init(void) | 21 | void qt_testability_init(void) |
632 | 21 | { | 22 | { |
633 | 22 | qDebug() << "Loading testability driver."; | 23 | qDebug() << "Loading testability driver."; |
634 | 24 | qDBusRegisterMetaType<NodeIntrospectionData>(); | ||
635 | 25 | qDBusRegisterMetaType<QList<NodeIntrospectionData> >(); | ||
636 | 23 | 26 | ||
637 | 24 | DBusObject* obj = new DBusObject; | 27 | DBusObject* obj = new DBusObject; |
638 | 25 | new AutopilotAdaptor(obj); | 28 | new AutopilotAdaptor(obj); |
639 | 26 | 29 | ||
640 | === modified file 'driver/rootnode.cpp' | |||
641 | --- driver/rootnode.cpp 2013-04-18 04:46:44 +0000 | |||
642 | +++ driver/rootnode.cpp 2013-09-16 18:00:39 +0000 | |||
643 | @@ -1,35 +1,32 @@ | |||
644 | 1 | #include "rootnode.h" | 1 | #include "rootnode.h" |
645 | 2 | #include "introspection.h" | ||
646 | 2 | 3 | ||
647 | 3 | #include <QObject> | 4 | #include <QObject> |
648 | 4 | #include <QCoreApplication> | 5 | #include <QCoreApplication> |
649 | 5 | #include <QStringList> | 6 | #include <QStringList> |
650 | 7 | #include <QDebug> | ||
651 | 6 | 8 | ||
652 | 7 | RootNode::RootNode(QCoreApplication* application) | 9 | RootNode::RootNode(QCoreApplication* application) |
654 | 8 | : QtNode(application, std::string()) | 10 | : QtNode(application) |
655 | 9 | , application_(application) | 11 | , application_(application) |
656 | 10 | { | 12 | { |
657 | 11 | } | 13 | } |
658 | 12 | 14 | ||
660 | 13 | QVariant RootNode::IntrospectNode() const | 15 | |
661 | 16 | NodeIntrospectionData RootNode::GetIntrospectionData() const | ||
662 | 14 | { | 17 | { |
665 | 15 | // return must be (name, state_map) | 18 | NodeIntrospectionData data; |
666 | 16 | QString object_name = QString::fromStdString(GetPath()); | 19 | data.object_path = QString::fromStdString(GetPath()); |
667 | 20 | data.state = GetNodeProperties(application_); | ||
668 | 17 | QStringList child_names; | 21 | QStringList child_names; |
669 | 18 | foreach(QObject* child, children_) | 22 | foreach(QObject* child, children_) |
670 | 19 | { | 23 | { |
671 | 20 | child_names.append(child->metaObject()->className()); | 24 | child_names.append(child->metaObject()->className()); |
672 | 21 | } | 25 | } |
673 | 22 | 26 | ||
684 | 23 | QVariantMap object_properties; | 27 | data.state["Children"] = PackProperty(child_names); |
685 | 24 | object_properties["Children"] = child_names; | 28 | data.state["id"] = PackProperty(GetId()); |
686 | 25 | object_properties["id"] = GetObjectId(); | 29 | return data; |
677 | 26 | QList<QVariant> object_tuple = { QVariant(object_name), QVariant(object_properties) }; | ||
678 | 27 | return QVariant(object_tuple); | ||
679 | 28 | } | ||
680 | 29 | |||
681 | 30 | qint64 RootNode::GetObjectId() const | ||
682 | 31 | { | ||
683 | 32 | return 1; | ||
687 | 33 | } | 30 | } |
688 | 34 | 31 | ||
689 | 35 | void RootNode::AddChild(QObject* child) | 32 | void RootNode::AddChild(QObject* child) |
690 | @@ -48,18 +45,10 @@ | |||
691 | 48 | return "/" + GetName(); | 45 | return "/" + GetName(); |
692 | 49 | } | 46 | } |
693 | 50 | 47 | ||
705 | 51 | bool RootNode::MatchProperty(const std::string& name, const std::string& value) const | 48 | xpathselect::NodeVector RootNode::Children() const |
706 | 52 | { | 49 | { |
707 | 53 | if (name == "id") | 50 | xpathselect::NodeVector children; |
697 | 54 | return QString::fromStdString(value).toLongLong() == GetObjectId(); | ||
698 | 55 | |||
699 | 56 | return false; | ||
700 | 57 | } | ||
701 | 58 | |||
702 | 59 | xpathselect::NodeList RootNode::Children() const | ||
703 | 60 | { | ||
704 | 61 | xpathselect::NodeList children; | ||
708 | 62 | foreach(QObject* child, children_) | 51 | foreach(QObject* child, children_) |
710 | 63 | children.push_back(std::make_shared<QtNode>(child, GetPath())); | 52 | children.push_back(std::make_shared<QtNode>(child, shared_from_this())); |
711 | 64 | return children; | 53 | return children; |
712 | 65 | } | 54 | } |
713 | 66 | 55 | ||
714 | === modified file 'driver/rootnode.h' | |||
715 | --- driver/rootnode.h 2013-04-18 04:46:44 +0000 | |||
716 | +++ driver/rootnode.h 2013-09-16 18:00:39 +0000 | |||
717 | @@ -13,15 +13,13 @@ | |||
718 | 13 | public: | 13 | public: |
719 | 14 | RootNode(QCoreApplication* application); | 14 | RootNode(QCoreApplication* application); |
720 | 15 | 15 | ||
723 | 16 | virtual QVariant IntrospectNode() const; | 16 | virtual NodeIntrospectionData GetIntrospectionData() const; |
722 | 17 | virtual qint64 GetObjectId() const; | ||
724 | 18 | 17 | ||
725 | 19 | void AddChild(QObject* child); | 18 | void AddChild(QObject* child); |
726 | 20 | 19 | ||
727 | 21 | virtual std::string GetName() const; | 20 | virtual std::string GetName() const; |
728 | 22 | virtual std::string GetPath() const; | 21 | virtual std::string GetPath() const; |
731 | 23 | virtual bool MatchProperty(const std::string& name, const std::string& value) const; | 22 | virtual xpathselect::NodeVector Children() const; |
730 | 24 | virtual xpathselect::NodeList Children() const; | ||
732 | 25 | private: | 23 | private: |
733 | 26 | QCoreApplication* application_; | 24 | QCoreApplication* application_; |
734 | 27 | QList<QObject*> children_; | 25 | QList<QObject*> children_; |
735 | 28 | 26 | ||
736 | === modified file 'tests/unittests/tst_introspection.cpp' | |||
737 | --- tests/unittests/tst_introspection.cpp 2013-06-26 09:00:13 +0000 | |||
738 | +++ tests/unittests/tst_introspection.cpp 2013-09-16 18:00:39 +0000 | |||
739 | @@ -26,6 +26,7 @@ | |||
740 | 26 | #include <QPushButton> | 26 | #include <QPushButton> |
741 | 27 | 27 | ||
742 | 28 | #include "introspection.h" | 28 | #include "introspection.h" |
743 | 29 | #include "qtnode.h" | ||
744 | 29 | 30 | ||
745 | 30 | QVariant IntrospectNode(QObject* obj); | 31 | QVariant IntrospectNode(QObject* obj); |
746 | 31 | 32 | ||
747 | @@ -46,6 +47,8 @@ | |||
748 | 46 | void test_properties_data(); | 47 | void test_properties_data(); |
749 | 47 | void test_properties(); | 48 | void test_properties(); |
750 | 48 | 49 | ||
751 | 50 | void test_property_matching(); | ||
752 | 51 | |||
753 | 49 | private: | 52 | private: |
754 | 50 | QMainWindow *m_object; | 53 | QMainWindow *m_object; |
755 | 51 | }; | 54 | }; |
756 | @@ -74,6 +77,7 @@ | |||
757 | 74 | 77 | ||
758 | 75 | m_object->setObjectName("testWindow"); | 78 | m_object->setObjectName("testWindow"); |
759 | 76 | m_object->setProperty("dynamicTestProperty", "testValue"); | 79 | m_object->setProperty("dynamicTestProperty", "testValue"); |
760 | 80 | m_object->setProperty("dynamicStringProperty", QString("testValue")); | ||
761 | 77 | m_object->setProperty("myUInt", QVariant(quint8(5))); | 81 | m_object->setProperty("myUInt", QVariant(quint8(5))); |
762 | 78 | m_object->setProperty("myStringList", QVariant(QStringList() << "string1" << "string2" << "string3")); | 82 | m_object->setProperty("myStringList", QVariant(QStringList() << "string1" << "string2" << "string3")); |
763 | 79 | m_object->setProperty("myColor", QColor("red")); | 83 | m_object->setProperty("myColor", QColor("red")); |
764 | @@ -113,25 +117,155 @@ | |||
765 | 113 | QTest::addColumn<QVariant>("firstResultPropertyValue"); | 117 | QTest::addColumn<QVariant>("firstResultPropertyValue"); |
766 | 114 | 118 | ||
767 | 115 | #ifdef QT5_SUPPORT | 119 | #ifdef QT5_SUPPORT |
771 | 116 | QTest::newRow("/") << "/" << 1 << "/tst_introspection" << "Children" << QVariant(QStringList() << "QMainWindow" << "QWidgetWindow"); | 120 | QTest::newRow("/") |
772 | 117 | QTest::newRow("//QWidget[id=6]") << "//QWidget[id=6]" << 1 << "/tst_introspection/QMainWindow/QWidget" << "objectName" << QVariant("centralTestWidget"); | 121 | << "/" |
773 | 118 | QTest::newRow("//QPushButton[id=9]") << "//QPushButton[id=9]" << 1 << "/tst_introspection/QMainWindow/QWidget/QPushButton" << "objectName" << QVariant("myButton2"); | 122 | << 1 |
774 | 123 | << "/tst_introspection" | ||
775 | 124 | << "Children" | ||
776 | 125 | << QVariant( | ||
777 | 126 | QVariantList() | ||
778 | 127 | << 0 | ||
779 | 128 | << QVariant( | ||
780 | 129 | QStringList() | ||
781 | 130 | << "QMainWindow" | ||
782 | 131 | << "QWidgetWindow" | ||
783 | 132 | ) | ||
784 | 133 | ); | ||
785 | 134 | |||
786 | 135 | QTest::newRow("//QWidget[id=6]") | ||
787 | 136 | << "//QWidget[id=6]" | ||
788 | 137 | << 1 | ||
789 | 138 | << "/tst_introspection/QMainWindow/QWidget" | ||
790 | 139 | << "objectName" | ||
791 | 140 | << QVariant( | ||
792 | 141 | QVariantList() | ||
793 | 142 | << 0 | ||
794 | 143 | << "centralTestWidget" | ||
795 | 144 | ); | ||
796 | 145 | |||
797 | 146 | QTest::newRow("//QPushButton[id=9]") | ||
798 | 147 | << "//QPushButton[id=9]" | ||
799 | 148 | << 1 | ||
800 | 149 | << "/tst_introspection/QMainWindow/QWidget/QPushButton" | ||
801 | 150 | << "objectName" | ||
802 | 151 | << QVariant( | ||
803 | 152 | QVariantList() | ||
804 | 153 | << 0 | ||
805 | 154 | << "myButton2" | ||
806 | 155 | ); | ||
807 | 119 | #else | 156 | #else |
810 | 120 | QTest::newRow("/") << "/" << 1 << "/tst_introspection" << "Children" << QVariant(QStringList() << "QMainWindow"); | 157 | QTest::newRow("/") |
811 | 121 | QTest::newRow("//QWidget[id=5]") << "//QWidget[id=5]" << 1 << "/tst_introspection/QMainWindow/QWidget" << "objectName" << QVariant("centralTestWidget"); | 158 | << "/" |
812 | 159 | << 1 | ||
813 | 160 | << "/tst_introspection" | ||
814 | 161 | << "Children" | ||
815 | 162 | << QVariant( | ||
816 | 163 | QVariantList() | ||
817 | 164 | << 0 | ||
818 | 165 | << "QMainWindow" | ||
819 | 166 | ); | ||
820 | 167 | |||
821 | 168 | QTest::newRow("//QWidget[id=5]") | ||
822 | 169 | << "//QWidget[id=5]" | ||
823 | 170 | << 1 | ||
824 | 171 | << "/tst_introspection/QMainWindow/QWidget" | ||
825 | 172 | << "objectName" | ||
826 | 173 | << QVariant( | ||
827 | 174 | QVariantList() | ||
828 | 175 | << 0 | ||
829 | 176 | << "centralTestWidget" | ||
830 | 177 | ); | ||
831 | 122 | 178 | ||
832 | 123 | // Depending on the environment, Qt4 could add a second QWidget at position 6. That moves other items down by one. | 179 | // Depending on the environment, Qt4 could add a second QWidget at position 6. That moves other items down by one. |
837 | 124 | if (Introspect("//QWidget[id=6]").count() > 0) { | 180 | if (Introspect("//QWidget[id=6]").count() > 0) |
838 | 125 | QTest::newRow("//QPushButton[id=9]") << "//QPushButton[id=9]" << 1 << "/tst_introspection/QMainWindow/QWidget/QPushButton" << "objectName" << QVariant("myButton2"); | 181 | { |
839 | 126 | } else { | 182 | QTest::newRow("//QPushButton[id=9]") |
840 | 127 | QTest::newRow("//QPushButton[id=8]") << "//QPushButton[id=8]" << 1 << "/tst_introspection/QMainWindow/QWidget/QPushButton" << "objectName" << QVariant("myButton2"); | 183 | << "//QPushButton[id=9]" |
841 | 184 | << 1 | ||
842 | 185 | << "/tst_introspection/QMainWindow/QWidget/QPushButton" | ||
843 | 186 | << "objectName" | ||
844 | 187 | << QVariant( | ||
845 | 188 | QVariantList() | ||
846 | 189 | << 0 | ||
847 | 190 | << "myButton2" | ||
848 | 191 | ); | ||
849 | 192 | } | ||
850 | 193 | else | ||
851 | 194 | { | ||
852 | 195 | QTest::newRow("//QPushButton[id=8]") | ||
853 | 196 | << "//QPushButton[id=8]" | ||
854 | 197 | << 1 | ||
855 | 198 | << "/tst_introspection/QMainWindow/QWidget/QPushButton" | ||
856 | 199 | << "objectName" | ||
857 | 200 | << QVariant( | ||
858 | 201 | QVariantList() | ||
859 | 202 | << 0 | ||
860 | 203 | << "myButton2" | ||
861 | 204 | ); | ||
862 | 128 | } | 205 | } |
863 | 129 | #endif | 206 | #endif |
864 | 130 | 207 | ||
869 | 131 | QTest::newRow("//GridLayout") << "//QGridLayout" << 1 << "/tst_introspection/QMainWindow/QWidget/QGridLayout" << "objectName" << QVariant("myTestLayout"); | 208 | QTest::newRow("/tst_introspection/QMainWindow/QWidget/QGridLayout") |
870 | 132 | QTest::newRow("//QPushButton") << "//QPushButton" << 2 << "/tst_introspection/QMainWindow/QWidget/QPushButton" << "objectName" << QVariant("myButton1"); | 209 | << "//QGridLayout" |
871 | 133 | QTest::newRow("//QWidget/*") << "//QWidget/*" << 5 << "/tst_introspection/QMainWindow/QWidget/QGridLayout" << "objectName" << QVariant("myTestLayout"); | 210 | << 1 |
872 | 134 | QTest::newRow("broken query") << "broken query" << 0 << QString() << QString() << QVariant(); | 211 | << "/tst_introspection/QMainWindow/QWidget/QGridLayout" |
873 | 212 | << "objectName" | ||
874 | 213 | << QVariant( | ||
875 | 214 | QVariantList() | ||
876 | 215 | << 0 | ||
877 | 216 | << "myTestLayout" | ||
878 | 217 | ); | ||
879 | 218 | |||
880 | 219 | QTest::newRow("parent of leaf node") | ||
881 | 220 | << "/tst_introspection/QMainWindow/QWidget/QGridLayout/.." | ||
882 | 221 | << 1 | ||
883 | 222 | << "/tst_introspection/QMainWindow/QWidget" | ||
884 | 223 | << "objectName" | ||
885 | 224 | << QVariant( | ||
886 | 225 | QVariantList() | ||
887 | 226 | << 0 | ||
888 | 227 | << "centralTestWidget" | ||
889 | 228 | ); | ||
890 | 229 | |||
891 | 230 | QTest::newRow("parent of root node") | ||
892 | 231 | << "/tst_introspection/.." | ||
893 | 232 | << 1 | ||
894 | 233 | << "/tst_introspection" | ||
895 | 234 | << "id" | ||
896 | 235 | << QVariant( | ||
897 | 236 | QVariantList() | ||
898 | 237 | << 0 | ||
899 | 238 | << 1 | ||
900 | 239 | ); | ||
901 | 240 | |||
902 | 241 | QTest::newRow("//QPushButton") | ||
903 | 242 | << "//QPushButton" | ||
904 | 243 | << 2 | ||
905 | 244 | << "/tst_introspection/QMainWindow/QWidget/QPushButton" | ||
906 | 245 | << "objectName" | ||
907 | 246 | << QVariant( | ||
908 | 247 | QVariantList() | ||
909 | 248 | << 0 | ||
910 | 249 | << "myButton1" | ||
911 | 250 | ); | ||
912 | 251 | |||
913 | 252 | QTest::newRow("//QWidget/*") | ||
914 | 253 | << "//QWidget/*" | ||
915 | 254 | << 5 | ||
916 | 255 | << "/tst_introspection/QMainWindow/QWidget/QGridLayout" | ||
917 | 256 | << "objectName" | ||
918 | 257 | << QVariant( | ||
919 | 258 | QVariantList() | ||
920 | 259 | << 0 | ||
921 | 260 | << "myTestLayout" | ||
922 | 261 | ); | ||
923 | 262 | |||
924 | 263 | QTest::newRow("broken query") | ||
925 | 264 | << "broken query" | ||
926 | 265 | << 0 | ||
927 | 266 | << QString() | ||
928 | 267 | << QString() | ||
929 | 268 | << QVariant(); | ||
930 | 135 | } | 269 | } |
931 | 136 | 270 | ||
932 | 137 | void tst_Introspection::test_introspect() | 271 | void tst_Introspection::test_introspect() |
933 | @@ -142,16 +276,15 @@ | |||
934 | 142 | QFETCH(QString, firstResultPropertyName); | 276 | QFETCH(QString, firstResultPropertyName); |
935 | 143 | QFETCH(QVariant, firstResultPropertyValue); | 277 | QFETCH(QVariant, firstResultPropertyValue); |
936 | 144 | 278 | ||
938 | 145 | QList<QVariant> resultList = Introspect(xpath); | 279 | QList<NodeIntrospectionData> resultList = Introspect(xpath); |
939 | 146 | 280 | ||
940 | 147 | QCOMPARE(resultList.count(), resultCount); | 281 | QCOMPARE(resultList.count(), resultCount); |
941 | 148 | 282 | ||
942 | 149 | if (resultCount > 0) { | 283 | if (resultCount > 0) { |
945 | 150 | QVariant firstResult = resultList.first(); | 284 | NodeIntrospectionData first_object = resultList.first(); |
944 | 151 | QVariantMap firstResultProperties = firstResult.toList().last().toMap(); | ||
946 | 152 | 285 | ||
949 | 153 | QCOMPARE(firstResult.toList().first().toString(), firstResultType); | 286 | QCOMPARE(first_object.object_path, firstResultType); |
950 | 154 | QCOMPARE(firstResultProperties.value(firstResultPropertyName), firstResultPropertyValue); | 287 | QCOMPARE(first_object.state.value(firstResultPropertyName), firstResultPropertyValue); |
951 | 155 | } | 288 | } |
952 | 156 | } | 289 | } |
953 | 157 | 290 | ||
954 | @@ -171,9 +304,9 @@ | |||
955 | 171 | qApp->setApplicationName(app_name); | 304 | qApp->setApplicationName(app_name); |
956 | 172 | 305 | ||
957 | 173 | #ifdef QT5_SUPPORT | 306 | #ifdef QT5_SUPPORT |
959 | 174 | QList<QVariant> result = Introspect("//QWidgetWindow"); | 307 | QList<NodeIntrospectionData> result = Introspect("//QWidgetWindow"); |
960 | 175 | #else | 308 | #else |
962 | 176 | QList<QVariant> result = Introspect("//QMainWindow"); | 309 | QList<NodeIntrospectionData> result = Introspect("//QMainWindow"); |
963 | 177 | #endif | 310 | #endif |
964 | 178 | 311 | ||
965 | 179 | QVERIFY(!result.isEmpty()); | 312 | QVERIFY(!result.isEmpty()); |
966 | @@ -185,25 +318,169 @@ | |||
967 | 185 | QTest::addColumn<QVariant>("propertyValue"); | 318 | QTest::addColumn<QVariant>("propertyValue"); |
968 | 186 | QTest::addColumn<bool>("fuzzyCompare"); | 319 | QTest::addColumn<bool>("fuzzyCompare"); |
969 | 187 | 320 | ||
989 | 188 | QTest::newRow("static property") << "objectName" << QVariant(m_object->objectName()) << false; | 321 | QTest::newRow("static property") |
990 | 189 | QTest::newRow("dynamic property") << "dynamicTestProperty" << m_object->property("dynamicTestProperty") << false; | 322 | << "objectName" |
991 | 190 | 323 | << QVariant( | |
992 | 191 | QTest::newRow("int") << "width" << QVariant(m_object->width()) << false; | 324 | QVariantList() |
993 | 192 | QTest::newRow("uint") << "myUInt" << m_object->property("myUInt") << false; | 325 | << 0 |
994 | 193 | QTest::newRow("bool") << "visible" << QVariant(m_object->isVisible()) << false; | 326 | << m_object->objectName() |
995 | 194 | QTest::newRow("double") << "windowOpacity" << QVariant(m_object->windowOpacity()) << true; | 327 | ) |
996 | 195 | 328 | << false; | |
997 | 196 | QTest::newRow("QString") << "objectName" << QVariant(m_object->objectName()) << false; | 329 | |
998 | 197 | QTest::newRow("QStringList") << "myStringList" << m_object->property("myStringList") << false; | 330 | QTest::newRow("dynamic property") |
999 | 198 | QTest::newRow("QSize") << "maximumSize" << QVariant(QList<QVariant>() << m_object->maximumWidth() << m_object->maximumHeight()) << false; | 331 | << "dynamicTestProperty" |
1000 | 199 | QTest::newRow("QPoint") << "pos" << QVariant(QList<QVariant>() << m_object->x() << m_object->y()) << false; | 332 | << QVariant( |
1001 | 200 | QTest::newRow("QRect") << "geometry" << QVariant(QList<QVariant>() << m_object->geometry().x() << m_object->geometry().y() << m_object->geometry().width() << m_object->geometry().height()) << false; | 333 | QVariantList() |
1002 | 201 | QTest::newRow("QColor") << "myColor" << QVariant(QList<QVariant>() << 255 << 0 << 0 << 255) << false; | 334 | << 0 |
1003 | 202 | QTest::newRow("QByteArray") << "myByteArray" << m_object->property("myByteArray") << false; | 335 | << m_object->property("dynamicTestProperty") |
1004 | 203 | QTest::newRow("QUrl") << "myUrl" << m_object->property("myUrl") << false; | 336 | ) |
1005 | 204 | QTest::newRow("QDateTime") << "myDateTime" << QVariant(m_object->property("myDateTime").toDateTime().toTime_t()) << false; | 337 | << false; |
1006 | 205 | QTest::newRow("QDate") << "myDate" << QVariant(m_object->property("myDate").toDateTime().toTime_t()) << false; | 338 | |
1007 | 206 | QTest::newRow("QTime") << "myTime" << QVariant(m_object->property("myTime").toTime().toString("hh:mm:ss")) << false; | 339 | QTest::newRow("int") |
1008 | 340 | << "width" | ||
1009 | 341 | << QVariant( | ||
1010 | 342 | QVariantList() | ||
1011 | 343 | << 0 | ||
1012 | 344 | << m_object->width() | ||
1013 | 345 | ) | ||
1014 | 346 | << false; | ||
1015 | 347 | |||
1016 | 348 | QTest::newRow("uint") | ||
1017 | 349 | << "myUInt" | ||
1018 | 350 | << QVariant( | ||
1019 | 351 | QVariantList() | ||
1020 | 352 | << 0 | ||
1021 | 353 | << m_object->property("myUInt") | ||
1022 | 354 | ) | ||
1023 | 355 | << false; | ||
1024 | 356 | |||
1025 | 357 | QTest::newRow("bool") | ||
1026 | 358 | << "visible" | ||
1027 | 359 | << QVariant( | ||
1028 | 360 | QVariantList() | ||
1029 | 361 | << 0 | ||
1030 | 362 | << m_object->isVisible() | ||
1031 | 363 | ) | ||
1032 | 364 | << false; | ||
1033 | 365 | |||
1034 | 366 | QTest::newRow("double") | ||
1035 | 367 | << "windowOpacity" | ||
1036 | 368 | << QVariant( | ||
1037 | 369 | QVariantList() | ||
1038 | 370 | << 0 | ||
1039 | 371 | << m_object->windowOpacity() | ||
1040 | 372 | ) | ||
1041 | 373 | << true; | ||
1042 | 374 | |||
1043 | 375 | QTest::newRow("QString") | ||
1044 | 376 | << "objectName" | ||
1045 | 377 | << QVariant( | ||
1046 | 378 | QVariantList() | ||
1047 | 379 | << 0 | ||
1048 | 380 | << m_object->objectName() | ||
1049 | 381 | ) | ||
1050 | 382 | << false; | ||
1051 | 383 | |||
1052 | 384 | QTest::newRow("QStringList") | ||
1053 | 385 | << "myStringList" | ||
1054 | 386 | << QVariant( | ||
1055 | 387 | QVariantList() | ||
1056 | 388 | << 0 | ||
1057 | 389 | << m_object->property("myStringList") | ||
1058 | 390 | ) | ||
1059 | 391 | << false; | ||
1060 | 392 | |||
1061 | 393 | QTest::newRow("QSize") | ||
1062 | 394 | << "maximumSize" | ||
1063 | 395 | << QVariant( | ||
1064 | 396 | QVariantList() | ||
1065 | 397 | << 3 | ||
1066 | 398 | << m_object->maximumWidth() | ||
1067 | 399 | << m_object->maximumHeight() | ||
1068 | 400 | ) | ||
1069 | 401 | << false; | ||
1070 | 402 | |||
1071 | 403 | QTest::newRow("QPoint") | ||
1072 | 404 | << "pos" | ||
1073 | 405 | << QVariant( | ||
1074 | 406 | QVariantList() | ||
1075 | 407 | << 2 | ||
1076 | 408 | << m_object->x() | ||
1077 | 409 | << m_object->y() | ||
1078 | 410 | ) | ||
1079 | 411 | << false; | ||
1080 | 412 | |||
1081 | 413 | QTest::newRow("QRect") | ||
1082 | 414 | << "geometry" | ||
1083 | 415 | << QVariant( | ||
1084 | 416 | QVariantList() | ||
1085 | 417 | << 1 | ||
1086 | 418 | << m_object->geometry().x() | ||
1087 | 419 | << m_object->geometry().y() | ||
1088 | 420 | << m_object->geometry().width() | ||
1089 | 421 | << m_object->geometry().height() | ||
1090 | 422 | ) | ||
1091 | 423 | << false; | ||
1092 | 424 | |||
1093 | 425 | QTest::newRow("QColor") | ||
1094 | 426 | << "myColor" | ||
1095 | 427 | << QVariant( | ||
1096 | 428 | QVariantList() | ||
1097 | 429 | << 4 | ||
1098 | 430 | << qvariant_cast<QColor>(m_object->property("myColor")).red() | ||
1099 | 431 | << qvariant_cast<QColor>(m_object->property("myColor")).green() | ||
1100 | 432 | << qvariant_cast<QColor>(m_object->property("myColor")).blue() | ||
1101 | 433 | << qvariant_cast<QColor>(m_object->property("myColor")).alpha() | ||
1102 | 434 | ) | ||
1103 | 435 | << false; | ||
1104 | 436 | |||
1105 | 437 | QTest::newRow("QByteArray") | ||
1106 | 438 | << "myByteArray" | ||
1107 | 439 | << QVariant( | ||
1108 | 440 | QVariantList() | ||
1109 | 441 | << 0 | ||
1110 | 442 | << m_object->property("myByteArray") | ||
1111 | 443 | ) | ||
1112 | 444 | << false; | ||
1113 | 445 | |||
1114 | 446 | QTest::newRow("QUrl") | ||
1115 | 447 | << "myUrl" | ||
1116 | 448 | << QVariant( | ||
1117 | 449 | QVariantList() | ||
1118 | 450 | << 0 | ||
1119 | 451 | << m_object->property("myUrl") | ||
1120 | 452 | ) | ||
1121 | 453 | << false; | ||
1122 | 454 | |||
1123 | 455 | QTest::newRow("QDateTime") | ||
1124 | 456 | << "myDateTime" | ||
1125 | 457 | << QVariant( | ||
1126 | 458 | QVariantList() | ||
1127 | 459 | << 5 | ||
1128 | 460 | << m_object->property("myDateTime").toDateTime().toTime_t() | ||
1129 | 461 | ) | ||
1130 | 462 | << false; | ||
1131 | 463 | |||
1132 | 464 | QTest::newRow("QDate") | ||
1133 | 465 | << "myDate" | ||
1134 | 466 | << QVariant( | ||
1135 | 467 | QVariantList() | ||
1136 | 468 | << 5 | ||
1137 | 469 | << m_object->property("myDate").toDateTime().toTime_t() | ||
1138 | 470 | ) | ||
1139 | 471 | << false; | ||
1140 | 472 | |||
1141 | 473 | QTest::newRow("QTime") | ||
1142 | 474 | << "myTime" | ||
1143 | 475 | << QVariant( | ||
1144 | 476 | QVariantList() | ||
1145 | 477 | << 6 | ||
1146 | 478 | << m_object->property("myTime").toTime().hour() | ||
1147 | 479 | << m_object->property("myTime").toTime().minute() | ||
1148 | 480 | << m_object->property("myTime").toTime().second() | ||
1149 | 481 | << m_object->property("myTime").toTime().msec() | ||
1150 | 482 | ) | ||
1151 | 483 | << false; | ||
1152 | 207 | } | 484 | } |
1153 | 208 | 485 | ||
1154 | 209 | void tst_Introspection::test_properties() | 486 | void tst_Introspection::test_properties() |
1155 | @@ -224,6 +501,16 @@ | |||
1156 | 224 | } | 501 | } |
1157 | 225 | } | 502 | } |
1158 | 226 | 503 | ||
1159 | 504 | void tst_Introspection::test_property_matching() | ||
1160 | 505 | { | ||
1161 | 506 | QtNode n(m_object); | ||
1162 | 507 | |||
1163 | 508 | QVERIFY(n.MatchStringProperty("dynamicStringProperty", "testValue") == true); | ||
1164 | 509 | QVERIFY(n.MatchStringProperty("dynamicTestProperty", "testValue") == true); | ||
1165 | 510 | QVERIFY(n.MatchIntegerProperty("myUInt", 5) == true); | ||
1166 | 511 | QVERIFY(n.MatchBooleanProperty("visible", true) == true); | ||
1167 | 512 | } | ||
1168 | 513 | |||
1169 | 227 | QTEST_MAIN(tst_Introspection) | 514 | QTEST_MAIN(tst_Introspection) |
1170 | 228 | 515 | ||
1171 | 229 | #include "tst_introspection.moc" | 516 | #include "tst_introspection.moc" |
1172 | 230 | 517 | ||
1173 | === modified file 'tests/unittests/unittests.pro' | |||
1174 | --- tests/unittests/unittests.pro 2013-03-17 16:53:30 +0000 | |||
1175 | +++ tests/unittests/unittests.pro 2013-09-16 18:00:39 +0000 | |||
1176 | @@ -5,7 +5,7 @@ | |||
1177 | 5 | 5 | ||
1178 | 6 | QT += testlib dbus widgets quick | 6 | QT += testlib dbus widgets quick |
1179 | 7 | 7 | ||
1181 | 8 | CONFIG += link_pkgconfig | 8 | CONFIG += link_pkgconfig debug |
1182 | 9 | PKGCONFIG += xpathselect | 9 | PKGCONFIG += xpathselect |
1183 | 10 | QMAKE_CXXFLAGS += -std=c++0x -Wl,--no-undefined | 10 | QMAKE_CXXFLAGS += -std=c++0x -Wl,--no-undefined |
1184 | 11 | 11 | ||
1185 | @@ -15,7 +15,8 @@ | |||
1186 | 15 | 15 | ||
1187 | 16 | INCLUDEPATH += ../../driver | 16 | INCLUDEPATH += ../../driver |
1188 | 17 | 17 | ||
1190 | 18 | SOURCES += tst_introspection.cpp \ | 18 | SOURCES += \ |
1191 | 19 | tst_introspection.cpp \ | ||
1192 | 19 | ../../driver/introspection.cpp \ | 20 | ../../driver/introspection.cpp \ |
1193 | 20 | ../../driver/rootnode.cpp \ | 21 | ../../driver/rootnode.cpp \ |
1194 | 21 | ../../driver/qtnode.cpp | 22 | ../../driver/qtnode.cpp |
FAILED: Continuous integration, rev:84 jenkins. qa.ubuntu. com/job/ autopilot- qt-ci/70/ jenkins. qa.ubuntu. com/job/ autopilot- qt-saucy- amd64-ci/ 12/console jenkins. qa.ubuntu. com/job/ autopilot- qt-saucy- armhf-ci/ 12/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ autopilot- qt-ci/70/ rebuild
http://