on 1163086 help sign in
unity8/tests/utils/modules/Unity/Test/UnityTestCase.qml
426 lines 15 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 // This is needed for waitForRendering calls to return
27 // if the watched element already got rendered
28 Rectangle {
29 width: units.gu(1)
30 height: width
31 parent: testCase.parent
32 border { width: units.dp(1); color: "black" }
33 opacity: 0.6
35 RotationAnimation on rotation {
36 running: true
37 from: 0
38 to: 360
39 loops: Animation.Infinite
40 duration: 1000
41 }
42 }
44 // Fake implementation to be provided to items under test
45 property var fakeDateTime: new function() {
46 this.currentTimeMs = 0
47 this.getCurrentTimeMs = function() {return this.currentTimeMs}
48 }
50 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
51 function mouseClick(item, x, y, button, modifiers, delay) {
52 if (button === undefined)
53 button = Qt.LeftButton;
54 if (modifiers === undefined)
55 modifiers = Qt.NoModifier;
56 if (delay === undefined)
57 delay = -1;
58 if (x === undefined)
59 x = item.width / 2;
60 if (y === undefined)
61 y = item.height / 2;
62 if (!qtest_events.mouseClick(item, x, y, button, modifiers, delay))
63 qtest_fail("window not shown", 2);
64 }
66 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
67 function mouseDoubleClick(item, x, y, button, modifiers, delay) {
68 if (button === undefined)
69 button = Qt.LeftButton;
70 if (modifiers === undefined)
71 modifiers = Qt.NoModifier;
72 if (delay === undefined)
73 delay = -1;
74 if (x === undefined)
75 x = item.width / 2;
76 if (y === undefined)
77 y = item.height / 2;
78 if (!qtest_events.mouseDoubleClick(item, x, y, button, modifiers, delay))
79 qtest_fail("window not shown", 2)
80 }
82 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
83 function mousePress(item, x, y, button, modifiers, delay) {
84 if (button === undefined)
85 button = Qt.LeftButton;
86 if (modifiers === undefined)
87 modifiers = Qt.NoModifier;
88 if (delay === undefined)
89 delay = -1;
90 if (x === undefined)
91 x = item.width / 2;
92 if (y === undefined)
93 y = item.height / 2;
94 if (!qtest_events.mousePress(item, x, y, button, modifiers, delay))
95 qtest_fail("window not shown", 2)
96 }
98 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
99 function mouseRelease(item, x, y, button, modifiers, delay) {
100 if (button === undefined)
101 button = Qt.LeftButton;
102 if (modifiers === undefined)
103 modifiers = Qt.NoModifier;
104 if (delay === undefined)
105 delay = -1;
106 if (x === undefined)
107 x = item.width / 2;
108 if (y === undefined)
109 y = item.height / 2;
110 if (!qtest_events.mouseRelease(item, x, y, button, modifiers, delay))
111 qtest_fail("window not shown", 2)
112 }
115 // Flickable won't recognise a single mouse move as dragging the flickable.
116 // Use 5 steps because it's what
117 // Qt uses in QQuickViewTestUtil::flick
118 // speed is in pixels/second
119 function mouseFlick(item, x, y, toX, toY, pressMouse, releaseMouse,
120 speed, iterations) {
121 pressMouse = ((pressMouse != null) ? pressMouse : true); // Default to true for pressMouse if not present
122 releaseMouse = ((releaseMouse != null) ? releaseMouse : true); // Default to true for releaseMouse if not present
124 // set a default speed if not specified
125 speed = (speed != null) ? speed : units.gu(10);
127 // set a default iterations if not specified
128 iterations = (iterations !== undefined) ? iterations : 5
130 var distance = Math.sqrt(Math.pow(toX - x, 2) + Math.pow(toY - y, 2))
131 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
133 var timeStep = totalTime / iterations
134 var diffX = (toX - x) / iterations
135 var diffY = (toY - y) / iterations
136 if (pressMouse) {
137 fakeDateTime.currentTimeMs += timeStep
138 mousePress(item, x, y)
139 }
140 for (var i = 0; i < iterations; ++i) {
141 fakeDateTime.currentTimeMs += timeStep
142 if (i === iterations - 1) {
143 // Avoid any rounding errors by making the last move be at precisely
144 // the point specified
145 mouseMove(item, toX, toY, iterations / speed)
146 } else {
147 mouseMove(item, x + (i + 1) * diffX, y + (i + 1) * diffY, iterations / speed)
148 }
149 }
150 if (releaseMouse) {
151 fakeDateTime.currentTimeMs += timeStep
152 mouseRelease(item, toX, toY)
153 }
154 }
157 // Find an object with the given name in the children tree of "obj"
158 function findChild(obj, objectName) {
159 return findChildIn(obj, "children", objectName);
160 }
162 // Find an object with the given name in the children tree of "obj"
163 // Including invisible children like animations, timers etc.
164 // Note: you should use findChild if you're not sure you need this
165 // as this tree is much bigger and might contain stuff that goes
166 // away randomly.
167 function findInvisibleChild(obj, objectName) {
168 return findChildIn(obj, "data", objectName);
169 }
171 // Find a child in the named property
172 function findChildIn(obj, prop, objectName) {
173 var childs = new Array(0);
174 childs.push(obj)
175 while (childs.length > 0) {
176 if (childs[0].objectName == objectName) {
177 return childs[0]
178 }
179 for (var i in childs[0][prop]) {
180 childs.push(childs[0][prop][i])
181 }
182 childs.splice(0, 1);
183 }
184 return null;
185 }
187 function findChildsByType(obj, typeName) {
188 var res = new Array(0);
189 for (var i in obj.children) {
190 var c = obj.children[i];
191 if (UT.Util.isInstanceOf(c, typeName)) {
192 res.push(c)
193 }
194 res = res.concat(findChildsByType(c, typeName));
195 }
196 return res;
197 }
199 // Type a full string instead of keyClick letter by letter
200 function typeString(str) {
201 for (var i = 0; i < str.length; i++) {
202 keyClick(str[i])
203 }
204 }
206 // Keeps executing a given parameter-less function until it returns the given
207 // expected result or the timemout is reached (in which case a test failure
208 // is generated)
209 function tryCompareFunction(func, expectedResult, timeout) {
210 var timeSpent = 0
211 if (timeout === undefined)
212 timeout = 5000;
213 var success = false
214 var actualResult
215 while (timeSpent < timeout && !success) {
216 actualResult = func()
217 success = qtest_compareInternal(actualResult, expectedResult)
218 if (success === false) {
219 wait(50)
220 timeSpent += 50
221 }
222 }
224 var act = qtest_results.stringify(actualResult)
225 var exp = qtest_results.stringify(expectedResult)
226 if (!qtest_results.compare(success,
227 "function returned unexpected result",
228 act, exp,
229 util.callerFile(), util.callerLine())) {
230 throw new Error("QtQuickTest::fail")
231 }
232 }
234 function flickToYEnd(item) {
235 var i = 0;
236 var x = item.width / 2;
237 var y = item.height - units.gu(1);
238 var toY = units.gu(1);
239 while (i < 5 && !item.atYEnd) {
240 touchFlick(item, x, y, x, toY);
241 tryCompare(item, "moving", false);
242 ++i;
243 }
244 tryCompare(item, "atYEnd", true);
245 }
247 function touchEvent(item) {
248 return UT.Util.touchEvent(item)
249 }
251 // speed is in pixels/second
252 function touchFlick(item, x, y, toX, toY, beginTouch, endTouch, speed, iterations) {
253 // Make sure the item is rendered
254 waitForRendering(item);
256 var root = fetchRootItem(item);
257 var rootFrom = item.mapToItem(root, x, y);
258 var rootTo = item.mapToItem(root, toX, toY);
260 // Default to true for beginTouch if not present
261 beginTouch = (beginTouch !== undefined) ? beginTouch : true
263 // Default to true for endTouch if not present
264 endTouch = (endTouch !== undefined) ? endTouch : true
266 // Set a default speed if not specified
267 speed = (speed !== undefined) ? speed : units.gu(10)
269 // Set a default iterations if not specified
270 var iterations = (iterations !== undefined) ? iterations : 5
272 var distance = Math.sqrt(Math.pow(rootTo.x - rootFrom.x, 2) + Math.pow(rootTo.Y - rootFrom.y, 2))
273 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
275 var timeStep = totalTime / iterations
276 var diffX = (rootTo.x - rootFrom.x) / iterations
277 var diffY = (rootTo.y - rootFrom.y) / iterations
278 if (beginTouch) {
279 fakeDateTime.currentTimeMs += timeStep
281 var event = touchEvent(item)
282 event.press(0 /* touchId */, rootFrom.x, rootFrom.y)
283 event.commit()
284 }
285 for (var i = 0; i < iterations; ++i) {
286 fakeDateTime.currentTimeMs += timeStep
287 if (i === iterations - 1) {
288 // Avoid any rounding errors by making the last move be at precisely
289 // the point specified
290 wait(iterations / speed)
291 var event = touchEvent(item)
292 event.move(0 /* touchId */, rootTo.x, rootTo.y)
293 event.commit()
294 } else {
295 wait(iterations / speed)
296 var event = touchEvent(item)
297 event.move(0 /* touchId */, rootFrom.x + (i + 1) * diffX, rootFrom.y + (i + 1) * diffY)
298 event.commit()
299 }
300 }
301 if (endTouch) {
302 fakeDateTime.currentTimeMs += timeStep
303 var event = touchEvent(item)
304 event.release(0 /* touchId */, rootTo.x, rootTo.y)
305 event.commit()
306 }
307 }
309 function touchPinch(item, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End) {
310 // Make sure the item is rendered
311 waitForRendering(item);
313 var event1 = touchEvent(item);
314 // first finger
315 event1.press(0, x1Start, y1Start);
316 event1.commit();
317 // second finger
318 event1.stationary(0);
319 event1.press(1, x2Start, y2Start);
320 event1.commit();
322 // pinch
323 for (var i = 0.0; i < 1.0; i += 0.02) {
324 event1.move(0, x1Start + (x1End - x1Start) * i, y1Start + (y1End - y1Start) * i);
325 event1.move(1, x2Start + (x2End - x2Start) * i, y2Start + (y2End - y2Start) * i);
326 event1.commit();
327 }
329 // release
330 event1.release(0, x1End, y1End);
331 event1.release(1, x2End, y2End);
332 event1.commit();
333 }
335 function fetchRootItem(item) {
336 if (item.parent)
337 return fetchRootItem(item.parent)
338 else
339 return item
340 }
342 function touchPress(item, x, y) {
343 var root = fetchRootItem(item)
344 var rootPoint = item.mapToItem(root, x, y)
346 var event = touchEvent(item)
347 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
348 event.commit()
349 }
351 function touchRelease(item, x, y) {
352 var root = fetchRootItem(item)
353 var rootPoint = item.mapToItem(root, x, y)
355 var event = touchEvent(item)
356 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
357 event.commit()
358 }
360 /*! \brief Tap the item with a touch event.
362 \param item The item to be tapped
363 \param x The x coordinate of the tap, defaults to horizontal center
364 \param y The y coordinate of the tap, defaults to vertical center
365 */
366 function tap(item, x, y) {
367 if (typeof x !== "number") x = item.width / 2;
368 if (typeof y !== "number") y = item.height / 2;
370 var root = fetchRootItem(item)
371 var rootPoint = item.mapToItem(root, x, y)
373 var event = touchEvent(item)
374 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
375 event.commit()
377 event = touchEvent(item)
378 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
379 event.commit()
380 }
382 Component.onCompleted: {
383 UT.Util.ensureTouchRegistryInstalled();
385 var rootItem = parent;
386 while (rootItem.parent != undefined) {
387 rootItem = rootItem.parent;
388 }
389 removeTimeConstraintsFromDirectionalDragAreas(rootItem);
390 }
392 /*
393 In qmltests, sequences of touch events are sent all at once, unlike in "real life".
394 Also qmltests might run really slowly, e.g. when run from inside virtual machines.
395 Thus to remove a variable that qmltests cannot really control, namely time, this
396 function removes all constraints from DirectionalDragAreas that are sensible to
397 elapsed time.
399 This effectively makes DirectionalDragAreas easier to fool.
400 */
401 function removeTimeConstraintsFromDirectionalDragAreas(item) {
403 // use duck-typing to identify a DirectionalDragArea
404 if (item.minSpeed != undefined
405 && item.maxSilenceTime != undefined
406 && item.compositionTime != undefined) {
407 item.minSpeed = 0;
408 item.maxSilenceTime = 60 * 60 * 1000;
409 item.compositionTime = 0;
410 } else {
411 for (var i in item.children) {
412 removeTimeConstraintsFromDirectionalDragAreas(item.children[i]);
413 }
414 }
415 }
417 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
418 function waitForRendering(item, timeout) {
419 if (timeout === undefined)
420 timeout = 5000;
421 if (!item)
422 qtest_fail("No item given to waitForRendering", 1);
423 return qtest_results.waitForRendering(item, timeout);
424 }