on aa2feff help sign in
unity8/tests/utils/modules/Unity/Test/UnityTestCase.qml
384 lines 13 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 // Flickable won't recognise a single mouse move as dragging the flickable.
41 // Use 5 steps because it's what
42 // Qt uses in QQuickViewTestUtil::flick
43 // speed is in pixels/second
44 function mouseFlick(item, x, y, toX, toY, pressMouse, releaseMouse,
45 speed, iterations) {
46 pressMouse = ((pressMouse != null) ? pressMouse : true); // Default to true for pressMouse if not present
47 releaseMouse = ((releaseMouse != null) ? releaseMouse : true); // Default to true for releaseMouse if not present
49 // set a default speed if not specified
50 speed = (speed != null) ? speed : units.gu(10);
52 // set a default iterations if not specified
53 iterations = (iterations !== undefined) ? iterations : 5
55 var distance = Math.sqrt(Math.pow(toX - x, 2) + Math.pow(toY - y, 2))
56 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
58 var timeStep = totalTime / iterations
59 var diffX = (toX - x) / iterations
60 var diffY = (toY - y) / iterations
61 if (pressMouse) {
62 fakeDateTime.currentTimeMs += timeStep
63 mousePress(item, x, y)
64 }
65 for (var i = 0; i < iterations; ++i) {
66 fakeDateTime.currentTimeMs += timeStep
67 if (i === iterations - 1) {
68 // Avoid any rounding errors by making the last move be at precisely
69 // the point specified
70 mouseMove(item, toX, toY, iterations / speed)
71 } else {
72 mouseMove(item, x + (i + 1) * diffX, y + (i + 1) * diffY, iterations / speed)
73 }
74 }
75 if (releaseMouse) {
76 fakeDateTime.currentTimeMs += timeStep
77 mouseRelease(item, toX, toY)
78 }
79 }
82 // Find an object with the given name in the children tree of "obj"
83 function findChild(obj, objectName) {
84 return findChildIn(obj, "children", objectName);
85 }
87 // Find an object with the given name in the children tree of "obj"
88 // Including invisible children like animations, timers etc.
89 // Note: you should use findChild if you're not sure you need this
90 // as this tree is much bigger and might contain stuff that goes
91 // away randomly.
92 function findInvisibleChild(obj, objectName) {
93 return findChildIn(obj, "data", objectName);
94 }
96 // Find a child in the named property
97 function findChildIn(obj, prop, objectName) {
98 var childs = new Array(0);
99 childs.push(obj)
100 while (childs.length > 0) {
101 if (childs[0].objectName == objectName) {
102 return childs[0]
103 }
104 for (var i in childs[0][prop]) {
105 childs.push(childs[0][prop][i])
106 }
107 childs.splice(0, 1);
108 }
109 return null;
110 }
112 // Type a full string instead of keyClick letter by letter
113 // TODO: this is not ugly, this is uber-ugly and does not support
114 // any special character. Remove the keyMap once keyClick(obj, char)
115 // has landed in upstream Qt.
116 function typeString(str) {
117 var keyMap = {
118 "a": Qt.Key_A,
119 "b": Qt.Key_B,
120 "c": Qt.Key_C,
121 "d": Qt.Key_D,
122 "e": Qt.Key_E,
123 "f": Qt.Key_F,
124 "g": Qt.Key_G,
125 "h": Qt.Key_H,
126 "i": Qt.Key_I,
127 "j": Qt.Key_J,
128 "k": Qt.Key_K,
129 "l": Qt.Key_L,
130 "m": Qt.Key_M,
131 "n": Qt.Key_N,
132 "o": Qt.Key_O,
133 "p": Qt.Key_P,
134 "q": Qt.Key_Q,
135 "r": Qt.Key_R,
136 "s": Qt.Key_S,
137 "t": Qt.Key_T,
138 "u": Qt.Key_U,
139 "v": Qt.Key_V,
140 "w": Qt.Key_W,
141 "x": Qt.Key_X,
142 "y": Qt.Key_Y,
143 "z": Qt.Key_Z,
144 "A": Qt.Key_A,
145 "B": Qt.Key_B,
146 "C": Qt.Key_C,
147 "D": Qt.Key_D,
148 "E": Qt.Key_E,
149 "F": Qt.Key_F,
150 "G": Qt.Key_G,
151 "H": Qt.Key_H,
152 "I": Qt.Key_I,
153 "J": Qt.Key_J,
154 "K": Qt.Key_K,
155 "L": Qt.Key_L,
156 "M": Qt.Key_M,
157 "N": Qt.Key_N,
158 "O": Qt.Key_O,
159 "P": Qt.Key_P,
160 "Q": Qt.Key_Q,
161 "R": Qt.Key_R,
162 "S": Qt.Key_S,
163 "T": Qt.Key_T,
164 "U": Qt.Key_U,
165 "V": Qt.Key_V,
166 "W": Qt.Key_W,
167 "X": Qt.Key_X,
168 "Y": Qt.Key_Y,
169 "Z": Qt.Key_Z,
170 "0": Qt.Key_0,
171 "1": Qt.Key_1,
172 "2": Qt.Key_2,
173 "3": Qt.Key_3,
174 "4": Qt.Key_4,
175 "5": Qt.Key_5,
176 "6": Qt.Key_6,
177 "7": Qt.Key_7,
178 "8": Qt.Key_8,
179 "9": Qt.Key_9,
180 " ": Qt.Key_Space,
181 }
182 for (var i = 0; i < str.length; i++) {
183 keyClick(keyMap[str[i]])
184 }
185 }
187 // Keeps executing a given parameter-less function until it returns the given
188 // expected result or the timemout is reached (in which case a test failure
189 // is generated)
190 function tryCompareFunction(func, expectedResult, timeout) {
191 var timeSpent = 0
192 if (timeout === undefined)
193 timeout = 5000;
194 var success = false
195 var actualResult
196 while (timeSpent < timeout && !success) {
197 actualResult = func()
198 success = qtest_compareInternal(actualResult, expectedResult)
199 if (success === false) {
200 wait(50)
201 timeSpent += 50
202 }
203 }
205 var act = qtest_results.stringify(actualResult)
206 var exp = qtest_results.stringify(expectedResult)
207 if (!qtest_results.compare(success,
208 "function returned unexpected result",
209 act, exp,
210 util.callerFile(), util.callerLine())) {
211 throw new Error("QtQuickTest::fail")
212 }
213 }
215 function touchEvent() {
216 return UT.Util.touchEvent()
217 }
219 // speed is in pixels/second
220 function touchFlick(item, x, y, toX, toY, beginTouch, endTouch, speed, iterations) {
221 // Make sure the item is rendered
222 waitForRendering(item);
224 var root = fetchRootItem(item);
225 var rootFrom = item.mapToItem(root, x, y);
226 var rootTo = item.mapToItem(root, toX, toY);
228 // Default to true for beginTouch if not present
229 beginTouch = (beginTouch !== undefined) ? beginTouch : true
231 // Default to true for endTouch if not present
232 endTouch = (endTouch !== undefined) ? endTouch : true
234 // Set a default speed if not specified
235 speed = (speed !== undefined) ? speed : units.gu(10)
237 // Set a default iterations if not specified
238 var iterations = (iterations !== undefined) ? iterations : 5
240 var distance = Math.sqrt(Math.pow(rootTo.x - rootFrom.x, 2) + Math.pow(rootTo.Y - rootFrom.y, 2))
241 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
243 var timeStep = totalTime / iterations
244 var diffX = (rootTo.x - rootFrom.x) / iterations
245 var diffY = (rootTo.y - rootFrom.y) / iterations
246 if (beginTouch) {
247 fakeDateTime.currentTimeMs += timeStep
249 var event = touchEvent()
250 event.press(0 /* touchId */, rootFrom.x, rootFrom.y)
251 event.commit()
252 }
253 for (var i = 0; i < iterations; ++i) {
254 fakeDateTime.currentTimeMs += timeStep
255 if (i === iterations - 1) {
256 // Avoid any rounding errors by making the last move be at precisely
257 // the point specified
258 wait(iterations / speed)
259 var event = touchEvent()
260 event.move(0 /* touchId */, rootTo.x, rootTo.y)
261 event.commit()
262 } else {
263 wait(iterations / speed)
264 var event = touchEvent()
265 event.move(0 /* touchId */, rootFrom.x + (i + 1) * diffX, rootFrom.y + (i + 1) * diffY)
266 event.commit()
267 }
268 }
269 if (endTouch) {
270 fakeDateTime.currentTimeMs += timeStep
271 var event = touchEvent()
272 event.release(0 /* touchId */, rootTo.x, rootTo.y)
273 event.commit()
274 }
275 }
277 function touchPinch(item, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End) {
278 // Make sure the item is rendered
279 waitForRendering(item);
281 var event1 = touchEvent();
282 // first finger
283 event1.press(0, x1Start, y1Start);
284 event1.commit();
285 // second finger
286 event1.stationary(0);
287 event1.press(1, x2Start, y2Start);
288 event1.commit();
290 // pinch
291 for (var i = 0.0; i < 1.0; i += 0.02) {
292 event1.move(0, x1Start + (x1End - x1Start) * i, y1Start + (y1End - y1Start) * i);
293 event1.move(1, x2Start + (x2End - x2Start) * i, y2Start + (y2End - y2Start) * i);
294 event1.commit();
295 }
297 // release
298 event1.release(0, x1End, y1End);
299 event1.release(1, x2End, y2End);
300 event1.commit();
301 }
303 function fetchRootItem(item) {
304 if (item.parent)
305 return fetchRootItem(item.parent)
306 else
307 return item
308 }
310 function touchPress(item, x, y) {
311 var root = fetchRootItem(item)
312 var rootPoint = item.mapToItem(root, x, y)
314 var event = touchEvent()
315 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
316 event.commit()
317 }
319 function touchRelease(item, x, y) {
320 var root = fetchRootItem(item)
321 var rootPoint = item.mapToItem(root, x, y)
323 var event = touchEvent()
324 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
325 event.commit()
326 }
328 /*! \brief Tap the item with a touch event.
330 \param item The item to be tapped
331 \param x The x coordinate of the tap, defaults to horizontal center
332 \param y The y coordinate of the tap, defaults to vertical center
333 */
334 function tap(item, x, y) {
335 if (typeof x !== "number") x = item.width / 2;
336 if (typeof y !== "number") y = item.height / 2;
338 var root = fetchRootItem(item)
339 var rootPoint = item.mapToItem(root, x, y)
341 var event = touchEvent()
342 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
343 event.commit()
345 event = touchEvent()
346 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
347 event.commit()
348 }
350 Component.onCompleted: {
351 UT.Util.ensureTouchRegistryInstalled();
353 var rootItem = parent;
354 while (rootItem.parent != undefined) {
355 rootItem = rootItem.parent;
356 }
357 removeTimeConstraintsFromDirectionalDragAreas(rootItem);
358 }
360 /*
361 In qmltests, sequences of touch events are sent all at once, unlike in "real life".
362 Also qmltests might run really slowly, e.g. when run from inside virtual machines.
363 Thus to remove a variable that qmltests cannot really control, namely time, this
364 function removes all constraints from DirectionalDragAreas that are sensible to
365 elapsed time.
367 This effectively makes DirectionalDragAreas easier to fool.
368 */
369 function removeTimeConstraintsFromDirectionalDragAreas(item) {
371 // use duck-typing to identify a DirectionalDragArea
372 if (item.minSpeed != undefined
373 && item.maxSilenceTime != undefined
374 && item.compositionTime != undefined) {
375 item.minSpeed = 0;
376 item.maxSilenceTime = 60 * 60 * 1000;
377 item.compositionTime = 0;
378 } else {
379 for (var i in item.children) {
380 removeTimeConstraintsFromDirectionalDragAreas(item.children[i]);
381 }
382 }
383 }