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) {
182  var timeSpent = 0
183  var timeout = 5000
184  var success = false
185  var actualResult
186  while (timeSpent < timeout && !success) {
187  actualResult = func()
188  success = qtest_compareInternal(actualResult, expectedResult)
189  if (success === false) {
190  wait(50)
191  timeSpent += 50
192  }
193  }
194 
195  var act = qtest_results.stringify(actualResult)
196  var exp = qtest_results.stringify(expectedResult)
197  if (!qtest_results.compare(success,
198  "function returned unexpected result",
199  act, exp,
200  util.callerFile(), util.callerLine())) {
201  throw new Error("QtQuickTest::fail")
202  }
203  }
204 
205  function touchEvent() {
206  return UT.Util.touchEvent()
207  }
208 
209  // speed is in pixels/second
210  function touchFlick(item, x, y, toX, toY, beginTouch, endTouch, speed, iterations) {
211  // Make sure the item is rendered
212  waitForRendering(item);
213 
214  // Default to true for beginTouch if not present
215  beginTouch = (beginTouch !== undefined) ? beginTouch : true
216 
217  // Default to true for endTouch if not present
218  endTouch = (endTouch !== undefined) ? endTouch : true
219 
220  // Set a default speed if not specified
221  speed = (speed !== undefined) ? speed : units.gu(10)
222 
223  // Set a default iterations if not specified
224  var iterations = (iterations !== undefined) ? iterations : 5
225 
226  var distance = Math.sqrt(Math.pow(toX - x, 2) + Math.pow(toY - y, 2))
227  var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
228 
229  var timeStep = totalTime / iterations
230  var diffX = (toX - x) / iterations
231  var diffY = (toY - y) / iterations
232  if (beginTouch) {
233  fakeDateTime.currentTimeMs += timeStep
234 
235  var event = touchEvent()
236  event.press(0 /* touchId */, x, y)
237  event.commit()
238  }
239  for (var i = 0; i < iterations; ++i) {
240  fakeDateTime.currentTimeMs += timeStep
241  if (i === iterations - 1) {
242  // Avoid any rounding errors by making the last move be at precisely
243  // the point specified
244  wait(iterations / speed)
245  var event = touchEvent()
246  event.move(0 /* touchId */, toX, toY)
247  event.commit()
248  } else {
249  wait(iterations / speed)
250  var event = touchEvent()
251  event.move(0 /* touchId */, x + (i + 1) * diffX, y + (i + 1) * diffY)
252  event.commit()
253  }
254  }
255  if (endTouch) {
256  fakeDateTime.currentTimeMs += timeStep
257  var event = touchEvent()
258  event.release(0 /* touchId */, toX, toY)
259  event.commit()
260  }
261  }
262 
263  function touchPinch(item, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End) {
264  // Make sure the item is rendered
265  waitForRendering(item);
266 
267  var event1 = touchEvent();
268  // first finger
269  event1.press(0, x1Start, y1Start);
270  event1.commit();
271  // second finger
272  event1.stationary(0);
273  event1.press(1, x2Start, y2Start);
274  event1.commit();
275 
276  // pinch
277  for (var i = 0.0; i < 1.0; i += 0.02) {
278  event1.move(0, x1Start + (x1End - x1Start) * i, y1Start + (y1End - y1Start) * i);
279  event1.move(1, x2Start + (x2End - x2Start) * i, y2Start + (y2End - y2Start) * i);
280  event1.commit();
281  }
282 
283  // release
284  event1.release(0, x1End, y1End);
285  event1.release(1, x2End, y2End);
286  event1.commit();
287  }
288 
289  function fetchRootItem(item) {
290  if (item.parent)
291  return fetchRootItem(item.parent)
292  else
293  return item
294  }
295 
296  function touchPress(item, x, y) {
297  var root = fetchRootItem(item)
298  var rootPoint = item.mapToItem(root, x, y)
299 
300  var event = touchEvent()
301  event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
302  event.commit()
303  }
304 
305  function touchRelease(item, x, y) {
306  var root = fetchRootItem(item)
307  var rootPoint = item.mapToItem(root, x, y)
308 
309  var event = touchEvent()
310  event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
311  event.commit()
312  }
313 
314  function tap(item, x, y) {
315  var root = fetchRootItem(item)
316  var rootPoint = item.mapToItem(root, x, y)
317 
318  var event = touchEvent()
319  event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
320  event.commit()
321 
322  event = touchEvent()
323  event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
324  event.commit()
325  }
326 
327  Component.onCompleted: {
328  var rootItem = parent;
329  while (rootItem.parent != undefined) {
330  rootItem = rootItem.parent;
331  }
332  removeTimeConstraintsFromDirectionalDragAreas(rootItem);
333  }
334 
335  /*
336  In qmltests, sequences of touch events are sent all at once, unlike in "real life".
337  Also qmltests might run really slowly, e.g. when run from inside virtual machines.
338  Thus to remove a variable that qmltests cannot really control, namely time, this
339  function removes all constraints from DirectionalDragAreas that are sensible to
340  elapsed time.
341 
342  This effectively makes DirectionalDragAreas easier to fool.
343  */
344  function removeTimeConstraintsFromDirectionalDragAreas(item) {
345 
346  // use duck-typing to identify a DirectionalDragArea
347  if (item.minSpeed != undefined
348  && item.maxSilenceTime != undefined
349  && item.compositionTime != undefined) {
350  item.minSpeed = 0;
351  item.maxSilenceTime = 60 * 60 * 1000;
352  item.compositionTime = 0;
353  } else {
354  for (var i in item.children) {
355  removeTimeConstraintsFromDirectionalDragAreas(item.children[i]);
356  }
357  }
358  }
359 }