2 * Copyright 2013 Canonical Ltd.
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.
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.
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/>.
19 import Ubuntu.Components 0.1
20 import Unity.Test 0.1 as UT
27 visible: testCase.running
28 anchors.centerIn: parent
29 Component.onCompleted: parent = testCase.parent
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}
40 // Flickable won't recognise a single mouse move as dragging the flickable.
41 // Use 5 steps because it's what
42 // Qt uses in QQuickViewTestUtil::flick
43 // speed is in pixels/second
44 function mouseFlick(item, x, y, toX, toY, pressMouse, releaseMouse,
46 pressMouse = ((pressMouse != null) ? pressMouse : true); // Default to true for pressMouse if not present
47 releaseMouse = ((releaseMouse != null) ? releaseMouse : true); // Default to true for releaseMouse if not present
49 // set a default speed if not specified
50 speed = (speed != null) ? speed : units.gu(10);
52 // set a default iterations if not specified
53 iterations = (iterations !== undefined) ? iterations : 5
55 var distance = Math.sqrt(Math.pow(toX - x, 2) + Math.pow(toY - y, 2))
56 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
58 var timeStep = totalTime / iterations
59 var diffX = (toX - x) / iterations
60 var diffY = (toY - y) / iterations
62 fakeDateTime.currentTimeMs += timeStep
63 mousePress(item, x, y)
65 for (var i = 0; i < iterations; ++i) {
66 fakeDateTime.currentTimeMs += timeStep
67 if (i === iterations - 1) {
68 // Avoid any rounding errors by making the last move be at precisely
69 // the point specified
70 mouseMove(item, toX, toY, iterations / speed)
72 mouseMove(item, x + (i + 1) * diffX, y + (i + 1) * diffY, iterations / speed)
76 fakeDateTime.currentTimeMs += timeStep
77 mouseRelease(item, toX, toY)
82 // Find an object with the given name in the children tree of "obj"
83 function findChild(obj, objectName) {
84 return findChildIn(obj, "children", objectName);
87 // Find an object with the given name in the children tree of "obj"
88 // Including invisible children like animations, timers etc.
89 // Note: you should use findChild if you're not sure you need this
90 // as this tree is much bigger and might contain stuff that goes
92 function findInvisibleChild(obj, objectName) {
93 return findChildIn(obj, "data", objectName);
96 // Find a child in the named property
97 function findChildIn(obj, prop, objectName) {
98 var childs = new Array(0);
100 while (childs.length > 0) {
101 if (childs[0].objectName == objectName) {
104 for (var i in childs[0][prop]) {
105 childs.push(childs[0][prop][i])
112 // Type a full string instead of keyClick letter by letter
113 // TODO: this is not ugly, this is uber-ugly and does not support
114 // any special character. Remove the keyMap once keyClick(obj, char)
115 // has landed in upstream Qt.
116 function typeString(str) {
182 for (var i = 0; i < str.length; i++) {
183 keyClick(keyMap[str[i]])
187 // Keeps executing a given parameter-less function until it returns the given
188 // expected result or the timemout is reached (in which case a test failure
190 function tryCompareFunction(func, expectedResult, timeout) {
192 if (timeout === undefined)
196 while (timeSpent < timeout && !success) {
197 actualResult = func()
198 success = qtest_compareInternal(actualResult, expectedResult)
199 if (success === false) {
205 var act = qtest_results.stringify(actualResult)
206 var exp = qtest_results.stringify(expectedResult)
207 if (!qtest_results.compare(success,
208 "function returned unexpected result",
210 util.callerFile(), util.callerLine())) {
211 throw new Error("QtQuickTest::fail")
215 function touchEvent() {
216 return UT.Util.touchEvent()
219 // speed is in pixels/second
220 function touchFlick(item, x, y, toX, toY, beginTouch, endTouch, speed, iterations) {
221 // Make sure the item is rendered
222 waitForRendering(item);
224 // Default to true for beginTouch if not present
225 beginTouch = (beginTouch !== undefined) ? beginTouch : true
227 // Default to true for endTouch if not present
228 endTouch = (endTouch !== undefined) ? endTouch : true
230 // Set a default speed if not specified
231 speed = (speed !== undefined) ? speed : units.gu(10)
233 // Set a default iterations if not specified
234 var iterations = (iterations !== undefined) ? iterations : 5
236 var distance = Math.sqrt(Math.pow(toX - x, 2) + Math.pow(toY - y, 2))
237 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
239 var timeStep = totalTime / iterations
240 var diffX = (toX - x) / iterations
241 var diffY = (toY - y) / iterations
243 fakeDateTime.currentTimeMs += timeStep
245 var event = touchEvent()
246 event.press(0 /* touchId */, x, y)
249 for (var i = 0; i < iterations; ++i) {
250 fakeDateTime.currentTimeMs += timeStep
251 if (i === iterations - 1) {
252 // Avoid any rounding errors by making the last move be at precisely
253 // the point specified
254 wait(iterations / speed)
255 var event = touchEvent()
256 event.move(0 /* touchId */, toX, toY)
259 wait(iterations / speed)
260 var event = touchEvent()
261 event.move(0 /* touchId */, x + (i + 1) * diffX, y + (i + 1) * diffY)
266 fakeDateTime.currentTimeMs += timeStep
267 var event = touchEvent()
268 event.release(0 /* touchId */, toX, toY)
273 function touchPinch(item, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End) {
274 // Make sure the item is rendered
275 waitForRendering(item);
277 var event1 = touchEvent();
279 event1.press(0, x1Start, y1Start);
282 event1.stationary(0);
283 event1.press(1, x2Start, y2Start);
287 for (var i = 0.0; i < 1.0; i += 0.02) {
288 event1.move(0, x1Start + (x1End - x1Start) * i, y1Start + (y1End - y1Start) * i);
289 event1.move(1, x2Start + (x2End - x2Start) * i, y2Start + (y2End - y2Start) * i);
294 event1.release(0, x1End, y1End);
295 event1.release(1, x2End, y2End);
299 function fetchRootItem(item) {
301 return fetchRootItem(item.parent)
306 function touchPress(item, x, y) {
307 var root = fetchRootItem(item)
308 var rootPoint = item.mapToItem(root, x, y)
310 var event = touchEvent()
311 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
315 function touchRelease(item, x, y) {
316 var root = fetchRootItem(item)
317 var rootPoint = item.mapToItem(root, x, y)
319 var event = touchEvent()
320 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
324 /*! \brief Tap the item with a touch event.
326 \param item The item to be tapped
327 \param x The x coordinate of the tap, defaults to horizontal center
328 \param y The y coordinate of the tap, defaults to vertical center
330 function tap(item, x, y) {
331 if (typeof x !== "number") x = item.width / 2;
332 if (typeof y !== "number") y = item.height / 2;
334 var root = fetchRootItem(item)
335 var rootPoint = item.mapToItem(root, x, y)
337 var event = touchEvent()
338 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
342 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
346 Component.onCompleted: {
347 UT.Util.ensureTouchRegistryInstalled();
349 var rootItem = parent;
350 while (rootItem.parent != undefined) {
351 rootItem = rootItem.parent;
353 removeTimeConstraintsFromDirectionalDragAreas(rootItem);
357 In qmltests, sequences of touch events are sent all at once, unlike in "real life".
358 Also qmltests might run really slowly, e.g. when run from inside virtual machines.
359 Thus to remove a variable that qmltests cannot really control, namely time, this
360 function removes all constraints from DirectionalDragAreas that are sensible to
363 This effectively makes DirectionalDragAreas easier to fool.
365 function removeTimeConstraintsFromDirectionalDragAreas(item) {
367 // use duck-typing to identify a DirectionalDragArea
368 if (item.minSpeed != undefined
369 && item.maxSilenceTime != undefined
370 && item.compositionTime != undefined) {
372 item.maxSilenceTime = 60 * 60 * 1000;
373 item.compositionTime = 0;
375 for (var i in item.children) {
376 removeTimeConstraintsFromDirectionalDragAreas(item.children[i]);