on xenial help sign in
Laggy backends
commit4c49adae1960c8129f7db80ba5f6150f513ccc78 authorNick Dedekind <nick.dedekind@canonical.com> dateWed 11 Mar 2015, 08:07 (12 years ago) parentd8d3d29
28 files changed+764−80
CMakeLists.txt+2 data/CMakeLists.txt+10 data/indicators-client.desktop.in+9 data/unity-indicator-test-service.conf+7 debian/indicators-client.install+2 debian/unity8-autopilot.install+3−1 include/paths.h.in+1−6 plugins/Utils/CMakeLists.txt+1 plugins/Utils/constants.cpp+27 plugins/Utils/constants.h+43 plugins/Utils/plugin.cpp+11 qml/Panel/Indicators/MenuItemFactory.qml+71−48 qml/Panel/Indicators/client/IndicatorsList.qml+2−2 run.sh+1−1 src/Panel/Indicators/CMakeLists.txt+1 src/Panel/Indicators/client/CMakeLists.txt−2 src/Panel/Indicators/indicator-test-service/CMakeLists.txt+24 src/Panel/Indicators/indicator-test-service/indicator-test-service.c+241 tests/autopilot/unity8/fixture_setup.py+33 tests/autopilot/unity8/indicators/__init__.py+54−10 tests/autopilot/unity8/indicators/tests/__init__.py+10−3 tests/autopilot/unity8/indicators/tests/test_display_indicator.py+3−1 tests/autopilot/unity8/indicators/tests/test_indicators.py+2−2 tests/autopilot/unity8/indicators/tests/test_test_indicator.py+192 tests/mocks/data/unity/indicators/com.canonical.indicator.test+10 tests/plugins/Unity/Indicators/indicatorsmanagertest.cpp−2 tests/plugins/Unity/Indicators/indicatorsmodeltest.cpp−2 tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml+4
CMakeLists.txt+2
@@ -105,6 +105,8 @@ declare_autopilot_test(shell unity8.shell ${CMAKE_SOURCE_DIR}/tests/autopilot/)
105105set(SHELL_APP unity8)
106106set(DASH_APP unity8-dash)
107107set(SCOPE_TOOL unity-scope-tool)
108+set(INDICATORS_CLIENT_APP indicators-client)
109+set(INDICATOR_TEST_SERVICE_APP unity-indicator-test-service)
108110
109111include_directories(
110112 ${CMAKE_SOURCE_DIR}/include
data/CMakeLists.txt+10
@@ -1,13 +1,23 @@
11# generate desktop files
22configure_file(${SHELL_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop @ONLY)
33configure_file(${DASH_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${DASH_APP}.desktop @ONLY)
4+configure_file(${INDICATORS_CLIENT_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${INDICATORS_CLIENT_APP}.desktop @ONLY)
45
56# install desktop files
67install(FILES
78 ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop
89 ${CMAKE_CURRENT_BINARY_DIR}/${DASH_APP}.desktop
10+ ${CMAKE_CURRENT_BINARY_DIR}/${INDICATORS_CLIENT_APP}.desktop
911 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
1012 )
1113
14+# install upstart config files
15+install(FILES
16+ ${SHELL_APP}.conf
17+ ${DASH_APP}.conf
18+ ${INDICATOR_TEST_SERVICE_APP}.conf
19+ DESTINATION ${CMAKE_INSTALL_DATADIR}/upstart/sessions
20+ )
21+
1222install(FILES com.canonical.Unity8.gschema.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/glib-2.0/schemas)
1323install(FILES unity8-dash.url-dispatcher DESTINATION ${CMAKE_INSTALL_DATADIR}/url-dispatcher/urls)
data/indicators-client.desktop.innew file+9
@@ -0,0 +1,9 @@
1+[Desktop Entry]
2+Type=Application
3+Name=Indicators Client
4+Comment=An interface for using the Scopes system
5+Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/indicators-client
6+Terminal=false
7+Icon=
8+NoDisplay=false
9+X-Ubuntu-Touch=true
data/unity-indicator-test-service.confnew file+7
@@ -0,0 +1,7 @@
1+description "Unity Shell v8 Test Indicator"
2+author "Nick Dedekind <nick.dedekind@canonical.com>"
3+
4+respawn
5+respawn limit 2 10
6+
7+exec ${BINARY:-unity-indicator-test-service} $ARGS
debian/indicators-client.install+2
@@ -1 +1,3 @@
1+data/indicators-client.conf usr/share/upstart/sessions/
2+usr/share/applications/indicators-client.desktop
13/usr/bin/indicators-client
debian/unity8-autopilot.install+3−1
@@ -1,2 +1,4 @@
1+data/unity-indicator-test-service.conf usr/share/upstart/sessions/
2+usr/bin/unity-indicator-test-service
13usr/lib/python*/*/unity*
2−usr/share/unity8/unlock-device
4+usr/share/unity8/unlock-device
include/paths.h.in+1−6
@@ -92,12 +92,7 @@ inline QString mockPluginsDir() {
9292
9393inline QStringList shellDataDirs() {
9494 QStringList dirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
95− if (!isRunningInstalled()) {
96− if (getenv("UNITY_TEST_ENV")==nullptr) {
97− dirs.prepend("@CMAKE_BINARY_DIR@/share");
98− }
99− }
100− else {
95+ if (isRunningInstalled()) {
10196 // append so by default we use xdg files.
10297 dirs.append(qmlDirectory());
10398 }
plugins/Utils/CMakeLists.txt+1
@@ -8,6 +8,7 @@ include_directories(
88)
99
1010set(QMLPLUGIN_SRC
11+ constants.cpp
1112 qlimitproxymodelqml.cpp
1213 unitysortfilterproxymodelqml.cpp
1314 relativetimeformatter.cpp
plugins/Utils/constants.cppnew file+27
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright 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 Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+*/
16+
17+#include "constants.h"
18+
19+Constants::Constants(QObject *parent)
20+ : QObject(parent)
21+{
22+ if (qgetenv("QT_LOAD_TESTABILITY").isEmpty()) {
23+ m_indicatorValueTimeout = 30000;
24+ } else {
25+ m_indicatorValueTimeout = 5000;
26+ }
27+}
plugins/Utils/constants.hnew file+43
@@ -0,0 +1,43 @@
1+/*
2+ * Copyright 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 Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+*/
16+
17+#ifndef CONSTANTS_H
18+#define CONSTANTS_H
19+
20+#include <QObject>
21+
22+/**
23+ * @brief The Constants class
24+ *
25+ * This singleton class exposes contants to Qml
26+ *
27+ */
28+
29+class Constants: public QObject
30+{
31+ Q_OBJECT
32+ Q_PROPERTY(int indicatorValueTimeout READ indicatorValueTimeout CONSTANT)
33+
34+public:
35+ Constants(QObject *parent = 0);
36+
37+ int indicatorValueTimeout() const { return m_indicatorValueTimeout; }
38+
39+private:
40+ int m_indicatorValueTimeout;
41+};
42+
43+#endif
plugins/Utils/plugin.cpp+11
@@ -34,6 +34,7 @@
3434#include "windowkeysfilter.h"
3535#include "easingcurve.h"
3636#include "windowstatestorage.h"
37+#include "constants.h"
3738
3839static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
3940{
@@ -42,6 +43,14 @@ static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEn
4243 return new WindowStateStorage();
4344}
4445
46+static QObject *createConstants(QQmlEngine *engine, QJSEngine *scriptEngine)
47+{
48+ Q_UNUSED(engine)
49+ Q_UNUSED(scriptEngine)
50+ return new Constants();
51+}
52+
53+
4554void UtilsPlugin::registerTypes(const char *uri)
4655{
4756 Q_ASSERT(uri == QLatin1String("Utils"));
@@ -55,6 +64,8 @@ void UtilsPlugin::registerTypes(const char *uri)
5564 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
5665 qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
5766 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
67+
68+ qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "Constants", createConstants);
5869}
5970
6071void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
qml/Panel/Indicators/MenuItemFactory.qml+71−48
@@ -19,6 +19,7 @@
1919
2020import QtQuick 2.0
2121import Ubuntu.Settings.Menus 0.1 as Menus
22+import Ubuntu.Settings.Components 0.1
2223import QMenuModel 0.1
2324import Utils 0.1 as Utils
2425import Ubuntu.Components.ListItems 0.1 as ListItems
@@ -84,6 +85,7 @@ Item {
8485 id: sliderMenu;
8586
8687 Menus.SliderMenu {
88+ id: sliderItem
8789 objectName: "sliderMenu"
8890 property QtObject menuData: null
8991 property var menuModel: menuFactory.menuModel
@@ -113,14 +115,8 @@ Item {
113115 onMenuIndexChanged: {
114116 loadAttributes();
115117 }
116− onServerValueChanged: {
117− // value can be changed by slider, so a binding won't work.
118− if (serverValue !== undefined) {
119− value = serverValue;
120− }
121− }
122118 onUpdated: {
123− menuModel.changeState(menuIndex, value);
119+ sliderMenuSync.activate()
124120 }
125121
126122 function loadAttributes() {
@@ -130,6 +126,23 @@ Item {
130126 'min-icon': 'icon',
131127 'max-icon': 'icon'});
132128 }
129+
130+ ServerActivationSync {
131+ id: sliderMenuSync
132+
133+ syncTimeout: Utils.Constants.indicatorValueTimeout
134+ bufferedSyncTimeout: true
135+
136+ userTarget: sliderItem
137+ userProperty: "value"
138+
139+ serverTarget: sliderItem
140+ serverProperty: "serverValue"
141+
142+ onActivated: {
143+ menuModel.changeState(menuIndex, value);
144+ }
145+ }
133146 }
134147 }
135148
@@ -280,6 +293,7 @@ Item {
280293 id: checkableMenu;
281294
282295 Menus.CheckableMenu {
296+ id: checkItem
283297 objectName: "checkableMenu"
284298 property QtObject menuData: null
285299 property int menuIndex: -1
@@ -290,26 +304,23 @@ Item {
290304 checked: serverChecked
291305 highlightWhenPressed: false
292306
293− onServerCheckedChanged: updateFromServer()
294− onTriggered: {
295− menuModel.activate(menuIndex);
296− resyncTimer.restart();
297− }
307+ onTriggered: checkMenuSync.activate()
298308
299− // value can be changed by menu, so a binding won't work.
300− function updateFromServer() {
301− resyncTimer.stop();
302− if (checked != serverChecked) {
303− checked = serverChecked;
304− }
305− }
309+ ServerActivationSync {
310+ id: checkMenuSync
311+ objectName: "sync"
312+
313+ syncTimeout: Utils.Constants.indicatorValueTimeout
306314
307− // Server value is not guaranteed to change to what we expect from an activation.
308− // In this case, we need to re-assert that we are presenting the UI with the set backend value.
309− Timer {
310− id: resyncTimer
311− interval: 1500
312− onTriggered: updateFromServer()
315+ userTarget: checkItem
316+ userProperty: "checked"
317+
318+ serverTarget: checkItem
319+ serverProperty: "serverChecked"
320+
321+ onActivated: {
322+ menuModel.activate(checkItem.menuIndex);
323+ }
313324 }
314325 }
315326 }
@@ -318,9 +329,11 @@ Item {
318329 id: switchMenu;
319330
320331 Menus.SwitchMenu {
332+ id: switchItem
321333 objectName: "switchMenu"
322334 property QtObject menuData: null
323335 property int menuIndex: -1
336+
324337 property bool serverChecked: menuData && menuData.isToggled || false
325338
326339 text: menuData && menuData.label || ""
@@ -329,26 +342,23 @@ Item {
329342 checked: serverChecked
330343 highlightWhenPressed: false
331344
332− onServerCheckedChanged: updateFromServer()
333− onTriggered: {
334− menuModel.activate(menuIndex);
335− resyncTimer.restart();
336− }
345+ onTriggered: switchMenuSync.activate()
337346
338− // value can be changed by menu, so a binding won't work.
339− function updateFromServer() {
340− resyncTimer.stop();
341− if (checked != serverChecked) {
342− checked = serverChecked;
343− }
344− }
347+ ServerActivationSync {
348+ id: switchMenuSync
349+ objectName: "sync"
345350
346− // Server value is not guaranteed to change to what we expect from an activation.
347− // In this case, we need to re-assert that we are presenting the UI with the set backend value.
348− Timer {
349− id: resyncTimer
350− interval: 1500
351− onTriggered: updateFromServer()
351+ syncTimeout: Utils.Constants.indicatorValueTimeout
352+
353+ userTarget: switchItem
354+ userProperty: "checked"
355+
356+ serverTarget: switchItem
357+ serverProperty: "serverChecked"
358+
359+ onActivated: {
360+ menuModel.activate(switchItem.menuIndex);
361+ }
352362 }
353363 }
354364 }
@@ -464,6 +474,7 @@ Item {
464474 id: accessPoint;
465475
466476 Menus.AccessPointMenu {
477+ id: apItem
467478 objectName: "accessPoint"
468479 property QtObject menuData: null
469480 property var menuModel: menuFactory.menuModel
@@ -485,10 +496,6 @@ Item {
485496 signalStrength: strengthAction.valid ? strengthAction.state : 0
486497 highlightWhenPressed: false
487498
488− onServerCheckedChanged: {
489− // value can be changed by menu, so a binding won't work.
490− active = serverChecked;
491− }
492499 onMenuModelChanged: {
493500 loadAttributes();
494501 }
@@ -496,7 +503,7 @@ Item {
496503 loadAttributes();
497504 }
498505 onTriggered: {
499− menuModel.activate(menuIndex);
506+ apMenuSync.activate();
500507 }
501508
502509 function loadAttributes() {
@@ -505,6 +512,22 @@ Item {
505512 'x-canonical-wifi-ap-is-secure': 'bool',
506513 'x-canonical-wifi-ap-strength-action': 'string'});
507514 }
515+
516+ ServerActivationSync {
517+ id: apMenuSync
518+
519+ syncTimeout: Utils.Constants.indicatorValueTimeout
520+
521+ userTarget: apItem
522+ userProperty: "active"
523+
524+ serverTarget: apItem
525+ serverProperty: "serverChecked"
526+
527+ onActivated: {
528+ menuModel.activate(apItem.menuIndex);
529+ }
530+ }
508531 }
509532 }
510533
qml/Panel/Indicators/client/IndicatorsList.qml+2−2
@@ -31,8 +31,8 @@ Page {
3131
3232 Indicators.IndicatorsModel {
3333 id: indicatorsModel
34−
35− Component.onCompleted: load(profile)
34+ profile: page.profile
35+ Component.onCompleted: load()
3636 }
3737
3838 ListView {
run.sh+1−1
@@ -65,6 +65,6 @@ else
6565 echo "Unity8 is already running, please stop it first"
6666 exit 1
6767 fi
68− /sbin/initctl start unity8 BINARY="`readlink -f $QML_PHONE_SHELL_PATH` $QML_PHONE_SHELL_ARGS $@" QML2_IMPORT_PATH=$QML2_IMPORT_PATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH
68+ /sbin/initctl start unity8 BINARY="`readlink -f $QML_PHONE_SHELL_PATH` $QML_PHONE_SHELL_ARGS $@" XDG_DATA_DIRS=$XDG_DATA_DIRS QML2_IMPORT_PATH=$QML2_IMPORT_PATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH
6969 tailf -n 0 ~/.cache/upstart/unity8.log
7070fi
src/Panel/Indicators/CMakeLists.txt+1
@@ -1 +1,2 @@
11add_subdirectory(client)
2+add_subdirectory(indicator-test-service)
src/Panel/Indicators/client/CMakeLists.txt−2
@@ -1,5 +1,4 @@
11project(indicators_client)
2−set(INDICATORS_CLIENT_APP indicators-client)
32
43set(INDICATORS_CLIENT_SRC
54 main.cpp
@@ -15,7 +14,6 @@ add_executable(${INDICATORS_CLIENT_APP}
1514)
1615
1716qt5_use_modules(${INDICATORS_CLIENT_APP} Core Widgets Quick)
18−set_target_properties(${INDICATORS_CLIENT_APP} PROPERTIES COMPILE_FLAGS -fPIC)
1917
2018install(TARGETS ${INDICATORS_CLIENT_APP}
2119 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
src/Panel/Indicators/indicator-test-service/CMakeLists.txtnew file+24
@@ -0,0 +1,24 @@
1+project(indicator-test-service)
2+pkg_search_module(GIO REQUIRED gio-2.0)
3+
4+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
5+
6+include_directories(
7+ ${CMAKE_CURRENT_SOURCE_DIR}
8+ ${GIO_INCLUDE_DIRS}
9+)
10+
11+set(INDICATOR_TEST_SERVICE_SRC
12+ indicator-test-service.c
13+)
14+
15+add_executable(${INDICATOR_TEST_SERVICE_APP}
16+ ${INDICATOR_TEST_SERVICE_SRC}
17+)
18+
19+target_link_libraries(${INDICATOR_TEST_SERVICE_APP} ${GIO_LDFLAGS})
20+
21+set_target_properties(${INDICATOR_TEST_SERVICE_APP} PROPERTIES COMPILE_FLAGS -fPIC)
22+
23+install(TARGETS ${INDICATOR_TEST_SERVICE_APP}
24+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
src/Panel/Indicators/indicator-test-service/indicator-test-service.cnew file+241
@@ -0,0 +1,241 @@
1+#include <gio/gio.h>
2+#include <stdio.h>
3+#include <stdlib.h>
4+
5+G_DEFINE_QUARK (SLIDER_VALUE, slider_value)
6+
7+typedef struct
8+{
9+ GSimpleActionGroup *actions;
10+ GMenu *menu;
11+
12+ guint actions_export_id;
13+ guint menu_export_id;
14+ int action_delay;
15+} IndicatorTestService;
16+
17+static void
18+bus_acquired (GDBusConnection *connection,
19+ const gchar *name,
20+ gpointer user_data)
21+{
22+ (void) name;
23+ IndicatorTestService *indicator = user_data;
24+ GError *error = NULL;
25+
26+ indicator->actions_export_id = g_dbus_connection_export_action_group (connection,
27+ "/com/canonical/indicator/test",
28+ G_ACTION_GROUP (indicator->actions),
29+ &error);
30+ if (indicator->actions_export_id == 0)
31+ {
32+ g_warning ("cannot export action group: %s", error->message);
33+ g_error_free (error);
34+ return;
35+ }
36+
37+ indicator->menu_export_id = g_dbus_connection_export_menu_model (connection,
38+ "/com/canonical/indicator/test/desktop",
39+ G_MENU_MODEL (indicator->menu),
40+ &error);
41+ if (indicator->menu_export_id == 0)
42+ {
43+ g_warning ("cannot export menu: %s", error->message);
44+ g_error_free (error);
45+ return;
46+ }
47+}
48+
49+static void
50+name_lost (GDBusConnection *connection,
51+ const gchar *name,
52+ gpointer user_data)
53+{
54+ (void) name;
55+ IndicatorTestService *indicator = user_data;
56+
57+ if (indicator->actions_export_id)
58+ g_dbus_connection_unexport_action_group (connection, indicator->actions_export_id);
59+
60+ if (indicator->menu_export_id)
61+ g_dbus_connection_unexport_menu_model (connection, indicator->menu_export_id);
62+}
63+
64+static void
65+activate_show (GSimpleAction *action,
66+ GVariant *parameter,
67+ gpointer user_data)
68+{
69+ (void) action;
70+ (void) parameter;
71+ (void) user_data;
72+ g_message ("showing");
73+}
74+
75+static gboolean
76+actual_switch (gpointer user_data)
77+{
78+ GSimpleAction *action = user_data;
79+
80+ GVariant* v = g_action_get_state(G_ACTION(action));
81+ gboolean state = g_variant_get_boolean (v);
82+ GVariant *new_state = g_variant_new_boolean (state == TRUE ? FALSE : TRUE);
83+
84+ g_simple_action_set_state(G_SIMPLE_ACTION(action), new_state);
85+
86+ g_variant_unref (v);
87+ g_message ("switching");
88+ return FALSE;
89+}
90+
91+static void
92+activate_switch (GSimpleAction *action,
93+ GVariant *parameter,
94+ gpointer user_data)
95+{
96+ (void) action;
97+ (void) parameter;
98+
99+ IndicatorTestService *indicator = user_data;
100+
101+ g_timeout_add(indicator->action_delay, actual_switch, action);
102+ g_message ("switch delay");
103+}
104+
105+static gboolean
106+actual_slide (gpointer user_data)
107+{
108+ GSimpleAction *action = user_data;
109+
110+ GVariant* new_state = g_object_get_qdata (G_OBJECT (action), slider_value_quark ());
111+
112+ g_simple_action_set_state(G_SIMPLE_ACTION(action), new_state);
113+
114+ g_message ("switching");
115+ return FALSE;
116+}
117+
118+void change_slider (GSimpleAction *action,
119+ GVariant *value,
120+ gpointer user_data)
121+{
122+ IndicatorTestService *indicator = user_data;
123+
124+ g_object_set_qdata (G_OBJECT (action), slider_value_quark (), g_variant_ref(value));
125+
126+ g_timeout_add(indicator->action_delay, actual_slide, action);
127+ g_message ("slide delay %.03f", g_variant_get_double(value));
128+}
129+
130+int
131+main (int argc, char **argv)
132+{
133+ IndicatorTestService indicator = { 0 };
134+ indicator.action_delay = -1;
135+ GMenuItem *item;
136+ GMenu *submenu;
137+ GActionEntry entries[] = {
138+ { "_header", NULL, NULL, "{'title': <'Test'>,"
139+ " 'label': <'Test'>,"
140+ " 'visible': <true>,"
141+ " 'accessible-desc': <'Test indicator'> }", NULL },
142+ { "action.show", activate_show, NULL, NULL, NULL },
143+ { "action.switch", activate_switch, NULL, "true", NULL },
144+ { "action.accessPoint", activate_switch, NULL, "false", NULL },
145+ { "action.slider", NULL, NULL, "0.5", change_slider }
146+ };
147+ GMainLoop *loop;
148+
149+ int help = 0;
150+ if (argc > 1)
151+ {
152+ int i;
153+ for (i = 1; i < argc; i++) {
154+ const char *arg = argv[i];
155+
156+ if (arg[0] == '-') {
157+ switch (arg[1])
158+ {
159+ case 't':
160+ {
161+ arg += 2;
162+ if (!arg[0] && i < argc-1) {
163+ i++;
164+ int delay = -1;
165+
166+ if (sscanf(argv[i], "%d", &delay) == 1) {
167+ indicator.action_delay = delay;
168+ } else {
169+ printf("Invalid action delay value: %s\n", argv[i]);
170+ help = 1;
171+ }
172+ } else {
173+ printf("Invalid action delay value: %s\n", argv[i]);
174+ help = 1;
175+ }
176+ break;
177+ }
178+ case 'h':
179+ help = 1;
180+ break;
181+ }
182+ }
183+ }
184+ }
185+
186+ if (help) {
187+ printf("Usage: %s [<options>]\n"
188+ " -t DELAY Action activation delay\n"
189+ " -h Show this help text\n"
190+ , argv[0]);
191+ return 0;
192+ }
193+
194+ indicator.actions = g_simple_action_group_new ();
195+ g_action_map_add_action_entries (G_ACTION_MAP (indicator.actions), entries, G_N_ELEMENTS (entries), &indicator);
196+
197+ submenu = g_menu_new ();
198+ g_menu_append (submenu, "Show", "indicator.action.show");
199+
200+ // Switch
201+ item = g_menu_item_new("Switch", "indicator.action.switch");
202+ g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.switch");
203+ g_menu_append_item(submenu, item);
204+
205+ // Access Point
206+ item = g_menu_item_new("Slider", "indicator.action.slider");
207+ g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.slider");
208+ g_menu_append_item(submenu, item);
209+
210+ // Access Point
211+ item = g_menu_item_new("Access Point", "indicator.action.accessPoint");
212+ g_menu_item_set_attribute (item, "x-canonical-type", "s", "unity.widgets.systemsettings.tablet.accesspoint");
213+ g_menu_append_item(submenu, item);
214+
215+
216+ item = g_menu_item_new (NULL, "indicator._header");
217+ g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.root");
218+ g_menu_item_set_submenu (item, G_MENU_MODEL (submenu));
219+ indicator.menu = g_menu_new ();
220+ g_menu_append_item (indicator.menu, item);
221+
222+ g_bus_own_name (G_BUS_TYPE_SESSION,
223+ "com.canonical.indicator.test",
224+ G_BUS_NAME_OWNER_FLAGS_NONE,
225+ bus_acquired,
226+ NULL,
227+ name_lost,
228+ &indicator,
229+ NULL);
230+
231+ loop = g_main_loop_new (NULL, FALSE);
232+ g_main_loop_run (loop);
233+
234+ g_object_unref (submenu);
235+ g_object_unref (item);
236+ g_object_unref (indicator.actions);
237+ g_object_unref (indicator.menu);
238+ g_object_unref (loop);
239+
240+ return 1;
241+}
tests/autopilot/unity8/fixture_setup.py+33
@@ -96,3 +96,36 @@ class DisplayRotationLock(fixtures.Fixture):
9696 'rotation-lock', value_string
9797 ]
9898 subprocess.check_output(command)
99+
100+class LaunchTestIndicatorService(fixtures.Fixture):
101+
102+ """Fixture to launch the indicator test service."""
103+
104+ def __init__(self, binary_path, variables):
105+ """Initialize an instance.
106+
107+ :param str binary_path: The path to the indicator test app binary.
108+ :param variables: The variables to use when launching the app.
109+ :type variables: A dictionary.
110+
111+ """
112+ super(LaunchTestIndicatorService, self).__init__()
113+ self.binary_path = binary_path
114+ self.variables = variables
115+
116+ def setUp(self):
117+ super(LaunchTestIndicatorService, self).setUp()
118+ self.addCleanup(self.stop_service)
119+ self.application_proxy = self.launch_service()
120+
121+ def launch_service(self):
122+ binary_arg = 'BINARY={}'.format(self.binary_path)
123+ testability_arg = 'QT_LOAD_TESTABILITY={}'.format(1)
124+ env_args = [
125+ '{}={}'.format(key, value) for key, value in self.variables.items()
126+ ]
127+ all_args = [binary_arg, testability_arg] + env_args
128+ process_helpers.start_job('unity-indicator-test-service', *all_args)
129+
130+ def stop_service(self):
131+ process_helpers.stop_job('unity-indicator-test-service')
tests/autopilot/unity8/indicators/__init__.py+54−10
@@ -16,9 +16,11 @@
1616# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
1818import ubuntuuitoolkit
19+import subprocess
1920from autopilot import introspection
2021
2122from unity8.shell import emulators
23+from unity8 import fixture_setup
2224
2325
2426class IndicatorPage(emulators.UnityEmulatorBase):
@@ -33,26 +35,25 @@ class IndicatorPage(emulators.UnityEmulatorBase):
3335 def validate_dbus_object(cls, path, state):
3436 return False
3537
38+class Indicator(object):
3639
37−class DisplayIndicator(object):
38−
39− def __init__(self, main_window):
40+ def __init__(self, main_window, name):
4041 self._main_window = main_window
42+ self._name = name
4143
4244 def is_indicator_icon_visible(self):
4345 panel_item = self._main_window.wait_select_single(
44− objectName='indicator-rotation-lock-panelItem')
46+ objectName=self._name+'-panelItem')
4547 return panel_item.indicatorVisible
4648
4749 def open(self):
48− """Open the display indicator page.
50+ """Open the indicator page.
4951
50− :return: The custom proxy object for the display indicator page.
52+ :return: The custom proxy object for the indicator page.
5153
5254 """
5355 if self.is_indicator_icon_visible():
54− return self._main_window.open_indicator_page(
55− 'indicator-rotation-lock')
56+ return self._main_window.open_indicator_page(self._name)
5657 else:
5758 return self._open_indicator_with_icon_not_visible()
5859
@@ -61,10 +62,10 @@ class DisplayIndicator(object):
6162 self._main_window.open_indicator_page('indicator-datetime')
6263 self._make_indicator_icon_visible()
6364 indicator_rotation_icon = self._main_window.select_single(
64− objectName='indicator-rotation-lock-panelItem')
65+ objectName=self._name+'-panelItem')
6566 self._main_window.pointing_device.click_object(indicator_rotation_icon)
6667 return self._main_window.wait_select_single(
67− objectName='indicator-rotation-lock-page')
68+ objectName=self._name+'-page')
6869
6970 def _make_indicator_icon_visible(self):
7071 indicators_bar_flickable = self._main_window.select_single(
@@ -95,6 +96,13 @@ class DisplayIndicator(object):
9596 self._main_window.close_indicator_page()
9697
9798
99+class DisplayIndicator(Indicator):
100+
101+ def __init__(self, main_window):
102+ super(DisplayIndicator, self).__init__(main_window, 'indicator-rotation-lock')
103+ self._main_window = main_window
104+
105+
98106class DisplayIndicatorPage(IndicatorPage):
99107
100108 """Autopilot helper for the display indicator page."""
@@ -122,3 +130,39 @@ class DisplayIndicatorPage(IndicatorPage):
122130 switcher = self._get_switcher()
123131 switcher.uncheck()
124132 switcher.checked.wait_for(False)
133+
134+
135+class TestIndicator(Indicator):
136+
137+ def __init__(self, main_window):
138+ super(TestIndicator, self).__init__(main_window, 'indicator-test')
139+ self._main_window = main_window
140+
141+
142+class TestIndicatorPage(IndicatorPage):
143+
144+ """Autopilot helper for the test indicator page."""
145+
146+ @classmethod
147+ def validate_dbus_object(cls, path, state):
148+ name = introspection.get_classname_from_path(path)
149+ if name == b'IndicatorPage':
150+ if state['objectName'][1] == 'indicator-test-page':
151+ return True
152+ return False
153+
154+ def get_switcher(self):
155+ return self.select_single(
156+ ubuntuuitoolkit.CheckBox, objectName='switcher')
157+
158+ def get_checkbox(self):
159+ return self.select_single(
160+ ubuntuuitoolkit.CheckBox, objectName='checkbox')
161+
162+ def get_switch_menu(self):
163+ return self.select_single(
164+ 'SwitchMenu', objectName='indicator.action.switch')
165+
166+ def get_checkbox_menu(self):
167+ return self.select_single(
168+ 'CheckableMenu', objectName='indicator.action.check')
tests/autopilot/unity8/indicators/tests/__init__.py+10−3
@@ -24,11 +24,18 @@ from unity8.shell import tests
2424
2525class IndicatorTestCase(tests.UnityTestCase):
2626
27− device_emulation_scenarios = tests._get_device_emulation_scenarios()
27+ device_emulation_scenarios = tests._get_device_emulation_scenarios('Nexus4')
2828
2929 def setUp(self):
30− if platform.model() == 'Desktop':
31− self.skipTest('Test cannot be run on the desktop.')
3230 super(IndicatorTestCase, self).setUp()
3331 self.unity_proxy = self.launch_unity()
3432 process_helpers.unlock_unity(self.unity_proxy)
33+
34+class DeviceIndicatorTestCase(IndicatorTestCase):
35+
36+ def setUp(self):
37+ # if platform.model() == 'Desktop':
38+ # self.skipTest('Test cannot be run on the desktop.')
39+ super(DeviceIndicatorTestCase, self).setUp()
40+ self.unity_proxy = self.launch_unity()
41+ process_helpers.unlock_unity(self.unity_proxy)
tests/autopilot/unity8/indicators/tests/test_display_indicator.py+3−1
@@ -23,7 +23,9 @@ from unity8 import (
2323from unity8.indicators import tests
2424
2525
26−class DisplayIndicatorTestCase(tests.IndicatorTestCase):
26+class DisplayIndicatorTestCase(tests.DeviceIndicatorTestCase):
27+
28+ scenarios = tests.IndicatorTestCase.device_emulation_scenarios
2729
2830 def test_indicator_icon_must_be_visible_after_rotation_locked(self):
2931 # TODO remove the skip when the bug is fixed. --elopio - 2015-01-20
tests/autopilot/unity8/indicators/tests/test_indicators.py+2−2
@@ -25,7 +25,7 @@ from autopilot import platform
2525from unity8.indicators import tests
2626
2727
28−class IndicatorExistsTestCase(tests.IndicatorTestCase):
28+class IndicatorExistsTestCase(tests.DeviceIndicatorTestCase):
2929
3030 indicator_scenarios = [
3131 ('Bluetooth', dict(indicator_name='indicator-bluetooth')),
@@ -53,7 +53,7 @@ class IndicatorExistsTestCase(tests.IndicatorTestCase):
5353 )
5454
5555
56−class IndicatorPageTitleMatchesWidgetTestCase(tests.IndicatorTestCase):
56+class IndicatorPageTitleMatchesWidgetTestCase(tests.DeviceIndicatorTestCase):
5757
5858 indicator_scenarios = [
5959 ('Bluetooth', dict(indicator_name='indicator-bluetooth',
tests/autopilot/unity8/indicators/tests/test_test_indicator.pynew file+192
@@ -0,0 +1,192 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Indicators Autopilot Test Suite
4+# Copyright (C) 2015 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
19+from unity8 import (
20+ fixture_setup,
21+ indicators,
22+ get_binary_path,
23+ process_helpers
24+)
25+from unity8.indicators import tests
26+
27+from autopilot.matchers import Eventually
28+from testtools.matchers import Equals
29+import time
30+
31+class TestIndicatorBaseTestCase(tests.IndicatorTestCase):
32+
33+ scenarios = tests.IndicatorTestCase.device_emulation_scenarios
34+
35+ def setUp(self, action_delay=30000):
36+ """
37+ :parameter action_delay: number of milliseconds for indicator backend
38+ to wait before performing actions
39+ """
40+ super(TestIndicatorBaseTestCase, self).setUp()
41+
42+ self.ensure_service_not_running();
43+
44+ binary_path = get_binary_path('unity-indicator-test-service')
45+ environment = {}
46+ environment['ARGS'] = '-t {0}'\
47+ .format(action_delay)
48+ self.launch_indicator_service(binary_path, environment)
49+
50+ # wait for the indicator to appear in unity
51+ self.indicator = indicators.TestIndicator(self.main_window)
52+ self.assertThat(
53+ self.indicator.is_indicator_icon_visible(),
54+ Eventually(Equals(True), timeout=20)
55+ )
56+ self.indicator_page = self.indicator.open()
57+
58+ def launch_indicator_service(self, binary_path, variables):
59+ launch_service_fixture = fixture_setup.LaunchTestIndicatorService(binary_path, variables)
60+ self.useFixture(launch_service_fixture)
61+
62+ def ensure_service_not_running(self):
63+ if process_helpers.is_job_running('unity-indicator-test-service'):
64+ process_helpers.stop_job('unity-indicator-test-service')
65+
66+
67+class TestLowLatencyIndicator(TestIndicatorBaseTestCase):
68+
69+ def setUp(self):
70+ """Set up for indicator actions to take 50 milliseconds for be actioned"""
71+ super(TestLowLatencyIndicator, self).setUp(50)
72+
73+
74+ def test_switch_reaches_server_value(self):
75+ """Test that switching causes the server to update"""
76+ self.assertThat(
77+ self.indicator_page.get_switch_menu().serverChecked,
78+ Equals(self.indicator_page.get_switcher().checked)
79+ )
80+
81+ self.indicator_page.get_switcher().change_state()
82+
83+ self.assertThat(
84+ self.indicator_page.get_switch_menu().serverChecked,
85+ Eventually(Equals(self.indicator_page.get_switcher().checked), timeout=10)
86+ )
87+
88+class TestMediumLatencyIndicator(TestIndicatorBaseTestCase):
89+
90+ def setUp(self):
91+ """Set up for indicator actions to take 2.5 seconds for be actioned"""
92+ super(TestMediumLatencyIndicator, self).setUp(2500)
93+
94+ def test_buffered_switch(self):
95+ """Test that switching multiple times will buffer activations if
96+ within the activation timeout period
97+
98+ See https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1390136 .
99+ """
100+ self.indicator_page.get_switcher().change_state()
101+ # will buffer change until it receives the change from server
102+ self.indicator_page.get_switcher().change_state()
103+ new_value = self.indicator_page.get_switcher().checked;
104+
105+ # backend will respond to first switch. The buffered activation should have gone to server now.
106+ self.assertThat(
107+ self.indicator_page.get_switch_menu().serverChecked,
108+ Eventually(Equals(not new_value), timeout=15)
109+ )
110+
111+ # front-end should not change while it is buffered
112+ self.assertThat(
113+ self.indicator_page.get_switcher().checked,
114+ Equals(new_value)
115+ )
116+
117+ # server will respond to the second activate
118+ self.assertThat(
119+ self.indicator_page.get_switch_menu().serverChecked,
120+ Eventually(Equals(new_value), timeout=15)
121+ )
122+
123+ # make sure we've got the server value set.
124+ self.assertThat(
125+ self.indicator_page.get_switcher().checked,
126+ Equals(self.indicator_page.get_switch_menu().serverChecked)
127+ )
128+
129+
130+
131+class TestHighLatencyIndicator(TestIndicatorBaseTestCase):
132+
133+ def setUp(self):
134+ """Set up for indicator actions to take 8 seconds for be actioned"""
135+ super(TestHighLatencyIndicator, self).setUp(8000)
136+
137+ def test_switch_reverts_to_server_value(self):
138+ """Test that switching a high latency backend value will revert to original value
139+ if not actioned in time.
140+
141+ See https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1390136 .
142+ """
143+ self.assertThat(
144+ self.indicator_page.get_switch_menu().serverChecked,
145+ Equals(self.indicator_page.get_switcher().checked)
146+ )
147+
148+ original_value = self.indicator_page.get_switcher().checked;
149+ self.indicator_page.get_switcher().change_state()
150+
151+ # switch should revert to original value after 5 seconds (30 seconds in real usage)
152+ self.assertThat(
153+ self.indicator_page.get_switch_menu().serverChecked,
154+ Eventually(Equals(original_value), timeout=15)
155+ )
156+ # value will eventually switch back. See test_switch_always_reaches_server_value
157+
158+ def test_switch_always_reaches_server_value(self):
159+ """Test that switching a high latency backend value will always end up following the
160+ server value, even if had a reversion
161+
162+ See https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1390136 .
163+ """
164+ self.test_switch_reverts_to_server_value()
165+
166+ self.assertThat(
167+ self.indicator_page.get_switch_menu().serverChecked,
168+ Eventually(Equals(self.indicator_page.get_switcher().checked), timeout=15)
169+ )
170+
171+ def test_revert_on_late_response(self):
172+ """If the server does not respond in the given amount of time
173+ then we wont switch again even if the switch is buffered
174+
175+ See https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1390136 .
176+ """
177+ self.indicator_page.get_switcher().change_state()
178+ # will buffer change until it receives the change from server
179+ self.indicator_page.get_switcher().change_state()
180+ new_value = self.indicator_page.get_switcher().checked;
181+
182+ # backend will respond to first switch. The buffered activation will be discarded
183+ self.assertThat(
184+ self.indicator_page.get_switch_menu().serverChecked,
185+ Eventually(Equals(not new_value), timeout=15)
186+ )
187+
188+ # make sure we've got the server value set.
189+ self.assertThat(
190+ self.indicator_page.get_switcher().checked,
191+ Equals(self.indicator_page.get_switch_menu().serverChecked)
192+ )
tests/mocks/data/unity/indicators/com.canonical.indicator.testnew file+10
@@ -0,0 +1,10 @@
1+[Indicator Service]
2+Name=indicator-test
3+ObjectPath=/com/canonical/indicator/test
4+Position=0
5+
6+[phone]
7+ObjectPath=/com/canonical/indicator/test/desktop
8+
9+[desktop]
10+ObjectPath=/com/canonical/indicator/test/desktop
tests/plugins/Unity/Indicators/indicatorsmanagertest.cpp−2
@@ -31,13 +31,11 @@ private Q_SLOTS:
3131
3232 void initTestCase()
3333 {
34− setenv("UNITY_TEST_ENV", "1", 1);
3534 setenv("XDG_DATA_DIRS", (sourceDirectory() + "/tests/data").toLatin1().data(), 1);
3635 }
3736
3837 void cleanupTestCase()
3938 {
40− unsetenv("UNITY_TEST_ENV");
4139 }
4240
4341 /*
tests/plugins/Unity/Indicators/indicatorsmodeltest.cpp−2
@@ -32,13 +32,11 @@ private Q_SLOTS:
3232
3333 void initTestCase()
3434 {
35− setenv("UNITY_TEST_ENV", "1", 1);
3635 setenv("XDG_DATA_DIRS", (sourceDirectory() + "/tests/data").toLatin1().data(), 1);
3736 }
3837
3938 void cleanupTestCase()
4039 {
41− unsetenv("UNITY_TEST_ENV");
4240 }
4341
4442 /*
tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml+4
@@ -819,6 +819,10 @@ Item {
819819 loader.data = menuData;
820820 loader.sourceComponent = factory.load(menuData);
821821
822+ var sync = findInvisibleChild(loader.item, "sync");
823+ verify(sync);
824+ sync.syncTimeout = 500;
825+
822826 compare(loader.item.checked, false, "Loader did not load check state");
823827 mouseClick(loader.item,
824828 loader.item.width / 2, loader.item.height / 2);