[Pkg-privacy-commits] [mat] 01/01: add GUI tests
Sascha Steinbiss
sascha at steinbiss.name
Sun Jan 24 22:14:20 UTC 2016
This is an automated email from the git hooks/post-receive script.
sascha-guest pushed a commit to branch gui_tests
in repository mat.
commit fade3ad9ff49cb9143931fa44310b187b2307751
Author: Sascha Steinbiss <sascha at steinbiss.name>
Date: Sun Jan 17 23:55:09 2016 +0000
add GUI tests
This adds a framework for running GUI autopkgtests using dogtail, Xvfb
and Python's unittest module. It also adds tests for some criteria
given in upstream's TESTING file:
- whether the GUI starts up with no messages on stderr
- whether the GUI actually cleans files (reusing the test file set
from the commandline version tests)
- whether unsupported files are handled as intended in cleaned archives
- whether the status bar displays the messages set in the GUI code
Translations are less straightforward to test for now.
---
debian/changelog | 7 +
debian/copyright | 4 +
debian/tests/check-mat | 260 ++++++++++++++++++++++++++++++++++++++
debian/tests/control | 4 +
debian/tests/gui-tests | 50 ++++++++
debian/tests/statusbar_watcher.py | 71 +++++++++++
6 files changed, 396 insertions(+)
diff --git a/debian/changelog b/debian/changelog
index e779240..0fe96b0 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+mat (0.6.1-3) UNRELEASED; urgency=medium
+
+ * Team upload.
+ * Add GUI testsuite to autopkgtest.
+
+ -- Sascha Steinbiss <sascha at steinbiss.name> Sat, 23 Jan 2016 17:18:44 +0000
+
mat (0.6.1-2) unstable; urgency=medium
[ Sascha Steinbiss ]
diff --git a/debian/copyright b/debian/copyright
index c81e01a..753ea7f 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -9,6 +9,10 @@ Files: debian/*
Copyright: © 2011-2015 intrigeri <intrigeri at debian.org>
License: GPL-2
+Files: debian/tests/*
+Copyright: © 2016 Sascha Steinbiss <sascha at steinbiss.name>
+License: GPL-2
+
Files: libmat/bencode/*
Copyright: © 2007 Petru Paler <petru at paler.net>
© 2011 Julien Voisin <julien.voisin at dustri.org>
diff --git a/debian/tests/check-mat b/debian/tests/check-mat
new file mode 100755
index 0000000..8a356f1
--- /dev/null
+++ b/debian/tests/check-mat
@@ -0,0 +1,260 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2016 Sascha Steinbiss <sascha at steinbiss.name>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License, version 2
+# as published by the Free Software Foundation.
+#
+# 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.
+#
+# On Debian GNU/Linux systems, the complete text of version 2 of the
+# General Public License can be found in `/usr/share/common-licenses/GPL-2'.
+
+import gi
+gi.require_version('Atspi', '2.0')
+gi.require_version('Wnck', '3.0')
+import glob
+import os
+import sys
+import shutil
+import time
+import unittest
+import tarfile
+import tempfile
+import subprocess
+import multiprocessing
+from dogtail import rawinput
+from dogtail import tree
+from dogtail.procedural import *
+from dogtail.predicate import GenericPredicate
+from dogtail.utils import run
+from libmat import mat
+from statusbar_watcher import StatusbarWatcher
+
+
+class TestMatGUIFunctional(unittest.TestCase):
+
+ def setUp(self):
+ self.file_list = []
+ self.tmpdir = "run"
+ os.mkdir(self.tmpdir)
+ for dirty_file in glob.glob("dirty/dirty*"):
+ dirty_dir = os.path.join(self.tmpdir, os.path.basename(dirty_file))
+ shutil.copy2(dirty_file, dirty_dir)
+ self.file_list.append(dirty_dir)
+ os.environ["LC_ALL"] = 'C'
+ self.pid = run('mat-gui')
+ self.rootapp = tree.root.application('mat-gui')
+ self.add = self.rootapp.child(roleName="tool bar").child(name="Add")
+ self.clean = self.rootapp.child(
+ roleName="tool bar").child(name="Clean")
+ self.mat_window_title = 'Metadata Anonymisation Toolkit'
+ focus.application("mat-gui")
+ focus.frame(self.mat_window_title)
+
+ def tearDown(self):
+ try:
+ os.kill(self.pid, 0)
+ os.system("kill -9 %i" % self.pid)
+ except OSError:
+ pass
+ for root, dirs, files in os.walk(self.tmpdir):
+ for d in dirs + files:
+ os.chmod(os.path.join(root, d), 0o777)
+ shutil.rmtree(self.tmpdir)
+
+ def make_unsupp_archive(self):
+ """
+ Create an archive with unsupported files.
+ """
+ self.tarpath = os.path.join(self.tmpdir, "test.tar.bz2")
+ tar = tarfile.open(self.tarpath, "w:bz2")
+ for f in glob.glob('*.py'):
+ tar.add(f)
+ tar.close()
+
+ def quit_via_menu(self):
+ """
+ Leave MAT via menu option.
+ """
+ self.rootapp.child(name="File", roleName="menu").click()
+ self.rootapp.child(name="Quit", roleName="menu item").click()
+
+ def test_start_no_warnings(self):
+ """
+ Checks that MAT starts up with no messages on stderr.
+ """
+ self.quit_via_menu()
+ err = ""
+
+ def runner():
+ o = subprocess.Popen("mat-gui",
+ stderr=subprocess.PIPE,
+ stdout=subprocess.PIPE)
+ lines_iterator = iter(o.stderr.readline, b"")
+ for line in lines_iterator:
+ err += str(err)
+ p = multiprocessing.Process(target=runner)
+ p.start()
+ p.join(10)
+ if p.is_alive():
+ self.assertEquals(len(err), 0)
+ p.terminate()
+ p.join()
+ os.system("killall -9 mat-gui || true")
+
+ def add_dirty_directory_contents(self):
+ """
+ Add the contents of a directory with dirty files to MAT.
+ """
+ focus.frame(self.mat_window_title)
+ self.add.click()
+ window = self.rootapp.child(roleName='file chooser', recursive=False)
+ window.child("mat").click()
+ window.child(self.tmpdir).click()
+ window.child("OK").click()
+ time.sleep(5)
+
+ def test_gui_cleans_stuff_reduced_pdf_quality(self):
+ """
+ Check whether files cleaned via the GUI are actually clean. With PDF
+ option set.
+ """
+ self.rootapp.child("Edit", roleName="menu").click()
+ self.rootapp.child("Preferences", roleName="menu item").click()
+ pref = self.rootapp.child("Preferences", roleName="dialog")
+ pref.child("Reduce PDF quality").click()
+ pref.child("OK").click()
+
+ self.add_dirty_directory_contents()
+ self.clean.click()
+ time.sleep(30)
+
+ self.quit_via_menu()
+
+ for f in self.file_list:
+ current_file = mat.create_class_file(f, False, add2archive=False)
+ self.assertTrue(current_file.is_clean())
+
+ def test_gui_cleans_stuff(self):
+ """
+ Check whether files cleaned via the GUI are actually clean.
+ """
+ self.add_dirty_directory_contents()
+ self.clean.click()
+ time.sleep(30)
+
+ self.quit_via_menu()
+
+ for f in self.file_list:
+ current_file = mat.create_class_file(f, False, add2archive=False)
+ self.assertTrue(current_file.is_clean())
+
+ def add_dirty_archive(self):
+ """
+ Add the contents of an archive wih unsupported files to MAT.
+ """
+ focus.frame(self.mat_window_title)
+ self.add.click()
+ window = self.rootapp.child(roleName='file chooser')
+ window.child("mat").click()
+ window.child(self.tmpdir).click()
+ window.child(self.tmpdir).click()
+ rawinput.typeText('test.tar.bz2')
+ time.sleep(1)
+ rawinput.pressKey('Down')
+ time.sleep(1)
+ rawinput.pressKey('Down')
+ time.sleep(1)
+ rawinput.pressKey('Return')
+ time.sleep(10)
+
+ def _test_gui_archive_with_unsupported_file(self, with_pref_set, select_in_dialog):
+ """
+ Generic runner for unsupported content archive processing.
+ """
+ self.make_unsupp_archive()
+
+ if with_pref_set:
+ self.rootapp.child("Edit", roleName="menu").click()
+ self.rootapp.child("Preferences", roleName="menu item").click()
+ pref = self.rootapp.child("Preferences", roleName="dialog")
+ pref.child("Add unsupported file to archives").click()
+ pref.child("OK").click()
+
+ self.add_dirty_archive()
+ self.clean.click()
+
+ nonsupp = self.rootapp.child("Non-supported files in archive")
+ if select_in_dialog:
+ # click-add all unsupported files in archive
+ children = nonsupp.findChildren(
+ GenericPredicate(roleName='table cell'))
+ for child in children:
+ if child.text is None:
+ # we don't want to click the filenames, just the checkboxes
+ child.click()
+ nonsupp.child("Clean", roleName="push button").click()
+
+ self.quit_via_menu()
+
+ def test_gui_archive_with_unsupported_file_select_in_dialog_pref_set(self):
+ self._test_gui_archive_with_unsupported_file(True, True)
+ current_file = mat.create_class_file(
+ self.tarpath, False, add2archive=False)
+ unsupported_files = set(current_file.is_clean(list_unsupported=True))
+ self.assertEqual(unsupported_files, set(glob.glob('*.py')))
+
+ def test_gui_archive_with_unsupported_file_select_in_dialog_pref_not_set(self):
+ self._test_gui_archive_with_unsupported_file(False, True)
+ current_file = mat.create_class_file(
+ self.tarpath, False, add2archive=False)
+ unsupported_files = set(current_file.is_clean(list_unsupported=True))
+ self.assertEqual(unsupported_files, set(glob.glob('*.py')))
+
+ def test_gui_archive_with_unsupported_file_pref_set(self):
+ self._test_gui_archive_with_unsupported_file(True, False)
+ current_file = mat.create_class_file(
+ self.tarpath, False, add2archive=False)
+ unsupported_files = set(current_file.is_clean(list_unsupported=True))
+ self.assertEqual(unsupported_files, set(glob.glob('*.py')))
+
+ def test_gui_archive_with_unsupported_file_pref_not_set(self):
+ self._test_gui_archive_with_unsupported_file(False, False)
+ current_file = mat.create_class_file(
+ self.tarpath, False, add2archive=False)
+ unsupported_files = set(current_file.is_clean(list_unsupported=True))
+ self.assertEqual(unsupported_files, set([]))
+
+ def test_gui_statusbar_updates(self):
+ """
+ Check whether the status bar gets the "Checking/Cleaning X" updates.
+ """
+
+ sw = StatusbarWatcher()
+ sw.start()
+ self.add_dirty_directory_contents()
+ sw.wait_until_last_item_is("Ready")
+ sw.stop()
+ for f in self.file_list:
+ self.assertIn(("Checking %s" % os.path.basename(f)), sw.stbm)
+ self.assertEquals(sw.stbm[-1], "Ready")
+
+ sw = StatusbarWatcher()
+ sw.start()
+ self.clean.click()
+ sw.wait_until_last_item_is("Ready")
+ sw.stop()
+ for f in self.file_list:
+ self.assertIn(("Cleaning %s" % os.path.basename(f)), sw.stbm)
+ self.assertEquals(sw.stbm[-1], "Ready")
+
+ self.quit_via_menu()
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/debian/tests/control b/debian/tests/control
index 23f794a..37196cd 100644
--- a/debian/tests/control
+++ b/debian/tests/control
@@ -1,3 +1,7 @@
Tests: test-installed
Depends: @
Restrictions: isolation-container, allow-stderr, needs-recommends
+
+Tests: gui-tests
+Depends: @, psmisc, xvfb, dbus-x11, gir1.2-wnck-3.0, libglib2.0-bin, python-dogtail
+Restrictions: isolation-container, allow-stderr, needs-recommends
diff --git a/debian/tests/gui-tests b/debian/tests/gui-tests
new file mode 100755
index 0000000..131154f
--- /dev/null
+++ b/debian/tests/gui-tests
@@ -0,0 +1,50 @@
+#!/bin/sh
+# autopkgtest check: Run GUI test suite for MAT.
+# Author: Sascha Steinbiss <sascha at steinbiss.name>
+set -e
+
+# set up test directory
+WORKDIR=$(mktemp -d)
+ORIGDIR=$(pwd)
+mkdir -p $WORKDIR/mat/dirty
+mkdir -p $WORKDIR/mat/clean
+cp -p test/dirty* $WORKDIR/mat/dirty
+cp -p test/clean* $WORKDIR/mat/clean
+cp -p test/*py $WORKDIR/mat
+
+# we don't always have a home directory, e.g. on buildds
+export XDG_CONFIG_HOME=$WORKDIR/.config
+export XDG_DATA_HOME=$WORKDIR/.local/share
+export XDG_CACHE_HOME=$WORKDIR/.cache
+
+# start Xvfb
+# note that the screen size must be that large to avoid clickable UI elements
+# going off screen, which would cause dogtail to fail with an error
+(Xvfb :5 -screen 0 1600x1200x24 -ac -noreset -v -fbdir $WORKDIR/ >/dev/null 2>&1 &)
+XVFB_PID=$!
+
+# XXX attach VNC session for local debugging only
+#x11vnc -ncache 10 -display :5 &
+#sleep 2
+#vncviewer localhost &
+
+# finish setting up X
+export DISPLAY=:5
+export XAUTHORITY=/dev/null
+
+# start local D-Bus session
+eval `dbus-launch`
+export DBUS_SESSION_BUS_ADDRESS
+
+# register clean up handler
+trap "rm -rf $WORKDIR && kill $DBUS_SESSION_BUS_PID $XVFB_PID" 0 ABRT TERM QUIT INT
+
+# enable assistive access, required for dogtail
+gsettings set org.gnome.desktop.interface toolkit-accessibility true
+
+# make sure no instance of MAT is running, as it confuses dogtail
+killall -q mat-gui || true
+
+# run test
+cd $WORKDIR/mat
+$ORIGDIR/debian/tests/check-mat
diff --git a/debian/tests/statusbar_watcher.py b/debian/tests/statusbar_watcher.py
new file mode 100644
index 0000000..56d4db6
--- /dev/null
+++ b/debian/tests/statusbar_watcher.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2016 Sascha Steinbiss <sascha at steinbiss.name>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License, version 2
+# as published by the Free Software Foundation.
+#
+# 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.
+#
+# On Debian GNU/Linux systems, the complete text of version 2 of the
+# General Public License can be found in `/usr/share/common-licenses/GPL-2'.
+
+import gi
+gi.require_version('Atspi', '2.0')
+gi.require_version('Wnck', '3.0')
+import pyatspi
+import Accessibility
+import re
+import time
+import threading
+
+
+class StatusbarWatcher(object):
+
+ def __init__(self, stbm = None):
+ self.stbm = stbm or []
+ self.running = False
+ self.t = None
+
+ def _runner():
+ def callback(event):
+ if isinstance(event.source, Accessibility.Accessible):
+ m = re.match(
+ '\[status bar \| ([^]]+)\]', str(event.source))
+ if m:
+ self.stbm.append(m.group(1))
+ self.callback = callback
+ pyatspi.Registry.registerEventListener(
+ self.callback, "object:property-change")
+ pyatspi.Registry.start()
+ self.runner = _runner
+
+ def start(self):
+ if not self.running:
+ self.t = threading.Thread(target=self.runner)
+ try:
+ self.t.start()
+ self.running = True
+ except:
+ self.stop()
+ raise
+
+ def stop(self):
+ pyatspi.Registry.stop()
+ pyatspi.Registry.deregisterEventListener(
+ self.callback, "object:property-change")
+ if self.t:
+ self.t.join()
+ self.running = False
+
+ def wait_until_last_item_is(self, item, timeout=30):
+ i = 0
+ while len(self.stbm) == 0 or self.stbm[-1] != str(item):
+ if i > timeout:
+ break
+ time.sleep(1)
+ i = i + 1
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/mat.git
More information about the Pkg-privacy-commits
mailing list