on cd24bfb help sign in
unity8/tests/utils/modules/Unity/Test/UnityTestCase.qml
402 lines 14 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 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
41 function mouseClick(item, x, y, button, modifiers, delay) {
42 if (button === undefined)
43 button = Qt.LeftButton;
44 if (modifiers === undefined)
45 modifiers = Qt.NoModifier;
46 if (delay === undefined)
47 delay = -1;
48 if (x === undefined)
49 x = item.width / 2;
50 if (y === undefined)
51 y = item.height / 2;
52 if (!qtest_events.mouseClick(item, x, y, button, modifiers, delay))
53 qtest_fail("window not shown", 2);
54 }
56 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
57 function mouseDoubleClick(item, x, y, button, modifiers, delay) {
58 if (button === undefined)
59 button = Qt.LeftButton;
60 if (modifiers === undefined)
61 modifiers = Qt.NoModifier;
62 if (delay === undefined)
63 delay = -1;
64 if (x === undefined)
65 x = item.width / 2;
66 if (y === undefined)
67 y = item.height / 2;
68 if (!qtest_events.mouseDoubleClick(item, x, y, button, modifiers, delay))
69 qtest_fail("window not shown", 2)
70 }
72 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
73 function mousePress(item, x, y, button, modifiers, delay) {
74 if (button === undefined)
75 button = Qt.LeftButton;
76 if (modifiers === undefined)
77 modifiers = Qt.NoModifier;
78 if (delay === undefined)
79 delay = -1;
80 if (x === undefined)
81 x = item.width / 2;
82 if (y === undefined)
83 y = item.height / 2;
84 if (!qtest_events.mousePress(item, x, y, button, modifiers, delay))
85 qtest_fail("window not shown", 2)
86 }
88 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
89 function mouseRelease(item, x, y, button, modifiers, delay) {
90 if (button === undefined)
91 button = Qt.LeftButton;
92 if (modifiers === undefined)
93 modifiers = Qt.NoModifier;
94 if (delay === undefined)
95 delay = -1;
96 if (x === undefined)
97 x = item.width / 2;
98 if (y === undefined)
99 y = item.height / 2;
100 if (!qtest_events.mouseRelease(item, x, y, button, modifiers, delay))
101 qtest_fail("window not shown", 2)
102 }
105 // Flickable won't recognise a single mouse move as dragging the flickable.
106 // Use 5 steps because it's what
107 // Qt uses in QQuickViewTestUtil::flick
108 // speed is in pixels/second
109 function mouseFlick(item, x, y, toX, toY, pressMouse, releaseMouse,
110 speed, iterations) {
111 pressMouse = ((pressMouse != null) ? pressMouse : true); // Default to true for pressMouse if not present
112 releaseMouse = ((releaseMouse != null) ? releaseMouse : true); // Default to true for releaseMouse if not present
114 // set a default speed if not specified
115 speed = (speed != null) ? speed : units.gu(10);
117 // set a default iterations if not specified
118 iterations = (iterations !== undefined) ? iterations : 5
120 var distance = Math.sqrt(Math.pow(toX - x, 2) + Math.pow(toY - y, 2))
121 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
123 var timeStep = totalTime / iterations
124 var diffX = (toX - x) / iterations
125 var diffY = (toY - y) / iterations
126 if (pressMouse) {
127 fakeDateTime.currentTimeMs += timeStep
128 mousePress(item, x, y)
129 }
130 for (var i = 0; i < iterations; ++i) {
131 fakeDateTime.currentTimeMs += timeStep
132 if (i === iterations - 1) {
133 // Avoid any rounding errors by making the last move be at precisely
134 // the point specified
135 mouseMove(item, toX, toY, iterations / speed)
136 } else {
137 mouseMove(item, x + (i + 1) * diffX, y + (i + 1) * diffY, iterations / speed)
138 }
139 }
140 if (releaseMouse) {
141 fakeDateTime.currentTimeMs += timeStep
142 mouseRelease(item, toX, toY)
143 }
144 }
147 // Find an object with the given name in the children tree of "obj"
148 function findChild(obj, objectName) {
149 return findChildIn(obj, "children", objectName);
150 }
152 // Find an object with the given name in the children tree of "obj"
153 // Including invisible children like animations, timers etc.
154 // Note: you should use findChild if you're not sure you need this
155 // as this tree is much bigger and might contain stuff that goes
156 // away randomly.
157 function findInvisibleChild(obj, objectName) {
158 return findChildIn(obj, "data", objectName);
159 }
161 // Find a child in the named property
162 function findChildIn(obj, prop, objectName) {
163 var childs = new Array(0);
164 childs.push(obj)
165 while (childs.length > 0) {
166 if (childs[0].objectName == objectName) {
167 return childs[0]
168 }
169 for (var i in childs[0][prop]) {
170 childs.push(childs[0][prop][i])
171 }
172 childs.splice(0, 1);
173 }
174 return null;
175 }
177 function findChildsByType(obj, typeName) {
178 var res = new Array(0);
179 for (var i in obj.children) {
180 var c = obj.children[i];
181 if (UT.Util.isInstanceOf(c, typeName)) {
182 res.push(c)
183 }
184 res = res.concat(findChildsByType(c, typeName));
185 }
186 return res;
187 }
189 // Type a full string instead of keyClick letter by letter
190 function typeString(str) {
191 for (var i = 0; i < str.length; i++) {
192 keyClick(str[i])
193 }
194 }
196 // Keeps executing a given parameter-less function until it returns the given
197 // expected result or the timemout is reached (in which case a test failure
198 // is generated)
199 function tryCompareFunction(func, expectedResult, timeout) {
200 var timeSpent = 0
201 if (timeout === undefined)
202 timeout = 5000;
203 var success = false
204 var actualResult
205 while (timeSpent < timeout && !success) {
206 actualResult = func()
207 success = qtest_compareInternal(actualResult, expectedResult)
208 if (success === false) {
209 wait(50)
210 timeSpent += 50
211 }
212 }
214 var act = qtest_results.stringify(actualResult)
215 var exp = qtest_results.stringify(expectedResult)
216 if (!qtest_results.compare(success,
217 "function returned unexpected result",
218 act, exp,
219 util.callerFile(), util.callerLine())) {
220 throw new Error("QtQuickTest::fail")
221 }
222 }
224 function touchEvent(item) {
225 return UT.Util.touchEvent(item)
226 }
228 // speed is in pixels/second
229 function touchFlick(item, x, y, toX, toY, beginTouch, endTouch, speed, iterations) {
230 // Make sure the item is rendered
231 waitForRendering(item);
233 var root = fetchRootItem(item);
234 var rootFrom = item.mapToItem(root, x, y);
235 var rootTo = item.mapToItem(root, toX, toY);
237 // Default to true for beginTouch if not present
238 beginTouch = (beginTouch !== undefined) ? beginTouch : true
240 // Default to true for endTouch if not present
241 endTouch = (endTouch !== undefined) ? endTouch : true
243 // Set a default speed if not specified
244 speed = (speed !== undefined) ? speed : units.gu(10)
246 // Set a default iterations if not specified
247 var iterations = (iterations !== undefined) ? iterations : 5
249 var distance = Math.sqrt(Math.pow(rootTo.x - rootFrom.x, 2) + Math.pow(rootTo.Y - rootFrom.y, 2))
250 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
252 var timeStep = totalTime / iterations
253 var diffX = (rootTo.x - rootFrom.x) / iterations
254 var diffY = (rootTo.y - rootFrom.y) / iterations
255 if (beginTouch) {
256 fakeDateTime.currentTimeMs += timeStep
258 var event = touchEvent(item)
259 event.press(0 /* touchId */, rootFrom.x, rootFrom.y)
260 event.commit()
261 }
262 for (var i = 0; i < iterations; ++i) {
263 fakeDateTime.currentTimeMs += timeStep
264 if (i === iterations - 1) {
265 // Avoid any rounding errors by making the last move be at precisely
266 // the point specified
267 wait(iterations / speed)
268 var event = touchEvent(item)
269 event.move(0 /* touchId */, rootTo.x, rootTo.y)
270 event.commit()
271 } else {
272 wait(iterations / speed)
273 var event = touchEvent(item)
274 event.move(0 /* touchId */, rootFrom.x + (i + 1) * diffX, rootFrom.y + (i + 1) * diffY)
275 event.commit()
276 }
277 }
278 if (endTouch) {
279 fakeDateTime.currentTimeMs += timeStep
280 var event = touchEvent(item)
281 event.release(0 /* touchId */, rootTo.x, rootTo.y)
282 event.commit()
283 }
284 }
286 function touchPinch(item, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End) {
287 // Make sure the item is rendered
288 waitForRendering(item);
290 var event1 = touchEvent(item);
291 // first finger
292 event1.press(0, x1Start, y1Start);
293 event1.commit();
294 // second finger
295 event1.move(0, x1Start, y1Start);
296 event1.press(1, x2Start, y2Start);
297 event1.commit();
298 // pinch
299 for (var i = 0.0; i < 1.0; i += 0.02) {
300 event1.move(0, x1Start + (x1End - x1Start) * i, y1Start + (y1End - y1Start) * i);
301 event1.move(1, x2Start + (x2End - x2Start) * i, y2Start + (y2End - y2Start) * i);
302 event1.commit();
303 }
305 // release
306 event1.release(0, x1End, y1End);
307 event1.release(1, x2End, y2End);
308 event1.commit();
309 }
311 function fetchRootItem(item) {
312 if (item.parent)
313 return fetchRootItem(item.parent)
314 else
315 return item
316 }
318 function touchPress(item, x, y) {
319 var root = fetchRootItem(item)
320 var rootPoint = item.mapToItem(root, x, y)
322 var event = touchEvent(item)
323 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
324 event.commit()
325 }
327 function touchRelease(item, x, y) {
328 var root = fetchRootItem(item)
329 var rootPoint = item.mapToItem(root, x, y)
331 var event = touchEvent(item)
332 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
333 event.commit()
334 }
336 /*! \brief Tap the item with a touch event.
338 \param item The item to be tapped
339 \param x The x coordinate of the tap, defaults to horizontal center
340 \param y The y coordinate of the tap, defaults to vertical center
341 */
342 function tap(item, x, y) {
343 if (typeof x !== "number") x = item.width / 2;
344 if (typeof y !== "number") y = item.height / 2;
346 var root = fetchRootItem(item)
347 var rootPoint = item.mapToItem(root, x, y)
349 var event = touchEvent(item)
350 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
351 event.commit()
353 event = touchEvent(item)
354 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
355 event.commit()
356 }
358 Component.onCompleted: {
359 UT.Util.ensureTouchRegistryInstalled();
361 var rootItem = parent;
362 while (rootItem.parent != undefined) {
363 rootItem = rootItem.parent;
364 }
365 removeTimeConstraintsFromDirectionalDragAreas(rootItem);
366 }
368 /*
369 In qmltests, sequences of touch events are sent all at once, unlike in "real life".
370 Also qmltests might run really slowly, e.g. when run from inside virtual machines.
371 Thus to remove a variable that qmltests cannot really control, namely time, this
372 function removes all constraints from DirectionalDragAreas that are sensible to
373 elapsed time.
375 This effectively makes DirectionalDragAreas easier to fool.
376 */
377 function removeTimeConstraintsFromDirectionalDragAreas(item) {
379 // use duck-typing to identify a DirectionalDragArea
380 if (item.minSpeed != undefined
381 && item.maxSilenceTime != undefined
382 && item.compositionTime != undefined) {
383 item.minSpeed = 0;
384 item.maxSilenceTime = 60 * 60 * 1000;
385 item.compositionTime = 0;
386 } else {
387 for (var i in item.children) {
388 removeTimeConstraintsFromDirectionalDragAreas(item.children[i]);
389 }
390 }
391 }
393 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
394 function waitForRendering(item, timeout) {
395 if (timeout === undefined)
396 timeout = 5000;
397 if (!item)
398 qtest_fail("No item given to waitForRendering", 1);
399 return qtest_results.waitForRendering(item, timeout);
400 }