[gnome-settings-daemon] Add support for plugin tests



commit 2fbb4040219bf564ac4dc9e2ec383c1522d0445b
Author: Martin Pitt <martinpitt gnome org>
Date:   Fri Jan 4 17:33:08 2013 +0100

    Add support for plugin tests
    
    Add a GSDTestCase Python class (tests/gsdtestcase.py) which provides
    functionality for writing daemon and plugin tests:
    
     * Launch temporary private session and system D-BUSes to avoid disturbing
       the currently running system and being able to use mock D-BUS services.
    
     * Run a minimal gnome-session to ensure plugins can check the session idle
       status, register to the session, etc.
    
     * Configure temporary local XDG directories to avoid disturbing the user's
       real settings and files.
    
     * Run a mock notification daemon to avoid showing notifications produced
       by test cases, using python-dbusmock.
       (http://pypi.python.org/pypi/python-dbusmock)
    
     * Provide API to start/stop a mock logind, using python-dbusmock.
    
     * Provide API to reste the session idle timer, using a small "shiftkey"
       helper program that sends a "left shift key" event through XTest.
    
    If any of the required dependencies (gnome-session, dbusmock, pygobject) is
    not present, it exits with an error message and exit code 0, to avoid
    breaking tests in minimal build environments.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=685951

 .gitignore             |    1 +
 Makefile.am            |    1 +
 configure.ac           |    9 +++
 tests/Makefile.am      |   11 +++
 tests/dummy.session    |    5 ++
 tests/dummyapp.desktop |    5 ++
 tests/gsdtestcase.py   |  188 ++++++++++++++++++++++++++++++++++++++++++++++++
 tests/shiftkey.c       |   56 ++++++++++++++
 8 files changed, 276 insertions(+), 0 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index ed51335..5fa6217 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,3 +56,4 @@ plugins/media-keys/test-media-window
 plugins/mouse/gsd-locate-pointer
 plugins/xrandr/gsd-xrandr-manager-glue.h
 plugins/xsettings/test-gtk-modules
+tests/shiftkey
diff --git a/Makefile.am b/Makefile.am
index 6bb3d58..e7ae030 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5,6 +5,7 @@ SUBDIRS = 			\
 	plugins			\
 	data			\
 	po 			\
+	tests 			\
 	$(NULL)
 
 if ENABLE_MAN
diff --git a/configure.ac b/configure.ac
index f7470ce..722c271 100644
--- a/configure.ac
+++ b/configure.ac
@@ -144,6 +144,14 @@ dnl ---------------------------------------------------------------------------
 PKG_CHECK_MODULES(COMMON, x11 kbproto xi)
 
 dnl ---------------------------------------------------------------------------
+dnl - XTest
+dnl ---------------------------------------------------------------------------
+
+PKG_CHECK_MODULES(XTEST, x11 xtst)
+AC_SUBST(XTEST_CFLAGS)
+AC_SUBST(XTEST_LIBS)
+
+dnl ---------------------------------------------------------------------------
 dnl - background
 dnl ---------------------------------------------------------------------------
 
@@ -517,6 +525,7 @@ data/org.gnome.settings-daemon.peripherals.wacom.gschema.xml.in
 data/org.gnome.settings-daemon.plugins.print-notifications.gschema.xml.in
 po/Makefile.in
 man/Makefile
+tests/Makefile
 ])
 
 dnl ---------------------------------------------------------------------------
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..da78a69
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,11 @@
+noinst_PROGRAMS = shiftkey
+
+shiftkey_SOURCES = shiftkey.c
+shiftkey_CFLAGS = -I$(top_builddir) $(XTEST_CFLAGS)
+shiftkey_LDADD = $(XTEST_LIBS)
+
+EXTRA_DIST =			\
+	gsdtestcase.py		\
+	dummy.session		\
+	dummyapp.desktop	\
+	$(NULL)
diff --git a/tests/dummy.session b/tests/dummy.session
new file mode 100644
index 0000000..96a1aa8
--- /dev/null
+++ b/tests/dummy.session
@@ -0,0 +1,5 @@
+[GNOME Session]
+Name=dummy
+RequiredComponents=dummyapp
+DesktopName=dummy
+
diff --git a/tests/dummyapp.desktop b/tests/dummyapp.desktop
new file mode 100644
index 0000000..3bb53b2
--- /dev/null
+++ b/tests/dummyapp.desktop
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Name=dummyapp
+Type=Application
+Exec=sleep 3600
+
diff --git a/tests/gsdtestcase.py b/tests/gsdtestcase.py
new file mode 100644
index 0000000..c2bedf8
--- /dev/null
+++ b/tests/gsdtestcase.py
@@ -0,0 +1,188 @@
+'''GNOME settings daemon test base class'''
+
+__author__ = 'Martin Pitt <martin pitt ubuntu com>'
+__copyright__ = '(C) 2013 Canonical Ltd.'
+__license__ = 'GPL v2 or later'
+
+import subprocess
+import time
+import os
+import os.path
+import tempfile
+import fcntl
+import shutil
+import sys
+from glob import glob
+
+try:
+    import dbusmock
+except ImportError:
+    sys.stderr.write('You need python-dbusmock (http://pypi.python.org/pypi/python-dbusmock) for this test suite.\n')
+    sys.exit(0)
+
+try:
+    from gi.repository import Gio
+except ImportError:
+    sys.stderr.write('You need pygobject and the Gio GIR for this test suite.\n')
+    sys.exit(0)
+
+if subprocess.call(['which', 'gnome-session'], stdout=subprocess.PIPE) != 0:
+    sys.stderr.write('You need gnome-session for this test suite.\n')
+    sys.exit(0)
+
+
+top_builddir = os.environ.get('TOP_BUILDDIR',
+                              os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+
+def set_nonblock(fd):
+    '''Set a file object to non-blocking'''
+
+    flags = fcntl.fcntl(fd, fcntl.F_GETFL)
+    fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
+
+
+class GSDTestCase(dbusmock.DBusTestCase):
+    '''Base class for settings daemon tests
+
+    This redirects the XDG directories to temporary directories, and runs local
+    session and system D-BUSes with a minimal GNOME session and a mock
+    notification daemon. It also provides common functionality for plugin
+    tests.
+    '''
+    @classmethod
+    def setUpClass(klass):
+        os.environ['GIO_USE_VFS'] = 'local'
+        # we do some string checks, disable translations
+        os.environ['LC_MESSAGES'] = 'C'
+        klass.workdir = tempfile.mkdtemp(prefix='gsd-power-test')
+
+        # tell dconf and friends to use our config/runtime directories
+        os.environ['XDG_CONFIG_HOME'] = os.path.join(klass.workdir, 'config')
+        os.environ['XDG_DATA_HOME'] = os.path.join(klass.workdir, 'data')
+        os.environ['XDG_RUNTIME_DIR'] = os.path.join(klass.workdir, 'runtime')
+
+        # work around https://bugzilla.gnome.org/show_bug.cgi?id=689136
+        os.makedirs(os.path.join(os.environ['XDG_CONFIG_HOME'], 'dconf'))
+
+        klass.start_system_bus()
+        klass.start_session_bus()
+        klass.system_bus_con = klass.get_dbus(True)
+        klass.session_bus_con = klass.get_dbus(False)
+
+        # we never want to cause notifications on the actual GUI
+        klass.p_notify = klass.spawn_server_template(
+            'notification_daemon', {}, stdout=subprocess.PIPE)[0]
+        set_nonblock(klass.p_notify.stdout)
+
+        klass.start_session()
+
+        klass.settings_session = Gio.Settings('org.gnome.desktop.session')
+
+    @classmethod
+    def tearDownClass(klass):
+        klass.stop_session()
+        dbusmock.DBusTestCase.tearDownClass()
+        shutil.rmtree(klass.workdir)
+
+    def run(self, result=None):
+        '''Show log files on failed tests
+
+        If the environment variable $SHELL_ON_FAIL is set, this runs bash in
+        the work directory; exit the shell to continue the tests. Otherwise it
+        shows all log files.
+        '''
+        if result:
+            orig_err_fail = result.errors + result.failures
+        super(GSDTestCase, self).run(result)
+        if result and result.errors + result.failures > orig_err_fail:
+            if 'SHELL_ON_FAIL' in os.environ:
+                subprocess.call(['bash', '-i'], cwd=self.workdir)
+            else:
+                for log_file in glob(os.path.join(self.workdir, '*.log')):
+                    with open(log_file) as f:
+                        print('\n----- %s -----\n%s\n------\n'
+                              % (log_file, f.read()))
+
+    @classmethod
+    def start_session(klass):
+        '''Start minimal GNOME session'''
+
+        # create dummy session type and component
+        d = os.path.join(klass.workdir, 'config', 'gnome-session', 'sessions')
+        if not os.path.isdir(d):
+            os.makedirs(d)
+        shutil.copy(os.path.join(os.path.dirname(__file__), 'dummy.session'), d)
+
+        d = os.path.join(klass.workdir, 'data', 'applications')
+        if not os.path.isdir(d):
+            os.makedirs(d)
+        shutil.copy(os.path.join(os.path.dirname(__file__), 'dummyapp.desktop'), d)
+
+        klass.session_log = open(os.path.join(klass.workdir, 'gnome-session.log'), 'wb')
+        klass.session = subprocess.Popen(['gnome-session', '-f',
+                                          '-a', os.path.join(klass.workdir, 'autostart'),
+                                          '--session=dummy', '--debug'],
+                                         stdout=klass.session_log,
+                                         stderr=subprocess.STDOUT)
+
+        # wait until the daemon is on the bus
+        try:
+            klass.wait_for_bus_object('org.gnome.SessionManager',
+                                      '/org/gnome/SessionManager')
+        except:
+            # on failure, print log
+            with open(klass.session_log.name) as f:
+                print('----- session log -----\n%s\n------' % f.read())
+            raise
+
+        klass.obj_session_mgr = klass.session_bus_con.get_object(
+            'org.gnome.SessionManager', '/org/gnome/SessionManager')
+
+        # give g-session some time to lazily initialize idle timers, etc.
+        time.sleep(3)
+
+    @classmethod
+    def stop_session(klass):
+        '''Stop GNOME session'''
+
+        assert klass.session
+        klass.session.terminate()
+        klass.session.wait()
+
+        klass.session_log.flush()
+        klass.session_log.close()
+
+    def start_logind(self):
+        '''start mock logind'''
+
+        self.logind = self.spawn_server('org.freedesktop.login1',
+                                        '/org/freedesktop/login1',
+                                        'org.freedesktop.login1.Manager',
+                                        system_bus=True,
+                                        stdout=subprocess.PIPE)
+        self.obj_logind = self.system_bus_con.get_object(
+            'org.freedesktop.login1', '/org/freedesktop/login1')
+
+        self.obj_logind.AddMethods('',
+            [
+                ('PowerOff', 'b', '', ''),
+                ('Suspend', 'b', '', ''),
+                ('Hibernate', 'b', '', ''),
+                ('Inhibit', 'ssss', 'h', 'ret = 5'),
+            ], dbus_interface='org.freedesktop.DBus.Mock')
+
+        # set log to nonblocking
+        set_nonblock(self.logind.stdout)
+
+    def stop_logind(self):
+        '''stop mock logind'''
+
+        self.logind.terminate()
+        self.logind.wait()
+
+    @classmethod
+    def reset_idle_timer(klass):
+        '''trigger activity to reset idle timer'''
+
+        subprocess.check_call([os.path.join(top_builddir, 'tests', 'shiftkey')])
diff --git a/tests/shiftkey.c b/tests/shiftkey.c
new file mode 100644
index 0000000..f02fc93
--- /dev/null
+++ b/tests/shiftkey.c
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Test helper program to send a "left shift key" event via XTest, to reset the
+ * idle timer.
+ *
+ * Copyright (C) 2013 Canonical Ltd.
+ * Author: Martin Pitt <martin pitt ubuntu com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include <X11/extensions/XTest.h>
+#include <X11/keysym.h>
+
+int
+main()
+{
+        Display *display = NULL;
+        int event_base, error_base, major_version, minor_version;
+
+        display = XOpenDisplay (NULL);
+
+        if (display == NULL) {
+                fputs ("Error: Cannot open display\n", stderr);
+                return 1;
+        }
+
+        if (!XTestQueryExtension (display, &event_base, &error_base, &major_version, &minor_version)) {
+                fputs ("Error: No XTest extension\n", stderr);
+                return 1;
+        }
+
+        /* send a left shift key; first press, then release */
+        XTestFakeKeyEvent (display, XK_Shift_L, True, 0);
+        XTestFakeKeyEvent (display, XK_Shift_L, False, 0);
+
+        XCloseDisplay (display);
+        return 0;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]