on xenial help sign in
Merge trunk.
a4550c5Michał Sawicz
@@ -167,10 +167,6 @@ NB: Manual test or interation doesn't make sense for all components or classes,
167particularly the non-graphical ones. In such cases no "tryFoo" make target is
167particularly the non-graphical ones. In such cases no "tryFoo" make target is
168provided.
168provided.
169
169
170−NB: The tests expect the locale to be the C locale, they may fail if run
171−under a different locale, if you get failures others can not reproduce
172−prepend 'LC_ALL=C' to the 'make test' command
173−
174Running autopilot tests
170Running autopilot tests
175=======================
171=======================
176
172
cmake/modules/QmlTest.cmake+2−4
@@ -59,11 +59,11 @@ macro(add_manual_qml_test SUBPATH COMPONENT_NAME)
59endmacro(add_manual_qml_test)
59endmacro(add_manual_qml_test)
60
60
61macro(add_qml_benchmark SUBPATH COMPONENT_NAME ITERATIONS)
61macro(add_qml_benchmark SUBPATH COMPONENT_NAME ITERATIONS)
62− add_qml_test_internal(${SUBPATH} ${COMPONENT_NAME} ${ITERATIONS})
62+ add_qml_test_internal(${SUBPATH} ${COMPONENT_NAME} ${ITERATIONS} ${ARGN})
63endmacro(add_qml_benchmark)
63endmacro(add_qml_benchmark)
64
64
65macro(add_qml_test SUBPATH COMPONENT_NAME)
65macro(add_qml_test SUBPATH COMPONENT_NAME)
66− add_qml_test_internal(${SUBPATH} ${COMPONENT_NAME} 0)
66+ add_qml_test_internal(${SUBPATH} ${COMPONENT_NAME} 0 ${ARGN})
67endmacro(add_qml_test)
67endmacro(add_qml_test)
68
68
69macro(add_qml_test_internal SUBPATH COMPONENT_NAME ITERATIONS)
69macro(add_qml_test_internal SUBPATH COMPONENT_NAME ITERATIONS)
@@ -156,8 +156,6 @@ macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS)
156
156
157 add_qmltest_target(xvfbtest${CLASS_NAME} "${xvfbtestCommand}" FALSE TRUE)
157 add_qmltest_target(xvfbtest${CLASS_NAME} "${xvfbtestCommand}" FALSE TRUE)
158 add_dependencies(qmluitests xvfbtest${CLASS_NAME})
158 add_dependencies(qmluitests xvfbtest${CLASS_NAME})
159−
160− add_manual_qml_test(. ${CLASS_NAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
161endmacro(add_binary_qml_test)
159endmacro(add_binary_qml_test)
162
160
163macro(add_qmltest_target qmltest_TARGET qmltest_command qmltest_NO_TARGETS qmltest_NO_ADD_TEST)
161macro(add_qmltest_target qmltest_TARGET qmltest_command qmltest_NO_TARGETS qmltest_NO_ADD_TEST)
data/CMakeLists.txt+1−2
@@ -1,8 +1,7 @@
1# generate desktop file
1# generate desktop file
2configure_file(${SHELL_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop @ONLY)
2configure_file(${SHELL_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop @ONLY)
3−configure_file(${SCOPE_TOOL}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${SCOPE_TOOL}.desktop @ONLY)
4
3
5# install desktop files
4# install desktop files
6−install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop ${CMAKE_CURRENT_BINARY_DIR}/${SCOPE_TOOL}.desktop
5+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop
7 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
6 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
8 )
7 )
data/unity-scope-tool.desktop.indeleted−8
@@ -1,8 +0,0 @@
1−[Desktop Entry]
2−Type=Application
3−Name=Unity Scope Tool
4−Comment=Developer tool for Unity scopes.
5−Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/unity-scope-tool
6−Terminal=false
7−Icon=unity-scope-tool
8−X-Ubuntu-Touch=true
data/unity8.conf+9−1
@@ -12,6 +12,9 @@ respawn
12expect stop
12expect stop
13
13
14pre-start script
14pre-start script
15+ # Tell unity-mir to raise SIGSTOP after we start
16+ initctl set-env UNITY_MIR_EMITS_SIGSTOP=1
17+
15 if [ -n "$MIR_SOCKET" ]; then
18 if [ -n "$MIR_SOCKET" ]; then
16 if [ -z "$UNITY_MIR_SOCKET" ]; then
19 if [ -z "$UNITY_MIR_SOCKET" ]; then
17 # Save original value of MIR_SOCKET in case we are restarted,
20 # Save original value of MIR_SOCKET in case we are restarted,
@@ -42,7 +45,12 @@ pre-start script
42 fi
45 fi
43
46
44 initctl emit scope-ui-starting
47 initctl emit scope-ui-starting
45− initctl emit indicator-services-start
48+ initctl emit --no-wait indicator-services-start
46end script
49end script
47
50
48exec ${BINARY:-unity8} $ARGS
51exec ${BINARY:-unity8} $ARGS
52+
53+post-stop script
54+ initctl set-env --global MIR_SOCKET=$UNITY_MIR_SOCKET
55+ initctl unset-env --global UNITY_MIR_SOCKET
56+end script
debian/changelog+33
@@ -1,3 +1,36 @@
1+unity8 (7.86+14.10.20140429.2-0ubuntu1) utopic; urgency=medium
2+
3+ [ Andrea Cimitan ]
4+ * Update upstart job to reflect latest unity-mir changes
5+ * Fix locale in qml tests and fixtimeformattertest (LP: #1301038)
6+ * Fix 1309135 (LP: #1309135)
7+
8+ [ Michał Sawicz ]
9+ * Split out unity8-common package
10+ * Don't wait for indicator services to start, and drop Scope Tool's
11+ .desktop file. (LP: #1310172)
12+
13+ [ Michael Terry ]
14+ * Stop clock from hiding when the 'show dash' button is pressed in
15+ greeter. (LP: #1308139)
16+ * Make swipe teases in the greeter more helpful and obvious (LP:
17+ #1267623)
18+
19+ [ Nick Dedekind ]
20+ * Fixed datetime indicator appointment colour (LP: #1307048)
21+
22+ [ Albert Astals ]
23+ * Improve Card creation time by adding loaders that make sure only
24+ what's needed is loaded (LP: #1297197)
25+ * CategoryDelegateRange: Fix condition for detecting overshooting
26+ * Make xvfbtests work in the DashView plugins
27+ * Fix binding loop in FilterGrid height
28+
29+ [ Victor R. Ruiz ]
30+ * Move autopilot notification code to a helper method.
31+
32+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 29 Apr 2014 15:21:33 +0000
33+
1unity8 (7.85+14.04.20140416-0ubuntu1) trusty; urgency=low
34unity8 (7.85+14.04.20140416-0ubuntu1) trusty; urgency=low
2
35
3 [ Albert Astals ]
36 [ Albert Astals ]
debian/control+5−3
@@ -74,7 +74,7 @@ Provides: indicator-renderer,
74Depends: gsettings-desktop-schemas,
74Depends: gsettings-desktop-schemas,
75 libcap2-bin,
75 libcap2-bin,
76 libglib2.0-bin,
76 libglib2.0-bin,
77− libunity-mir1,
77+ libunity-mir1 (>= 0.4),
78 qmenumodel-qml (>= 0.2.7),
78 qmenumodel-qml (>= 0.2.7),
79 qml-module-qtquick-xmllistmodel,
79 qml-module-qtquick-xmllistmodel,
80 qtdeclarative5-gsettings1.0,
80 qtdeclarative5-gsettings1.0,
@@ -106,6 +106,8 @@ Depends: qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
106 unity-scopes-impl-0,
106 unity-scopes-impl-0,
107 unity8-fake-env | unity-application-impl,
107 unity8-fake-env | unity-application-impl,
108 ${misc:Depends},
108 ${misc:Depends},
109+Breaks: unity8 (<< 7.86),
110+Replaces: unity8 (<< 7.86),
109Description: Unity 8 shell (common files)
111Description: Unity 8 shell (common files)
110 The Unity 8 shell is the primary user interface for Ubuntu devices.
112 The Unity 8 shell is the primary user interface for Ubuntu devices.
111 .
113 .
@@ -128,8 +130,8 @@ Depends: gir1.2-glib-2.0,
128 python3-fixtures,
130 python3-fixtures,
129 python3-gi,
131 python3-gi,
130 ubuntu-ui-toolkit-autopilot,
132 ubuntu-ui-toolkit-autopilot,
131− unity8 (>= ${source:Version}),
133+ unity8 (= ${source:Version}),
132− unity8-fake-env (>= ${source:Version}),
134+ unity8-fake-env (= ${source:Version}),
133 url-dispatcher-tools,
135 url-dispatcher-tools,
134 ${misc:Depends},
136 ${misc:Depends},
135 ${python3:Depends},
137 ${python3:Depends},
debian/unity-scope-tool.install−1
@@ -1,3 +1,2 @@
1usr/bin/unity-scope-tool
1usr/bin/unity-scope-tool
2−usr/share/applications/unity-scope-tool.desktop
3usr/share/unity8/ScopeTool.qml
2usr/share/unity8/ScopeTool.qml
plugins/Unity/Indicators/qml/MenuItemFactory.qml+1−1
@@ -273,7 +273,7 @@ Item {
273 text: menuData && menuData.label || ""
273 text: menuData && menuData.label || ""
274 iconSource: menuData && menuData.icon || "image://theme/calendar"
274 iconSource: menuData && menuData.icon || "image://theme/calendar"
275 time: timeFormatter.timeString
275 time: timeFormatter.timeString
276− eventColor: menu && menu.ext.hasOwnProperty("xCanonicalColor") ? menu.ext.xCanonicalColor : Qt.rgba(0.0, 0.0, 0.0, 0.0)
276+ eventColor: getExtendedProperty(extendedData, "xCanonicalColor", Qt.rgba(0.0, 0.0, 0.0, 0.0))
277 enabled: menuData && menuData.sensitive || false
277 enabled: menuData && menuData.sensitive || false
278
278
279 onMenuModelChanged: {
279 onMenuModelChanged: {
po/unity8.pot+7−3
@@ -8,7 +8,7 @@ msgid ""
8msgstr ""
8msgstr ""
9"Project-Id-Version: unity8\n"
9"Project-Id-Version: unity8\n"
10"Report-Msgid-Bugs-To: \n"
10"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2014-03-25 16:48+0100\n"
11+"POT-Creation-Date: 2014-04-10 11:13-0400\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -84,7 +84,7 @@ msgstr ""
84msgid "See less"
84msgid "See less"
85msgstr ""
85msgstr ""
86
86
87−#: qml/Dash/DashApps.qml:38
87+#: qml/Dash/DashApps.qml:37
88msgid "Recent"
88msgid "Recent"
89msgstr ""
89msgstr ""
90
90
@@ -113,6 +113,10 @@ msgstr ""
113msgid "Send"
113msgid "Send"
114msgstr ""
114msgstr ""
115
115
116+#: qml/Greeter/Greeter.qml:151
117+msgid "Swipe to unlock"
118+msgstr ""
119+
116#: qml/Greeter/Lockscreen.qml:149
120#: qml/Greeter/Lockscreen.qml:149
117msgid "Emergency Call"
121msgid "Emergency Call"
118msgstr ""
122msgstr ""
@@ -158,7 +162,7 @@ msgstr ""
158msgid "Search"
162msgid "Search"
159msgstr ""
163msgstr ""
160
164
161−#: qml/Shell.qml:417
165+#: qml/Shell.qml:348
162#, qt-format
166#, qt-format
163msgid "Please enter %1"
167msgid "Please enter %1"
164msgstr ""
168msgstr ""
qml/Components/FilterGrid.qml+37−24
@@ -28,11 +28,6 @@ import "../Components"
28Item {
28Item {
29 id: root
29 id: root
30
30
31− /* If true, the number of elements displayed will be limited by collapsedRowCount.
32− If false, all elements will be displayed, effectively looking the same as a regular
33− ResponsiveGridView. */
34− property bool filter: true
35−
36 /* Whether, when collapsed, a button should be displayed enabling the user to expand
31 /* Whether, when collapsed, a button should be displayed enabling the user to expand
37 the grid to its full size. */
32 the grid to its full size. */
38 readonly property bool expandable: model.count > rowsWhenCollapsed * iconTileGrid.columns
33 readonly property bool expandable: model.count > rowsWhenCollapsed * iconTileGrid.columns
@@ -65,30 +60,48 @@ Item {
65 readonly property alias pressDelay: iconTileGrid.pressDelay
60 readonly property alias pressDelay: iconTileGrid.pressDelay
66 property alias highlightIndex: iconTileGrid.highlightIndex
61 property alias highlightIndex: iconTileGrid.highlightIndex
67 readonly property alias currentItem: iconTileGrid.currentItem
62 readonly property alias currentItem: iconTileGrid.currentItem
63+ readonly property alias filtered: d.filter
64+
65+ QtObject {
66+ id: d
67+ // We do have filter and collapsed properties because we need to decouple
68+ // the real filtering with the animation since the closing animation
69+ // i.e. setFilter(false. true) we still need to not be filtering until
70+ // the animation finishes otherwise we hide the items when the animation
71+ // is still running
72+ property bool filter: true
73+ property bool collapsed: true
74+ }
68
75
69− height: !filterAnimation.running ? childrenRect.height : height
76+ height: d.collapsed ? root.collapsedHeight : root.uncollapsedHeight
70 clip: filterAnimation.running
77 clip: filterAnimation.running
71
78
79+ Behavior on height {
80+ id: heightBehaviour
81+ enabled: false
72− NumberAnimation {
82+ NumberAnimation {
73− property bool filterEndValue
74− id: filterAnimation
83+ id: filterAnimation
75− target: root
76− property: "height"
77− to: filterEndValue ? root.collapsedHeight : root.uncollapsedHeight
78− // Duration and easing here match the ListViewWithPageHeader::m_contentYAnimation
84+ // Duration and easing here match the ListViewWithPageHeader::m_contentYAnimation
79− // otherwise since both animations can run at the same time you'll get
85+ // otherwise since both animations can run at the same time you'll get
80− // some visual weirdness.
86+ // some visual weirdness.
81− duration: 200
87+ duration: 200
82− easing.type: Easing.InOutQuad
88+ easing.type: Easing.InOutQuad
83− onStopped: {
89+ onRunningChanged: {
84− root.filter = filterEndValue;
90+ if (!running) {
91+ d.filter = d.collapsed;
92+ }
93+ heightBehaviour.enabled = false;
94+ }
85 }
95 }
86 }
96 }
87
97
88− function startFilterAnimation(filter) {
98+ function setFilter(filter, animate) {
89− filterAnimation.filterEndValue = filter
99+ heightBehaviour.enabled = animate;
90− filterAnimation.start();
100+ d.collapsed = filter;
101+ if (!animate || !filter) {
102+ d.filter = filter;
103+ }
91− }
104+ }
92
105
93 ResponsiveGridView {
106 ResponsiveGridView {
94 id: iconTileGrid
107 id: iconTileGrid
@@ -105,7 +118,7 @@ Item {
105
118
106 model: LimitProxyModel {
119 model: LimitProxyModel {
107 model: root.model
120 model: root.model
108− limit: (filter && !filterAnimation.running) ? rowsWhenCollapsed * iconTileGrid.columns : -1
121+ limit: d.filter ? rowsWhenCollapsed * iconTileGrid.columns : -1
109 }
122 }
110 }
123 }
111}
124}
qml/Dash/CardFilterGrid.qml+3−8
@@ -28,9 +28,10 @@ DashRenderer {
28 verticalSpacing: units.gu(1)
28 verticalSpacing: units.gu(1)
29 currentItem: filterGrid.currentItem
29 currentItem: filterGrid.currentItem
30 height: filterGrid.height
30 height: filterGrid.height
31+ filtered: filterGrid.filtered
31
32
32− function startFilterAnimation(filter) {
33+ function setFilter(filter, animate) {
33− filterGrid.startFilterAnimation(filter)
34+ filterGrid.setFilter(filter, animate)
34 }
35 }
35
36
36 FilterGrid {
37 FilterGrid {
@@ -41,7 +42,6 @@ DashRenderer {
41 delegateHeight: cardTool.cardHeight
42 delegateHeight: cardTool.cardHeight
42 verticalSpacing: genericFilterGrid.verticalSpacing
43 verticalSpacing: genericFilterGrid.verticalSpacing
43 model: genericFilterGrid.model
44 model: genericFilterGrid.model
44− filter: genericFilterGrid.filter
45 collapsedRowCount: Math.min(2, cardTool && cardTool.template && cardTool.template["collapsed-rows"] || 2)
45 collapsedRowCount: Math.min(2, cardTool && cardTool.template && cardTool.template["collapsed-rows"] || 2)
46 delegateCreationBegin: genericFilterGrid.delegateCreationBegin
46 delegateCreationBegin: genericFilterGrid.delegateCreationBegin
47 delegateCreationEnd: genericFilterGrid.delegateCreationEnd
47 delegateCreationEnd: genericFilterGrid.delegateCreationEnd
@@ -66,10 +66,5 @@ DashRenderer {
66 onPressAndHold: genericFilterGrid.pressAndHold(index, card.y)
66 onPressAndHold: genericFilterGrid.pressAndHold(index, card.y)
67 }
67 }
68 }
68 }
69−
70− onFilterChanged: {
71− genericFilterGrid.filter = filter
72− filter = Qt.binding(function() { return genericFilterGrid.filter })
73− }
74 }
69 }
75}
70}
qml/Dash/DashRenderer.qml+3−3
@@ -20,8 +20,8 @@ Item {
20 // Can the item be expanded?
20 // Can the item be expanded?
21 property bool expandable: false
21 property bool expandable: false
22
22
23− // In case it can be expanded, should we filter it
23+ // In case it can be expanded, is it filtered
24− property bool filter: true
24+ property bool filtered: true
25
25
26 property int collapsedHeight: height
26 property int collapsedHeight: height
27
27
@@ -58,6 +58,6 @@ Item {
58 /// @param itemY is y of the held delegate
58 /// @param itemY is y of the held delegate
59 signal pressAndHold(int index, real itemY)
59 signal pressAndHold(int index, real itemY)
60
60
61− function startFilterAnimation(filter) {
61+ function setFilter(filter, animate) {
62 }
62 }
63}
63}
qml/Dash/GenericScopeView.qml+13−15
@@ -122,7 +122,7 @@ FocusScope {
122 showDivider: false
122 showDivider: false
123
123
124 readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false
124 readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false
125− readonly property bool filtered: rendererLoader.item ? rendererLoader.item.filter : true
125+ readonly property bool filtered: rendererLoader.item ? rendererLoader.item.filtered : true
126 readonly property string category: categoryId
126 readonly property string category: categoryId
127 readonly property var item: rendererLoader.item
127 readonly property var item: rendererLoader.item
128
128
@@ -168,9 +168,7 @@ FocusScope {
168 item.objectName = Qt.binding(function() { return categoryId })
168 item.objectName = Qt.binding(function() { return categoryId })
169 if (item.expandable) {
169 if (item.expandable) {
170 var shouldFilter = categoryId != categoryView.expandedCategoryId;
170 var shouldFilter = categoryId != categoryView.expandedCategoryId;
171− if (shouldFilter != item.filter) {
171+ item.setFilter(shouldFilter, false /*animate*/);
172− item.filter = shouldFilter;
173− }
174 }
172 }
175 updateDelegateCreationRange();
173 updateDelegateCreationRange();
176 item.cardTool = cardTool;
174 item.cardTool = cardTool;
@@ -219,11 +217,8 @@ FocusScope {
219 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
217 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
220 var growingVisible = !shouldFilter && y + height < categoryView.height;
218 var growingVisible = !shouldFilter && y + height < categoryView.height;
221 if (!previewListView.open || !shouldFilter) {
219 if (!previewListView.open || !shouldFilter) {
222− if (shrinkingVisible || growingVisible) {
220+ var animate = shrinkingVisible || growingVisible;
223− item.startFilterAnimation(shouldFilter)
221+ item.setFilter(shouldFilter, animate)
224− } else {
225− item.filter = shouldFilter;
226− }
227 if (!shouldFilter && !previewListView.open) {
222 if (!shouldFilter && !previewListView.open) {
228 categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
223 categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
229 }
224 }
@@ -238,12 +233,15 @@ FocusScope {
238 }
233 }
239
234
240 function updateDelegateCreationRange() {
235 function updateDelegateCreationRange() {
236+ if (categoryView.moving) {
241− // Do not update the range if we are overshooting up or down, since we'll come back
237+ // Do not update the range if we are overshooting up or down, since we'll come back
242− // to the stable position and delete/create items without any reason
238+ // to the stable position and delete/create items without any reason
243− if (categoryView.contentY < categoryView.originY) {
239+ if (categoryView.contentY < categoryView.originY) {
244− return;
240+ return;
241+ } else if (categoryView.contentHeight - categoryView.originY > categoryView.height &&
245− } else if (categoryView.contentHeight > categoryView.height && categoryView.contentY + categoryView.height > categoryView.contentHeight) {
242+ categoryView.contentY + categoryView.height > categoryView.contentHeight) {
246− return;
243+ return;
244+ }
247 }
245 }
248
246
249 if (item && item.hasOwnProperty("delegateCreationBegin")) {
247 if (item && item.hasOwnProperty("delegateCreationBegin")) {
qml/Dash/Previews/PreviewRatingInput.qml+2−2
@@ -155,8 +155,8 @@ PreviewWidget {
155 objectName: "submitButton"
155 objectName: "submitButton"
156
156
157 readonly property bool readyToSubmit: {
157 readonly property bool readyToSubmit: {
158− if (reviewTextArea.text === "" ||
159− (widgetData["visible"] === "both" && rating.value < 0)) return false;
158+ if ((widgetData["required"] !== "review" && rating.value < 0) ||
159+ (widgetData["required"] !== "rating" && reviewTextArea.text === "")) return false;
160 else return true;
160 else return true;
161 }
161 }
162
162
qml/Greeter/Clock.qml+3−6
@@ -24,14 +24,11 @@ Item {
24 implicitWidth: childrenRect.width
24 implicitWidth: childrenRect.width
25 implicitHeight: childrenRect.height
25 implicitHeight: childrenRect.height
26
26
27− // Allows to set the current Date. Will be overwritten if active
27+ // Allows to set the current Date. Will be overwritten if visible
28 property date currentDate
28 property date currentDate
29
29
30− // If active, time will be updated through the indicators service
31− property bool active: clock.enabled && clock.visible
32−
33 Component.onCompleted: {
30 Component.onCompleted: {
34− if (active) {
31+ if (visible) {
35 currentDate = new Date()
32 currentDate = new Date()
36 }
33 }
37 }
34 }
@@ -42,7 +39,7 @@ Item {
42
39
43 busName: "com.canonical.indicator.datetime"
40 busName: "com.canonical.indicator.datetime"
44 actionsObjectPath: "/com/canonical/indicator/datetime"
41 actionsObjectPath: "/com/canonical/indicator/datetime"
45− menuObjectPath: clock.active ? "/com/canonical/indicator/datetime/phone" : ""
42+ menuObjectPath: clock.visible ? "/com/canonical/indicator/datetime/phone" : ""
46
43
47 Indicators.RootActionState {
44 Indicators.RootActionState {
48 menu: timeModel.model
45 menu: timeModel.model
qml/Greeter/Greeter.qml+115−42
@@ -28,10 +28,10 @@ Showable {
28 property url defaultBackground
28 property url defaultBackground
29
29
30 // 1 when fully shown and 0 when fully hidden
30 // 1 when fully shown and 0 when fully hidden
31− property real showProgress: MathUtils.clamp((width + x) / width, 0, 1)
31+ property real showProgress: MathUtils.clamp((width - Math.abs(x)) / width, 0, 1)
32
32
33 showAnimation: StandardAnimation { property: "x"; to: 0 }
33 showAnimation: StandardAnimation { property: "x"; to: 0 }
34− hideAnimation: StandardAnimation { property: "x"; to: -width }
34+ hideAnimation: __leftHideAnimation
35
35
36 property alias dragHandleWidth: dragHandle.width
36 property alias dragHandleWidth: dragHandle.width
37 property alias model: greeterContentLoader.model
37 property alias model: greeterContentLoader.model
@@ -40,19 +40,81 @@ Showable {
40 readonly property bool narrowMode: !multiUser && height > width
40 readonly property bool narrowMode: !multiUser && height > width
41 readonly property bool multiUser: LightDM.Users.count > 1
41 readonly property bool multiUser: LightDM.Users.count > 1
42
42
43− readonly property bool leftTeaserPressed: greeterContentLoader.status == Loader.Ready &&
44− greeterContentLoader.item.leftTeaserPressed
45− readonly property bool rightTeaserPressed: greeterContentLoader.status == Loader.Ready &&
46− greeterContentLoader.item.rightTeaserPressed
47−
48 readonly property int currentIndex: greeterContentLoader.currentIndex
43 readonly property int currentIndex: greeterContentLoader.currentIndex
49
44
45+ property var __leftHideAnimation: StandardAnimation { property: "x"; to: -width }
46+ property var __rightHideAnimation: StandardAnimation { property: "x"; to: width }
47+
50 signal selected(int uid)
48 signal selected(int uid)
51 signal unlocked(int uid)
49 signal unlocked(int uid)
50+ signal tease()
51+
52+ function hideRight() {
53+ if (shown) {
54+ hideAnimation = __rightHideAnimation
55+ hide()
56+ }
57+ }
58+
59+ onRequiredChanged: {
60+ // Reset hide animation to default once we're finished with it
61+ if (!required) {
62+ // Put back on left for reliable show direction and so that
63+ // if normal hide() is called, we don't animate from right.
64+ x = -width
65+ hideAnimation = __leftHideAnimation
66+ }
67+ }
68+
69+ // Bi-directional revealer
70+ DraggingArea {
71+ id: dragHandle
72+ anchors.fill: parent
73+ enabled: greeter.narrowMode || !greeter.locked
74+ orientation: Qt.Horizontal
75+ propagateComposedEvents: true
76+
77+ Component.onCompleted: {
78+ // set evaluators to baseline of dragValue == 0
79+ leftEvaluator.reset()
80+ rightEvaluator.reset()
81+ }
82+
83+ function maybeTease() {
84+ if (!greeter.locked || greeter.narrowMode)
85+ greeter.tease();
86+ }
87+
88+ onClicked: maybeTease()
89+ onDragStart: maybeTease()
90+ onPressAndHold: {} // eat event, but no need to tease, as drag will cover it
91+
92+ onDragEnd: {
93+ if (rightEvaluator.shouldAutoComplete())
94+ greeter.hideRight()
95+ else if (leftEvaluator.shouldAutoComplete())
96+ greeter.hide();
97+ else
98+ greeter.show(); // undo drag
99+ }
100+
101+ onDragValueChanged: {
102+ // dragValue is kept as a "step" value since we do this x adjusting on the fly
103+ greeter.x += dragValue
104+ }
105+
106+ EdgeDragEvaluator {
107+ id: rightEvaluator
108+ trackedPosition: dragHandle.dragValue + greeter.x
109+ maxDragDistance: parent.width
110+ direction: Direction.Rightwards
111+ }
52
112
53− onRightTeaserPressedChanged: {
113+ EdgeDragEvaluator {
54− if (rightTeaserPressed && (!locked || narrowMode) && x == 0) {
114+ id: leftEvaluator
55− teasingTimer.start();
115+ trackedPosition: dragHandle.dragValue + greeter.x
116+ maxDragDistance: parent.width
117+ direction: Direction.Leftwards
56 }
118 }
57 }
119 }
58
120
@@ -82,45 +144,56 @@ Showable {
82 }
144 }
83 }
145 }
84
146
85− Timer {
147+ onTease: showLabelAnimation.start()
86− id: teasingTimer
87− interval: 200
88− }
89
148
90− states: [
149+ Label {
91− State {
150+ id: swipeHint
92− name: "teasing"
151+ visible: greeter.shown
152+ property real baseOpacity: 0.5
153+ opacity: 0.0
154+ anchors.horizontalCenter: parent.horizontalCenter
155+ anchors.bottom: parent.bottom
156+ anchors.bottomMargin: units.gu(5)
157+ text: i18n.tr("Swipe to unlock")
158+ color: "white"
159+ font.weight: Font.Light
160+
161+ SequentialAnimation on opacity {
162+ id: showLabelAnimation
93− when: teasingTimer.running
163+ running: false
94− PropertyChanges {
164+ loops: 2
95− target: greeter
165+
96− x: -dragHandle.hintDisplacement
166+ StandardAnimation {
167+ from: 0.0
168+ to: swipeHint.baseOpacity
169+ duration: UbuntuAnimation.SleepyDuration
97 }
170 }
98− }
171+ PauseAnimation { duration: UbuntuAnimation.BriskDuration }
99− ]
172+ StandardAnimation {
100− transitions: [
173+ from: swipeHint.baseOpacity
101− Transition {
102− from: "*"
103− to: "*"
174+ to: 0.0
104− NumberAnimation {
175+ duration: UbuntuAnimation.SleepyDuration
105− target: greeter
106− property: "x"
107− duration: 300
108− easing.type: Easing.OutCubic
109 }
176 }
110 }
177 }
111− ]
178+ }
112−
113− DragHandle {
114− id: dragHandle
115
179
180+ // right side shadow
181+ Image {
182+ anchors.left: parent.right
116 anchors.top: parent.top
183 anchors.top: parent.top
117 anchors.bottom: parent.bottom
184 anchors.bottom: parent.bottom
118− anchors.right: parent.right
185+ visible: parent.required
119−
186+ fillMode: Image.Tile
120− hintDisplacement: units.gu(2)
187+ source: "../graphics/dropshadow_right.png"
121−
188+ }
122− enabled: greeter.narrowMode || !greeter.locked
123
189
124− direction: Direction.Leftwards
190+ // left side shadow
191+ Image {
192+ anchors.right: parent.left
193+ anchors.top: parent.top
194+ anchors.bottom: parent.bottom
195+ visible: parent.required
196+ fillMode: Image.Tile
197+ source: "../graphics/dropshadow_left.png"
125 }
198 }
126}
199}
qml/Greeter/GreeterContent.qml+1−10
@@ -20,15 +20,11 @@ import Ubuntu.Components 0.1
20import LightDM 0.1 as LightDM
20import LightDM 0.1 as LightDM
21import "../Components"
21import "../Components"
22
22
23−MouseArea {
23+Item {
24 id: root
24 id: root
25 anchors.fill: parent
25 anchors.fill: parent
26
26
27 property bool ready: background.source == "" || background.status == Image.Ready || background.status == Image.Error
27 property bool ready: background.source == "" || background.status == Image.Ready || background.status == Image.Error
28− property bool leftTeaserPressed: teasingMouseArea.pressed &&
29− teasingMouseArea.mouseX < teasingMouseArea.width / 2
30− property bool rightTeaserPressed: teasingMouseArea.pressed &&
31− teasingMouseArea.mouseX > teasingMouseArea.width / 2
32
28
33 signal selected(int uid)
29 signal selected(int uid)
34 signal unlocked(int uid)
30 signal unlocked(int uid)
@@ -74,11 +70,6 @@ MouseArea {
74 opacity: 0.4
70 opacity: 0.4
75 }
71 }
76
72
77− MouseArea {
78− id: teasingMouseArea
79− anchors.fill: parent
80− }
81−
82 Loader {
73 Loader {
83 id: loginLoader
74 id: loginLoader
84 objectName: "loginLoader"
75 objectName: "loginLoader"
qml/Greeter/Infographics.qml+3
@@ -457,5 +457,8 @@ Item {
457 !circleGrowAnimTimer.running)
457 !circleGrowAnimTimer.running)
458 infographic.model.nextDataSource()
458 infographic.model.nextDataSource()
459 }
459 }
460+
461+ onClicked: mouse.accepted = false
462+ onPressed: mouse.accepted = false
460 }
463 }
461}
464}
qml/Hud/Hud.qml+1−1
@@ -491,6 +491,6 @@ Item {
491 anchors.top: hudContentClipper.top
491 anchors.top: hudContentClipper.top
492 anchors.bottom: hudContentClipper.bottom
492 anchors.bottom: hudContentClipper.bottom
493 fillMode: Image.Tile
493 fillMode: Image.Tile
494− source: "graphics/dropshadow_right.png"
494+ source: "../graphics/dropshadow_right.png"
495 }
495 }
496}
496}
qml/Shell.qml+59−45
@@ -148,8 +148,8 @@ FocusScope {
148
148
149 available: !greeter.shown && !lockscreen.shown
149 available: !greeter.shown && !lockscreen.shown
150 hides: [stages, launcher, panel.indicators]
150 hides: [stages, launcher, panel.indicators]
151− shown: disappearingAnimationProgress !== 1.0
151+ shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0
152− enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
152+ enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
153
153
154 anchors {
154 anchors {
155 fill: parent
155 fill: parent
@@ -159,18 +159,14 @@ FocusScope {
159 contentScale: 1.0 - 0.2 * disappearingAnimationProgress
159 contentScale: 1.0 - 0.2 * disappearingAnimationProgress
160 opacity: 1.0 - disappearingAnimationProgress
160 opacity: 1.0 - disappearingAnimationProgress
161 property real disappearingAnimationProgress: {
161 property real disappearingAnimationProgress: {
162− if (greeter.shown) {
162+ if (stages.overlayMode) {
163− return greeter.showProgress;
163+ return 0;
164 } else {
164 } else {
165− if (stages.overlayMode) {
166− return 0;
167− }
168 return stages.showProgress
165 return stages.showProgress
169 }
166 }
170 }
167 }
171
168
172− // FIXME: only necessary because stages.showProgress and
169+ // FIXME: only necessary because stages.showProgress is not animated
173− // greeterRevealer.animatedProgress are not animated
174 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
170 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
175 }
171 }
176 }
172 }
@@ -362,52 +358,66 @@ FocusScope {
362 }
358 }
363 }
359 }
364
360
365− Greeter {
361+ Rectangle {
366− id: greeter
362+ anchors.fill: parent
367− objectName: "greeter"
363+ color: "black"
368−
364+ opacity: greeterWrapper.showProgress * 0.8
369− available: true
365+ }
370− hides: [launcher, panel.indicators, hud]
371− shown: true
372−
373− defaultBackground: shell.background
374
366
367+ Item {
368+ // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
369+ id: greeterWrapper
370+ x: launcher.progress
375 y: panel.panelHeight
371 y: panel.panelHeight
376 width: parent.width
372 width: parent.width
377 height: parent.height - panel.panelHeight
373 height: parent.height - panel.panelHeight
378
374
379− dragHandleWidth: shell.edgeSize
375+ Behavior on x {SmoothedAnimation{velocity: 600}}
380
376
377+ readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
378+
379+ Greeter {
380+ id: greeter
381+ objectName: "greeter"
382+
383+ available: true
384+ hides: [launcher, panel.indicators, hud]
385+ shown: true
386+
387+ defaultBackground: shell.background
388+
389+ width: parent.width
390+ height: parent.height
391+
392+ dragHandleWidth: shell.edgeSize
393+
381− onShownChanged: {
394+ onShownChanged: {
382− if (shown) {
395+ if (shown) {
383− lockscreen.reset();
396+ lockscreen.reset();
384− // If there is only one user, we start authenticating with that one here.
397+ // If there is only one user, we start authenticating with that one here.
385− // If there are more users, the Greeter will handle that
398+ // If there are more users, the Greeter will handle that
386− if (LightDM.Users.count == 1) {
399+ if (LightDM.Users.count == 1) {
387− LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
400+ LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
401+ }
402+ greeter.forceActiveFocus();
388 }
403 }
389− greeter.forceActiveFocus();
390 }
404 }
391− }
392−
393− onUnlocked: greeter.hide()
394− onSelected: {
395− // Update launcher items for new user
396− var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
397− AccountsService.user = user;
398− LauncherModel.setUser(user);
399− }
400
405
401− onLeftTeaserPressedChanged: {
406+ onUnlocked: greeter.hide()
402− if (leftTeaserPressed) {
407+ onSelected: {
403− launcher.tease();
408+ // Update launcher items for new user
409+ var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
410+ AccountsService.user = user;
411+ LauncherModel.setUser(user);
404 }
412 }
405− }
406
413
414+ onTease: launcher.tease()
415+
407− Binding {
416+ Binding {
408− target: ApplicationManager
417+ target: ApplicationManager
409− property: "suspended"
418+ property: "suspended"
410− value: greeter.shown && greeter.showProgress == 1
419+ value: greeter.shown && greeterWrapper.showProgress == 1
420+ }
411 }
421 }
412 }
422 }
413
423
@@ -538,7 +548,7 @@ FocusScope {
538 anchors.bottom: parent.bottom
548 anchors.bottom: parent.bottom
539 width: parent.width
549 width: parent.width
540 dragAreaWidth: shell.edgeSize
550 dragAreaWidth: shell.edgeSize
541− available: (!greeter.shown || greeter.narrowMode) && edgeDemo.launcherEnabled
551+ available: edgeDemo.launcherEnabled
542
552
543 onShowDashHome: {
553 onShowDashHome: {
544 if (edgeDemo.running)
554 if (edgeDemo.running)
@@ -553,6 +563,10 @@ FocusScope {
553 launcher.hide();
563 launcher.hide();
554 }
564 }
555 }
565 }
566+ if (greeter.shown) {
567+ greeter.hideRight();
568+ launcher.hide();
569+ }
556 }
570 }
557 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
571 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
558 onLauncherApplicationSelected: {
572 onLauncherApplicationSelected: {
qml/graphics/dropshadow_left@20.pngnew file

Binary file not shown.

qml/Hud/graphics/dropshadow_right@20.png → qml/graphics/dropshadow_right@20.pngrenamed

Binary file not shown.

tests/autopilot/unity8/shell/__init__.py+50−1
@@ -21,8 +21,9 @@
21
21
22from time import sleep
22from time import sleep
23from functools import wraps
23from functools import wraps
24−import logging
24+from gi.repository import Notify
25
25
26+import logging
26
27
27logger = logging.getLogger(__name__)
28logger = logging.getLogger(__name__)
28
29
@@ -48,6 +49,7 @@ def disable_qml_mocking(fn):
48 return fn(*args, **kwargs)
49 return fn(*args, **kwargs)
49 return wrapper
50 return wrapper
50
51
52+
51class DragMixin(object):
53class DragMixin(object):
52 def _drag(self, x1, y1, x2, y2):
54 def _drag(self, x1, y1, x2, y2):
53 # XXX This ugly code is here just temporarily, waiting for drag
55 # XXX This ugly code is here just temporarily, waiting for drag
@@ -69,3 +71,50 @@ class DragMixin(object):
69 self.touch._finger_move(int(x2), int(y2))
71 self.touch._finger_move(int(x2), int(y2))
70 except AttributeError:
72 except AttributeError:
71 self.touch._device.finger_move(int(x2), int(y2))
73 self.touch._device.finger_move(int(x2), int(y2))
74+
75+
76+def create_ephemeral_notification(
77+ summary='',
78+ body='',
79+ icon=None,
80+ hints=[],
81+ urgency='NORMAL'
82+):
83+ """Create an ephemeral (non-interactive) notification
84+
85+ :param summary: Summary text for the notification
86+ :param body: Body text to display in the notification
87+ :param icon: Path string to the icon to use
88+ :param hint_strings: List of tuples containing the 'name' and value
89+ for setting the hint strings for the notification
90+ :param urgency: Urgency string for the noticiation, either: 'LOW',
91+ 'NORMAL', 'CRITICAL'
92+ """
93+ Notify.init('Unity8')
94+
95+ logger.info(
96+ "Creating ephemeral: summary(%s), body(%s), urgency(%r) "
97+ "and Icon(%s)",
98+ summary,
99+ body,
100+ urgency,
101+ icon
102+ )
103+
104+ notification = Notify.Notification.new(summary, body, icon)
105+
106+ for hint in hints:
107+ key, value = hint
108+ notification.set_hint_string(key, value)
109+ logger.info("Adding hint to notification: (%s, %s)", key, value)
110+ notification.set_urgency(_get_urgency(urgency))
111+
112+ return notification
113+
114+
115+def _get_urgency(urgency):
116+ """Translates urgency string to enum."""
117+ _urgency_enums = {'LOW': Notify.Urgency.LOW,
118+ 'NORMAL': Notify.Urgency.NORMAL,
119+ 'CRITICAL': Notify.Urgency.CRITICAL}
120+ return _urgency_enums.get(urgency.upper())
tests/autopilot/unity8/shell/tests/test_emulators.py+1−1
@@ -196,7 +196,7 @@ class DashAppsEmulatorTestCase(DashBaseTestCase):
196 category)
196 category)
197 grid = category_element.select_single('CardFilterGrid')
197 grid = category_element.select_single('CardFilterGrid')
198 filtergrid = grid.select_single('FilterGrid')
198 filtergrid = grid.select_single('FilterGrid')
199− if (grid.filter):
199+ if (grid.filtered):
200 return filtergrid.collapsedRowCount * filtergrid.columns
200 return filtergrid.collapsedRowCount * filtergrid.columns
201 else:
201 else:
202 return filtergrid.uncollapsedRowCount * filtergrid.columns
202 return filtergrid.uncollapsedRowCount * filtergrid.columns
tests/autopilot/unity8/shell/tests/test_notifications.py+14−57
@@ -21,6 +21,7 @@
21
21
22from __future__ import absolute_import
22from __future__ import absolute_import
23
23
24+from unity8 import shell
24from unity8.process_helpers import unlock_unity
25from unity8.process_helpers import unlock_unity
25from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
26from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
26
27
@@ -64,7 +65,8 @@ class NotificationsBase(UnityTestCase):
64 if os.path.abspath(__file__).startswith('/usr/'):
65 if os.path.abspath(__file__).startswith('/usr/'):
65 return '/usr/share/unity8/graphics/' + icon_name
66 return '/usr/share/unity8/graphics/' + icon_name
66 else:
67 else:
67− return os.path.dirname(__file__) + "/../../../../../qml/graphics/" + icon_name
68+ return os.path.dirname(__file__) + (
69+ "/../../../../../qml/graphics/" + icon_name)
68
70
69 def _get_notifications_list(self):
71 def _get_notifications_list(self):
70 return self.main_window.select_single(
72 return self.main_window.select_single(
@@ -354,7 +356,7 @@ class EphemeralNotificationsTests(NotificationsBase):
354 )
356 )
355 ]
357 ]
356
358
357− notification = self._create_ephemeral_notification(
359+ notification = shell.create_ephemeral_notification(
358 summary,
360 summary,
359 body,
361 body,
360 icon_path,
362 icon_path,
@@ -391,7 +393,7 @@ class EphemeralNotificationsTests(NotificationsBase):
391 )
393 )
392 ]
394 ]
393
395
394− notification = self._create_ephemeral_notification(
396+ notification = shell.create_ephemeral_notification(
395 summary,
397 summary,
396 None,
398 None,
397 None,
399 None,
@@ -433,7 +435,7 @@ class EphemeralNotificationsTests(NotificationsBase):
433 body_critical = 'Dude, this is so urgent you have no idea :)'
435 body_critical = 'Dude, this is so urgent you have no idea :)'
434 icon_path_critical = self._get_icon_path('avatars/anna_olsson.png')
436 icon_path_critical = self._get_icon_path('avatars/anna_olsson.png')
435
437
436− notification_normal = self._create_ephemeral_notification(
438+ notification_normal = shell.create_ephemeral_notification(
437 summary_normal,
439 summary_normal,
438 body_normal,
440 body_normal,
439 icon_path_normal,
441 icon_path_normal,
@@ -441,7 +443,7 @@ class EphemeralNotificationsTests(NotificationsBase):
441 )
443 )
442 notification_normal.show()
444 notification_normal.show()
443
445
444− notification_low = self._create_ephemeral_notification(
446+ notification_low = shell.create_ephemeral_notification(
445 summary_low,
447 summary_low,
446 body_low,
448 body_low,
447 icon_path_low,
449 icon_path_low,
@@ -449,7 +451,7 @@ class EphemeralNotificationsTests(NotificationsBase):
449 )
451 )
450 notification_low.show()
452 notification_low.show()
451
453
452− notification_critical = self._create_ephemeral_notification(
454+ notification_critical = shell.create_ephemeral_notification(
453 summary_critical,
455 summary_critical,
454 body_critical,
456 body_critical,
455 icon_path_critical,
457 icon_path_critical,
@@ -510,7 +512,7 @@ class EphemeralNotificationsTests(NotificationsBase):
510 summary = 'Summary-Body'
512 summary = 'Summary-Body'
511 body = 'This is a superfluous notification'
513 body = 'This is a superfluous notification'
512
514
513− notification = self._create_ephemeral_notification(summary, body)
515+ notification = shell.create_ephemeral_notification(summary, body)
514 notification.show()
516 notification.show()
515
517
516 notification = notify_list.wait_select_single(
518 notification = notify_list.wait_select_single(
@@ -534,7 +536,7 @@ class EphemeralNotificationsTests(NotificationsBase):
534
536
535 summary = 'Summary-Only'
537 summary = 'Summary-Only'
536
538
537− notification = self._create_ephemeral_notification(summary)
539+ notification = shell.create_ephemeral_notification(summary)
538 notification.show()
540 notification.show()
539
541
540 notification = notify_list.wait_select_single(
542 notification = notify_list.wait_select_single(
@@ -554,7 +556,7 @@ class EphemeralNotificationsTests(NotificationsBase):
554 body = 'This is the original content of this notification-bubble.'
556 body = 'This is the original content of this notification-bubble.'
555 icon_path = self._get_icon_path('avatars/funky.png')
557 icon_path = self._get_icon_path('avatars/funky.png')
556
558
557− notification = self._create_ephemeral_notification(
559+ notification = shell.create_ephemeral_notification(
558 summary,
560 summary,
559 body,
561 body,
560 icon_path
562 icon_path
@@ -594,7 +596,7 @@ class EphemeralNotificationsTests(NotificationsBase):
594 icon_path = self._get_icon_path('avatars/anna_olsson.png')
596 icon_path = self._get_icon_path('avatars/anna_olsson.png')
595 hint_icon = self._get_icon_path('applicationIcons/phone-app.png')
597 hint_icon = self._get_icon_path('applicationIcons/phone-app.png')
596
598
597− notification = self._create_ephemeral_notification(
599+ notification = shell.create_ephemeral_notification(
598 summary,
600 summary,
599 body,
601 body,
600 icon_path
602 icon_path
@@ -638,7 +640,7 @@ class EphemeralNotificationsTests(NotificationsBase):
638 body = 'Hey Bro Coly!'
640 body = 'Hey Bro Coly!'
639 icon_path = self._get_icon_path('avatars/amanda.png')
641 icon_path = self._get_icon_path('avatars/amanda.png')
640 body_sum = body
642 body_sum = body
641− notification = self._create_ephemeral_notification(
643+ notification = shell.create_ephemeral_notification(
642 summary,
644 summary,
643 body,
645 body,
644 icon_path,
646 icon_path,
@@ -674,7 +676,7 @@ class EphemeralNotificationsTests(NotificationsBase):
674 for new_body in bodies:
676 for new_body in bodies:
675 body = new_body
677 body = new_body
676 body_sum += '\n' + body
678 body_sum += '\n' + body
677− notification = self._create_ephemeral_notification(
679+ notification = shell.create_ephemeral_notification(
678 summary,
680 summary,
679 body,
681 body,
680 icon_path,
682 icon_path,
@@ -695,48 +697,3 @@ class EphemeralNotificationsTests(NotificationsBase):
695 False,
697 False,
696 1.0
698 1.0
697 )
699 )
698−
699− def _create_ephemeral_notification(
700− self,
701− summary="",
702− body="",
703− icon=None,
704− hints=[],
705− urgency="NORMAL"
706− ):
707− """Create an ephemeral (non-interactive) notification
708−
709− :param summary: Summary text for the notification
710− :param body: Body text to display in the notification
711− :param icon: Path string to the icon to use
712− :param hint_strings: List of tuples containing the 'name' and value
713− for setting the hint strings for the notification
714− :param urgency: Urgency string for the noticiation, either: 'LOW',
715− 'NORMAL', 'CRITICAL'
716−
717− """
718− logger.info(
719− "Creating ephemeral: summary(%s), body(%s), urgency(%r) "
720− "and Icon(%s)",
721− summary,
722− body,
723− urgency,
724− icon
725− )
726−
727− n = Notify.Notification.new(summary, body, icon)
728−
729− for hint in hints:
730− key, value = hint
731− n.set_hint_string(key, value)
732− logger.info("Adding hint to notification: (%s, %s)", key, value)
733− n.set_urgency(self._get_urgency(urgency))
734−
735− return n
736−
737− def _get_urgency(self, urgency):
738− """Translates urgency string to enum."""
739− _urgency_enums = {'LOW': Notify.Urgency.LOW,
740− 'NORMAL': Notify.Urgency.NORMAL,
741− 'CRITICAL': Notify.Urgency.CRITICAL}
742− return _urgency_enums.get(urgency.upper())
tests/autopilot/unity8/shell/tests/test_upstart.py+2−2
@@ -82,7 +82,7 @@ class UpstartIntegrationTests(UnityTestCase):
82 ))
82 ))
83
83
84 def test_no_sigstop(self):
84 def test_no_sigstop(self):
85− self.patch_environment("UPSTART_JOB", "foo")
85+ self.patch_environment("UNITY_MIR_EMITS_SIGSTOP", "")
86 self._launch_unity()
86 self._launch_unity()
87 self._set_proxy()
87 self._set_proxy()
88
88
@@ -91,7 +91,7 @@ class UpstartIntegrationTests(UnityTestCase):
91 logger.debug("Unity loaded and ready.")
91 logger.debug("Unity loaded and ready.")
92
92
93 def test_expect_sigstop(self):
93 def test_expect_sigstop(self):
94− self.patch_environment("UPSTART_JOB", "unity8")
94+ self.patch_environment("UNITY_MIR_EMITS_SIGSTOP", "1")
95 self._launch_unity()
95 self._launch_unity()
96 self.assertThat(lambda: os.WIFSTOPPED(self._get_status()), Eventually(Equals(True)), "Unity8 should raise SIGSTOP when ready")
96 self.assertThat(lambda: os.WIFSTOPPED(self._get_status()), Eventually(Equals(True)), "Unity8 should raise SIGSTOP when ready")
97
97
tests/plugins/DashViews/CMakeLists.txt+23−35
@@ -25,46 +25,41 @@ remove_definitions(-DQT_NO_KEYWORDS)
25add_definitions(-DDASHVIEWSTEST_FOLDER="${CMAKE_CURRENT_SOURCE_DIR}")
25add_definitions(-DDASHVIEWSTEST_FOLDER="${CMAKE_CURRENT_SOURCE_DIR}")
26add_definitions(-DBUILT_PLUGINS_DIR="${CMAKE_BINARY_DIR}/plugins")
26add_definitions(-DBUILT_PLUGINS_DIR="${CMAKE_BINARY_DIR}/plugins")
27
27
28+macro(add_lvwph_test FILENAME TESTNAME)
29+ add_executable(${TESTNAME}TestExec
30+ ${FILENAME}test.cpp
28−add_executable(listviewwithpageheadertestExec listviewwithpageheadertest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/listviewwithpageheader.cpp)
31+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/listviewwithpageheader.cpp)
29−qt5_use_modules(listviewwithpageheadertestExec Test Core Qml)
32+ qt5_use_modules(${TESTNAME}TestExec Test Core Qml)
30−target_link_libraries(listviewwithpageheadertestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
33+ target_link_libraries(${TESTNAME}TestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
31−add_custom_target(testlistviewwithpageheader ${CMAKE_CURRENT_BINARY_DIR}/listviewwithpageheadertestExec -o ${CMAKE_BINARY_DIR}/testlistviewwithpageheader.xml,xunitxml -o -,txt)
32
34
33−add_executable(listviewwithpageheadertestsectionExec listviewwithpageheadertestsection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/listviewwithpageheader.cpp)
35+ add_binary_qml_test(${TESTNAME} "" "DashViews-qml")
34−qt5_use_modules(listviewwithpageheadertestsectionExec Test Core Qml)
36+endmacro()
35−target_link_libraries(listviewwithpageheadertestsectionExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
36−add_custom_target(testlistviewwithpageheadersection ${CMAKE_CURRENT_BINARY_DIR}/listviewwithpageheadertestsectionExec -o ${CMAKE_BINARY_DIR}/testlistviewwithpageheadersection.xml,xunitxml -o -,txt)
37
37
38−add_executable(listviewwithpageheadertestsectionexternalmodelExec listviewwithpageheadertestsectionexternalmodel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/listviewwithpageheader.cpp)
38+add_lvwph_test(listviewwithpageheader ListViewWithPageHeader)
39−qt5_use_modules(listviewwithpageheadertestsectionexternalmodelExec Test Core Qml)
39+add_lvwph_test(listviewwithpageheadersection ListViewWithPageHeaderSection)
40−target_link_libraries(listviewwithpageheadertestsectionexternalmodelExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
40+add_lvwph_test(listviewwithpageheadersectionexternalmodel ListViewWithPageHeaderSectionExternalModel)
41−add_custom_target(testlistviewwithpageheadersectionexternalmodel ${CMAKE_CURRENT_BINARY_DIR}/listviewwithpageheadertestsectionexternalmodelExec -o ${CMAKE_BINARY_DIR}/testlistviewwithpageheadersectionexternalmodel.xml,xunitxml -o -,txt)
42
41
43macro(add_dashview_try_test FILENAME TESTNAME)
42macro(add_dashview_try_test FILENAME TESTNAME)
44− add_executable(${FILENAME}testExec
43+ add_executable(${TESTNAME}TestExec
45 ${FILENAME}test.cpp
44 ${FILENAME}test.cpp
46 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/${FILENAME}.cpp
45 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/${FILENAME}.cpp
47 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/abstractdashview.cpp
46 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/abstractdashview.cpp
48 )
47 )
49− qt5_use_modules(${FILENAME}testExec Test Core Qml)
48+ qt5_use_modules(${TESTNAME}TestExec Test Core Qml)
50− target_link_libraries(${FILENAME}testExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
49+ target_link_libraries(${TESTNAME}TestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
51− add_custom_target(test${TESTNAME}
52− ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME}testExec
53− -o ${CMAKE_BINARY_DIR}/test${TESTNAME}.xml,xunitxml
54− -o -,txt
55− DEPENDS ${FILENAME}testExec DashViews-qml
56− )
57
50
51+ add_binary_qml_test(${TESTNAME} "" "DashViews-qml")
52+
58− add_executable(${FILENAME}tryExec
53+ add_executable(${TESTNAME}tryExec
59 ${FILENAME}try.cpp
54 ${FILENAME}try.cpp
60 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/${FILENAME}.cpp
55 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/${FILENAME}.cpp
61 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/abstractdashview.cpp
56 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/abstractdashview.cpp
62 )
57 )
63− qt5_use_modules(${FILENAME}tryExec Test Core Qml)
58+ qt5_use_modules(${TESTNAME}tryExec Test Core Qml)
64− target_link_libraries(${FILENAME}tryExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
59+ target_link_libraries(${TESTNAME}tryExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
65 add_custom_target(try${TESTNAME}
60 add_custom_target(try${TESTNAME}
66− ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME}tryExec
61+ ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME}tryExec
67− DEPENDS ${FILENAME}tryExec DashViews-qml
62+ DEPENDS ${TESTNAME}tryExec DashViews-qml
68 )
63 )
69endmacro()
64endmacro()
70
65
@@ -72,13 +67,6 @@ add_dashview_try_test(verticaljournal VerticalJournal)
72add_dashview_try_test(horizontaljournal HorizontalJournal)
67add_dashview_try_test(horizontaljournal HorizontalJournal)
73add_dashview_try_test(organicgrid OrganicGrid)
68add_dashview_try_test(organicgrid OrganicGrid)
74
69
70+set(qmltest_DEFAULT_TARGETS qmluitests)
75set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
71set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
76−add_qml_test(. listviewwithpageheaderqml IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
72+add_qml_test(. ListViewWithPageHeaderQML IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
77−
78−add_dependencies(qmluitests testlistviewwithpageheader)
79−add_dependencies(qmluitests testlistviewwithpageheadersection)
80−add_dependencies(qmluitests testlistviewwithpageheadersectionexternalmodel)
81−add_dependencies(qmluitests testlistviewwithpageheaderqml)
82−add_dependencies(qmluitests testVerticalJournal)
83−add_dependencies(qmluitests testHorizontalJournal)
84−add_dependencies(qmluitests testOrganicGrid)
tests/plugins/DashViews/listviewwithpageheadertestsectionexternalmodel.cpp → tests/plugins/DashViews/listviewwithpageheadersectionexternalmodeltest.cpprenamed+1−1
@@ -248,4 +248,4 @@ private:
248
248
249QTEST_MAIN(ListViewWithPageHeaderTestSection)
249QTEST_MAIN(ListViewWithPageHeaderTestSection)
250
250
251−#include "listviewwithpageheadertestsectionexternalmodel.moc"
251+#include "listviewwithpageheadersectionexternalmodeltest.moc"
tests/plugins/DashViews/listviewwithpageheadertestsection.cpp → tests/plugins/DashViews/listviewwithpageheadersectiontest.cpprenamed+1−1
@@ -2166,4 +2166,4 @@ private:
2166
2166
2167QTEST_MAIN(ListViewWithPageHeaderTestSection)
2167QTEST_MAIN(ListViewWithPageHeaderTestSection)
2168
2168
2169−#include "listviewwithpageheadertestsection.moc"
2169+#include "listviewwithpageheadersectiontest.moc"
tests/plugins/DashViews/tst_listviewwithpageheaderqml.qml → tests/plugins/DashViews/tst_ListViewWithPageHeaderQML.qmlrenamed

The content is the same.

tests/plugins/Ubuntu/Gestures/CMakeLists.txt+1
@@ -20,6 +20,7 @@ macro(add_gesture_ui_test CLASSNAME)
20 target_link_libraries(${CLASSNAME}TestExec UbuntuGestureQml)
20 target_link_libraries(${CLASSNAME}TestExec UbuntuGestureQml)
21
21
22 add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles)
22 add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles)
23+ add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
23endmacro(add_gesture_ui_test)
24endmacro(add_gesture_ui_test)
24
25
25macro(add_gesture_test CLASSNAME)
26macro(add_gesture_test CLASSNAME)
tests/plugins/Utils/CMakeLists.txt+2−1
@@ -11,7 +11,8 @@ macro(run_tests)
11 -o ${CMAKE_BINARY_DIR}/${_test}.xml,xunitxml
11 -o ${CMAKE_BINARY_DIR}/${_test}.xml,xunitxml
12 -o -,txt)
12 -o -,txt)
13 add_test(NAME ${_test} COMMAND ${testCommand})
13 add_test(NAME ${_test} COMMAND ${testCommand})
14+ set_tests_properties(${_test} PROPERTIES ENVIRONMENT "LC_ALL=C")
14− add_custom_target(${_test} ${testCommand})
15+ add_custom_target(${_test} env "LC_ALL=C" ${testCommand})
15 add_executable(${_test}Exec ${_test}.cpp modeltest.cpp)
16 add_executable(${_test}Exec ${_test}.cpp modeltest.cpp)
16 qt5_use_modules(${_test}Exec Test Core Qml)
17 qt5_use_modules(${_test}Exec Test Core Qml)
17 target_link_libraries(${_test}Exec
18 target_link_libraries(${_test}Exec
tests/qmltests/CMakeLists.txt+1−1
@@ -16,7 +16,7 @@ add_qml_test(utils/Unity/Test UnityTest)
16
16
17set(qmltest_DEFAULT_TARGETS qmluitests)
17set(qmltest_DEFAULT_TARGETS qmluitests)
18set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
18set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
19−set(qmltest_DEFAULT_PROPERTIES "")
19+set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
20
20
21add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks
21add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks
22 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
22 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
tests/qmltests/Components/CMakeLists.txt+1
@@ -11,6 +11,7 @@ qt5_use_modules(DragHandleTestExec Test Core Qml Gui Quick)
11target_link_libraries(DragHandleTestExec UbuntuGestureQml)
11target_link_libraries(DragHandleTestExec UbuntuGestureQml)
12
12
13add_binary_qml_test(DragHandle ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures "")
13add_binary_qml_test(DragHandle ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures "")
14+add_manual_qml_test(. DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
14
15
15add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
16add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
16add_definitions(-DTEST_DIR="${CMAKE_CURRENT_LIST_DIR}")
17add_definitions(-DTEST_DIR="${CMAKE_CURRENT_LIST_DIR}")
tests/qmltests/Components/tst_FilterGrid.qml+1−1
@@ -50,6 +50,7 @@ Rectangle {
50 CheckBox {
50 CheckBox {
51 id: filterCheckBox
51 id: filterCheckBox
52 checked: true
52 checked: true
53+ onCheckedChanged: filterGrid.setFilter(checked, false /*animate*/)
53 }
54 }
54 }
55 }
55 }
56 }
@@ -99,7 +100,6 @@ Rectangle {
99 maximumNumberOfColumns: 3
100 maximumNumberOfColumns: 3
100 collapsedRowCount:
101 collapsedRowCount:
101 collapsedRowCountSelector.values[collapsedRowCountSelector.selectedIndex]
102 collapsedRowCountSelector.values[collapsedRowCountSelector.selectedIndex]
102− filter: filterCheckBox.checked
103 minimumHorizontalSpacing: units.gu(1)
103 minimumHorizontalSpacing: units.gu(1)
104 delegateWidth: units.gu(6)
104 delegateWidth: units.gu(6)
105 delegateHeight: units.gu(6)
105 delegateHeight: units.gu(6)
tests/qmltests/Dash/Previews/tst_PreviewRatingInput.qml+21−2
@@ -36,6 +36,8 @@ Rectangle {
36 property var triggeredDataReviewOnly: { "rating": invalidInputRating, "review": validInputText, "author": null }
36 property var triggeredDataReviewOnly: { "rating": invalidInputRating, "review": validInputText, "author": null }
37
37
38 property var widgetDataBoth: { "visible": "both", "required": "both" }
38 property var widgetDataBoth: { "visible": "both", "required": "both" }
39+ property var widgetDataBothRequireRating: { "visible": "both", "required": "rating" }
40+ property var widgetDataBothRequireReview: { "visible": "both", "required": "review" }
39 property var widgetDataRating: { "visible": "rating", "required": "rating" }
41 property var widgetDataRating: { "visible": "rating", "required": "rating" }
40 property var widgetDataRatingBroken: { "visible": "rating", "required": "review" }
42 property var widgetDataRatingBroken: { "visible": "rating", "required": "review" }
41 property var widgetDataReview: { "visible": "review", "required": "review" }
43 property var widgetDataReview: { "visible": "review", "required": "review" }
@@ -85,6 +87,14 @@ Rectangle {
85 {tag: "both, rating-only review", widgetData: widgetDataBoth, inputRating: validInputRating, inputText: invalidInputText, emitted: false},
87 {tag: "both, rating-only review", widgetData: widgetDataBoth, inputRating: validInputRating, inputText: invalidInputText, emitted: false},
86 {tag: "both, review-only review", widgetData: widgetDataBoth, inputRating: invalidInputRating, inputText: validInputText, emitted: false},
88 {tag: "both, review-only review", widgetData: widgetDataBoth, inputRating: invalidInputRating, inputText: validInputText, emitted: false},
87 {tag: "both, complete review", widgetData: widgetDataBoth, inputRating: validInputRating, inputText: validInputText, emitted: true},
89 {tag: "both, complete review", widgetData: widgetDataBoth, inputRating: validInputRating, inputText: validInputText, emitted: true},
90+ {tag: "both require rating, null review", widgetData: widgetDataBothRequireRating, inputRating: invalidInputRating, inputText: invalidInputText, emitted: false},
91+ {tag: "both require rating, rating-only review", widgetData: widgetDataBothRequireRating, inputRating: validInputRating, inputText: invalidInputText, emitted: true},
92+ {tag: "both require rating, review-only review", widgetData: widgetDataBothRequireRating, inputRating: invalidInputRating, inputText: validInputText, emitted: false},
93+ {tag: "both require rating, complete review", widgetData: widgetDataBothRequireRating, inputRating: validInputRating, inputText: validInputText, emitted: true},
94+ {tag: "both require review, null review", widgetData: widgetDataBothRequireReview, inputRating: invalidInputRating, inputText: invalidInputText, emitted: false},
95+ {tag: "both require review, rating-only review", widgetData: widgetDataBothRequireReview, inputRating: validInputRating, inputText: invalidInputText, emitted: false},
96+ {tag: "both require review, review-only review", widgetData: widgetDataBothRequireReview, inputRating: invalidInputRating, inputText: validInputText, emitted: true},
97+ {tag: "both require review, complete review", widgetData: widgetDataBothRequireReview, inputRating: validInputRating, inputText: validInputText, emitted: true},
88 {tag: "rating, null review", widgetData: widgetDataRating, inputRating: invalidInputRating, inputText: invalidInputText, emitted: false},
98 {tag: "rating, null review", widgetData: widgetDataRating, inputRating: invalidInputRating, inputText: invalidInputText, emitted: false},
89 {tag: "rating, rating-only review", widgetData: widgetDataRating, inputRating: validInputRating, inputText: invalidInputText, emitted: true},
99 {tag: "rating, rating-only review", widgetData: widgetDataRating, inputRating: validInputRating, inputText: invalidInputText, emitted: true},
90 {tag: "rating, review-only review", widgetData: widgetDataRating, inputRating: invalidInputRating, inputText: validInputText, emitted: false},
100 {tag: "rating, review-only review", widgetData: widgetDataRating, inputRating: invalidInputRating, inputText: validInputText, emitted: false},
@@ -114,7 +124,9 @@ Rectangle {
114 verify(rating.visible === true);
124 verify(rating.visible === true);
115
125
116 rating.value = data.inputRating;
126 rating.value = data.inputRating;
117− if (data.widgetData["required"] !== "rating" || data.inputRating < 0) {
127+ if (data.widgetData["required"] !== "rating" ||
128+ data.widgetData["visible"] !== "rating" ||
129+ data.inputRating < 0) {
118 compare(spy.count, 0);
130 compare(spy.count, 0);
119 } else {
131 } else {
120 compare(spy.count, 1);
132 compare(spy.count, 1);
@@ -129,7 +141,14 @@ Rectangle {
129 reviewTextArea.text = data.inputText;
141 reviewTextArea.text = data.inputText;
130 mouseClick(submitButton, submitButton.width / 2, submitButton.height / 2);
142 mouseClick(submitButton, submitButton.width / 2, submitButton.height / 2);
131 switch (data.widgetData["required"]) {
143 switch (data.widgetData["required"]) {
144+ case "rating": {
145+ if (rating.visible === false || data.inputRating < 0) {
132− case "rating": compare(spy.count, 0); break;
146+ compare(spy.count, 0);
147+ } else {
148+ compare(spy.count, 1);
149+ }
150+ break;
151+ }
133 case "both":
152 case "both":
134 default: {
153 default: {
135 if (data.inputRating < 0 || data.inputText === "") {
154 if (data.inputRating < 0 || data.inputText === "") {
tests/qmltests/Dash/tst_GenericScopeView.qml+3−3
@@ -140,7 +140,7 @@ Item {
140 mouseClick(header, header.width / 2, header.height / 2);
140 mouseClick(header, header.width / 2, header.height / 2);
141 tryCompareFunction(function() { middleHeight = category.height; return category.height > initialHeight; }, true);
141 tryCompareFunction(function() { middleHeight = category.height; return category.height > initialHeight; }, true);
142 tryCompare(category, "filtered", false);
142 tryCompare(category, "filtered", false);
143− verify(category.height > middleHeight);
143+ tryCompareFunction(function() { return category.height > middleHeight; }, true);
144
144
145 mouseClick(header, header.width / 2, header.height / 2);
145 mouseClick(header, header.width / 2, header.height / 2);
146 verify(category.expandable);
146 verify(category.expandable);
@@ -165,7 +165,7 @@ Item {
165
165
166 mouseClick(header2, header2.width / 2, header2.height / 2);
166 mouseClick(header2, header2.width / 2, header2.height / 2);
167 tryCompare(category2, "filtered", false);
167 tryCompare(category2, "filtered", false);
168− tryCompare(category2FilterGrid, "filter", false);
168+ tryCompare(category2FilterGrid, "filtered", false);
169
169
170 categoryListView.positionAtBeginning();
170 categoryListView.positionAtBeginning();
171
171
@@ -182,7 +182,7 @@ Item {
182 mouseClick(header0, header0.width / 2, header0.height / 2);
182 mouseClick(header0, header0.width / 2, header0.height / 2);
183 tryCompare(category0, "filtered", false);
183 tryCompare(category0, "filtered", false);
184 tryCompare(category2, "filtered", true);
184 tryCompare(category2, "filtered", true);
185− tryCompare(category2FilterGrid, "filter", true);
185+ tryCompare(category2FilterGrid, "filtered", true);
186 mouseClick(header0, header0.width / 2, header0.height / 2);
186 mouseClick(header0, header0.width / 2, header0.height / 2);
187 tryCompare(category0, "filtered", true);
187 tryCompare(category0, "filtered", true);
188 tryCompare(category2, "filtered", true);
188 tryCompare(category2, "filtered", true);
tests/qmltests/Greeter/tst_Clock.qml+3−3
@@ -71,10 +71,10 @@ Rectangle {
71 var dateString = Qt.formatDate(dateObj, Qt.DefaultLocaleLongDate)
71 var dateString = Qt.formatDate(dateObj, Qt.DefaultLocaleLongDate)
72 var timeString = Qt.formatTime(dateObj)
72 var timeString = Qt.formatTime(dateObj)
73
73
74− clock.enabled = false
74+ clock.visible = false
75 var timeModel = findInvisibleChild(clock, "timeModel")
75 var timeModel = findInvisibleChild(clock, "timeModel")
76
76
77− compare(timeModel.menuObjectPath, "", "Clock shouldn't be connected to Indicators when not active.")
77+ compare(timeModel.menuObjectPath, "", "Clock shouldn't be connected to Indicators when not visible.")
78
78
79 clock.currentDate = dateObj
79 clock.currentDate = dateObj
80
80
@@ -83,7 +83,7 @@ Rectangle {
83 var timeLabel = findChild(clock, "timeLabel")
83 var timeLabel = findChild(clock, "timeLabel")
84 compare(timeLabel.text, timeString, "Not the expected time")
84 compare(timeLabel.text, timeString, "Not the expected time")
85
85
86− clock.enabled = true
86+ clock.visible = true
87
87
88 verify(timeModel.menuObjectPath != "", "Should be connected to Indicators.")
88 verify(timeModel.menuObjectPath != "", "Should be connected to Indicators.")
89 }
89 }
tests/qmltests/Greeter/tst_Phone.qml+10−50
@@ -61,6 +61,12 @@ Item {
61 signalName: "unlocked"
61 signalName: "unlocked"
62 }
62 }
63
63
64+ SignalSpy {
65+ id: teaseSpy
66+ target: greeter
67+ signalName: "tease"
68+ }
69+
64 UT.UnityTestCase {
70 UT.UnityTestCase {
65 name: "Greeter"
71 name: "Greeter"
66 when: windowShown
72 when: windowShown
@@ -82,56 +88,10 @@ Item {
82 }
88 }
83
89
84 function test_teasingArea(data) {
90 function test_teasingArea(data) {
85− tryCompare(greeter, "leftTeaserPressed", false)
91+ teaseSpy.clear()
86− tryCompare(greeter, "rightTeaserPressed", false)
87− mousePress(greeter, data.posX, greeter.height - units.gu(1))
88− tryCompare(greeter, "leftTeaserPressed", data.leftPressed)
89− tryCompare(greeter, "rightTeaserPressed", data.rightPressed)
90− mouseRelease(greeter, data.posX, greeter.height - units.gu(1))
91− tryCompare(greeter, "leftTeaserPressed", false)
92− tryCompare(greeter, "rightTeaserPressed", false)
93− }
94−
95− function test_teaseLockedUnlocked_data() {
96− return [
97− {tag: "unlocked", locked: false, moved: false},
98− {tag: "locked", locked: true, moved: false},
99− {tag: "moved", locked: false, moved: true}
100− ];
101− }
102−
103− function test_teaseLockedUnlocked(data) {
104− tryCompare(greeter, "rightTeaserPressed", false);
105− tryCompare(greeter, "x", 0);
106− greeter.locked = data.locked;
107−
108− // simulate greeter being in the middle of a swipe
109− if (data.moved) {
110− greeter.x = -units.gu(4);
111− tryCompare(greeter, "x", -units.gu(4));
112− }
113−
114− mouseClick(greeter, greeter.width - units.gu(5), greeter.height - units.gu(1));
92+ mouseClick(greeter, data.posX, greeter.height - units.gu(1))
115− greeter.minX = 0; // This is needed because the transition actually makes x jump once before animating
116−
117− if (!data.moved) {
118− // Check if it has been moved over by 2 GUs. Give it a 2 pixel grace area
119− // because animation duration and teaseTimer are the same duration and
120− // might cause slight offsets
121− tryCompareFunction(function() { return greeter.minX <= -units.gu(2) + 2}, true);
122− } else {
123− // waiting 100ms to make sure nothing moves
124− wait(100);
93+ teaseSpy.wait()
125− compare(greeter.minX, 0, "Greeter teasing not disabled even though it's locked or moving.");
94+ tryCompare(teaseSpy, "count", 1)
126− }
127−
128− // Restore position in case we moved it for the test
129− if (data.moved) {
130− greeter.x = 0;
131− }
132−
133− // Wait until we're back to 0
134− tryCompareFunction(function() { return greeter.x;}, 0);
135 }
95 }
136
96
137 function test_statsWelcomeScreen() {
97 function test_statsWelcomeScreen() {
tests/qmltests/Greeter/tst_Tablet.qml+36−8
@@ -54,6 +54,12 @@ Item {
54 signalName: "selected"
54 signalName: "selected"
55 }
55 }
56
56
57+ SignalSpy {
58+ id: teaseSpy
59+ target: greeter
60+ signalName: "tease"
61+ }
62+
57 UT.UnityTestCase {
63 UT.UnityTestCase {
58 name: "Greeter"
64 name: "Greeter"
59 when: windowShown
65 when: windowShown
@@ -325,14 +331,36 @@ Item {
325 }
331 }
326
332
327 function test_teasingArea(data) {
333 function test_teasingArea(data) {
328− tryCompare(greeter, "leftTeaserPressed", false)
334+ teaseSpy.clear()
329− tryCompare(greeter, "rightTeaserPressed", false)
330− mousePress(greeter, data.posX, greeter.height - units.gu(1))
335+ mouseClick(greeter, data.posX, greeter.height - units.gu(1))
331− tryCompare(greeter, "leftTeaserPressed", data.leftPressed)
336+ teaseSpy.wait()
332− tryCompare(greeter, "rightTeaserPressed", data.rightPressed)
337+ tryCompare(teaseSpy, "count", 1)
338+ }
339+
340+ function test_teaseLockedUnlocked_data() {
341+ return [
342+ {tag: "unlocked", locked: false, narrow: false},
343+ {tag: "locked", locked: true, narrow: false},
344+ ];
345+ }
346+
347+ function test_teaseLockedUnlocked(data) {
348+ teaseSpy.clear()
349+ greeter.locked = data.locked;
350+
333− mouseRelease(greeter, data.posX, greeter.height - units.gu(1))
351+ mouseClick(greeter, greeter.width - units.gu(5), greeter.height - units.gu(1));
334− tryCompare(greeter, "leftTeaserPressed", false)
352+
335− tryCompare(greeter, "rightTeaserPressed", false)
353+ if (!data.locked || data.narrow) {
354+ teaseSpy.wait()
355+ tryCompare(teaseSpy, "count", 1);
356+ } else {
357+ // waiting 100ms to make sure nothing happens
358+ wait(100);
359+ compare(teaseSpy.count, 0, "Greeter teasing not disabled even though it's locked.");
360+ }
361+
362+ // Reset value
363+ greeter.locked = false;
336 }
364 }
337
365
338 function test_dbus_set_active_entry() {
366 function test_dbus_set_active_entry() {
tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml+29−4
@@ -19,6 +19,7 @@ import QtTest 1.0
19import Unity.Test 0.1 as UT
19import Unity.Test 0.1 as UT
20import QMenuModel 0.1
20import QMenuModel 0.1
21import Unity.Indicators 0.1 as Indicators
21import Unity.Indicators 0.1 as Indicators
22+import Utils 0.1 as Utils
22
23
23Item {
24Item {
24 id: testView
25 id: testView
@@ -42,6 +43,10 @@ Item {
42 }
43 }
43 }
44 }
44
45
46+ Utils.GDateTimeFormatter {
47+ id: timeFormatter
48+ }
49+
45 UT.UnityTestCase {
50 UT.UnityTestCase {
46 name: "MenuItemFactory"
51 name: "MenuItemFactory"
47 when: windowShown
52 when: windowShown
@@ -358,8 +363,10 @@ Item {
358
363
359 function test_create_alarmMenu_data() {
364 function test_create_alarmMenu_data() {
360 return [
365 return [
361− {label: "testLabel1", enabled: true, icon: "file:///testIcon1" },
366+ {label: "testLabel1", enabled: true, icon: "file:///testIcon1", color: Qt.rgba(0, 0, 0, 0),
367+ time: new Date(2014, 04, 14).getTime()*1000, timeFormat: "%a %d %b %l:%M %p"},
362− {label: "testLabel2", enabled: false, icon: "file:///testIcon2" },
368+ {label: "testLabel2", enabled: false, icon: "file:///testIcon2", color: Qt.rgba(1, 0, 0, 0),
369+ time: new Date(2015, 12, 31).getTime()*1000, timeFormat: "%A" },
363 ];
370 ];
364 }
371 }
365
372
@@ -368,6 +375,12 @@ Item {
368 menuData.label = data.label;
375 menuData.label = data.label;
369 menuData.sensitive = data.enabled;
376 menuData.sensitive = data.enabled;
370 menuData.icon = data.icon;
377 menuData.icon = data.icon;
378+ menuData.ext = {
379+ 'xCanonicalTime': data.time,
380+ 'xCanonicalTimeFormat': data.timeFormat
381+ };
382+ timeFormatter.format = data.timeFormat;
383+ timeFormatter.time = data.time;
371
384
372 loader.data = menuData;
385 loader.data = menuData;
373 loader.sourceComponent = factory.load(menuData);
386 loader.sourceComponent = factory.load(menuData);
@@ -376,13 +389,16 @@ Item {
376
389
377 compare(loader.item.text, data.label, "Label does not match data");
390 compare(loader.item.text, data.label, "Label does not match data");
378 compare(loader.item.iconSource, data.icon, "Icon does not match data");
391 compare(loader.item.iconSource, data.icon, "Icon does not match data");
392+ compare(loader.item.time, timeFormatter.timeString, "Time does not match data");
379 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
393 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
380 }
394 }
381
395
382 function test_create_appointmentMenu_data() {
396 function test_create_appointmentMenu_data() {
383 return [
397 return [
384− {label: "testLabel1", enabled: true, icon: "file:///testIcon1" },
398+ {label: "testLabel1", enabled: true, icon: "file:///testIcon1", color: Qt.rgba(0, 0, 0, 0),
399+ time: new Date(2014, 04, 14).getTime()*1000, timeFormat: "%a %d %b %l:%M %p"},
385− {label: "testLabel2", enabled: false, icon: "file:///testIcon2" },
400+ {label: "testLabel2", enabled: false, icon: "file:///testIcon2", color: Qt.rgba(1, 0, 0, 0),
401+ time: new Date(2015, 12, 31).getTime()*1000, timeFormat: "%A" },
386 ];
402 ];
387 }
403 }
388
404
@@ -391,6 +407,13 @@ Item {
391 menuData.label = data.label;
407 menuData.label = data.label;
392 menuData.sensitive = data.enabled;
408 menuData.sensitive = data.enabled;
393 menuData.icon = data.icon;
409 menuData.icon = data.icon;
410+ menuData.ext = {
411+ 'xCanonicalColor': data.colour,
412+ 'xCanonicalTime': data.time,
413+ 'xCanonicalTimeFormat': data.timeFormat
414+ };
415+ timeFormatter.format = data.timeFormat;
416+ timeFormatter.time = data.time;
394
417
395 loader.data = menuData;
418 loader.data = menuData;
396 loader.sourceComponent = factory.load(menuData);
419 loader.sourceComponent = factory.load(menuData);
@@ -399,6 +422,8 @@ Item {
399
422
400 compare(loader.item.text, data.label, "Label does not match data");
423 compare(loader.item.text, data.label, "Label does not match data");
401 compare(loader.item.iconSource, data.icon, "Icon does not match data");
424 compare(loader.item.iconSource, data.icon, "Icon does not match data");
425+ compare(loader.item.time, timeFormatter.timeString, "Time does not match data");
426+ compare(loader.item.eventColor, data.color, "Colour does not match data");
402 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
427 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
403 }
428 }
404
429