[gnome-sound-recorder/testing] Add tests



commit ab18af5fa417de3058e539f8a798037777eaf577
Author: Meg Ford <megford gnome org>
Date:   Fri Dec 2 23:04:52 2016 -0600

    Add tests

 Makefile.am                  |    7 ++-
 autogen.sh                   |    2 +-
 configure.ac                 |    6 ++-
 data/Makefile.am             |    2 +-
 glib-tap.mk                  |  134 ++++++++++++++++++++++++++++++++++
 m4/behave-installed-tests.m4 |  137 +++++++++++++++++++++++++++++++++++
 m4/glibtests.m4              |   28 +++++++
 src/application.js           |  108 +++++++++++++++++-----------
 tests/app_test.feature       |    9 +++
 tests/environment.py         |   88 +++++++++++++++++++++++
 tests/steps/app_test.py      |   53 ++++++++++++++
 tests/testutil.py            |  162 ++++++++++++++++++++++++++++++++++++++++++
 12 files changed, 689 insertions(+), 47 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index a7ad7f3..328f2e6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -21,4 +21,9 @@ GITIGNOREFILES = .anjuta .anjuta_sym_db.db m4/
 
 distdir = $(PACKAGE_NAME)-$(VERSION)
 
-include $(top_srcdir)/git.mk
+@BEHAVE_INSTALLED_TESTS_RULE@
+INSTALLED_TESTS = \
+       test_app.feature \
+       $(NULL)
+INSTALLED_TESTS_TYPE = session-exclusive
+-include $(top_srcdir)/git.mk
diff --git a/autogen.sh b/autogen.sh
index 6376354..313b320 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -5,7 +5,7 @@ srcdir=`dirname $0`
 test -z "$srcdir" && srcdir=.
 
 PKG_NAME="gnome-sound-recorder"
-ACLOCAL_FLAGS="-I libgd $ACLOCAL_FLAGS"
+ACLOCAL_FLAGS="-I libgd $ACLOCAL_FLAGS --enable-installed-tests"
 
 test -f $srcdir/configure.ac || {
     echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
diff --git a/configure.ac b/configure.ac
index fa9daa8..12d3eb2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -12,7 +12,6 @@ AC_SUBST(GETTEXT_PACKAGE)
 AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
                    [The prefix for our gettext translation domains.])
 IT_PROG_INTLTOOL([0.26])
-
 GLIB_GSETTINGS
 GOBJECT_INTROSPECTION_REQUIRE([1.0])
 PKG_CHECK_MODULES([DEPS], [gdk-3.0
@@ -27,6 +26,8 @@ AC_PATH_PROG(GJS, [gjs])
 GLIB_COMPILE_RESOURCES=`$PKG_CONFIG --variable glib_compile_resources gio-2.0`
 AC_SUBST(GLIB_COMPILE_RESOURCES)
 
+dnl Installed tests
+BEHAVE_INSTALLED_TESTS
 
 AC_OUTPUT([
 Makefile
@@ -36,3 +37,6 @@ data/icons/Makefile
 src/Makefile
 po/Makefile.in
 ])
+AC_MSG_NOTICE([
+       Installed tests:          $enable_installed_tests
+])
diff --git a/data/Makefile.am b/data/Makefile.am
index acacfe6..abc1371 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -10,7 +10,7 @@ apps_DATA = org.gnome.SoundRecorder.desktop
 
 gsettings_SCHEMAS = org.gnome.gnome-sound-recorder.gschema.xml
 
-@INTLTOOL_XML_NOMERGE_RULE@ 
+@INTLTOOL_XML_NOMERGE_RULE@
 @GSETTINGS_RULES@
 
 
diff --git a/glib-tap.mk b/glib-tap.mk
new file mode 100644
index 0000000..de272a7
--- /dev/null
+++ b/glib-tap.mk
@@ -0,0 +1,134 @@
+# GLIB - Library of useful C routines
+
+TESTS_ENVIRONMENT= \
+       G_TEST_SRCDIR="$(abs_srcdir)"           \
+       G_TEST_BUILDDIR="$(abs_builddir)"       \
+       G_DEBUG=gc-friendly                     \
+       MALLOC_CHECK_=2                         \
+       MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256))
+LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/tap-driver.sh
+LOG_COMPILER = $(top_srcdir)/tap-test
+
+NULL =
+
+# initialize variables for unconditional += appending
+BUILT_SOURCES =
+BUILT_EXTRA_DIST =
+CLEANFILES = *.log *.trs
+DISTCLEANFILES =
+MAINTAINERCLEANFILES =
+EXTRA_DIST =
+TESTS =
+
+installed_test_LTLIBRARIES =
+installed_test_PROGRAMS =
+installed_test_SCRIPTS =
+nobase_installed_test_DATA =
+
+noinst_LTLIBRARIES =
+noinst_PROGRAMS =
+noinst_SCRIPTS =
+noinst_DATA =
+
+check_LTLIBRARIES =
+check_PROGRAMS =
+check_SCRIPTS =
+check_DATA =
+
+# We support a fairly large range of possible variables.  It is expected that all types of files in a test 
suite
+# will belong in exactly one of the following variables.
+#
+# First, we support the usual automake suffixes, but in lowercase, with the customary meaning:
+#
+#   test_programs, test_scripts, test_data, test_ltlibraries
+#
+# The above are used to list files that are involved in both uninstalled and installed testing.  The
+# test_programs and test_scripts are taken to be actual testcases and will be run as part of the test suite.
+# Note that _data is always used with the nobase_ automake variable name to ensure that installed test data 
is
+# installed in the same way as it appears in the package layout.
+#
+# In order to mark a particular file as being only for one type of testing, use 'installed' or 'uninstalled',
+# like so:
+#
+#   installed_test_programs, uninstalled_test_programs
+#   installed_test_scripts, uninstalled_test_scripts
+#   installed_test_data, uninstalled_test_data
+#   installed_test_ltlibraries, uninstalled_test_ltlibraries
+#
+# Additionally, we support 'extra' infixes for programs and scripts.  This is used for support 
programs/scripts
+# that should not themselves be run as testcases (but exist to be used from other testcases):
+#
+#   test_extra_programs, installed_test_extra_programs, uninstalled_test_extra_programs
+#   test_extra_scripts, installed_test_extra_scripts, uninstalled_test_extra_scripts
+#
+# Additionally, for _scripts and _data, we support the customary dist_ prefix so that the named script or 
data
+# file automatically end up in the tarball.
+#
+#   dist_test_scripts, dist_test_data, dist_test_extra_scripts
+#   dist_installed_test_scripts, dist_installed_test_data, dist_installed_test_extra_scripts
+#   dist_uninstalled_test_scripts, dist_uninstalled_test_data, dist_uninstalled_test_extra_scripts
+#
+# Note that no file is automatically disted unless it appears in one of the dist_ variables.  This follows 
the
+# standard automake convention of not disting programs scripts or data by default.
+#
+# test_programs, test_scripts, uninstalled_test_programs and uninstalled_test_scripts (as well as their 
disted
+# variants) will be run as part of the in-tree 'make check'.  These are all assumed to be runnable under
+# gtester.  That's a bit strange for scripts, but it's possible.
+
+TESTS += $(test_programs) $(test_scripts) $(uninstalled_test_programs) $(uninstalled_test_scripts) \
+         $(dist_test_scripts) $(dist_uninstalled_test_scripts)
+
+# Note: build even the installed-only targets during 'make check' to ensure that they still work.
+# We need to do a bit of trickery here and manage disting via EXTRA_DIST instead of using dist_ prefixes to
+# prevent automake from mistreating gmake functions like $(wildcard ...) and $(addprefix ...) as if they were
+# filenames, including removing duplicate instances of the opening part before the space, eg. '$(addprefix'.
+all_test_programs     = $(test_programs) $(uninstalled_test_programs) $(installed_test_programs) \
+                        $(test_extra_programs) $(uninstalled_test_extra_programs) 
$(installed_test_extra_programs)
+all_test_scripts      = $(test_scripts) $(uninstalled_test_scripts) $(installed_test_scripts) \
+                        $(test_extra_scripts) $(uninstalled_test_extra_scripts) 
$(installed_test_extra_scripts)
+all_dist_test_scripts = $(dist_test_scripts) $(dist_uninstalled_test_scripts) $(dist_installed_test_scripts) 
\
+                        $(dist_test_extra_scripts) $(dist_uninstalled_test_extra_scripts) 
$(dist_installed_test_extra_scripts)
+all_test_scripts     += $(all_dist_test_scripts)
+EXTRA_DIST           += $(all_dist_test_scripts)
+all_test_data         = $(test_data) $(uninstalled_test_data) $(installed_test_data)
+all_dist_test_data    = $(dist_test_data) $(dist_uninstalled_test_data) $(dist_installed_test_data)
+all_test_data        += $(all_dist_test_data)
+EXTRA_DIST           += $(all_dist_test_data)
+all_test_ltlibs       = $(test_ltlibraries) $(uninstalled_test_ltlibraries) $(installed_test_ltlibraries)
+
+if ENABLE_ALWAYS_BUILD_TESTS
+noinst_LTLIBRARIES += $(all_test_ltlibs)
+noinst_PROGRAMS += $(all_test_programs)
+noinst_SCRIPTS += $(all_test_scripts)
+noinst_DATA += $(all_test_data)
+else
+check_LTLIBRARIES += $(all_test_ltlibs)
+check_PROGRAMS += $(all_test_programs)
+check_SCRIPTS += $(all_test_scripts)
+check_DATA += $(all_test_data)
+endif
+
+if ENABLE_INSTALLED_TESTS
+installed_test_PROGRAMS += $(test_programs) $(installed_test_programs) \
+                          $(test_extra_programs) $(installed_test_extra_programs)
+installed_test_SCRIPTS += $(test_scripts) $(installed_test_scripts) \
+                          $(test_extra_scripts) $(test_installed_extra_scripts)
+installed_test_SCRIPTS += $(dist_test_scripts) $(dist_test_extra_scripts) \
+                          $(dist_installed_test_scripts) $(dist_installed_test_extra_scripts)
+nobase_installed_test_DATA += $(test_data) $(installed_test_data)
+nobase_installed_test_DATA += $(dist_test_data) $(dist_installed_test_data)
+installed_test_LTLIBRARIES += $(test_ltlibraries) $(installed_test_ltlibraries)
+installed_testcases = $(test_programs) $(installed_test_programs) \
+                      $(test_scripts) $(installed_test_scripts) \
+                      $(dist_test_scripts) $(dist_installed_test_scripts)
+
+installed_test_meta_DATA = $(installed_testcases:=.test)
+
+%.test: %$(EXEEXT) Makefile
+       $(AM_V_GEN) (echo '[Test]' > $@.tmp; \
+       echo 'Type=session-exclusive' >> $@.tmp; \
+       echo 'Exec=$(installed_testdir)/$(notdir $<)' >> $@.tmp; \
+       mv $@.tmp $@)
+
+CLEANFILES += $(installed_test_meta_DATA)
+endif
diff --git a/m4/behave-installed-tests.m4 b/m4/behave-installed-tests.m4
new file mode 100644
index 0000000..cba1217
--- /dev/null
+++ b/m4/behave-installed-tests.m4
@@ -0,0 +1,137 @@
+# How to use the installed tests m4
+#
+#   Place BEHAVE_INSTALLED_TESTS somewhere in configure.ac
+#
+#   Writing your Makefile.am
+#   ~~~~~~~~~~~~~~~~~~~~~~~~
+#
+#   Somewhere in your Makefile.am in this test directory, you need to declare
+#   the following variables:
+#
+#       INSTALLED_TESTS=list of tags for tests to install
+#       INSTALLED_TESTS_TYPE=session-exclusive
+#
+#   First the list of tests which should be installed, followed by
+#   the type of test they should be configured as. The type can
+#   be 'session' or 'session-exclusive'
+#
+#   More information about valid types can be found here:
+#      https://wiki.gnome.org/GnomeGoals/InstalledTests
+#
+#   The last variable is optional, but can be useful to configure
+#   your test program to run in the installed environment as opposed
+#   to the normal `make check' run.
+#
+#   Then place this somewhere in your Makefile.am
+#
+#       @BEHAVE_INSTALLED_TESTS_RULE@
+#
+#   And the following in configure.ac
+#
+#       BEHAVE_INSTALLED_TESTS
+#
+#   And that's it, now your unit tests will be installed along with
+#   a .test metadata file into $(pkglibexecdir) if --enable-installed-tests
+#   is passed to your configure script, and will be run automatically
+#   by the continuous integration servers.
+#
+#   FIXME: Change the above link to point to real documentation, not
+#   a gnome goal page which might disappear at some point.
+#
+# BUGS: This macro hooks into install-exec-am and install-data-am
+# which are internals of Automake. This is because Automake doesnt
+# consider the regular install-exec-local / install-exec-hook or
+# data install components unless variables have been setup for them
+# in advance.
+#
+# This doesnt seem to present a problem, but it is depending on
+# internals of Automake instead of clear documented API.
+
+# Place this in configure.ac to enable
+# the installed tests option.
+
+AC_DEFUN([BEHAVE_INSTALLED_TESTS], [
+AC_PREREQ([2.50])dnl
+AC_REQUIRE([AM_NLS])dnl
+
+  AC_PROG_INSTALL
+  AC_PROG_MKDIR_P
+  AC_PROG_LIBTOOL
+
+  AC_ARG_ENABLE(installed-tests,
+               [AC_HELP_STRING([--enable-installed-tests],
+                               [enable installed unit tests [default=no]])],,
+               [enable_installed_tests="no"])
+
+  AM_CONDITIONAL([BEHAVE_INSTALLED_TESTS_ENABLED],[test "x$enable_installed_tests" = "xyes"])
+  AC_SUBST([BEHAVE_INSTALLED_TESTS_ENABLED], [$enable_installed_tests])
+
+  # Define the rule for makefiles
+  BEHAVE_INSTALLED_TESTS_RULE='
+
+ifeq ($(BEHAVE_INSTALLED_TESTS_ENABLED),yes)
+
+install-exec-am: installed-tests-exec-hook
+install-data-am: installed-tests-data-hook
+uninstall-am: uninstall-tests-hook
+
+META_DIRECTORY=${DESTDIR}${datadir}/installed-tests/${PACKAGE}
+EXEC_DIRECTORY=${DESTDIR}${pkglibexecdir}/installed-tests
+
+BEHAVE_FEATURES=$(wildcard $(srcdir)/tests/*.feature)
+BEHAVE_STEP_DEFINITION=$(wildcard $(srcdir)/tests/steps/*.py)
+BEHAVE_COMMON_FILES=$(srcdir)/tests/environment.py $(srcdir)/tests/common_steps.py
+
+FINAL_TEST_ENVIRONMENT=
+ifneq ($(INSTALLED_TESTS_ENVIRONMENT),)
+      FINAL_TEST_ENVIRONMENT="env $(INSTALLED_TESTS_ENVIRONMENT)"
+endif
+
+installed-tests-exec-hook:
+       @$(MKDIR_P) $(EXEC_DIRECTORY);
+       @for feature in $(BEHAVE_FEATURES); do                                                                
                  \
+           $(LIBTOOL) --mode=install $(INSTALL) --mode=777 $$feature $(EXEC_DIRECTORY);\
+       done
+       @for common_file in $(BEHAVE_COMMON_FILES); do                                                        
                  \
+           $(LIBTOOL) --mode=install $(INSTALL) --mode=777 $$common_file $(EXEC_DIRECTORY);\
+       done
+       @$(MKDIR_P) $(EXEC_DIRECTORY)/steps;
+       @for step_definition in $(BEHAVE_STEP_DEFINITION); do                                                 
                  \
+           $(LIBTOOL) --mode=install $(INSTALL) --mode=777 $$step_definition $(EXEC_DIRECTORY)/steps;\
+       done
+
+
+installed-tests-data-hook:
+       @$(MKDIR_P) $(META_DIRECTORY);
+       @for test in $(INSTALLED_TESTS); do                                                     \
+           echo "Installing $$test.test to $(META_DIRECTORY)";                                 \
+           echo m4_escape([[Test]]) > $(META_DIRECTORY)/$$test.test;                           \
+           echo "Exec=behave-2.7 $(pkglibexecdir)/installed-tests"     \
+                                                  >> $(META_DIRECTORY)/$$test.test;            \
+           echo "Type=$(INSTALLED_TESTS_TYPE)" >> $(META_DIRECTORY)/$$test.test;               \
+       done
+
+uninstall-tests-hook:
+       @for feature in $(BEHAVE_FEATURES); do\
+           echo "Removing feature $(EXEC_DIRECTORY) $$feature";\
+           $(LIBTOOL) --mode=uninstall $(RM) $(EXEC_DIRECTORY)/$$feature;\
+       done
+       @for common_file in $(BEHAVE_COMMON_FILES); do\
+           echo "Removing feature $(EXEC_DIRECTORY) $$common_file";\
+           $(LIBTOOL) --mode=uninstall $(RM) $(EXEC_DIRECTORY)/$$common_file;\
+       done
+       @for step_definition in $(BEHAVE_STEP_DEFINITION); do\
+           echo "Removing feature $(EXEC_DIRECTORY)/steps $$step_definition";\
+           $(LIBTOOL) --mode=uninstall $(RM) $(EXEC_DIRECTORY)/steps/$$step_definition;\
+       done
+       @for test in $(INSTALLED_TESTS); do\
+           $(LIBTOOL) --mode=uninstall $(RM) $(META_DIRECTORY)/$$test.test;\
+       done
+
+endif
+'
+
+  # substitute @BEHAVE_INSTALLED_TESTS_RULE@ in Makefiles
+  AC_SUBST([BEHAVE_INSTALLED_TESTS_RULE])
+  m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([BEHAVE_INSTALLED_TESTS_RULE])])
+])
diff --git a/m4/glibtests.m4 b/m4/glibtests.m4
new file mode 100644
index 0000000..7d5920a
--- /dev/null
+++ b/m4/glibtests.m4
@@ -0,0 +1,28 @@
+dnl GLIB_TESTS
+dnl
+
+AC_DEFUN([GLIB_TESTS],
+[
+  AC_ARG_ENABLE(installed-tests,
+                AS_HELP_STRING([--enable-installed-tests],
+                               [Enable installation of some test cases]),
+                [case ${enableval} in
+                  yes) ENABLE_INSTALLED_TESTS="1"  ;;
+                  no)  ENABLE_INSTALLED_TESTS="" ;;
+                  *) AC_MSG_ERROR([bad value ${enableval} for --enable-installed-tests]) ;;
+                 esac])
+  AM_CONDITIONAL([ENABLE_INSTALLED_TESTS], test "$ENABLE_INSTALLED_TESTS" = "1")
+  AC_ARG_ENABLE(always-build-tests,
+                AS_HELP_STRING([--enable-always-build-tests],
+                               [Enable always building tests during 'make all']),
+                [case ${enableval} in
+                  yes) ENABLE_ALWAYS_BUILD_TESTS="1"  ;;
+                  no)  ENABLE_ALWAYS_BUILD_TESTS="" ;;
+                  *) AC_MSG_ERROR([bad value ${enableval} for --enable-always-build-tests]) ;;
+                 esac])
+  AM_CONDITIONAL([ENABLE_ALWAYS_BUILD_TESTS], test "$ENABLE_ALWAYS_BUILD_TESTS" = "1")
+  if test "$ENABLE_INSTALLED_TESTS" = "1"; then
+    AC_SUBST(installed_test_metadir, [${datadir}/installed-tests/]AC_PACKAGE_NAME)
+    AC_SUBST(installed_testdir, [${libexecdir}/installed-tests/]AC_PACKAGE_NAME)
+  fi
+])
diff --git a/src/application.js b/src/application.js
index bef64bc..2293381 100644
--- a/src/application.js
+++ b/src/application.js
@@ -19,6 +19,7 @@
 
 const Gio = imports.gi.Gio;
 const GLib = imports.gi.GLib;
+const GMenu = imports.gi.GMenu;
 const Gst = imports.gi.Gst;
 const Gtk = imports.gi.Gtk;
 const Lang = imports.lang;
@@ -39,41 +40,58 @@ const Application = new Lang.Class({
     Extends: Gtk.Application,
 
     _init: function() {
-        this.parent({ application_id: pkg.name }); 
-        GLib.set_application_name(_("SoundRecorder"));         
+        this.parent({ application_id: pkg.name });
+        GLib.set_application_name(_("SoundRecorder"));
     },
-    
+
+    /* Callback functions for Application Menu ActionEntries */
+
+    _onPreferencesAction: function() {
+            this._showPreferences();
+    },
+
+    _onAboutAction: function() {
+            this._showAbout();
+    },
+
+    _onQuitAction: function() {
+            this.quit();
+    },
+
+    /* End Callback functions for Application Menu ActionEntries */
+
     _initAppMenu: function() {
         let menu = new Gio.Menu();
         let section = new Gio.Menu();
         menu.append_section(null, section);
         section.append(_("Preferences"), 'app.preferences');
-        section = new Gio.Menu();
-        menu.append_section(null, section);
-        section.append(_("About"), 'app.about');
-        section.append(_("Quit"),'app.quit');
+        let sectionTwo = new Gio.Menu();
+        menu.append_section(null, sectionTwo);
+        sectionTwo.append(_("About"), 'app.about');
+        sectionTwo.append(_("Quit"),'app.quit');
         this.set_app_menu(menu);
-        let preferences = new Gio.SimpleAction({ name: 'preferences' });
-        preferences.connect('activate', Lang.bind(this,
-            function() {
-                this._showPreferences();
-            }));
-        this.add_action(preferences);
-        
+
         let aboutAction = new Gio.SimpleAction({ name: 'about' });
-        aboutAction.connect('activate', Lang.bind(this, 
+        aboutAction.connect('activate', Lang.bind(this,
             function() {
-                this._showAbout();
+                this._onAboutAction();
             }));
         this.add_action(aboutAction);
-        
+
+        let preferencesAction = new Gio.SimpleAction({ name: 'preferences' });
+        preferencesAction.connect('activate', Lang.bind(this,
+            function() {
+                this._onPreferencesAction();
+            }));
+        this.add_action(preferencesAction);
+
         let quitAction = new Gio.SimpleAction({ name: 'quit' });
         quitAction.connect('activate', Lang.bind(this,
             function() {
-                this.quit();
+                this._onQuitAction();
             }));
-         this.add_action(quitAction);
-         this.add_accelerator('<Primary>q', 'app.quit', null);
+        this.add_action(quitAction);
+        this.add_accelerator('<Primary>q', 'app.quit', null);
     },
 
     vfunc_startup: function() {
@@ -87,30 +105,31 @@ const Application = new Lang.Class({
         settings = new Gio.Settings({ schema: 'org.gnome.gnome-sound-recorder' });
         this.ensure_directory();
     },
-    
-    ensure_directory: function() {
-        /* Translators: "Recordings" here refers to the name of the directory where the application places 
files */
-        let path = GLib.build_filenamev([GLib.get_home_dir(), _("Recordings")]);
-
-        // Ensure Recordings directory
-        GLib.mkdir_with_parents(path, parseInt("0755", 8));
-        this.saveDir = Gio.file_new_for_path(path);
-    },
 
     vfunc_activate: function() {
         (this.window = new MainWindow.MainWindow({ application: this })).show();
     },
-    
+
     onWindowDestroy: function() {
         if (MainWindow.wave.pipeline)
             MainWindow.wave.pipeline.set_state(Gst.State.NULL);
-        if (MainWindow._record.pipeline) 
+        if (MainWindow._record.pipeline)
             MainWindow._record.pipeline.set_state(Gst.State.NULL);
-        
-        if (MainWindow.play.play) 
-            MainWindow.play.play.set_state(Gst.State.NULL);        
+
+        if (MainWindow.play.play)
+            MainWindow.play.play.set_state(Gst.State.NULL);
+    },
+
+    ensure_directory: function() {
+        /* Translators: "Recordings" here refers to the name of the directory where the application places 
files */
+        let path = GLib.build_filenamev([GLib.get_home_dir(), _("Recordings")]);
+
+        // Ensure Recordings directory
+        GLib.mkdir_with_parents(path, parseInt("0755", 8));
+        this.saveDir = Gio.file_new_for_path(path);
     },
-    
+
+    /* Functions for showing the Preferences Dialog and setting preferences */
     _showPreferences: function() {
         let preferencesDialog = new Preferences.Preferences();
 
@@ -118,13 +137,13 @@ const Application = new Lang.Class({
             function(widget, response) {
                 preferencesDialog.widget.destroy();
             }));
-    },   
-        
+    },
+
     getPreferences: function() {
         let set = settings.get_int("media-type-preset");
         return set;
      },
-    
+
     setPreferences: function(profileName) {
         settings.set_int("media-type-preset", profileName);
     },
@@ -137,27 +156,30 @@ const Application = new Lang.Class({
     setChannelsPreferences: function(channel) {
         settings.set_int("channel", channel);
     },
-     
+
     getMicVolume: function() {
         let micVolLevel = settings.get_double("mic-volume");
         return micVolLevel;
     },
-     
+
     setMicVolume: function(level) {
          settings.set_double("mic-volume", level);
     },
-    
+
     getSpeakerVolume: function() {
         let speakerVolLevel = settings.get_double("speaker-volume");
         return speakerVolLevel;
     },
-     
+
     setSpeakerVolume: function(level) {
          settings.set_double("speaker-volume", level);
     },
-    
+
+    /* End functions for showing the Preferences Dialog and setting preferences */
+
     _showAbout: function() {
         let aboutDialog = new Gtk.AboutDialog();
+
         aboutDialog.artists = [ 'Reda Lazri <the red shortcut gmail com>',
                                 'Garrett LeSage <garrettl gmail com>',
                                 'Hylke Bons <hylkebons gmail com>',
diff --git a/tests/app_test.feature b/tests/app_test.feature
new file mode 100644
index 0000000..3a2aa3a
--- /dev/null
+++ b/tests/app_test.feature
@@ -0,0 +1,9 @@
+Feature: Smoke tests
+
+  Background:
+    * Make sure gnome-sound-recorder is running
+
+  @about
+  Scenario: About dialog
+    * Open About dialog
+    Then About UI is displayed
\ No newline at end of file
diff --git a/tests/environment.py b/tests/environment.py
new file mode 100644
index 0000000..b53a886
--- /dev/null
+++ b/tests/environment.py
@@ -0,0 +1,88 @@
+from time import sleep
+from dogtail.utils import isA11yEnabled, enableA11y
+if not isA11yEnabled():
+    enableA11y(True)
+
+from common_steps import App
+from dogtail.config import config
+from os import system, makedirs
+from shutil import copyfile
+
+
+def before_all(context):
+    """
+    Setup stuff
+    Being executed before all features
+    """
+
+    try:
+        # Skip dogtail actions to print to stdout
+        config.logDebugToStdOut = False
+        config.typingDelay = 0.2
+
+        context.app_class = App('gnome-sound-recorder')
+
+        context.screenshot_counter = 0
+        context.save_screenshots = False
+    except Exception as e:
+        print('Error in before_all: %s' % e.message)
+
+
+def before_tag(context, tag):
+    try:
+        # Copy screenshots
+        if 'screenshot' in tag:
+            context.save_screenshots = True
+            context.screenshot_dir = '../sr_screenshots'
+            makedirs(context.screenshot_dir)
+    except Exception as e:
+        print('Error in before_tag: %s' % str(e))
+
+
+def after_step(context, step):
+    try:
+        if hasattr(context, 'embed'):
+            # Embed screenshot if HTML report is used
+            system('dbus-send --session --type=method_call ' +
+                   "--dest='org.gnome.Shell.Screenshot' " +
+                   "'/org/gnome/Shell/Screenshot' " +
+                   'org.gnome.Shell.Screenshot.Screenshot ' +
+                   'boolean:false boolean:false string:/tmp/screenshot.png')
+            if context.save_screenshots:
+                # Don't embed screenshot if this is a screenshot tour page
+                name = '%s/screenshot_%s_%02d.png' % (
+                    context.screenshot_dir,
+                    context.current_locale,
+                    context.screenshot_counter)
+
+                copyfile('/tmp/screenshot.png', name)
+                context.screenshot_counter += 1
+            else:
+                context.embed('image/png',
+                              open('/tmp/screenshot.png', 'r').read())
+    except Exception as e:
+        print('Error in after_step: %s' % str(e))
+
+
+def before_scenario(context, scenario):
+    """ Cleanup previous settings and make sure we have test files in /tmp """
+    try:
+        # cleanup()
+        print('before')
+    except Exception as e:
+        print('Error in before_scenario: %s' % e.message)
+
+
+def after_scenario(context, scenario):
+    """Teardown for each scenario
+    Kill eog (in order to make this reliable we send sigkill)
+    """
+    try:
+        # Stop the app
+        context.app_class.kill()
+
+        # Pause after the scenario
+        sleep(1)
+    except Exception as e:
+        # Stupid behave simply crashes if an exception has occurred
+        print('Error in after_scenario: %s' % e.message)
diff --git a/tests/steps/app_test.py b/tests/steps/app_test.py
new file mode 100644
index 0000000..03035a4
--- /dev/null
+++ b/tests/steps/app_test.py
@@ -0,0 +1,53 @@
+#! /usr/bin/python
+import pyatspi
+import os
+import sys
+
+from gi.repository import Gio, GLib
+from subprocess import Popen, PIPE
+
+from behave import step, then
+from dogtail import i18n
+from dogtail import tree
+from dogtail import utils
+from dogtail.procedural import *
+from common_steps import *
+
+
+@step(u'Open About dialog')
+def open_about_dialog(context):
+    context.execute_steps(u'* Select "About" in GApplication menu')
+    dialog_name = 'About Sound Recorder'
+    context.about_dialog = context.app.dialog(translate(dialog_name))
+
+
+@step(u'Open and close About dialog')
+def open_and_close_about_dialog(context):
+    context.execute_steps(u'* Select "About" in GApplication menu')
+    keyCombo('<Esc>')
+
+
+@then(u'About UI is displayed')
+def about_ui_is_displayed(context):
+    dialog_elem = context.about_dialog.child
+
+    def is_displayed(role, ui_string):
+        if role == 'radio button':
+            return dialog_elem(translate(ui_string), role).checked
+
+        return dialog_elem(translate(ui_string), role).showing
+
+    name_err = 'App name is not displayed'
+    website_err = 'Website link is not displayed'
+    lic_err = 'License link is not displayed'
+    about_err = 'About tab is not selected'
+    credit_err = 'Credits tab is selected'
+
+    assert is_displayed('label', 'Sound Recorder'), name_err
+    assert is_displayed('label', 'Website'), website_err
+    assert is_displayed('label',
+                        'This program comes with absolutely no warranty.\n'
+                        'See the GNU General Public License,'
+                        ' version 2 or later for details.'), lic_err
+    assert is_displayed('radio button', 'About'), about_err
+    assert not is_displayed('radio button', 'Credits'), credit_err
diff --git a/tests/testutil.py b/tests/testutil.py
new file mode 100644
index 0000000..08f3c50
--- /dev/null
+++ b/tests/testutil.py
@@ -0,0 +1,162 @@
+# ! /usr/bin/python
+import os
+import sys
+from subprocess import Popen, PIPE
+from gi.repository import GLib, Gio
+from dogtail.utils import *
+
+from behave import step
+from dogtail import i18n
+from dogtail.predicate import *
+from dogtail.procedural import *
+from dogtail.rawinput import keyCombo, absoluteMotion, pressKey
+from dogtail.tree import *
+from unittest import TestCase
+
+
+settings = Gio.Settings.new('org.gnome.desktop.interface')
+settings.set_boolean('toolkit-accessibility', True)
+
+
+class App(object):
+    """
+    This class does all basic events with the app
+    """
+    def __init__(self, appName, shortcut='<Control><Q>', a11yAppName=None,
+                 forceKill=True, parameters='', recordVideo=False):
+        """
+        Initialize object App
+        forceKill  is the app supposed to be kill before/after test?
+        parameters any params the app needs to to start using startViaCommand
+        """
+        # The appname is the command to run the app
+        self.appCommand = appName
+        # The default quit shortcut
+        self.shortcut = shortcut
+        self.forceKill = forceKill
+        self.parameters = parameters
+        self.internCommand = self.appCommand.lower()
+        # The app's a11y name is different than binary
+        self.a11yAppName = a11yAppName
+        # Start gnome-shell recording while running the app
+        self.recordVideo = recordVideo
+        self.pid = None
+        # A way of overcoming overview autospawn when mouse in 1,1 from start
+        pressKey('Esc')
+        absoluteMotion(100, 100, 2)
+        # attempt to make a recording of the test
+        if self.recordVideo:
+            keyCombo('<Control><Alt><Shift>R')
+
+    def isRunning(self):
+        """
+        Is the app running?
+        """
+        if self.a11yAppName is None:
+            self.a11yAppName = 'org.gnome.SoundRecorder'
+        # Trap weird bus errors
+        for attempt in xrange(0, 30):
+            sleep(1)
+            try:
+                return self.a11yAppName in [x.name for x in root.applications()]
+            except GLib.GError:
+                continue
+        raise Exception('10 at-spi errors, seems that bus is blocked')
+
+    def kill(self):
+        """
+        Kill the app via 'killall'
+        """
+        if self.recordVideo:
+            keyCombo('<Control><Alt><Shift>R')
+        try:
+            self.process.kill()
+        except:
+            # Fall back to killall
+            Popen('killall ' + self.appCommand, shell=True).wait()
+
+    def startViaCommand(self):
+        """
+        Start the app via command
+        """
+        if self.forceKill and self.isRunning():
+            self.kill()
+
+        assert not self.isRunning(), 'Application cannot be stopped'
+        command = '%s %s' % (self.appCommand, self.parameters)
+        self.pid = run(command)
+
+        assert self.isRunning(), 'Application failed to start'
+        return root.application(self.a11yAppName)
+
+    def closeViaShortcut(self):
+        """
+        Close the app via shortcut
+        """
+        if not self.isRunning():
+            raise Exception('App is not running')
+
+        keyCombo(self.shortcut)
+        assert not self.isRunning(), 'Application cannot be stopped'
+
+
+@step(u'Make sure gnome-sound-recorder is running')
+def ensure_app_running(context):
+    context.app = context.app_class.startViaCommand()
+
+
+@step(u'Set locale to "{locale}"')
+def set_locale_to(context, locale):
+    environ['LANG'] = locale
+    i18n.translationDbs = []
+    i18n.loadTranslationsFromPackageMoFiles('eog')
+    i18n.loadTranslationsFromPackageMoFiles('gtk30')
+
+    context.current_locale = locale
+    context.screenshot_counter = 0
+
+
+def translate(string):
+    translation = i18n.translate(string)
+    if translation == []:
+        translation = string
+    else:
+        if len(translation) > 1:
+            print("Options for '%s'" % string)
+            print(translation)
+        translation = translation[-1].decode('utf-8')
+
+    return translation
+
+
+# GApplication menu steps
+@step(u'Open GApplication menu')
+def get_gmenu(context):
+    GnomeShell().getApplicationMenuButton(app_name='Sound Recorder').click()
+
+
+@step(u'Close GApplication menu')
+def close_gmenu(context):
+    GnomeShell().getApplicationMenuButton(app_name='Sound Recorder').click()
+    doDelay(2)
+
+
+@step(u'Select "{name}" in GApplication menu')
+def select_app_menu_item(context, name):
+    """
+    Clicking on the App menu fails to open the dialog,
+    so use key combinations to navigate.
+    """
+    keyCombo('<Super_L><F10>')
+    if name == 'Preferences':
+        # first item, we're already there
+        pass
+    elif name == 'About':
+        pressKey('Down')
+
+    elif name == 'Quit':
+        pressKey('Down')
+        pressKey('Down')
+
+    pressKey('Enter')
+    doDelay(2)


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