Skip to content
Commits on Source (4)
mat (0.6.1-5) UNRELEASED; urgency=medium
* Team upload.
* Add GUI testsuite to autopkgtest.
-- Sascha Steinbiss <sascha@steinbiss.name> Sat, 23 Jan 2016 17:18:44 +0000
mat (0.6.1-4) unstable; urgency=medium
* New patch (Make-the-Nautilus-extension-work-again.patch) cherry-picked
......
......@@ -9,6 +9,10 @@ Files: debian/*
Copyright: © 2011-2015 intrigeri <intrigeri@debian.org>
License: GPL-2
Files: debian/tests/*
Copyright: © 2016 Sascha Steinbiss <sascha@steinbiss.name>
License: GPL-2
Files: libmat/bencode/*
Copyright: © 2007 Petru Paler <petru@paler.net>
© 2011 Julien Voisin <julien.voisin@dustri.org>
......
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Sascha Steinbiss <sascha@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")
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, wait=True):
"""
Add the contents of a directory with dirty files to MAT.
"""
self.add.click()
window = self.rootapp.child(roleName='file chooser', recursive=False)
window.child("mat").click()
window.child(self.tmpdir).click()
if wait:
sw = StatusbarWatcher()
sw.start()
window.child("OK").click()
if wait:
sw.wait_until_last_item_is("Ready")
sw.stop()
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()
sw = StatusbarWatcher()
sw.start()
self.clean.click()
sw.wait_until_last_item_is("Ready")
sw.stop()
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()
sw = StatusbarWatcher()
sw.start()
self.clean.click()
sw.wait_until_last_item_is("Ready")
sw.stop()
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 with unsupported files to MAT.
"""
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)
sw = StatusbarWatcher()
sw.start()
rawinput.pressKey('Return')
sw.wait_until_last_item_is("Ready")
sw.stop()
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(False)
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()
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
#!/bin/sh
# autopkgtest check: Run GUI test suite for MAT.
# Author: Sascha Steinbiss <sascha@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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Sascha Steinbiss <sascha@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