2 * Copyright 2013-2016 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/>.
19import Unity.Application 0.1
20import Ubuntu.Components 1.3
21import Unity.Test 0.1 as UT
26 property var util: TestUtil {id:util}
28 // This is needed for waitForRendering calls to return
29 // if the watched element already got rendered
34 parent: testCase.parent
35 border { width: units.dp(1); color: "black" }
38 visible: testCase.running
40 RotationAnimation on rotation {
41 running: rotatingRectangle.visible
44 loops: Animation.Infinite
49 // Fake implementation to be provided to items under test
50 property var fakeDateTime: new function() {
51 this.currentTimeMs = 0
52 this.getCurrentTimeMs = function() {return this.currentTimeMs}
55 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
56 function mouseClick(item, x, y, button, modifiers, delay) {
57 if (button === undefined)
58 button = Qt.LeftButton;
59 if (modifiers === undefined)
60 modifiers = Qt.NoModifier;
61 if (delay === undefined)
67 if (!qtest_events.mouseClick(item, x, y, button, modifiers, delay))
68 qtest_fail("window not shown", 2);
71 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
72 function mouseDoubleClick(item, x, y, button, modifiers, delay) {
73 if (button === undefined)
74 button = Qt.LeftButton;
75 if (modifiers === undefined)
76 modifiers = Qt.NoModifier;
77 if (delay === undefined)
83 if (!qtest_events.mouseDoubleClick(item, x, y, button, modifiers, delay))
84 qtest_fail("window not shown", 2)
87 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
88 function mousePress(item, x, y, button, modifiers, delay) {
89 if (button === undefined)
90 button = Qt.LeftButton;
91 if (modifiers === undefined)
92 modifiers = Qt.NoModifier;
93 if (delay === undefined)
99 if (!qtest_events.mousePress(item, x, y, button, modifiers, delay))
100 qtest_fail("window not shown", 2)
103 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
104 function mouseRelease(item, x, y, button, modifiers, delay) {
105 if (button === undefined)
106 button = Qt.LeftButton;
107 if (modifiers === undefined)
108 modifiers = Qt.NoModifier;
109 if (delay === undefined)
115 if (!qtest_events.mouseRelease(item, x, y, button, modifiers, delay))
116 qtest_fail("window not shown", 2)
120 // Flickable won't recognise a single mouse move as dragging the flickable.
121 // Use 5 steps because it's what
122 // Qt uses in QQuickViewTestUtil::flick
123 // speed is in pixels/second
124 function mouseFlick(item, x, y, toX, toY, pressMouse, releaseMouse,
126 pressMouse = ((pressMouse != null) ? pressMouse : true); // Default to true for pressMouse if not present
127 releaseMouse = ((releaseMouse != null) ? releaseMouse : true); // Default to true for releaseMouse if not present
129 // set a default speed if not specified
130 speed = (speed != null) ? speed : units.gu(10);
132 // set a default iterations if not specified
133 iterations = (iterations !== undefined) ? iterations : 5
135 var distance = Math.sqrt(Math.pow(toX - x, 2) + Math.pow(toY - y, 2))
136 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
138 var timeStep = totalTime / iterations
139 var diffX = (toX - x) / iterations
140 var diffY = (toY - y) / iterations
142 fakeDateTime.currentTimeMs += timeStep
143 mousePress(item, x, y)
145 for (var i = 0; i < iterations; ++i) {
146 fakeDateTime.currentTimeMs += timeStep
147 if (i === iterations - 1) {
148 // Avoid any rounding errors by making the last move be at precisely
149 // the point specified
150 mouseMove(item, toX, toY, iterations / speed)
152 mouseMove(item, x + (i + 1) * diffX, y + (i + 1) * diffY, iterations / speed)
156 fakeDateTime.currentTimeMs += timeStep
157 mouseRelease(item, toX, toY)
162 // Find an object with the given name in the children tree of "obj"
163 function findChild(obj, objectName) {
164 return findChildIn(obj, "children", objectName);
167 // Find an object with the given name in the children tree of "obj"
168 // Including invisible children like animations, timers etc.
169 // Note: you should use findChild if you're not sure you need this
170 // as this tree is much bigger and might contain stuff that goes
172 function findInvisibleChild(obj, objectName) {
173 return findChildIn(obj, "data", objectName);
176 // Find a child in the named property
177 function findChildIn(obj, prop, objectName) {
178 var childs = new Array(0);
180 while (childs.length > 0) {
181 if (childs[0].objectName == objectName) {
184 for (var i in childs[0][prop]) {
185 childs.push(childs[0][prop][i])
192 function findChildsByType(obj, typeName) {
193 var res = new Array(0);
194 for (var i in obj.children) {
195 var c = obj.children[i];
196 if (UT.Util.isInstanceOf(c, typeName)) {
199 res = res.concat(findChildsByType(c, typeName));
204 // Type a full string instead of keyClick letter by letter
205 function typeString(str) {
206 for (var i = 0; i < str.length; i++) {
211 // Keeps executing a given parameter-less function until it returns the given
212 // expected result or the timemout is reached (in which case a test failure
214 function tryCompareFunction(func, expectedResult, timeout) {
216 if (timeout === undefined)
220 while (timeSpent < timeout && !success) {
221 actualResult = func()
222 success = qtest_compareInternal(actualResult, expectedResult)
223 if (success === false) {
229 var act = qtest_results.stringify(actualResult)
230 var exp = qtest_results.stringify(expectedResult)
231 if (!qtest_results.compare(success,
232 "function returned unexpected result",
234 util.callerFile(), util.callerLine())) {
235 throw new Error("QtQuickTest::fail")
239 function flickToYEnd(item) {
241 var x = item.width / 2;
242 var y = item.height - units.gu(1);
243 var toY = units.gu(1);
244 var maxIterations = 5 + item.contentHeight / item.height;
245 while (i < maxIterations && !item.atYEnd) {
246 touchFlick(item, x, y, x, toY);
247 tryCompare(item, "moving", false);
250 tryCompare(item, "atYEnd", true);
253 function touchEvent(item) {
254 return UT.Util.touchEvent(item)
257 // speed is in pixels/second
258 function touchFlick(item, x, y, toX, toY, beginTouch, endTouch, speed, iterations) {
259 // Make sure the item is rendered
260 waitForRendering(item);
262 var root = fetchRootItem(item);
263 var rootFrom = item.mapToItem(root, x, y);
264 var rootTo = item.mapToItem(root, toX, toY);
266 // Default to true for beginTouch if not present
267 beginTouch = (beginTouch !== undefined) ? beginTouch : true
269 // Default to true for endTouch if not present
270 endTouch = (endTouch !== undefined) ? endTouch : true
272 // Set a default speed if not specified
273 speed = (speed !== undefined) ? speed : units.gu(10)
275 // Set a default iterations if not specified
276 var iterations = (iterations !== undefined) ? iterations : 10
278 var distance = Math.sqrt(Math.pow(rootTo.x - rootFrom.x, 2) + Math.pow(rootTo.Y - rootFrom.y, 2))
279 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
281 var timeStep = totalTime / iterations
282 var diffX = (rootTo.x - rootFrom.x) / iterations
283 var diffY = (rootTo.y - rootFrom.y) / iterations
285 fakeDateTime.currentTimeMs += timeStep
287 var event = touchEvent(item)
288 event.press(0 /* touchId */, rootFrom.x, rootFrom.y)
291 for (var i = 0; i < iterations; ++i) {
292 fakeDateTime.currentTimeMs += timeStep
293 if (i === iterations - 1) {
294 // Avoid any rounding errors by making the last move be at precisely
295 // the point specified
296 wait(iterations / speed)
297 var event = touchEvent(item)
298 event.move(0 /* touchId */, rootTo.x, rootTo.y)
301 wait(iterations / speed)
302 var event = touchEvent(item)
303 event.move(0 /* touchId */, rootFrom.x + (i + 1) * diffX, rootFrom.y + (i + 1) * diffY)
308 fakeDateTime.currentTimeMs += timeStep
309 var event = touchEvent(item)
310 event.release(0 /* touchId */, rootTo.x, rootTo.y)
315 // perform a drag in the given direction until the given condition is true
316 // The condition is a function to be evaluated after every step
317 function touchDragUntil(item, startX, startY, stepX, stepY, condition) {
318 multiTouchDragUntil([0], item, startX, startY, stepX, stepY, condition);
321 function multiTouchDragUntil(touchIds, item, startX, startY, stepX, stepY, condition) {
322 var root = fetchRootItem(item);
323 var pos = item.mapToItem(root, startX, startY);
325 // convert step to scene coords
327 var stepStart = item.mapToItem(root, 0, 0);
328 var stepEnd = item.mapToItem(root, stepX, stepY);
330 stepX = stepEnd.x - stepStart.x;
331 stepY = stepEnd.y - stepStart.y;
333 var event = touchEvent(item)
334 for (var i = 0; i < touchIds.length; i++) {
335 event.press(touchIds[i], pos.x, pos.y)
339 // we have to stop at some point
343 while (!condition() && stepsDone < maxSteps) {
345 fakeDateTime.currentTimeMs += 25;
350 event = touchEvent(item);
351 for (i = 0; i < touchIds.length; i++) {
352 event.move(touchIds[i], pos.x, pos.y);
359 event = touchEvent(item)
360 for (i = 0; i < touchIds.length; i++) {
361 event.release(touchIds[i], pos.x, pos.y)
366 function touchMove(item, tox, toy) { multiTouchMove(0, item, tox, toy); }
368 function multiTouchMove(touchId, item, tox, toy) {
369 if (typeof touchId !== "number") touchId = 0;
370 var root = fetchRootItem(item)
371 var rootPoint = item.mapToItem(root, tox, toy)
373 var event = touchEvent(item);
374 event.move(touchId, rootPoint.x, rootPoint.y);
378 function touchPinch(item, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End) {
379 // Make sure the item is rendered
380 waitForRendering(item);
382 var event1 = touchEvent(item);
384 event1.press(0, x1Start, y1Start);
387 event1.move(0, x1Start, y1Start);
388 event1.press(1, x2Start, y2Start);
392 for (var i = 0.0; i < 1.0; i += 0.02) {
393 event1.move(0, x1Start + (x1End - x1Start) * i, y1Start + (y1End - y1Start) * i);
394 event1.move(1, x2Start + (x2End - x2Start) * i, y2Start + (y2End - y2Start) * i);
399 event1.release(0, x1End, y1End);
400 event1.release(1, x2End, y2End);
404 function fetchRootItem(item) {
406 return fetchRootItem(item.parent)
411 function touchPress(item, x, y) { multiTouchPress(0, item, x, y, []); }
413 /*! \brief Release a touch point
415 \param touchId The touchId to be pressed
417 \param x The x coordinate of the press, defaults to horizontal center
418 \param y The y coordinate of the press, defaults to vertical center
419 \param stationaryPoints An array of touchIds which are "already touched"
421 function multiTouchPress(touchId, item, x, y, stationaryPoints) {
422 if (typeof touchId !== "number") touchId = 0;
423 if (typeof x !== "number") x = item.width / 2;
424 if (typeof y !== "number") y = item.height / 2;
425 if (typeof stationaryPoints !== "object") stationaryPoints = []
426 var root = fetchRootItem(item)
427 var rootPoint = item.mapToItem(root, x, y)
429 var event = touchEvent(item)
430 event.press(touchId, rootPoint.x, rootPoint.y)
431 for (var i = 0; i < stationaryPoints.length; i++) {
432 event.stationary(stationaryPoints[i]);
437 function touchRelease(item, x, y) { multiTouchRelease(0, item, x, y, []); }
439 /*! \brief Release a touch point
441 \param touchId The touchId to be released
443 \param x The x coordinate of the release, defaults to horizontal center
444 \param y The y coordinate of the release, defaults to vertical center
445 \param stationaryPoints An array of touchIds which are "still touched"
447 function multiTouchRelease(touchId, item, x, y, stationaryPoints) {
448 if (typeof touchId !== "number") touchId = 0;
449 if (typeof x !== "number") x = item.width / 2;
450 if (typeof y !== "number") y = item.height / 2;
451 if (typeof stationaryPoints !== "object") stationaryPoints = []
452 var root = fetchRootItem(item)
453 var rootPoint = item.mapToItem(root, x, y)
455 var event = touchEvent(item)
456 event.release(touchId, rootPoint.x, rootPoint.y)
457 for (var i = 0; i < stationaryPoints.length; i++) {
458 event.stationary(stationaryPoints[i]);
463 /*! \brief Tap the item with a touch event.
465 \param item The item to be tapped
466 \param x The x coordinate of the tap, defaults to horizontal center
467 \param y The y coordinate of the tap, defaults to vertical center
469 function tap(item, x, y) {
470 multiTouchTap([0], item, x, y);
473 function multiTouchTap(touchIds, item, x, y) {
474 if (typeof touchIds !== "object") touchIds = [0];
475 if (typeof x !== "number") x = item.width / 2;
476 if (typeof y !== "number") y = item.height / 2;
478 var root = fetchRootItem(item)
479 var rootPoint = item.mapToItem(root, x, y)
481 var event = touchEvent(item)
482 for (var i = 0; i < touchIds.length; i++) {
483 event.press(touchIds[i], rootPoint.x, rootPoint.y)
487 event = touchEvent(item)
488 for (i = 0; i < touchIds.length; i++) {
489 event.release(touchIds[i], rootPoint.x, rootPoint.y)
495 Component.onCompleted: {
496 var rootItem = parent;
497 while (rootItem.parent != undefined) {
498 rootItem = rootItem.parent;
500 removeTimeConstraintsFromDirectionalDragAreas(rootItem);
504 In qmltests, sequences of touch events are sent all at once, unlike in "real life".
505 Also qmltests might run really slowly, e.g. when run from inside virtual machines.
506 Thus to remove a variable that qmltests cannot really control, namely time, this
507 function removes all constraints from DirectionalDragAreas that are sensible to
510 This effectively makes DirectionalDragAreas easier to fool.
512 function removeTimeConstraintsFromDirectionalDragAreas(item) {
514 // use duck-typing to identify a DirectionalDragArea
515 if (item.removeTimeConstraints != undefined) {
516 item.removeTimeConstraints();
518 for (var i in item.children) {
519 removeTimeConstraintsFromDirectionalDragAreas(item.children[i]);
524 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
525 function waitForRendering(item, timeout) {
526 if (timeout === undefined)
529 qtest_fail("No item given to waitForRendering", 1);
530 return qtest_results.waitForRendering(item, timeout);
534 Wait until any transition animation has finished for the given StateGroup or Item
536 function waitUntilTransitionsEnd(stateGroup) {
537 var transitions = stateGroup.transitions;
538 for (var i = 0; i < transitions.length; ++i) {
539 var transition = transitions[i];
540 tryCompare(transition, "running", false, 2000);
545 kill all (fake) running apps but unity8-dash, bringing Unity.Application back to its initial state
547 function killApps() {
548 while (ApplicationManager.count > 1) {
549 var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
550 var application = ApplicationManager.get(appIndex);
551 ApplicationManager.stopApplication(application.appId);
552 // wait until all zombie surfaces are gone. As MirSurfaceItems hold references over them.
553 // They won't be gone until those surface items are destroyed.
554 tryCompareFunction(function() { return application.surfaceList.count }, 0);
555 tryCompare(application, "state", ApplicationInfo.Stopped);
557 compare(ApplicationManager.count, 1);
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list