Unity 8
 All Classes Functions Properties
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 fetchRootItem(item) {
264  if (item.parent)
265  return fetchRootItem(item.parent)
266  else
267  return item
268  }
269 
270  function touchPress(item, x, y) {
271  var root = fetchRootItem(item)
272  var rootPoint = item.mapToItem(root, x, y)
273 
274  var event = touchEvent()
275  event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
276  event.commit()
277  }
278 
279  function touchRelease(item, x, y) {
280  var root = fetchRootItem(item)
281  var rootPoint = item.mapToItem(root, x, y)
282 
283  var event = touchEvent()
284  event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
285  event.commit()
286  }
287 
288  function tap(item, x, y) {
289  var event = touchEvent()
290  event.press(0 /* touchId */, x, y)
291  event.commit()
292 
293  event = touchEvent()
294  event.release(0 /* touchId */, x, y)
295  event.commit()
296  }
297 
298  Component.onCompleted: {
299  var rootItem = parent;
300  while (rootItem.parent != undefined) {
301  rootItem = rootItem.parent;
302  }
303  removeTimeConstraintsFromDirectionalDragAreas(rootItem);
304  }
305 
306  /*
307  In qmltests, sequences of touch events are sent all at once, unlike in "real life".
308  Also qmltests might run really slowly, e.g. when run from inside virtual machines.
309  Thus to remove a variable that qmltests cannot really control, namely time, this
310  function removes all constraints from DirectionalDragAreas that are sensible to
311  elapsed time.
312 
313  This effectively makes DirectionalDragAreas easier to fool.
314  */
315  function removeTimeConstraintsFromDirectionalDragAreas(item) {
316 
317  // use duck-typing to identify a DirectionalDragArea
318  if (item.minSpeed != undefined
319  && item.maxSilenceTime != undefined
320  && item.compositionTime != undefined) {
321  item.minSpeed = 0;
322  item.maxSilenceTime = 60 * 60 * 1000;
323  item.compositionTime = 0;
324  } else {
325  for (var i in item.children) {
326  removeTimeConstraintsFromDirectionalDragAreas(item.children[i]);
327  }
328  }
329  }
330 }