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