on 1a3ebbd help sign in
unity8/tests/utils/modules/Unity/Test/UnityTestCase.qml
467 lines 16 kB history raw
1/*
2 * Copyright 2013 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
17import QtQuick 2.0
18import QtTest 1.0
19import Ubuntu.Components 0.1
20import Unity.Test 0.1 as UT
22TestCase {
23 id: testCase
24 TestUtil {id:util}
26 ActivityIndicator {
27 visible: testCase.running
28 anchors.centerIn: parent
29 Component.onCompleted: parent = testCase.parent
30 z: 100
31 running: visible
32 }
34 // Fake implementation to be provided to items under test
35 property var fakeDateTime: new function() {
36 this.currentTimeMs = 0
37 this.getCurrentTimeMs = function() {return this.currentTimeMs}
38 }
40 function mouseClick(item, x, y, button, modifiers, delay) {
41 if (button === undefined)
42 button = Qt.LeftButton;
43 if (modifiers === undefined)
44 modifiers = Qt.NoModifier;
45 if (delay === undefined)
46 delay = -1;
47 if (x === undefined)
48 x = item.width / 2;
49 if (y === undefined)
50 y = item.height / 2;
51 if (!qtest_events.mouseClick(item, x, y, button, modifiers, delay))
52 qtest_fail("window not shown", 2);
53 }
55 function mouseDoubleClick(item, x, y, button, modifiers, delay) {
56 if (button === undefined)
57 button = Qt.LeftButton;
58 if (modifiers === undefined)
59 modifiers = Qt.NoModifier;
60 if (delay === undefined)
61 delay = -1;
62 if (x === undefined)
63 x = item.width / 2;
64 if (y === undefined)
65 y = item.height / 2;
66 if (!qtest_events.mouseDoubleClick(item, x, y, button, modifiers, delay))
67 qtest_fail("window not shown", 2)
68 }
70 function mousePress(item, x, y, button, modifiers, delay) {
71 if (button === undefined)
72 button = Qt.LeftButton;
73 if (modifiers === undefined)
74 modifiers = Qt.NoModifier;
75 if (delay === undefined)
76 delay = -1;
77 if (x === undefined)
78 x = item.width / 2;
79 if (y === undefined)
80 y = item.height / 2;
81 if (!qtest_events.mousePress(item, x, y, button, modifiers, delay))
82 qtest_fail("window not shown", 2)
83 }
85 function mouseRelease(item, x, y, button, modifiers, delay) {
86 if (button === undefined)
87 button = Qt.LeftButton;
88 if (modifiers === undefined)
89 modifiers = Qt.NoModifier;
90 if (delay === undefined)
91 delay = -1;
92 if (x === undefined)
93 x = item.width / 2;
94 if (y === undefined)
95 y = item.height / 2;
96 if (!qtest_events.mouseRelease(item, x, y, button, modifiers, delay))
97 qtest_fail("window not shown", 2)
98 }
101 // Flickable won't recognise a single mouse move as dragging the flickable.
102 // Use 5 steps because it's what
103 // Qt uses in QQuickViewTestUtil::flick
104 // speed is in pixels/second
105 function mouseFlick(item, x, y, toX, toY, pressMouse, releaseMouse,
106 speed, iterations) {
107 pressMouse = ((pressMouse != null) ? pressMouse : true); // Default to true for pressMouse if not present
108 releaseMouse = ((releaseMouse != null) ? releaseMouse : true); // Default to true for releaseMouse if not present
110 // set a default speed if not specified
111 speed = (speed != null) ? speed : units.gu(10);
113 // set a default iterations if not specified
114 iterations = (iterations !== undefined) ? iterations : 5
116 var distance = Math.sqrt(Math.pow(toX - x, 2) + Math.pow(toY - y, 2))
117 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
119 var timeStep = totalTime / iterations
120 var diffX = (toX - x) / iterations
121 var diffY = (toY - y) / iterations
122 if (pressMouse) {
123 fakeDateTime.currentTimeMs += timeStep
124 mousePress(item, x, y)
125 }
126 for (var i = 0; i < iterations; ++i) {
127 fakeDateTime.currentTimeMs += timeStep
128 if (i === iterations - 1) {
129 // Avoid any rounding errors by making the last move be at precisely
130 // the point specified
131 mouseMove(item, toX, toY, iterations / speed)
132 } else {
133 mouseMove(item, x + (i + 1) * diffX, y + (i + 1) * diffY, iterations / speed)
134 }
135 }
136 if (releaseMouse) {
137 fakeDateTime.currentTimeMs += timeStep
138 mouseRelease(item, toX, toY)
139 }
140 }
143 // Find an object with the given name in the children tree of "obj"
144 function findChild(obj, objectName) {
145 return findChildIn(obj, "children", objectName);
146 }
148 // Find an object with the given name in the children tree of "obj"
149 // Including invisible children like animations, timers etc.
150 // Note: you should use findChild if you're not sure you need this
151 // as this tree is much bigger and might contain stuff that goes
152 // away randomly.
153 function findInvisibleChild(obj, objectName) {
154 return findChildIn(obj, "data", objectName);
155 }
157 // Find a child in the named property
158 function findChildIn(obj, prop, objectName) {
159 var childs = new Array(0);
160 childs.push(obj)
161 while (childs.length > 0) {
162 if (childs[0].objectName == objectName) {
163 return childs[0]
164 }
165 for (var i in childs[0][prop]) {
166 childs.push(childs[0][prop][i])
167 }
168 childs.splice(0, 1);
169 }
170 return null;
171 }
173 function findChildsByType(obj, typeName) {
174 var res = new Array(0);
175 for (var i in obj.children) {
176 var c = obj.children[i];
177 if (UT.Util.isInstanceOf(c, typeName)) {
178 res.push(c)
179 }
180 res = res.concat(findChildsByType(c, typeName));
181 }
182 return res;
183 }
185 // Type a full string instead of keyClick letter by letter
186 // TODO: this is not ugly, this is uber-ugly and does not support
187 // any special character. Remove the keyMap once keyClick(obj, char)
188 // has landed in upstream Qt.
189 function typeString(str) {
190 var keyMap = {
191 "a": Qt.Key_A,
192 "b": Qt.Key_B,
193 "c": Qt.Key_C,
194 "d": Qt.Key_D,
195 "e": Qt.Key_E,
196 "f": Qt.Key_F,
197 "g": Qt.Key_G,
198 "h": Qt.Key_H,
199 "i": Qt.Key_I,
200 "j": Qt.Key_J,
201 "k": Qt.Key_K,
202 "l": Qt.Key_L,
203 "m": Qt.Key_M,
204 "n": Qt.Key_N,
205 "o": Qt.Key_O,
206 "p": Qt.Key_P,
207 "q": Qt.Key_Q,
208 "r": Qt.Key_R,
209 "s": Qt.Key_S,
210 "t": Qt.Key_T,
211 "u": Qt.Key_U,
212 "v": Qt.Key_V,
213 "w": Qt.Key_W,
214 "x": Qt.Key_X,
215 "y": Qt.Key_Y,
216 "z": Qt.Key_Z,
217 "A": Qt.Key_A,
218 "B": Qt.Key_B,
219 "C": Qt.Key_C,
220 "D": Qt.Key_D,
221 "E": Qt.Key_E,
222 "F": Qt.Key_F,
223 "G": Qt.Key_G,
224 "H": Qt.Key_H,
225 "I": Qt.Key_I,
226 "J": Qt.Key_J,
227 "K": Qt.Key_K,
228 "L": Qt.Key_L,
229 "M": Qt.Key_M,
230 "N": Qt.Key_N,
231 "O": Qt.Key_O,
232 "P": Qt.Key_P,
233 "Q": Qt.Key_Q,
234 "R": Qt.Key_R,
235 "S": Qt.Key_S,
236 "T": Qt.Key_T,
237 "U": Qt.Key_U,
238 "V": Qt.Key_V,
239 "W": Qt.Key_W,
240 "X": Qt.Key_X,
241 "Y": Qt.Key_Y,
242 "Z": Qt.Key_Z,
243 "0": Qt.Key_0,
244 "1": Qt.Key_1,
245 "2": Qt.Key_2,
246 "3": Qt.Key_3,
247 "4": Qt.Key_4,
248 "5": Qt.Key_5,
249 "6": Qt.Key_6,
250 "7": Qt.Key_7,
251 "8": Qt.Key_8,
252 "9": Qt.Key_9,
253 " ": Qt.Key_Space,
254 }
255 for (var i = 0; i < str.length; i++) {
256 keyClick(keyMap[str[i]])
257 }
258 }
260 // Keeps executing a given parameter-less function until it returns the given
261 // expected result or the timemout is reached (in which case a test failure
262 // is generated)
263 function tryCompareFunction(func, expectedResult, timeout) {
264 var timeSpent = 0
265 if (timeout === undefined)
266 timeout = 5000;
267 var success = false
268 var actualResult
269 while (timeSpent < timeout && !success) {
270 actualResult = func()
271 success = qtest_compareInternal(actualResult, expectedResult)
272 if (success === false) {
273 wait(50)
274 timeSpent += 50
275 }
276 }
278 var act = qtest_results.stringify(actualResult)
279 var exp = qtest_results.stringify(expectedResult)
280 if (!qtest_results.compare(success,
281 "function returned unexpected result",
282 act, exp,
283 util.callerFile(), util.callerLine())) {
284 throw new Error("QtQuickTest::fail")
285 }
286 }
288 function touchEvent() {
289 return UT.Util.touchEvent()
290 }
292 // speed is in pixels/second
293 function touchFlick(item, x, y, toX, toY, beginTouch, endTouch, speed, iterations) {
294 // Make sure the item is rendered
295 waitForRendering(item);
297 var root = fetchRootItem(item);
298 var rootFrom = item.mapToItem(root, x, y);
299 var rootTo = item.mapToItem(root, toX, toY);
301 // Default to true for beginTouch if not present
302 beginTouch = (beginTouch !== undefined) ? beginTouch : true
304 // Default to true for endTouch if not present
305 endTouch = (endTouch !== undefined) ? endTouch : true
307 // Set a default speed if not specified
308 speed = (speed !== undefined) ? speed : units.gu(10)
310 // Set a default iterations if not specified
311 var iterations = (iterations !== undefined) ? iterations : 5
313 var distance = Math.sqrt(Math.pow(rootTo.x - rootFrom.x, 2) + Math.pow(rootTo.Y - rootFrom.y, 2))
314 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
316 var timeStep = totalTime / iterations
317 var diffX = (rootTo.x - rootFrom.x) / iterations
318 var diffY = (rootTo.y - rootFrom.y) / iterations
319 if (beginTouch) {
320 fakeDateTime.currentTimeMs += timeStep
322 var event = touchEvent()
323 event.press(0 /* touchId */, rootFrom.x, rootFrom.y)
324 event.commit()
325 }
326 for (var i = 0; i < iterations; ++i) {
327 fakeDateTime.currentTimeMs += timeStep
328 if (i === iterations - 1) {
329 // Avoid any rounding errors by making the last move be at precisely
330 // the point specified
331 wait(iterations / speed)
332 var event = touchEvent()
333 event.move(0 /* touchId */, rootTo.x, rootTo.y)
334 event.commit()
335 } else {
336 wait(iterations / speed)
337 var event = touchEvent()
338 event.move(0 /* touchId */, rootFrom.x + (i + 1) * diffX, rootFrom.y + (i + 1) * diffY)
339 event.commit()
340 }
341 }
342 if (endTouch) {
343 fakeDateTime.currentTimeMs += timeStep
344 var event = touchEvent()
345 event.release(0 /* touchId */, rootTo.x, rootTo.y)
346 event.commit()
347 }
348 }
350 function touchPinch(item, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End) {
351 // Make sure the item is rendered
352 waitForRendering(item);
354 var event1 = touchEvent();
355 // first finger
356 event1.press(0, x1Start, y1Start);
357 event1.commit();
358 // second finger
359 event1.stationary(0);
360 event1.press(1, x2Start, y2Start);
361 event1.commit();
363 // pinch
364 for (var i = 0.0; i < 1.0; i += 0.02) {
365 event1.move(0, x1Start + (x1End - x1Start) * i, y1Start + (y1End - y1Start) * i);
366 event1.move(1, x2Start + (x2End - x2Start) * i, y2Start + (y2End - y2Start) * i);
367 event1.commit();
368 }
370 // release
371 event1.release(0, x1End, y1End);
372 event1.release(1, x2End, y2End);
373 event1.commit();
374 }
376 function fetchRootItem(item) {
377 if (item.parent)
378 return fetchRootItem(item.parent)
379 else
380 return item
381 }
383 function touchPress(item, x, y) {
384 var root = fetchRootItem(item)
385 var rootPoint = item.mapToItem(root, x, y)
387 var event = touchEvent()
388 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
389 event.commit()
390 }
392 function touchRelease(item, x, y) {
393 var root = fetchRootItem(item)
394 var rootPoint = item.mapToItem(root, x, y)
396 var event = touchEvent()
397 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
398 event.commit()
399 }
401 /*! \brief Tap the item with a touch event.
403 \param item The item to be tapped
404 \param x The x coordinate of the tap, defaults to horizontal center
405 \param y The y coordinate of the tap, defaults to vertical center
406 */
407 function tap(item, x, y) {
408 if (typeof x !== "number") x = item.width / 2;
409 if (typeof y !== "number") y = item.height / 2;
411 var root = fetchRootItem(item)
412 var rootPoint = item.mapToItem(root, x, y)
414 var event = touchEvent()
415 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
416 event.commit()
418 event = touchEvent()
419 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
420 event.commit()
421 }
423 Component.onCompleted: {
424 UT.Util.ensureTouchRegistryInstalled();
426 var rootItem = parent;
427 while (rootItem.parent != undefined) {
428 rootItem = rootItem.parent;
429 }
430 removeTimeConstraintsFromDirectionalDragAreas(rootItem);
431 }
433 /*
434 In qmltests, sequences of touch events are sent all at once, unlike in "real life".
435 Also qmltests might run really slowly, e.g. when run from inside virtual machines.
436 Thus to remove a variable that qmltests cannot really control, namely time, this
437 function removes all constraints from DirectionalDragAreas that are sensible to
438 elapsed time.
440 This effectively makes DirectionalDragAreas easier to fool.
441 */
442 function removeTimeConstraintsFromDirectionalDragAreas(item) {
444 // use duck-typing to identify a DirectionalDragArea
445 if (item.minSpeed != undefined
446 && item.maxSilenceTime != undefined
447 && item.compositionTime != undefined) {
448 item.minSpeed = 0;
449 item.maxSilenceTime = 60 * 60 * 1000;
450 item.compositionTime = 0;
451 } else {
452 for (var i in item.children) {
453 removeTimeConstraintsFromDirectionalDragAreas(item.children[i]);
454 }
455 }
456 }
458 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
459 function waitForRendering(item, timeout) {
460 if (timeout === undefined)
461 timeout = 5000;
462 if (!item)
463 qtest_fail("No item given to waitForRendering", 1);
464 return qtest_results.waitForRendering(item, timeout);
465 }