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
26 // This is needed for waitForRendering calls to return
27 // if the watched element already got rendered
31 parent: testCase.parent
32 border { width: units.dp(1); color: "black" }
35 visible: testCase.running
37 RotationAnimation on rotation {
38 running: parent.visible
41 loops: Animation.Infinite
46 // Fake implementation to be provided to items under test
47 property var fakeDateTime: new function() {
48 this.currentTimeMs = 0
49 this.getCurrentTimeMs = function() {return this.currentTimeMs}
52 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
53 function mouseClick(item, x, y, button, modifiers, delay) {
54 if (button === undefined)
55 button = Qt.LeftButton;
56 if (modifiers === undefined)
57 modifiers = Qt.NoModifier;
58 if (delay === undefined)
64 if (!qtest_events.mouseClick(item, x, y, button, modifiers, delay))
65 qtest_fail("window not shown", 2);
68 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
69 function mouseDoubleClick(item, x, y, button, modifiers, delay) {
70 if (button === undefined)
71 button = Qt.LeftButton;
72 if (modifiers === undefined)
73 modifiers = Qt.NoModifier;
74 if (delay === undefined)
80 if (!qtest_events.mouseDoubleClick(item, x, y, button, modifiers, delay))
81 qtest_fail("window not shown", 2)
84 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
85 function mousePress(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)
96 if (!qtest_events.mousePress(item, x, y, button, modifiers, delay))
97 qtest_fail("window not shown", 2)
100 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
101 function mouseRelease(item, x, y, button, modifiers, delay) {
102 if (button === undefined)
103 button = Qt.LeftButton;
104 if (modifiers === undefined)
105 modifiers = Qt.NoModifier;
106 if (delay === undefined)
112 if (!qtest_events.mouseRelease(item, x, y, button, modifiers, delay))
113 qtest_fail("window not shown", 2)
117 // Flickable won't recognise a single mouse move as dragging the flickable.
118 // Use 5 steps because it's what
119 // Qt uses in QQuickViewTestUtil::flick
120 // speed is in pixels/second
121 function mouseFlick(item, x, y, toX, toY, pressMouse, releaseMouse,
123 pressMouse = ((pressMouse != null) ? pressMouse : true); // Default to true for pressMouse if not present
124 releaseMouse = ((releaseMouse != null) ? releaseMouse : true); // Default to true for releaseMouse if not present
126 // set a default speed if not specified
127 speed = (speed != null) ? speed : units.gu(10);
129 // set a default iterations if not specified
130 iterations = (iterations !== undefined) ? iterations : 5
132 var distance = Math.sqrt(Math.pow(toX - x, 2) + Math.pow(toY - y, 2))
133 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
135 var timeStep = totalTime / iterations
136 var diffX = (toX - x) / iterations
137 var diffY = (toY - y) / iterations
139 fakeDateTime.currentTimeMs += timeStep
140 mousePress(item, x, y)
142 for (var i = 0; i < iterations; ++i) {
143 fakeDateTime.currentTimeMs += timeStep
144 if (i === iterations - 1) {
145 // Avoid any rounding errors by making the last move be at precisely
146 // the point specified
147 mouseMove(item, toX, toY, iterations / speed)
149 mouseMove(item, x + (i + 1) * diffX, y + (i + 1) * diffY, iterations / speed)
153 fakeDateTime.currentTimeMs += timeStep
154 mouseRelease(item, toX, toY)
159 // Find an object with the given name in the children tree of "obj"
160 function findChild(obj, objectName) {
161 return findChildIn(obj, "children", objectName);
164 // Find an object with the given name in the children tree of "obj"
165 // Including invisible children like animations, timers etc.
166 // Note: you should use findChild if you're not sure you need this
167 // as this tree is much bigger and might contain stuff that goes
169 function findInvisibleChild(obj, objectName) {
170 return findChildIn(obj, "data", objectName);
173 // Find a child in the named property
174 function findChildIn(obj, prop, objectName) {
175 var childs = new Array(0);
177 while (childs.length > 0) {
178 if (childs[0].objectName == objectName) {
181 for (var i in childs[0][prop]) {
182 childs.push(childs[0][prop][i])
189 function findChildsByType(obj, typeName) {
190 var res = new Array(0);
191 for (var i in obj.children) {
192 var c = obj.children[i];
193 if (UT.Util.isInstanceOf(c, typeName)) {
196 res = res.concat(findChildsByType(c, typeName));
201 // Type a full string instead of keyClick letter by letter
202 function typeString(str) {
203 for (var i = 0; i < str.length; i++) {
208 // Keeps executing a given parameter-less function until it returns the given
209 // expected result or the timemout is reached (in which case a test failure
211 function tryCompareFunction(func, expectedResult, timeout) {
213 if (timeout === undefined)
217 while (timeSpent < timeout && !success) {
218 actualResult = func()
219 success = qtest_compareInternal(actualResult, expectedResult)
220 if (success === false) {
226 var act = qtest_results.stringify(actualResult)
227 var exp = qtest_results.stringify(expectedResult)
228 if (!qtest_results.compare(success,
229 "function returned unexpected result",
231 util.callerFile(), util.callerLine())) {
232 throw new Error("QtQuickTest::fail")
236 function flickToYEnd(item) {
238 var x = item.width / 2;
239 var y = item.height - units.gu(1);
240 var toY = units.gu(1);
241 while (i < 5 && !item.atYEnd) {
242 touchFlick(item, x, y, x, toY);
243 tryCompare(item, "moving", false);
246 tryCompare(item, "atYEnd", true);
249 function touchEvent(item) {
250 return UT.Util.touchEvent(item)
253 // speed is in pixels/second
254 function touchFlick(item, x, y, toX, toY, beginTouch, endTouch, speed, iterations) {
255 // Make sure the item is rendered
256 waitForRendering(item);
258 var root = fetchRootItem(item);
259 var rootFrom = item.mapToItem(root, x, y);
260 var rootTo = item.mapToItem(root, toX, toY);
262 // Default to true for beginTouch if not present
263 beginTouch = (beginTouch !== undefined) ? beginTouch : true
265 // Default to true for endTouch if not present
266 endTouch = (endTouch !== undefined) ? endTouch : true
268 // Set a default speed if not specified
269 speed = (speed !== undefined) ? speed : units.gu(10)
271 // Set a default iterations if not specified
272 var iterations = (iterations !== undefined) ? iterations : 5
274 var distance = Math.sqrt(Math.pow(rootTo.x - rootFrom.x, 2) + Math.pow(rootTo.Y - rootFrom.y, 2))
275 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
277 var timeStep = totalTime / iterations
278 var diffX = (rootTo.x - rootFrom.x) / iterations
279 var diffY = (rootTo.y - rootFrom.y) / iterations
281 fakeDateTime.currentTimeMs += timeStep
283 var event = touchEvent(item)
284 event.press(0 /* touchId */, rootFrom.x, rootFrom.y)
287 for (var i = 0; i < iterations; ++i) {
288 fakeDateTime.currentTimeMs += timeStep
289 if (i === iterations - 1) {
290 // Avoid any rounding errors by making the last move be at precisely
291 // the point specified
292 wait(iterations / speed)
293 var event = touchEvent(item)
294 event.move(0 /* touchId */, rootTo.x, rootTo.y)
297 wait(iterations / speed)
298 var event = touchEvent(item)
299 event.move(0 /* touchId */, rootFrom.x + (i + 1) * diffX, rootFrom.y + (i + 1) * diffY)
304 fakeDateTime.currentTimeMs += timeStep
305 var event = touchEvent(item)
306 event.release(0 /* touchId */, rootTo.x, rootTo.y)
311 function touchPinch(item, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End) {
312 // Make sure the item is rendered
313 waitForRendering(item);
315 var event1 = touchEvent(item);
317 event1.press(0, x1Start, y1Start);
320 event1.move(0, x1Start, y1Start);
321 event1.press(1, x2Start, y2Start);
325 for (var i = 0.0; i < 1.0; i += 0.02) {
326 event1.move(0, x1Start + (x1End - x1Start) * i, y1Start + (y1End - y1Start) * i);
327 event1.move(1, x2Start + (x2End - x2Start) * i, y2Start + (y2End - y2Start) * i);
332 event1.release(0, x1End, y1End);
333 event1.release(1, x2End, y2End);
337 function fetchRootItem(item) {
339 return fetchRootItem(item.parent)
344 function touchPress(item, x, y) {
345 var root = fetchRootItem(item)
346 var rootPoint = item.mapToItem(root, x, y)
348 var event = touchEvent(item)
349 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
353 function touchRelease(item, x, y) {
354 var root = fetchRootItem(item)
355 var rootPoint = item.mapToItem(root, x, y)
357 var event = touchEvent(item)
358 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
362 /*! \brief Tap the item with a touch event.
364 \param item The item to be tapped
365 \param x The x coordinate of the tap, defaults to horizontal center
366 \param y The y coordinate of the tap, defaults to vertical center
368 function tap(item, x, y) {
369 if (typeof x !== "number") x = item.width / 2;
370 if (typeof y !== "number") y = item.height / 2;
372 var root = fetchRootItem(item)
373 var rootPoint = item.mapToItem(root, x, y)
375 var event = touchEvent(item)
376 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
379 event = touchEvent(item)
380 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
384 Component.onCompleted: {
385 UT.Util.ensureTouchRegistryInstalled();
387 var rootItem = parent;
388 while (rootItem.parent != undefined) {
389 rootItem = rootItem.parent;
391 removeTimeConstraintsFromDirectionalDragAreas(rootItem);
395 In qmltests, sequences of touch events are sent all at once, unlike in "real life".
396 Also qmltests might run really slowly, e.g. when run from inside virtual machines.
397 Thus to remove a variable that qmltests cannot really control, namely time, this
398 function removes all constraints from DirectionalDragAreas that are sensible to
401 This effectively makes DirectionalDragAreas easier to fool.
403 function removeTimeConstraintsFromDirectionalDragAreas(item) {
405 // use duck-typing to identify a DirectionalDragArea
406 if (item.minSpeed != undefined
407 && item.maxSilenceTime != undefined
408 && item.compositionTime != undefined) {
410 item.maxSilenceTime = 60 * 60 * 1000;
411 item.compositionTime = 0;
413 for (var i in item.children) {
414 removeTimeConstraintsFromDirectionalDragAreas(item.children[i]);
419 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
420 function waitForRendering(item, timeout) {
421 if (timeout === undefined)
424 qtest_fail("No item given to waitForRendering", 1);
425 return qtest_results.waitForRendering(item, timeout);