on d424707 help sign in
unity8/tests/utils/modules/Unity/Test/UnityTestCase.qml
391 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 // Type a full string instead of keyClick letter by letter
178 function typeString(str) {
179 for (var i = 0; i < str.length; i++) {
180 keyClick(str[i])
181 }
182 }
184 // Keeps executing a given parameter-less function until it returns the given
185 // expected result or the timemout is reached (in which case a test failure
186 // is generated)
187 function tryCompareFunction(func, expectedResult, timeout) {
188 var timeSpent = 0
189 if (timeout === undefined)
190 timeout = 5000;
191 var success = false
192 var actualResult
193 while (timeSpent < timeout && !success) {
194 actualResult = func()
195 success = qtest_compareInternal(actualResult, expectedResult)
196 if (success === false) {
197 wait(50)
198 timeSpent += 50
199 }
200 }
202 var act = qtest_results.stringify(actualResult)
203 var exp = qtest_results.stringify(expectedResult)
204 if (!qtest_results.compare(success,
205 "function returned unexpected result",
206 act, exp,
207 util.callerFile(), util.callerLine())) {
208 throw new Error("QtQuickTest::fail")
209 }
210 }
212 function touchEvent() {
213 return UT.Util.touchEvent()
214 }
216 // speed is in pixels/second
217 function touchFlick(item, x, y, toX, toY, beginTouch, endTouch, speed, iterations) {
218 // Make sure the item is rendered
219 waitForRendering(item);
221 var root = fetchRootItem(item);
222 var rootFrom = item.mapToItem(root, x, y);
223 var rootTo = item.mapToItem(root, toX, toY);
225 // Default to true for beginTouch if not present
226 beginTouch = (beginTouch !== undefined) ? beginTouch : true
228 // Default to true for endTouch if not present
229 endTouch = (endTouch !== undefined) ? endTouch : true
231 // Set a default speed if not specified
232 speed = (speed !== undefined) ? speed : units.gu(10)
234 // Set a default iterations if not specified
235 var iterations = (iterations !== undefined) ? iterations : 5
237 var distance = Math.sqrt(Math.pow(rootTo.x - rootFrom.x, 2) + Math.pow(rootTo.Y - rootFrom.y, 2))
238 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
240 var timeStep = totalTime / iterations
241 var diffX = (rootTo.x - rootFrom.x) / iterations
242 var diffY = (rootTo.y - rootFrom.y) / iterations
243 if (beginTouch) {
244 fakeDateTime.currentTimeMs += timeStep
246 var event = touchEvent()
247 event.press(0 /* touchId */, rootFrom.x, rootFrom.y)
248 event.commit()
249 }
250 for (var i = 0; i < iterations; ++i) {
251 fakeDateTime.currentTimeMs += timeStep
252 if (i === iterations - 1) {
253 // Avoid any rounding errors by making the last move be at precisely
254 // the point specified
255 wait(iterations / speed)
256 var event = touchEvent()
257 event.move(0 /* touchId */, rootTo.x, rootTo.y)
258 event.commit()
259 } else {
260 wait(iterations / speed)
261 var event = touchEvent()
262 event.move(0 /* touchId */, rootFrom.x + (i + 1) * diffX, rootFrom.y + (i + 1) * diffY)
263 event.commit()
264 }
265 }
266 if (endTouch) {
267 fakeDateTime.currentTimeMs += timeStep
268 var event = touchEvent()
269 event.release(0 /* touchId */, rootTo.x, rootTo.y)
270 event.commit()
271 }
272 }
274 function touchPinch(item, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End) {
275 // Make sure the item is rendered
276 waitForRendering(item);
278 var event1 = touchEvent();
279 // first finger
280 event1.press(0, x1Start, y1Start);
281 event1.commit();
282 // second finger
283 event1.stationary(0);
284 event1.press(1, x2Start, y2Start);
285 event1.commit();
287 // pinch
288 for (var i = 0.0; i < 1.0; i += 0.02) {
289 event1.move(0, x1Start + (x1End - x1Start) * i, y1Start + (y1End - y1Start) * i);
290 event1.move(1, x2Start + (x2End - x2Start) * i, y2Start + (y2End - y2Start) * i);
291 event1.commit();
292 }
294 // release
295 event1.release(0, x1End, y1End);
296 event1.release(1, x2End, y2End);
297 event1.commit();
298 }
300 function fetchRootItem(item) {
301 if (item.parent)
302 return fetchRootItem(item.parent)
303 else
304 return item
305 }
307 function touchPress(item, x, y) {
308 var root = fetchRootItem(item)
309 var rootPoint = item.mapToItem(root, x, y)
311 var event = touchEvent()
312 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
313 event.commit()
314 }
316 function touchRelease(item, x, y) {
317 var root = fetchRootItem(item)
318 var rootPoint = item.mapToItem(root, x, y)
320 var event = touchEvent()
321 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
322 event.commit()
323 }
325 /*! \brief Tap the item with a touch event.
327 \param item The item to be tapped
328 \param x The x coordinate of the tap, defaults to horizontal center
329 \param y The y coordinate of the tap, defaults to vertical center
330 */
331 function tap(item, x, y) {
332 if (typeof x !== "number") x = item.width / 2;
333 if (typeof y !== "number") y = item.height / 2;
335 var root = fetchRootItem(item)
336 var rootPoint = item.mapToItem(root, x, y)
338 var event = touchEvent()
339 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
340 event.commit()
342 event = touchEvent()
343 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
344 event.commit()
345 }
347 Component.onCompleted: {
348 UT.Util.ensureTouchRegistryInstalled();
350 var rootItem = parent;
351 while (rootItem.parent != undefined) {
352 rootItem = rootItem.parent;
353 }
354 removeTimeConstraintsFromDirectionalDragAreas(rootItem);
355 }
357 /*
358 In qmltests, sequences of touch events are sent all at once, unlike in "real life".
359 Also qmltests might run really slowly, e.g. when run from inside virtual machines.
360 Thus to remove a variable that qmltests cannot really control, namely time, this
361 function removes all constraints from DirectionalDragAreas that are sensible to
362 elapsed time.
364 This effectively makes DirectionalDragAreas easier to fool.
365 */
366 function removeTimeConstraintsFromDirectionalDragAreas(item) {
368 // use duck-typing to identify a DirectionalDragArea
369 if (item.minSpeed != undefined
370 && item.maxSilenceTime != undefined
371 && item.compositionTime != undefined) {
372 item.minSpeed = 0;
373 item.maxSilenceTime = 60 * 60 * 1000;
374 item.compositionTime = 0;
375 } else {
376 for (var i in item.children) {
377 removeTimeConstraintsFromDirectionalDragAreas(item.children[i]);
378 }
379 }
380 }
382 // TODO This function can be removed altogether once we use Qt 5.5 which has the same feature
383 function waitForRendering(item, timeout) {
384 if (timeout === undefined)
385 timeout = 5000;
386 if (!item)
387 qtest_fail("No item given to waitForRendering", 1);
388 return qtest_results.waitForRendering(item, timeout);
389 }