on xenial help sign in
[ Albert Astals Cid ]

* Make runtests fake a test error if make fails * Make the test more stable * Use dbus-test-runner instead of dbus-launch * DirectionalDragArea: improvements & API grooming (LP: #1417920) * Fix EdgeDragEvaluator when a drag can happen both ways (Direction.Horizontal) [ Josh Arenson ] * Remove panelHeight property as it is unused. [ Leo Arias ] * Initial clean up of the autopilot tests set up. Removed the touch device from the test case. Moved the restart of unity to a fixture. Removed the unused DragMixin. Updated the setUpClass to use process_helpers. Removed the workaround for bug #1238417, already fixed. Use the toolkit helper to set the testability environment variable. Fixed the indicators base class that was restarting unity twice. (LP: #1238417, #1447206) * Use the base class from the toolkit in autopilot tests. [ Michael Zanetti ] * emit application-stop when we're going down (LP: #1326513) [ Michał Sawicz ] * UNITY_SCOPES_LIST is no more [ handsome_feng<445865575@qq.com> ] * When click the favorite scope in Dash Manager , it just return to the corresponding scope page. (LP: #1447056)

commitbf8ab8b75a14754e493db383e2fb01a64cd9d8b0 authorMichael Zanetti <michael.zanetti@canonical.com> dateMon 11 May 2015, 19:42 (11 years ago) parent7e4b5ed82841c4
38 files changed+483−246
cmake/modules/QmlTest.cmake+10−8 data/unity8-dash.conf−5 data/unity8.conf+1 debian/changelog+30−12 plugins/Ubuntu/Gestures/DirectionalDragArea.cpp+14−1 plugins/Ubuntu/Gestures/DirectionalDragArea.h+16 po/fr.po+4−4 po/ur.po+1−1 qml/Components/EdgeDragEvaluator.qml+17−14 qml/Dash/Dash.qml+5 qml/Dash/ScopesList.qml+2 qml/Dash/ScopesListCategory.qml+5−1 qml/Greeter/CoverPage.qml+2−2 qml/Launcher/Launcher.qml+3−3 qml/Panel/IndicatorsMenu.qml+2−2 qml/Shell.qml−1 qml/Stages/SpreadMaths.qml+8−1 runtests.sh+3 tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+1−1 tests/autopilot/unity8/fixture_setup.py+69−3 tests/autopilot/unity8/indicators/__init__.py+2−4 tests/autopilot/unity8/indicators/tests/__init__.py−2 tests/autopilot/unity8/process_helpers.py+2−1 tests/autopilot/unity8/shell/__init__.py−24 tests/autopilot/unity8/shell/emulators/__init__.py−6 tests/autopilot/unity8/shell/emulators/dash.py+6−6 tests/autopilot/unity8/shell/emulators/greeter.py+2−4 tests/autopilot/unity8/shell/emulators/launcher.py+3−2 tests/autopilot/unity8/shell/emulators/main_window.py+2−2 tests/autopilot/unity8/shell/tests/__init__.py+32−111 tests/autopilot/unity8/shell/tests/test_upstart.py+5−4 tests/plugins/Dash/listviewwithpageheadertest.cpp+8−2 tests/qmltests/Components/CMakeLists.txt+17−3 tests/qmltests/Components/tst_EdgeDragEvaluator.cpp+165 tests/qmltests/Components/tst_EdgeDragEvaluator.qml+27 tests/qmltests/tst_Shell.qml+14−10 tests/qmltests/tst_ShellWithPin.qml+2−2 tests/utils/modules/Unity/Test/UnityTestCase.qml+3−4
cmake/modules/QmlTest.cmake+10−8
@@ -155,10 +155,11 @@ macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS ENVVAR)
155155 set(testCommand
156156 ${ENVVAR}
157157 LD_LIBRARY_PATH=${LD_PATH}
158− dbus-launch
159− ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
160− -o ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
161− -o -,txt)
158+ dbus-test-runner
159+ -m 300
160+ -t ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
161+ -p -o -p ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
162+ -p -o -p -,txt)
162163
163164 add_qmltest_target(test${CLASS_NAME} "${testCommand}" FALSE TRUE)
164165 add_dependencies(test${CLASS_NAME} ${CLASS_NAME}TestExec ${DEPS})
@@ -173,10 +174,11 @@ macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS ENVVAR)
173174 ${ENVVAR}
174175 LD_LIBRARY_PATH=${LD_PATH}
175176 xvfb-run --server-args "-screen 0 1024x768x24" --auto-servernum
176− dbus-launch
177− ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
178− -o ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
179− -o -,txt)
177+ dbus-test-runner
178+ -m 300
179+ -t ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
180+ -p -o -p ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
181+ -p -o -p -,txt)
180182
181183 add_qmltest_target(xvfbtest${CLASS_NAME} "${xvfbtestCommand}" FALSE TRUE)
182184 add_dependencies(qmluitests xvfbtest${CLASS_NAME})
data/unity8-dash.conf−5
@@ -18,11 +18,6 @@ respawn
1818env APP_ID=unity8-dash
1919
2020pre-start script
21− if [ -z "$UNITY_SCOPES_LIST" ]; then
22− # FIXME: remove once we have this in dconf
23− initctl set-env UNITY_SCOPES_LIST="clickscope;musicaggregator;videoaggregator"
24− fi
25−
2621 initctl emit scope-ui-starting
2722end script
2823
data/unity8.conf+1
@@ -54,4 +54,5 @@ post-stop script
5454 initctl set-env --global MIR_SOCKET=$UNITY_MIR_SOCKET
5555 initctl unset-env --global UNITY_MIR_SOCKET
5656 initctl unset-env --global MIR_SERVER_PROMPT_FILE
57+ initctl emit application-end
5758end script
debian/changelog+30−12
@@ -1,21 +1,39 @@
1−unity8 (8.02+15.04.20150423-0ubuntu1) vivid; urgency=medium
1+unity8 (8.02+15.04.20150505-0ubuntu1) vivid; urgency=medium
22
3− [ CI Train Bot ]
4− * New rebuild forced.
3+ [ Albert Astals Cid ]
4+ * Make runtests fake a test error if make fails
5+ * Make the test more stable
6+ * Use dbus-test-runner instead of dbus-launch
57
68 [ Daniel d'Andrada ]
7− * Tapping home key shows unity8-dash home added:
8− plugins/Utils/ElapsedTimer.h plugins/Utils/HomeKeyWatcher.cpp
9− plugins/Utils/HomeKeyWatcher.h plugins/Utils/Timer.cpp
10− plugins/Utils/Timer.h tests/plugins/Utils/homekeywatchertest.cpp
9+ * DirectionalDragArea: improvements & API grooming (LP: #1417920)
10+ * Fix EdgeDragEvaluator when a drag can happen both ways
11+ (Direction.Horizontal)
12+
13+ [ Josh Arenson ]
14+ * Remove panelHeight property as it is unused.
15+
16+ [ Leo Arias ]
17+ * Initial clean up of the autopilot tests set up. Removed the touch
18+ device from the test case. Moved the restart of unity to a fixture.
19+ Removed the unused DragMixin. Updated the setUpClass to use
20+ process_helpers. Removed the workaround for bug #1238417, already
21+ fixed. Use the toolkit helper to set the testability environment
22+ variable. Fixed the indicators base class that was restarting unity
23+ twice. (LP: #1238417, #1447206)
24+ * Use the base class from the toolkit in autopilot tests.
25+
26+ [ Michael Zanetti ]
27+ * emit application-stop when we're going down (LP: #1326513)
1128
1229 [ Michał Sawicz ]
13− * Tapping home key shows unity8-dash home added:
14− plugins/Utils/ElapsedTimer.h plugins/Utils/HomeKeyWatcher.cpp
15− plugins/Utils/HomeKeyWatcher.h plugins/Utils/Timer.cpp
16− plugins/Utils/Timer.h tests/plugins/Utils/homekeywatchertest.cpp
30+ * UNITY_SCOPES_LIST is no more
31+
32+ [ handsome_feng<445865575@qq.com> ]
33+ * When click the favorite scope in Dash Manager , it just return to
34+ the corresponding scope page. (LP: #1447056)
1735
18− -- CI Train Bot <ci-train-bot@canonical.com> Thu, 23 Apr 2015 12:52:51 +0000
36+ -- CI Train Bot <ci-train-bot@canonical.com> Tue, 05 May 2015 14:47:24 +0000
1937
2038unity8 (8.02+15.04.20150422-0ubuntu1) vivid; urgency=medium
2139
plugins/Ubuntu/Gestures/DirectionalDragArea.cpp+14−1
@@ -176,6 +176,11 @@ bool DirectionalDragArea::dragging() const
176176 return d->status == DirectionalDragAreaPrivate::Recognized;
177177}
178178
179+bool DirectionalDragArea::pressed() const
180+{
181+ return d->status != DirectionalDragAreaPrivate::WaitingForTouch;
182+}
183+
179184bool DirectionalDragArea::immediateRecognition() const
180185{
181186 return d->immediateRecognition;
@@ -189,6 +194,13 @@ void DirectionalDragArea::setImmediateRecognition(bool enabled)
189194 }
190195}
191196
197+void DirectionalDragArea::removeTimeConstraints()
198+{
199+ d->setMaxTime(60 * 60 * 1000);
200+ d->compositionTime = 0;
201+ ddaDebug("removed time constraints");
202+}
203+
192204bool DirectionalDragArea::event(QEvent *event)
193205{
194206 if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
@@ -371,7 +383,6 @@ void DirectionalDragAreaPrivate::touchEvent_absent(QTouchEvent *event)
371383 allGood = false;
372384 } else {
373385 // that's our candidate
374− touchId = touchPoint.id();
375386 newTouchPoint = &touchPoint;
376387 }
377388 }
@@ -588,9 +599,11 @@ void DirectionalDragAreaPrivate::setStatus(Status newStatus)
588599 if (oldStatus == Recognized) {
589600 Q_EMIT q->draggingChanged(false);
590601 }
602+ Q_EMIT q->pressedChanged(false);
591603 break;
592604 case Undecided:
593605 recognitionTimer->start();
606+ Q_EMIT q->pressedChanged(true);
594607 break;
595608 case Recognized:
596609 Q_EMIT q->draggingChanged(true);
plugins/Ubuntu/Gestures/DirectionalDragArea.h+16
@@ -64,6 +64,9 @@ class UBUNTUGESTURESQML_EXPORT DirectionalDragArea : public QQuickItem {
6464 // Whether a drag gesture is taking place
6565 Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)
6666
67+ // Whether the drag area is pressed.
68+ Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged)
69+
6770 // Whether a gesture should be Recognized as soon a touch lands on the area.
6871 // With this property enabled it will work pretty much like a MultiPointTouchArea,
6972 // just with a different API.
@@ -92,14 +95,27 @@ public:
9295
9396 bool dragging() const;
9497
98+ bool pressed() const;
99+
95100 bool immediateRecognition() const;
96101 void setImmediateRecognition(bool enabled);
97102
98103 bool event(QEvent *e) override;
99104
105+ /*
106+ In qmltests, sequences of touch events are sent all at once, unlike in "real life".
107+ Also qmltests might run really slowly, e.g. when run from inside virtual machines.
108+ Thus to remove a variable that qmltests cannot really control, namely time, this
109+ function removes all constraints that are sensible to elapsed time.
110+
111+ This effectively makes the DirectionalDragArea easier to fool.
112+ */
113+ Q_INVOKABLE void removeTimeConstraints();
114+
100115Q_SIGNALS:
101116 void directionChanged(Direction::Type direction);
102117 void draggingChanged(bool value);
118+ void pressedChanged(bool value);
103119 void distanceChanged(qreal value);
104120 void sceneDistanceChanged(qreal value);
105121 void touchXChanged(qreal value);
po/fr.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-19 21:55+0000\n"
11+"PO-Revision-Date: 2015-05-10 15:54+0000\n"
1212"Last-Translator: Anne <anneonyme017@gmail.com>\n"
1313"Language-Team: French <fr@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-05-11 05:41+0000\n"
19+"X-Generator: Launchpad (build 17487)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -377,7 +377,7 @@ msgstr "Appuyez ici pour continuer."
377377
378378#: qml/Tutorial/TutorialContent.qml:81
379379msgid "Click here to finish."
380−msgstr ""
380+msgstr "Cliquez ici pour terminer."
381381
382382#: qml/Tutorial/TutorialLeftFinish.qml:24
383383msgid "These are the shortcuts to favorite apps"
po/ur.po+1−1
@@ -15,7 +15,7 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-05-06 05:45+0000\n"
18+"X-Launchpad-Export-Date: 2015-05-09 05:49+0000\n"
1919"X-Generator: Launchpad (build 17474)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
qml/Components/EdgeDragEvaluator.qml+17−14
@@ -61,18 +61,12 @@ AxisVelocityCalculator {
6161 }
6262
6363 velocity = calculate();
64+ minVelocity = __calculateMinimumVelocityForAutoCompletion();
6465
65− if (direction == Direction.Horizontal) {
66− minVelocity = __calculateMinimumVelocityForAutoCompletion(Math.abs(deltaPos));
67− return Math.abs(velocity) >= minVelocity;
66+ if (_dragDirectionIsPositive()) {
67+ return velocity >= minVelocity;
6868 } else {
69− minVelocity = __calculateMinimumVelocityForAutoCompletion(deltaPos);
70− if (Direction.isPositive(direction) && (velocity >= minVelocity)
71− || !Direction.isPositive(direction) && (velocity <= minVelocity)) {
72− return true;
73− } else {
74− return false;
75− }
69+ return velocity <= minVelocity;
7670 }
7771 }
7872
@@ -81,11 +75,20 @@ AxisVelocityCalculator {
8175 // speedThreshold in pixels per millisecond
8276 property real __speedThresholdMs: speedThreshold / 1000.0
8377
84− // Minimum velocity when a drag total distance is zero
85− property real __v0: Direction.isPositive(direction) ? __speedThresholdMs : - __speedThresholdMs
78+ function _dragDirectionIsPositive() {
79+ if (direction === Direction.Horizontal) {
80+ return (trackedPosition - __startPosition) > 0;
81+ } else {
82+ return Direction.isPositive(direction);
83+ }
84+ }
85+
86+ function __calculateMinimumVelocityForAutoCompletion() {
87+ // Minimum velocity when a drag total distance is zero
88+ var v0 = _dragDirectionIsPositive() ? __speedThresholdMs : - __speedThresholdMs;
89+ var deltaPos = trackedPosition - __startPosition;
8690
87− function __calculateMinimumVelocityForAutoCompletion(distance) {
88− return __v0 - ((__speedThresholdMs / dragThreshold) * distance);
91+ return v0 - ((__speedThresholdMs / dragThreshold) * deltaPos);
8992 }
9093
9194 function reset() {
qml/Dash/Dash.qml+5
@@ -187,6 +187,11 @@ Showable {
187187 onRequestFavoriteMoveTo: {
188188 scopes.moveFavoriteTo(scopeId, index);
189189 }
190+ onRequestRestore: {
191+ bottomEdgeController.enableAnimation = true;
192+ bottomEdgeController.progress = 0;
193+ dash.setCurrentScope(scopeId, false, false);
194+ }
190195
191196 Binding {
192197 target: scopesList.scope
qml/Dash/ScopesList.qml+2
@@ -31,6 +31,7 @@ Item {
3131 signal storeClicked()
3232 signal requestFavorite(string scopeId, bool favorite)
3333 signal requestFavoriteMoveTo(string scopeId, int index)
34+ signal requestRestore(string scopeId)
3435
3536 state: "browse"
3637
@@ -124,6 +125,7 @@ Item {
124125 onRequestEditMode: root.state = "edit";
125126 onRequestScopeMoveTo: root.requestFavoriteMoveTo(scopeId, index);
126127 onRequestActivate: root.scope.activate(result);
128+ onRequestRestore: root.requestRestore(scopeId);
127129 }
128130 }
129131 }
qml/Dash/ScopesListCategory.qml+5−1
@@ -35,6 +35,7 @@ Item {
3535 signal requestEditMode()
3636 signal requestScopeMoveTo(string scopeId, int index)
3737 signal requestActivate(var result)
38+ signal requestRestore(string scopeId)
3839
3940 implicitHeight: visible ? childrenRect.height : 0
4041
@@ -93,7 +94,10 @@ Item {
9394
9495 onClicked: {
9596 if (!editMode) {
96− root.requestActivate(result);
97+ if (root.isFavoritesFeed)
98+ root.requestRestore(model.scopeId);
99+ else
100+ root.requestActivate(result);
97101 }
98102 }
99103 onPressAndHold: {
qml/Greeter/CoverPage.qml+2−2
@@ -135,8 +135,8 @@ Showable {
135135 enabled: root.draggable
136136 direction: Direction.Horizontal
137137
138− onDraggingChanged: {
139− if (dragging) {
138+ onPressedChanged: {
139+ if (pressed) {
140140 root.tease();
141141 showLabelAnimation.start();
142142 }
qml/Launcher/Launcher.qml+3−3
@@ -210,7 +210,7 @@ Item {
210210 bottom: parent.bottom
211211 }
212212 x: -width
213− visible: root.x > 0 || x > -width
213+ visible: root.x > 0 || x > -width || dragArea.pressed
214214 model: LauncherModel
215215
216216 property bool animate: true
@@ -283,11 +283,11 @@ Item {
283283 width: root.dragAreaWidth
284284 height: root.height
285285
286− onTouchXChanged: {
286+ onDistanceChanged: {
287287 if (!dragging || launcher.state == "visible")
288288 return;
289289
290− panel.x = Math.min(0, touchX - panel.width) - root.x
290+ panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
291291 }
292292
293293 onDraggingChanged: {
qml/Panel/IndicatorsMenu.qml+2−2
@@ -187,8 +187,8 @@ Showable {
187187 autoCompleteDragThreshold: maxTotalDragDistance / 2
188188 stretch: true
189189
190− onDraggingChanged: {
191− if (dragging) {
190+ onPressedChanged: {
191+ if (pressed) {
192192 touchPressTime = new Date().getTime();
193193 } else {
194194 var touchReleaseTime = new Date().getTime();
qml/Shell.qml−1
@@ -58,7 +58,6 @@ Item {
5858 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
5959 property url background: asImageTester.status == Image.Ready ? asImageTester.source
6060 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
61− readonly property real panelHeight: panel.panelHeight
6261
6362 property bool sideStageEnabled: shell.width >= units.gu(100)
6463 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
qml/Stages/SpreadMaths.qml+8−1
@@ -86,7 +86,7 @@ Item {
8686 return linearAnimation(0, 1, rightFoldingAreaX, flickableWidth - margins - stackWidth, rightEasing.value);
8787 }
8888
89− return linearX;
89+ return linearX//margins + foldingAreaWidth + centerEasing.value * (linearX - margins - foldingAreaWidth);
9090 }
9191
9292 readonly property int animatedY: sceneHeight - itemHeight - spreadBottomOffset
@@ -128,6 +128,13 @@ Item {
128128 return (progress - startProgress) * (endValue - startValue) / (endProgress - startProgress) + startValue;
129129 }
130130
131+ EasingCurve {
132+ id: centerEasing
133+ type: EasingCurve.InOutSine
134+ property int totalDistance: flickableWidth - foldingAreaWidth * 2 - margins * 2
135+ progress: (linearX - margins - foldingAreaWidth) / totalDistance
136+ }
137+
131138 EasingCurve {
132139 id: leftEasing
133140 type: EasingCurve.OutSine
runtests.sh+3
@@ -42,6 +42,9 @@ sleep 1
4242cd -
4343
4444make -k qmltests
45+if [ $? -ne 0 ]; then
46+ echo '<?xml version="1.0" encoding="UTF-8" ?><testsuite errors="0" failures="1" tests="1" name="makeExitStatusTest"><properties/><testcase result="fail" name="makeExitStatus"><failure message="Make test did not suceed" result="fail"/></testcase><system-err/></testsuite>' > testMakeExitStatus.xml
47+fi
4548
4649cd -
4750
tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+1−1
@@ -47,7 +47,7 @@ class ApplicationLifecycleTests(tests.ApplicationLifeCycleTestCase):
4747 end_y = start_y
4848
4949 logger.info("Swiping screen from the right edge")
50− self.touch.drag(start_x, start_y, end_x, end_y)
50+ self.main_window.pointing_device.drag(start_x, start_y, end_x, end_y)
5151
5252 def launch_fake_app(self):
5353 _, desktop_file_path = self.create_test_application()
tests/autopilot/unity8/fixture_setup.py+69−3
@@ -18,20 +18,86 @@
1818#
1919
2020import fixtures
21−import subprocess
2221import logging
22+import os
23+import subprocess
2324
25+import ubuntuuitoolkit
2426from autopilot import introspection
2527
2628from unity8 import (
2729 get_binary_path,
2830 process_helpers
2931)
30−from unity8.shell import emulators
32+
3133
3234logger = logging.getLogger(__name__)
3335
3436
37+class RestartUnityWithTestability(fixtures.Fixture):
38+
39+ """Fixture to launch Unity8 with testability.
40+
41+ :ivar unity_proxy: The Autopilot proxy object for the Unity shell.
42+
43+ """
44+
45+ unity_proxy = None
46+
47+ def __init__(self, binary_path, variables):
48+ """Initialize the fixture instance.
49+
50+ :param str binary_path: The path to the Dash app binary.
51+ :param cli_arguments: The arguments to pass when launching the
52+ :param variables: The variables to use when launching the app.
53+ :type variables: A dictionary.
54+
55+ """
56+ super().__init__()
57+ self.binary_path = binary_path
58+ self.variables = variables
59+
60+ def setUp(self):
61+ """Restart unity with testability when the fixture is used."""
62+ super().setUp()
63+ self.addCleanup(self.stop_unity)
64+ self.restart_unity()
65+
66+ def restart_unity(self):
67+ self.restart_unity_with_testability()
68+
69+ def restart_unity_with_testability(self):
70+ self._unlink_mir_socket()
71+
72+ binary_arg = 'BINARY={}'.format(self.binary_path)
73+ variable_args = [
74+ '{}={}'.format(key, value) for key, value in self.variables.items()
75+ ]
76+ all_args = [binary_arg] + variable_args
77+
78+ self.unity_proxy = process_helpers.restart_unity_with_testability(
79+ *all_args)
80+
81+ def _unlink_mir_socket(self):
82+ # FIXME: we shouldn't be doing this
83+ # $MIR_SOCKET, fallback to $XDG_RUNTIME_DIR/mir_socket and
84+ # /tmp/mir_socket as last resort
85+ try:
86+ os.unlink(
87+ os.getenv('MIR_SOCKET',
88+ os.path.join(os.getenv('XDG_RUNTIME_DIR', "/tmp"),
89+ "mir_socket")))
90+ except OSError:
91+ pass
92+ try:
93+ os.unlink("/tmp/mir_socket")
94+ except OSError:
95+ pass
96+
97+ def stop_unity(self):
98+ process_helpers.stop_job('unity8')
99+
100+
35101class LaunchDashApp(fixtures.Fixture):
36102
37103 """Fixture to launch the Dash app."""
@@ -65,7 +131,7 @@ class LaunchDashApp(fixtures.Fixture):
65131 pid = process_helpers.start_job('unity8-dash', *all_args)
66132 return introspection.get_proxy_object_for_existing_process(
67133 pid=pid,
68− emulator_base=emulators.UnityEmulatorBase,
134+ emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase
69135 )
70136
71137 def stop_application(self):
tests/autopilot/unity8/indicators/__init__.py+2−4
@@ -18,10 +18,8 @@
1818import ubuntuuitoolkit
1919from autopilot import introspection
2020
21−from unity8.shell import emulators
2221
23−
24−class IndicatorPage(emulators.UnityEmulatorBase):
22+class IndicatorPage(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
2523
2624 """Autopilot helper for the IndicatorPage component."""
2725
@@ -166,7 +164,7 @@ class TestIndicatorPage(IndicatorPage):
166164 return self.select_single(objectName='indicator.action.slider')
167165
168166
169−class Slider(emulators.UnityEmulatorBase):
167+class Slider(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
170168
171169 """Autopilot helper for the Slider component."""
172170
tests/autopilot/unity8/indicators/tests/__init__.py−2
@@ -40,5 +40,3 @@ class DeviceIndicatorTestCase(IndicatorTestCase):
4040 if platform.model() == 'Desktop':
4141 self.skipTest('Test cannot be run on the desktop.')
4242 super().setUp()
43− self.unity_proxy = self.launch_unity()
44− process_helpers.unlock_unity(self.unity_proxy)
tests/autopilot/unity8/process_helpers.py+2−1
@@ -21,6 +21,7 @@ import logging
2121import subprocess
2222import dbus
2323
24+import ubuntuuitoolkit
2425from autopilot.exceptions import ProcessSearchError
2526from autopilot.introspection import get_proxy_object_for_existing_process
2627
@@ -271,5 +272,5 @@ def _get_unity_pid():
271272def _get_unity_proxy_object(pid):
272273 return get_proxy_object_for_existing_process(
273274 pid=pid,
274− emulator_base=emulators.UnityEmulatorBase,
275+ emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase
275276 )
tests/autopilot/unity8/shell/__init__.py−24
@@ -19,7 +19,6 @@
1919
2020"""unity shell autopilot tests and emulators - sub level package."""
2121
22−from time import sleep
2322from functools import wraps
2423from gi.repository import Notify
2524
@@ -38,29 +37,6 @@ def disable_qml_mocking(fn):
3837 return wrapper
3938
4039
41−class DragMixin():
42− def _drag(self, x1, y1, x2, y2):
43− # XXX This ugly code is here just temporarily, waiting for drag
44− # improvements to land on autopilot so we don't have to access device
45− # private internal attributes. --elopio - 2014-02-12
46− cur_x = x1
47− cur_y = y1
48− dx = 1.0 * (x2 - x1) / 100
49− dy = 1.0 * (y2 - y1) / 100
50− for i in range(0, 100):
51− try:
52− self.touch._finger_move(int(cur_x), int(cur_y))
53− except AttributeError:
54− self.touch._device.finger_move(int(cur_x), int(cur_y))
55− sleep(0.002)
56− cur_x += dx
57− cur_y += dy
58− try:
59− self.touch._finger_move(int(x2), int(y2))
60− except AttributeError:
61− self.touch._device.finger_move(int(x2), int(y2))
62−
63−
6440def create_ephemeral_notification(
6541 summary='',
6642 body='',
tests/autopilot/unity8/shell/emulators/__init__.py−6
@@ -17,12 +17,6 @@
1717# along with this program. If not, see <http://www.gnu.org/licenses/>.
1818#
1919
20−import ubuntuuitoolkit
21−
22−
23−class UnityEmulatorBase(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
24− """A base class for all unity emulators."""
25−
2620
2721class UnityEmulatorException(Exception):
2822 """Exception raised when there is an error with the Unity emulators."""
tests/autopilot/unity8/shell/emulators/dash.py+6−6
@@ -18,13 +18,13 @@
1818#
1919
2020import logging
21−import ubuntuuitoolkit
22−
23−from unity8.shell import emulators
2421
22+import ubuntuuitoolkit
2523from autopilot import logging as autopilot_logging
2624from autopilot.introspection import dbus
2725
26+from unity8.shell import emulators
27+
2828
2929logger = logging.getLogger(__name__)
3030
@@ -40,7 +40,7 @@ class DashApp():
4040 self.dash = self.main_view.select_single(Dash)
4141
4242
43−class Dash(emulators.UnityEmulatorBase):
43+class Dash(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
4444 """An emulator that understands the Dash."""
4545
4646 def __init__(self, *args):
@@ -188,7 +188,7 @@ class ListViewWithPageHeader(ubuntuuitoolkit.QQuickFlickable):
188188 pass
189189
190190
191−class GenericScopeView(emulators.UnityEmulatorBase):
191+class GenericScopeView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
192192 """Autopilot emulator for generic scopes."""
193193
194194 @autopilot_logging.log_action(logger.info)
@@ -255,5 +255,5 @@ class GenericScopeView(emulators.UnityEmulatorBase):
255255 return result
256256
257257
258−class Preview(emulators.UnityEmulatorBase):
258+class Preview(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
259259 """Autopilot custom proxy object for generic previews."""
tests/autopilot/unity8/shell/emulators/greeter.py+2−4
@@ -1,7 +1,7 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
22#
33# Unity Autopilot Test Suite
4−# Copyright (C) 2012, 2013, 2014 Canonical
4+# Copyright (C) 2012, 2013, 2014, 2015 Canonical
55#
66# This program is free software: you can redistribute it and/or modify
77# it under the terms of the GNU General Public License as published by
@@ -18,12 +18,10 @@
1818#
1919
2020import ubuntuuitoolkit
21−
2221from autopilot.utilities import sleep
23−from unity8.shell.emulators import UnityEmulatorBase
2422
2523
26−class Greeter(UnityEmulatorBase):
24+class Greeter(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
2725 """An emulator that understands the greeter screen."""
2826
2927 def wait_swiped_away(self):
tests/autopilot/unity8/shell/emulators/launcher.py+3−2
@@ -1,7 +1,7 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
22#
33# Unity Autopilot Test Suite
4−# Copyright (C) 2012, 2013, 2014 Canonical
4+# Copyright (C) 2012, 2013, 2014, 2015 Canonical
55#
66# This program is free software: you can redistribute it and/or modify
77# it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
2020import logging
2121
2222import autopilot.logging
23+import ubuntuuitoolkit
2324
2425from unity8.shell import emulators
2526
@@ -27,7 +28,7 @@ from unity8.shell import emulators
2728logger = logging.getLogger(__name__)
2829
2930
30−class Launcher(emulators.UnityEmulatorBase):
31+class Launcher(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
3132
3233 """An emulator that understands the Launcher."""
3334
tests/autopilot/unity8/shell/emulators/main_window.py+2−2
@@ -19,17 +19,17 @@
1919
2020import logging
2121
22+import ubuntuuitoolkit
2223from autopilot import logging as autopilot_logging
2324from autopilot import input
2425
25−from unity8.shell import emulators
2626from unity8.shell.emulators.greeter import Greeter
2727from unity8.shell.emulators.launcher import Launcher
2828
2929logger = logging.getLogger(__name__)
3030
3131
32−class QQuickView(emulators.UnityEmulatorBase):
32+class QQuickView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
3333 """An emulator class that makes it easy to interact with the shell"""
3434
3535 def get_greeter(self):
tests/autopilot/unity8/shell/tests/__init__.py+32−111
@@ -24,17 +24,17 @@ try:
2424except ImportError:
2525 Gio = None
2626
27+import logging
28+import os
29+
2730from autopilot import introspection
2831from autopilot.platform import model
2932from autopilot.testcase import AutopilotTestCase
3033from autopilot.matchers import Eventually
31−from autopilot.input import Touch
3234from autopilot.display import Display
33−import logging
34−import os.path
35−import subprocess
36−import sys
3735from testtools.matchers import Equals
36+
37+import ubuntuuitoolkit
3838from ubuntuuitoolkit import (
3939 fixture_setup as toolkit_fixtures,
4040 ubuntu_scenarios
@@ -51,7 +51,6 @@ from unity8 import (
5151 fixture_setup,
5252 process_helpers
5353)
54−from unity8.shell import emulators
5554from unity8.shell.emulators import (
5655 dash as dash_helpers,
5756 main_window as main_window_emulator,
@@ -93,36 +92,25 @@ class UnityTestCase(AutopilotTestCase):
9392 @classmethod
9493 def setUpClass(cls):
9594 try:
96− output = subprocess.check_output(
97− ["/sbin/initctl", "status", "unity8"],
98− stderr=subprocess.STDOUT,
99− universal_newlines=True,
100− )
101− except subprocess.CalledProcessError as e:
102− sys.stderr.write(
103− "Error: `initctl status unity8` failed, most probably the "
104− "unity8 session could not be found:\n\n"
105− "{0}\n"
106− "Please install unity8 or copy data/unity8.conf to "
107− "{1}\n".format(
108− e.output,
109− os.path.join(os.getenv("XDG_CONFIG_HOME",
110− os.path.join(os.getenv("HOME"),
111− ".config")
112− ),
113− "upstart")
114− )
115− )
116− sys.exit(1)
117−
118− if "start/" in output:
119− sys.stderr.write(
120− "Error: Unity is currently running, these tests require it to "
121− "be 'stopped'.\n"
122− "Please run this command before running these tests: \n"
123− "initctl stop unity8\n"
95+ is_unity_running = process_helpers.is_job_running('unity8')
96+ except process_helpers.JobError as e:
97+ xdg_config_home = os.getenv(
98+ 'XDG_CONFIG_HOME', os.path.join(os.getenv('HOME'), '.config'))
99+ upstart_config_path = os.path.join(xdg_config_home, 'upstart')
100+ logger.error(
101+ '`initctl status unity8` failed, most probably the '
102+ 'unity8 session could not be found:\n\n'
103+ '{0}\n'
104+ 'Please install unity8 or copy data/unity8.conf to '
105+ '{1}\n'.format(e.output, upstart_config_path)
124106 )
125− sys.exit(2)
107+ raise e
108+ else:
109+ assert not is_unity_running, (
110+ 'Unity is currently running, these tests require it to be '
111+ 'stopped.\n'
112+ 'Please run this command before running these tests: \n'
113+ 'initctl stop unity8\n')
126114
127115 def setUp(self):
128116 super().setUp()
@@ -134,13 +122,6 @@ class UnityTestCase(AutopilotTestCase):
134122 self._data_dirs_mock_enabled = True
135123 self._environment = {}
136124
137− # FIXME: This is a work around re: lp:1238417
138− if model() != "Desktop":
139− from autopilot.input import _uinput
140− _uinput._touch_device = _uinput.create_touch_device()
141− self.addCleanup(_uinput._touch_device.close)
142−
143− self.touch = Touch.create()
144125 self._setup_display_details()
145126
146127 def _setup_display_details(self):
@@ -196,40 +177,6 @@ class UnityTestCase(AutopilotTestCase):
196177 divisor = divisor * 2
197178 return divisor
198179
199− def _patch_environment(self, key, value):
200− """Wrapper for patching env for upstart environment."""
201− try:
202− current_value = subprocess.check_output(
203− ["/sbin/initctl", "get-env", "--global", key],
204− stderr=subprocess.STDOUT,
205− universal_newlines=True,
206− ).rstrip()
207− except subprocess.CalledProcessError:
208− current_value = None
209−
210− subprocess.call([
211− "/sbin/initctl",
212− "set-env",
213− "--global",
214− "%s=%s" % (key, value)
215− ], stderr=subprocess.STDOUT)
216− self.addCleanup(self._upstart_reset_env, key, current_value)
217−
218− def _upstart_reset_env(self, key, value):
219− logger.info("Resetting upstart env %s to %s", key, value)
220− if value is None:
221− subprocess.call(
222− ["/sbin/initctl", "unset-env", key],
223− stderr=subprocess.STDOUT,
224− )
225− else:
226− subprocess.call([
227− "/sbin/initctl",
228− "set-env",
229− "--global",
230− "%s=%s" % (key, value)
231− ], stderr=subprocess.STDOUT)
232−
233180 def launch_unity(self, mode="full-greeter", *args):
234181 """
235182 Launch the unity shell, return a proxy object for it.
@@ -257,21 +204,6 @@ class UnityTestCase(AutopilotTestCase):
257204 if self._data_dirs_mock_enabled:
258205 self._patch_data_dirs()
259206
260− # FIXME: we shouldn't be doing this
261− # $MIR_SOCKET, fallback to $XDG_RUNTIME_DIR/mir_socket and
262− # /tmp/mir_socket as last resort
263− try:
264− os.unlink(
265− os.getenv('MIR_SOCKET',
266− os.path.join(os.getenv('XDG_RUNTIME_DIR', "/tmp"),
267− "mir_socket")))
268− except OSError:
269− pass
270− try:
271− os.unlink("/tmp/mir_socket")
272− except OSError:
273− pass
274−
275207 unity8_cli_args_list = ["--mode={}".format(mode)]
276208 if len(args) != 0:
277209 unity8_cli_args_list += args
@@ -297,26 +229,15 @@ class UnityTestCase(AutopilotTestCase):
297229
298230 def _launch_unity_with_upstart(self, binary_path, args):
299231 logger.info("Starting unity")
300− self._patch_environment("QT_LOAD_TESTABILITY", 1)
301−
302− binary_arg = "BINARY=%s" % binary_path
303− extra_args = "ARGS=%s" % " ".join(args)
304− env_args = ["%s=%s" % (k, v) for k, v in self._environment.items()]
305− all_args = [binary_arg, extra_args] + env_args
232+ self.useFixture(toolkit_fixtures.InitctlEnvironmentVariable(
233+ global_=True, QT_LOAD_TESTABILITY=1))
306234
307− self.addCleanup(self._cleanup_launching_upstart_unity)
308−
309− return process_helpers.restart_unity_with_testability(*all_args)
310−
311− def _cleanup_launching_upstart_unity(self):
312− logger.info("Stopping unity")
313− try:
314− subprocess.check_output(
315− ["/sbin/initctl", "stop", "unity8"],
316− stderr=subprocess.STDOUT
317− )
318− except subprocess.CalledProcessError:
319− logger.warning("Appears unity was already stopped!")
235+ variables = self._environment
236+ variables['ARGS'] = " ".join(args)
237+ launch_unity_fixture = fixture_setup.RestartUnityWithTestability(
238+ binary_path, variables)
239+ self.useFixture(launch_unity_fixture)
240+ return launch_unity_fixture.unity_proxy
320241
321242 def _patch_data_dirs(self):
322243 data_dirs = get_data_dirs(self._data_dirs_mock_enabled)
@@ -369,7 +290,7 @@ class UnityTestCase(AutopilotTestCase):
369290 pid = process_helpers.get_job_pid('unity8-dash')
370291 dash_proxy = introspection.get_proxy_object_for_existing_process(
371292 pid=pid,
372− emulator_base=emulators.UnityEmulatorBase,
293+ emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase
373294 )
374295 dash_app = dash_helpers.DashApp(dash_proxy)
375296 return dash_app.dash
tests/autopilot/unity8/shell/tests/test_upstart.py+5−4
@@ -1,7 +1,7 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
22#
33# Unity Autopilot Test Suite
4−# Copyright (C) 2013, 2014 Canonical
4+# Copyright (C) 2013, 2014, 2015 Canonical
55#
66# This program is free software: you can redistribute it and/or modify
77# it under the terms of the GNU General Public License as published by
@@ -26,13 +26,13 @@ import subprocess
2626import time
2727
2828import fixtures
29−from testtools.matchers import Equals, MismatchError
29+import ubuntuuitoolkit
3030from autopilot.matchers import Eventually
3131from autopilot.introspection import get_proxy_object_for_existing_process
32+from testtools.matchers import Equals, MismatchError
3233from ubuntuuitoolkit import ubuntu_scenarios
3334
3435from unity8 import get_binary_path
35−from unity8.shell.emulators import UnityEmulatorBase
3636from unity8.shell.tests import UnityTestCase
3737
3838import logging
@@ -90,10 +90,11 @@ class UpstartIntegrationTests(UnityTestCase):
9090 self.addCleanup(ensure_stopped)
9191
9292 def _set_proxy(self):
93+ proxy_base = ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase
9394 super()._set_proxy(
9495 get_proxy_object_for_existing_process(
9596 pid=self.process.pid,
96− emulator_base=UnityEmulatorBase,))
97+ emulator_base=proxy_base))
9798
9899 def test_no_sigstop(self):
99100 self.useFixture(
tests/plugins/Dash/listviewwithpageheadertest.cpp+8−2
@@ -973,9 +973,15 @@ private Q_SLOTS:
973973 QCOMPARE(lvwph->contentY(), 1258.);
974974 QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
975975
976− changeContentY(-1700);
977−
976+ changeContentY(-800);
977+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 5);
978+ changeContentY(-400);
979+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 8);
980+ changeContentY(-200);
981+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 10);
982+ changeContentY(-300);
978983 QTRY_COMPARE(lvwph->m_visibleItems.count(), 12);
984+
979985 QCOMPARE(lvwph->m_firstVisibleIndex, 0);
980986 verifyItem(0, -308., 75., true);
981987 verifyItem(1, -233., 75., true);
tests/qmltests/Components/CMakeLists.txt+17−3
@@ -1,3 +1,7 @@
1+add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
2+add_definitions(-DTEST_DIR="${CMAKE_CURRENT_LIST_DIR}")
3+add_definitions(-DTESTS_UTILS_MODULES_DIR="${CMAKE_BINARY_DIR}/tests/utils/modules")
4+
15########## tst_DragHandle
26
37include_directories(
@@ -15,6 +19,16 @@ target_link_libraries(DragHandleTestExec UbuntuGesturesQml UbuntuGestures)
1519add_binary_qml_test(DragHandle ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures "" "")
1620add_manual_qml_test(. DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${CMAKE_BINARY_DIR}/tests/utils/modules)
1721
18−add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
19−add_definitions(-DTEST_DIR="${CMAKE_CURRENT_LIST_DIR}")
20−add_definitions(-DTESTS_UTILS_MODULES_DIR="${CMAKE_BINARY_DIR}/tests/utils/modules")
22+########## tst_EdgeDragEvaluator
23+
24+include_directories(
25+ ${CMAKE_SOURCE_DIR}/plugins/Ubuntu/Gestures
26+ ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures
27+ ${CMAKE_CURRENT_BINARY_DIR}
28+ )
29+
30+add_executable(EdgeDragEvaluatorTestExec tst_EdgeDragEvaluator.cpp)
31+qt5_use_modules(EdgeDragEvaluatorTestExec Test Core Qml Gui Quick)
32+target_link_libraries(EdgeDragEvaluatorTestExec UbuntuGesturesQml UbuntuGestures)
33+
34+add_binary_qml_test(EdgeDragEvaluator ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures "" "")
tests/qmltests/Components/tst_EdgeDragEvaluator.cppnew file+165
@@ -0,0 +1,165 @@
1+/*
2+ * Copyright (C) 2015 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+#include <functional>
18+#include <QtTest>
19+#include <QObject>
20+#include <QQuickView>
21+#include <QQmlEngine>
22+#include <QQuickItem>
23+
24+// Ubuntu.Gestures plugin
25+#include <AxisVelocityCalculator.h>
26+#include <Direction.h>
27+#include <Timer.h>
28+
29+using namespace UbuntuGestures;
30+
31+class tst_EdgeDragEvaluator: public QObject
32+{
33+ Q_OBJECT
34+public:
35+ tst_EdgeDragEvaluator() {}
36+private Q_SLOTS:
37+ void initTestCase(); // will be called before the first test function is executed
38+ void cleanupTestCase(); // will be called after the last test function was executed.
39+
40+ void init(); // called right before each and every test function is executed
41+ void cleanup(); // called right after each and every test function is executed
42+
43+ void horizontalDirection();
44+ void horizontalDirection_data();
45+
46+private:
47+ void drag(qreal &pos, qreal distance, qreal speed);
48+
49+ QQuickView *createView();
50+ QQuickView *m_view;
51+ FakeTimer *m_fakeTimer;
52+ QSharedPointer<FakeTimeSource> m_fakeTimeSource;
53+ AxisVelocityCalculator *m_dragEvaluator;
54+};
55+
56+
57+void tst_EdgeDragEvaluator::initTestCase()
58+{
59+ m_view = 0;
60+}
61+
62+void tst_EdgeDragEvaluator::cleanupTestCase()
63+{
64+}
65+
66+void tst_EdgeDragEvaluator::init()
67+{
68+
69+ m_view = createView();
70+ m_view->setSource(QUrl::fromLocalFile(TEST_DIR"/tst_EdgeDragEvaluator.qml"));
71+ m_view->show();
72+ QVERIFY(QTest::qWaitForWindowExposed(m_view));
73+ QVERIFY(m_view->rootObject() != 0);
74+ qApp->processEvents();
75+
76+ m_fakeTimeSource.reset(new FakeTimeSource);
77+ m_fakeTimer = new FakeTimer(m_fakeTimeSource);
78+
79+ m_dragEvaluator =
80+ m_view->rootObject()->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
81+ QVERIFY(m_dragEvaluator);
82+ m_dragEvaluator->setTimeSource(m_fakeTimeSource);
83+}
84+
85+void tst_EdgeDragEvaluator::cleanup()
86+{
87+ m_dragEvaluator = nullptr;
88+
89+ delete m_view;
90+ m_view = 0;
91+
92+ delete m_fakeTimer;
93+ m_fakeTimer = 0;
94+
95+ m_fakeTimeSource.reset();
96+}
97+
98+QQuickView *tst_EdgeDragEvaluator::createView()
99+{
100+ QQuickView *window = new QQuickView(0);
101+ window->setResizeMode(QQuickView::SizeRootObjectToView);
102+ window->engine()->addImportPath(QLatin1String(UBUNTU_GESTURES_PLUGIN_DIR));
103+ window->engine()->addImportPath(QLatin1String(TEST_DIR));
104+
105+ return window;
106+}
107+
108+void tst_EdgeDragEvaluator::drag(qreal &pos, qreal distance, qreal speed)
109+{
110+ int numSteps = 10;
111+ qreal distanceStep = distance / ((qreal)numSteps);
112+ int timeStep = qAbs(distanceStep / speed);
113+
114+ for (int i = 0; i < numSteps; ++i) {
115+ m_fakeTimeSource->m_msecsSinceReference += timeStep;
116+ pos += distanceStep;
117+ m_dragEvaluator->setTrackedPosition(pos);
118+ }
119+}
120+
121+void tst_EdgeDragEvaluator::horizontalDirection()
122+{
123+ qreal maxDragDistance = 100.;
124+ qreal speedThreshold = 100.;
125+ qreal speedThresholdMs = speedThreshold / 1000.;
126+ QFETCH(qreal, dragDirection);
127+
128+ m_dragEvaluator->setProperty("maxDragDistance", QVariant::fromValue(maxDragDistance));
129+ m_dragEvaluator->setProperty("dragThreshold", QVariant::fromValue(maxDragDistance * 0.5));
130+ m_dragEvaluator->setProperty("speedThreshold", QVariant::fromValue(speedThreshold));
131+
132+ // If direction is "Horizontal" it means it can drag leftwards or rightwards
133+ m_dragEvaluator->setProperty("direction", QVariant::fromValue((int)Direction::Horizontal));
134+
135+ qreal pos = 0.;
136+ m_dragEvaluator->setTrackedPosition(pos);
137+
138+ // Go far beyond dragThreshold
139+ drag(pos, maxDragDistance * 0.75 * dragDirection /*distance*/, speedThresholdMs * 0.5 /*speed*/);
140+
141+ // Then come back a bit, getting close to dragThreshold
142+ drag(pos, -maxDragDistance * 0.20 * dragDirection /*distance*/, speedThresholdMs * 0.5 /*speed*/);
143+
144+ QVariant shouldAutoComplete;
145+ bool ok = QMetaObject::invokeMethod(m_dragEvaluator, "shouldAutoComplete", Qt::DirectConnection,
146+ Q_RETURN_ARG(QVariant, shouldAutoComplete));
147+ QVERIFY(ok);
148+
149+ // It should not autocomplete because the current negative velocity (speedThresholdMs * 0.5)
150+ // is bigger (in absolute terms) than the minimum negative velocity at this position needed to
151+ // cancel autocompletion (speedThreshold * 0.1)
152+ QCOMPARE(shouldAutoComplete.toBool(), false);
153+}
154+
155+void tst_EdgeDragEvaluator::horizontalDirection_data()
156+{
157+ QTest::addColumn<qreal>("dragDirection");
158+
159+ QTest::newRow("right") << 1.;
160+ QTest::newRow("left") << -1.;
161+}
162+
163+QTEST_MAIN(tst_EdgeDragEvaluator)
164+
165+#include "tst_EdgeDragEvaluator.moc"
tests/qmltests/Components/tst_EdgeDragEvaluator.qmlnew file+27
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright (C) 2015 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 "../../../qml/Components"
19+
20+Item {
21+ width: units.gu(70)
22+ height: units.gu(70)
23+
24+ EdgeDragEvaluator {
25+ objectName: "edgeDragEvaluator"
26+ }
27+}
tests/qmltests/tst_Shell.qml+14−10
@@ -461,11 +461,11 @@ Rectangle {
461461 function test_phoneLeftEdgeDrag_data() {
462462 return [
463463 {tag: "without launcher",
464− revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app",
464+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
465465 launcherHides: true, greeterShown: false},
466466
467467 {tag: "with launcher",
468− revealLauncher: true, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app",
468+ revealLauncher: true, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
469469 launcherHides: true, greeterShown: false},
470470
471471 {tag: "small swipe",
@@ -473,7 +473,7 @@ Rectangle {
473473 launcherHides: false, greeterShown: false},
474474
475475 {tag: "long swipe",
476− revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app",
476+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
477477 launcherHides: true, greeterShown: false},
478478
479479 {tag: "small swipe with greeter",
@@ -481,11 +481,11 @@ Rectangle {
481481 launcherHides: false, greeterShown: true},
482482
483483 {tag: "long swipe with greeter",
484− revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app",
484+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "dialer-app",
485485 launcherHides: true, greeterShown: true},
486486
487487 {tag: "swipe over dash",
488− revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "unity8-dash",
488+ revealLauncher: false, swipeLength: units.gu(30), appHides: true, focusedApp: "unity8-dash",
489489 launcherHides: false, greeterShown: false},
490490 ];
491491 }
@@ -690,7 +690,7 @@ Rectangle {
690690
691691 dashCommunicatorSpy.clear();
692692 // Minimize the application we just launched
693− swipeFromLeftEdge(units.gu(27));
693+ swipeFromLeftEdge(shell.width * 0.75);
694694
695695 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
696696
@@ -847,7 +847,8 @@ Rectangle {
847847 }
848848
849849 function dragLauncherIntoView() {
850− var launcherPanel = findChild(shell, "launcherPanel");
850+ var launcher = findChild(shell, "launcher");
851+ var launcherPanel = findChild(launcher, "launcherPanel");
851852 verify(launcherPanel.x = - launcherPanel.width);
852853
853854 var touchStartX = 2;
@@ -855,6 +856,7 @@ Rectangle {
855856 touchFlick(shell, touchStartX, touchStartY, launcherPanel.width + units.gu(1), touchStartY);
856857
857858 tryCompare(launcherPanel, "x", 0);
859+ tryCompare(launcher, "state", "visible");
858860 }
859861
860862 function tapOnAppIconInLauncher() {
@@ -891,7 +893,9 @@ Rectangle {
891893 function swipeFromLeftEdge(swipeLength) {
892894 var touchStartX = 2;
893895 var touchStartY = shell.height / 2;
894− touchFlick(shell, touchStartX, touchStartY, swipeLength, touchStartY);
896+ touchFlick(shell,
897+ touchStartX , touchStartY,
898+ touchStartX + swipeLength, touchStartY);
895899 }
896900
897901 function itemIsOnScreen(item) {
@@ -958,7 +962,7 @@ Rectangle {
958962 tryCompareFunction(function() { return app.session !== null && app.session.surface !== null }, true);
959963
960964 // Minimize the application we just launched
961− swipeFromLeftEdge(units.gu(26) + 1);
965+ swipeFromLeftEdge(shell.width * 0.75);
962966
963967 waitUntilDashIsFocused();
964968
@@ -1040,7 +1044,7 @@ Rectangle {
10401044
10411045 compare(panel.fullscreenMode, true);
10421046
1043− touchFlick(shell, units.gu(2), touchStartY, units.gu(10), touchStartY, false, false);
1047+ touchFlick(shell, units.gu(2), touchStartY, shell.width * 0.5, touchStartY, false, false);
10441048
10451049 tryCompare(panel, "fullscreenMode", false);
10461050
tests/qmltests/tst_ShellWithPin.qml+2−2
@@ -612,14 +612,14 @@ Item {
612612 tryCompare(coverPage, "showProgress", 1);
613613
614614 // Swipe cover page away
615− touchFlick(shell, 2, shell.height / 2, units.gu(27), shell.height / 2);
615+ touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);
616616 tryCompare(launcher, "x", -launcher.width);
617617 tryCompare(coverPage, "showProgress", 0);
618618 compare(lockscreen.shown, true);
619619 compare(ApplicationManager.focusedApplicationId, "gallery-app");
620620
621621 // Now attempt a swipe on lockscreen
622− touchFlick(shell, 2, shell.height / 2, units.gu(27), shell.height / 2);
622+ touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);
623623 tryCompare(launcher, "x", 0);
624624 compare(lockscreen.shown, true);
625625 compare(ApplicationManager.focusedApplicationId, "gallery-app");
tests/utils/modules/Unity/Test/UnityTestCase.qml+3−4
@@ -269,7 +269,7 @@ TestCase {
269269 speed = (speed !== undefined) ? speed : units.gu(10)
270270
271271 // Set a default iterations if not specified
272− var iterations = (iterations !== undefined) ? iterations : 5
272+ var iterations = (iterations !== undefined) ? iterations : 10
273273
274274 var distance = Math.sqrt(Math.pow(rootTo.x - rootFrom.x, 2) + Math.pow(rootTo.Y - rootFrom.y, 2))
275275 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
@@ -403,9 +403,8 @@ TestCase {
403403 function removeTimeConstraintsFromDirectionalDragAreas(item) {
404404
405405 // use duck-typing to identify a DirectionalDragArea
406− if (item.maxTime != undefined && item.compositionTime != undefined) {
407− item.maxTime = 60 * 60 * 1000;
408− item.compositionTime = 0;
406+ if (item.removeTimeConstraints != undefined) {
407+ item.removeTimeConstraints();
409408 } else {
410409 for (var i in item.children) {
411410 removeTimeConstraintsFromDirectionalDragAreas(item.children[i]);