...
 
Commits (9)
......@@ -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
......@@ -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"
......
......@@ -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([0.9.6])
......@@ -115,6 +114,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
......@@ -124,3 +125,6 @@ data/icons/Makefile
src/Makefile
po/Makefile.in
])
AC_MSG_NOTICE([
Installed tests: $enable_installed_tests
])
......@@ -16,7 +16,7 @@
<description>Maps media types to audio encoder preset names. If there is no mapping set, the default encoder settings will be used.</description>
</key>
<key name="channel" type="i">
<default>1</default>
<default>2</default>
<summary>Available channels</summary>
<description>Maps available channels. If there is not no mapping set, stereo channel will be used by default.</description>
</key>
......
# 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])])
])
......@@ -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;
......@@ -43,37 +44,54 @@ const Application = new Lang.Class({
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,
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() {
......@@ -88,15 +106,6 @@ const Application = new Lang.Class({
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();
},
......@@ -111,6 +120,16 @@ const Application = new Lang.Class({
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();
......@@ -155,9 +174,11 @@ const Application = new Lang.Class({
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>',
......
......@@ -177,7 +177,7 @@ const MainView = new Lang.Class({
if (play.getPipeStates() == PipelineStates.PLAYING) {
play.stopPlaying();
let listRow = this.listBox.get_selected_row();
let rowWidget = listRow.get_child(this.widget);
let rowWidget = listRow.get_child();
rowWidget.foreach(Lang.bind(this,
function(child) {
......@@ -578,7 +578,7 @@ const MainView = new Lang.Class({
hasPreviousSelRow: function() {
this.destroyLoadMoreButton();
if (previousSelRow != null) {
let rowWidget = previousSelRow.get_child(this.widget);
let rowWidget = previousSelRow.get_child();
rowWidget.foreach(Lang.bind(this,
function(child) {
let alwaysShow = child.get_no_show_all();
......@@ -628,7 +628,7 @@ const MainView = new Lang.Class({
}
previousSelRow = selectedRow;
let selectedRowWidget = previousSelRow.get_child(this.widget);
let selectedRowWidget = previousSelRow.get_child();
selectedRowWidget.show_all();
selectedRowWidget.foreach(Lang.bind(this,
function(child) {
......@@ -717,7 +717,7 @@ const MainView = new Lang.Class({
if (activeState == PipelineStates.PLAYING) {
play.pausePlaying();
let rowWidget = listRow.get_child(this.widget);
let rowWidget = listRow.get_child();
rowWidget.foreach(Lang.bind(this,
function(child) {
......@@ -741,7 +741,7 @@ const MainView = new Lang.Class({
if (activeState != PipelineStates.PLAYING) {
play.startPlaying();
let rowWidget = listRow.get_child(this.widget);
let rowWidget = listRow.get_child();
rowWidget.foreach(Lang.bind(this,
function(child) {
......
......@@ -50,6 +50,7 @@ const WaveForm = new Lang.Class({
_init: function(grid, file) {
this._grid = grid;
this.recordTime = null;
let placeHolder = -100;
for (let i = 0; i < 40; i++)
......@@ -175,7 +176,6 @@ const WaveForm = new Lang.Class({
start = this.recordTime;
}
let i = 0;
let xAxis = 0;
let end = start + 40;
let width = this.drawing.get_allocated_width();
......@@ -195,9 +195,8 @@ const WaveForm = new Lang.Class({
cr.setSourceRGBA(0.0, 185, 161, 255);
}
for(i = start; i <= end; i++) {
// Keep moving until we get to a non-null array member
for(let i = start; i <= end; i++) {
// Keep moving until we get to a non-negative array member
if (peaks[i] < 0) {
cr.moveTo((xAxis * pixelsPerSample), (waveheight - (peaks[i] * waveheight)))
}
......
Feature: Smoke tests
Background:
* Make sure gnome-sound-recorder is running
@mainwindow_refresh_del_file
Scenario: Create Recording
* Create Recording
Then Delete the file
Then MainWindow is shown
Then Recordings directory is present
@no_recordings
Scenario: No recordings in the Recordings folder
* Delete the Recordings folder
Then MainWindow is shown
Then Recordings directory is present
@mainwindow_refresh_del_dir
Scenario: Create Recording
* Create Recording
Then Delete the Recordings folder
Then MainWindow is shown
Then Recordings directory is present
@about
Scenario: About dialog
* Open About dialog
Then About UI is displayed
Then Press Credits
@quit_via_app_menu
Scenario: Quit via app menu
* Select "Quit" from the app menu
Then gnome-sound-recorder is not running
@preferences
Scenario: Preferences dialog
* Open Preferences dialog
Then Preferences UI is displayed
@codecs
Scenario: Record using all codecs and channels options
* Changing codecs and channels works @codecs
Scenario: Record using all codecs and channels options
* Changing codecs and channels works
@quit_via_shortcut
Scenario: Quit via shortcut
* Press the quit shortcut
Then gnome-sound-recorder is not running
\ No newline at end of file
# ! /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')
print("wtf")
pressKey('Enter')
doDelay(2)
from os import system, makedirs
from shutil import copyfile
from time import sleep
from dogtail.config import config
from dogtail.utils import isA11yEnabled, enableA11y
from common_steps import App
if not isA11yEnabled():
enableA11y(True)
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)
#! /usr/bin/python
from datetime import datetime, timedelta
import os
import sys
import time
import shutil
from gi.repository import Gio, GLib
import pyatspi
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 *
def display_err(err_str):
return '{} is not displayed'.format(err_str)
def is_displayed(ui, ui_string, role=None):
if role == 'radio button':
return ui(translate(ui_string), role).checked
elif not role:
return ui(translate(ui_string)).showing
return ui(translate(ui_string), role).showing
def record_with_codec_works(context, ui, codec_name):
ui.child('Record').click()
time.sleep(10)
ui.child('Done').click()
done_time = datetime.now().strftime('%H:%M:%S')
mod_time = done_time.split(':')
print(len(mod_time[0]))
print(mod_time[0])
if mod_time[0] > 12:
hour = int(mod_time[0]) % 12
if len(str(hour)) < 2:
str_time = '0'
else:
str_time = ''
str_time += str(hour) + ':' + mod_time[1]
else:
if len(mod_time[0]) < 2:
str_time = '0'
str_time = mod_time[0] + ':' + mod_time[1]
pressKey('Down')
pressKey('Right')
pressKey('Right')
pressKey('Enter')
dialog_name = 'Info'
info_ui = context.app.dialog(translate(dialog_name)).child
assert is_displayed(info_ui, codec_name), display_err('Correct codec')
# Inexact match
print(list(iter(context.app.dialog(translate(
dialog_name)).children))[1][0].children[1].name)
print(str_time)
assert (str_time in list(iter(context.app.dialog(translate(
dialog_name)).children))[1][0].children[1].name), \
display_err('Correct date modified')
pressKey('Esc')
@step(u'Delete the Recordings folder')
def delete_recordings_folder(context):
dir_path = GLib.build_filenamev([GLib.get_home_dir(), "Recordings"])
print(dir_path)
shutil.rmtree(dir_path)
# # Wait for the app to refresh before moving on to the next test
time.sleep(30)
@step(u'Delete the file')
def delete_file(context):
file_path = GLib.build_filenamev([GLib.get_home_dir(), "Recordings", "Clip 1"])
shutil.rmtree(file_path)
# # Wait for the app to refresh before moving on to the next test
time.sleep(30)
@then(u'MainWindow is shown')
def mainwindow_shown(context):
ui = context.app
emptyPageDirections = 'Use the Record button to make sound recordings'
emptyPageTitle = 'Add Recordings'
assert (emptyPageDirections in list(
list(
list(
list(
list(
list(
iter(
ui.children))[0][1].children)[0].children
)[0].children)[0].children)[0].children)[0].name), \
display_err('emptyPageDirections')
assert (emptyPageTitle in list(
list(
list(
list(
list(
list(
iter(
ui.children))[0][1].children)[0].children
)[0].children)[0].children)[0].children)[1].name), \
display_err('emptyPageTitle')
@then(u'Recordings directory is present')
def record_dir_present(context):
ui = context.app
dir_path = os.path.join(GLib.get_home_dir(), 'Recordings')
assert os.path.isdir(dir_path), 'Recordings directory is not present'
@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):
ui = context.about_dialog.child
assert is_displayed(ui, 'Sound Recorder', 'label'), display_err('App name')
assert is_displayed(ui, 'Website', 'label'), display_err('Website link')
assert is_displayed(ui,
'This program comes with absolutely no warranty.\n'
'See the GNU General Public License,'
' version 2 or later for details.',
'label'), display_err('License link')
assert is_displayed(ui, 'About', 'radio button'), display_err('About tab')
assert not is_displayed(ui, 'Credits', 'radio button'), \
display_err('Credits tab')
@then(u'Press Credits')
def press_credits(context):
ui = context.about_dialog.child
pressKey('Right')
pressKey('Enter')
assert is_displayed(ui, 'Credits', 'radio button'), \
display_err('Credits tab')
@step(u'Open Preferences dialog')
def open_pref_dialog(context):
context.execute_steps(u'* Select "Preferences" in GApplication menu')
dialog_name = 'Preferences'
context.pref_dialog = context.app.dialog(translate(dialog_name))
@then(u'Preferences UI is displayed')
def pref_ui_is_displayed(context):
# for some reason the global contect.pref_dialog doesn't work here
# so assign again fo this test
dialog_name = 'Preferences'
ui = context.app.dialog(translate(dialog_name)).child
# for i in iter(context.app.dialog(translate(dialog_name)).children):
# for j in i:
# for k in j.children:
# print(k.role)
# print(k.name)
assert is_displayed(ui, 'Ogg Vorbis'), display_err('Correct codec')
assert is_displayed(ui, 'Stereo'), display_err('Stereo label')
assert is_displayed(ui, 'Volume', 'label'), display_err('Volume level')
assert is_displayed(ui, 'Microphone', 'label'), \
display_err('Mic volume level')
@step(u'Create Recording')
def change_codecs_channels(context):
application = context.app
record_with_codec_works(context, application, 'Ogg Vorbis')
@step(u'Changing codecs and channels works')
def change_codecs_channels(context):
application = context.app
record_with_codec_works(context, application, 'Ogg Vorbis')
context.execute_steps(u'* Select "Preferences" in GApplication menu')
dialog_name = 'Preferences'
ui = context.app.dialog(translate(dialog_name)).child
ui(translate('Stereo')).click(button=1)
pressKey('Down')
pressKey('Enter')
assert is_displayed(ui, 'Mono'), display_err('Stereo label')
pressKey('Esc')
record_with_codec_works(context, application, 'Ogg Vorbis')
context.execute_steps(u'* Select "Preferences" in GApplication menu')
ui = context.app.dialog(translate(dialog_name)).child
pressKey('Down')
pressKey('Enter')
assert is_displayed(ui, 'Opus'), display_err('Correct codec')
assert is_displayed(ui, 'Mono'), display_err('Stereo label')
pressKey('Esc')
record_with_codec_works(context, application, 'Opus')
context.execute_steps(u'* Select "Preferences" in GApplication menu')
ui = context.app.dialog(translate(dialog_name)).child
ui(translate('Mono')).click(button=1)
pressKey('Down')
pressKey('Enter')
assert is_displayed(ui, 'Stereo'), display_err('Stereo label')
assert is_displayed(ui, 'Opus'), display_err('Correct codec')
pressKey('Esc')
record_with_codec_works(context, application, 'Opus')
# Flac
context.execute_steps(u'* Select "Preferences" in GApplication menu')
ui = context.app.dialog(translate(dialog_name)).child
pressKey('Down')
assert is_displayed(ui, 'FLAC'), display_err('Correct codec')
assert is_displayed(ui, 'Stereo'), display_err('Stereo label')
pressKey('Esc')
record_with_codec_works(context, application, 'FLAC')
context.execute_steps(u'* Select "Preferences" in GApplication menu')
ui = context.app.dialog(translate(dialog_name)).child
ui(translate('Stereo')).click(button=1)
pressKey('Down')
pressKey('Enter')
assert is_displayed(ui, 'Mono'), display_err('Stereo label')
assert is_displayed(ui, 'FLAC'), display_err('Correct codec')
pressKey('Esc')
record_with_codec_works(context, application, 'FLAC')
# MP3
context.execute_steps(u'* Select "Preferences" in GApplication menu')
ui = context.app.dialog(translate(dialog_name)).child
pressKey('Down')
assert is_displayed(ui, 'MP3'), display_err('Correct codec')
assert is_displayed(ui, 'Mono'), display_err('Stereo label')
pressKey('Esc')
record_with_codec_works(context, application, 'MP3')
context.execute_steps(u'* Select "Preferences" in GApplication menu')
ui = context.app.dialog(translate(dialog_name)).child
ui(translate('Mono')).click(button=1)
pressKey('Down')
pressKey('Enter')
assert is_displayed(ui, 'Stereo'), display_err('Stereo label')
assert is_displayed(ui, 'MP3'), display_err('Correct codec')
pressKey('Esc')
record_with_codec_works(context, application, 'MP3')
context.execute_steps(u'* Select "Preferences" in GApplication menu')
ui = context.app.dialog(translate(dialog_name)).child
pressKey('Down')
assert is_displayed(ui, 'MOV'), display_err('Correct codec')
assert is_displayed(ui, 'Stereo'), display_err('Stereo label')
pressKey('Esc')
record_with_codec_works(context, application, 'MOV')
context.execute_steps(u'* Select "Preferences" in GApplication menu')
ui = context.app.dialog(translate(dialog_name)).child
ui(translate('Stereo')).click(button=1)
pressKey('Down')
pressKey('Enter')
assert is_displayed(ui, 'Stereo'), display_err('Stereo label')
assert is_displayed(ui, 'MOV'), display_err('Correct codec')
pressKey('Esc')
record_with_codec_works(context, application, 'MOV')
pressKey('Up')
@step(u'Select "Quit" from the app menu')
def quit_menu(context):
context.execute_steps(u'* Select "Quit" in GApplication menu')
@step(u'Press the quit shortcut')
def quit_sc(context):
keyCombo('<Control><q>')
# ! /usr/bin/python
import os
import sys
from subprocess import Popen, PIPE
from gi.repository import GLib, Gio
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 dogtail.utils 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)