#!/usr/bin/python
# test DistUtilsExtra.auto
import unittest
import shutil
import tempfile
import os
import subprocess
class T(unittest.TestCase):
def setUp(self):
self.maxDiff = None
self.src = tempfile.mkdtemp()
self._mksrc('setup.py', '''
# ignore warning about import from local path
import warnings
warnings.filterwarnings('ignore', 'Module DistUtilsExtra was already imported from.*')
warnings.filterwarnings('ignore', 'pipe2 set errno ENOSYS.*')
from DistUtilsExtra.auto import setup
setup(
name='foo',
version='0.1',
description='Test suite package',
url='https://foo.example.com',
license='GPL v2 or later',
author='Martin Pitt',
author_email='martin.pitt@example.com',
)
''')
self.snapshot = None
self.install_tree = None
def tearDown(self):
try:
# check that setup.py clean removes everything
(o, e, s) = self.setup_py(['clean', '-a'])
self.assertEqual(s, 0, o+e)
cruft = self.diff_snapshot()
self.assertEqual(cruft, '', 'no cruft after cleaning:\n' + cruft)
finally:
shutil.rmtree(self.src)
if self.snapshot:
shutil.rmtree(self.snapshot)
if self.install_tree:
shutil.rmtree(self.install_tree)
self.src = None
self.snapshot = None
self.install_tree = None
#
# actual tests come here
#
def test_empty(self):
'''empty source tree (just setup.py)'''
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertNotIn('following files are not recognized', o)
f = self.installed_files()
# just installs the .egg_info
self.assertEqual(len(f), 1)
self.assertTrue(f[0].endswith('.egg-info'))
def test_vcs(self):
'''Ignores revision control files'''
self._mksrc('.shelf/1')
self._mksrc('.bzr/revs')
self._mksrc('.git/config')
self._mksrc('.svn/revs')
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertNotIn('following files are not recognized', o)
f = self.installed_files()
# just installs the .egg_info
self.assertEqual(len(f), 1)
self.assertTrue(f[0].endswith('.egg-info'))
def test_modules(self):
'''Python modules'''
self._mksrc('yesme.py', b'x ="a\xc3\xa4b\xe2\x99\xa5"'.decode('UTF-8'))
self._mksrc('stuff/notme.py', b'x ="a\xc3\xa4b\xe2\x99\xa5"'.decode('UTF-8'))
self._mksrc('stuff/withencoding.py', b'# -*- Mode: Python; coding: utf-8; -*- \nfoo = 1'.decode('UTF-8'))
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertIn('following files are not recognized', o)
self.assertIn('\n stuff/notme.py\n', o)
f = '\n'.join(self.installed_files())
self.assertIn('-packages/yesme.py', f)
self.assertNotIn('notme', f)
def test_packages(self):
'''Python packages'''
self._mksrc('foopkg/__init__.py', '')
self._mksrc('foopkg/bar.py')
self._mksrc('foopkg/baz.py')
self._mksrc('noinit/notme.py')
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertIn('following files are not recognized', o)
self.assertIn('\n noinit/notme.py\n', o)
f = '\n'.join(self.installed_files())
self.assertIn('foopkg/__init__.py', f)
self.assertIn('foopkg/bar.py', f)
self.assertNotIn('noinit', f)
def test_dbus(self):
'''D-BUS configuration and service files'''
# D-BUS ACL configuration file
self._mksrc('daemon/com.example.foo.conf', '''
''')
# non-D-BUS configuration file
self._mksrc('daemon/defaults.conf', 'start = True\nlog = syslog')
# D-BUS system service
self._mksrc('daemon/com.example.foo.service', '''[D-BUS Service]
Name=com.example.Foo
Exec=/usr/lib/foo/foo_daemon
User=root''')
# D-BUS session service
self._mksrc('gui/com.example.foo.gui.service', '''[D-BUS Service]
Name=com.example.Foo.GUI
Exec=/usr/bin/foo-gtk
''')
# non-D-BUS .service file
self._mksrc('stuff/super.service', 'I am a file')
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertIn('following files are not recognized', o)
self.assertIn('\n stuff/super.service\n', o)
f = self.installed_files()
self.assertEqual(len(f), 4) # 3 D-BUS files plus .egg-info
self.assertIn('/etc/dbus-1/system.d/com.example.foo.conf', f)
self.assertIn('/usr/share/dbus-1/system-services/com.example.foo.service', f)
self.assertIn('/usr/share/dbus-1/services/com.example.foo.gui.service', f)
self.assertNotIn('super.service', '\n'.join(f))
def test_gsettings(self):
'''GSettings schema files'''
# schema files in dedicated directory
self._mksrc('data/glib-2.0/schemas/org.test.myapp.gschema.xml')
self._mksrc('data/glib-2.0/schemas/gschemas.compiled')
# schema files in data directory
self._mksrc('data/org.test.myapp2.gschema.xml')
self._mksrc('data/gschemas.compiled')
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
f = self.installed_files()
self.assertEqual(len(f), 3) # 2 schema files plus .egg-info
self.assertIn('/usr/share/glib-2.0/schemas/org.test.myapp.gschema.xml', f)
self.assertNotIn('gschemas.compiled', '\n'.join(f))
def test_apport_hook(self):
'''Apport hooks'''
self._mksrc('apport/foo.py', '''import os
def add_info(report):
pass
''')
self._mksrc('apport/source_foo.py', '''import os
def add_info(report):
pass
''')
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertNotIn('following files are not recognized', o)
f = self.installed_files()
self.assertEqual(len(f), 3, f) # 2 hook files plus .egg-info
self.assertIn('/usr/share/apport/package-hooks/foo.py', f)
self.assertIn('/usr/share/apport/package-hooks/source_foo.py', f)
def test_po(self):
'''gettext *.po files'''
self._mkpo()
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertNotIn('following files are not recognized', o)
f = self.installed_files()
self.assertIn('/usr/share/locale/de/LC_MESSAGES/foo.mo', f)
self.assertIn('/usr/share/locale/fr/LC_MESSAGES/foo.mo', f)
self.assertNotIn('junk', '\n'.join(f))
msgunfmt = subprocess.Popen(['msgunfmt',
os.path.join(self.install_tree,
'usr/share/locale/de/LC_MESSAGES/foo.mo')],
stdout=subprocess.PIPE)
out = msgunfmt.communicate()[0].decode()
self.assertEqual(out, self._src_contents('po/de.po'))
def test_policykit(self):
'''*.policy.in PolicyKit files'''
self._mksrc('daemon/com.example.foo.policy.in', '''
Foo project
https://foo.example.com
<_description>Good morning
<_message>Hello
yes
''')
self._mkpo()
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertNotIn('following files are not recognized', o)
f = self.installed_files()
self.assertIn('/usr/share/polkit-1/actions/com.example.foo.policy', f)
p = self._installed_contents('usr/share/polkit-1/actions/com.example.foo.policy')
self.assertIn('Good morning', p)
self.assertIn('Guten Morgen', p)
self.assertIn('Hello', p)
self.assertIn('Hallo', p)
def test_desktop(self):
'''*.desktop.in files'''
self._mksrc('gui/foogtk.desktop.in', '''[Desktop Entry]
_Name=Hello
_Comment=Good morning
Exec=/bin/foo''')
self._mksrc('gui/autostart/fooapplet.desktop.in', '''[Desktop Entry]
_Name=Hello
_Comment=Good morning
Exec=/usr/bin/fooapplet''')
self._mkpo()
self._mksrc('data/foosettings.desktop.in', '''[Desktop Entry]
_Name=Hello
_Comment=Good morning
Exec=/bin/foosettings''')
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertNotIn('following files are not recognized', o)
f = self.installed_files()
self.assertIn('/usr/share/autostart/fooapplet.desktop', f)
self.assertIn('/usr/share/applications/foogtk.desktop', f)
self.assertIn('/usr/share/applications/foosettings.desktop', f)
# data/*.desktop.in shouldn't go to data dir
self.assertNotIn('/usr/share/foo/', f)
p = self._installed_contents('usr/share/autostart/fooapplet.desktop')
self.assertIn('\nName=Hello\n', p)
self.assertIn('\nName[de]=Hallo\n', p)
self.assertIn('\nComment[fr]=Bonjour\n', p)
def test_icons(self):
'''data/icons/'''
self._mksrc('data/icons/scalable/actions/press.png')
self._mksrc('data/icons/48x48/apps/foo.png')
scalable_icon_path = os.path.join(self.src, 'data', 'icons', 'scalable')
os.symlink(os.path.join(scalable_icon_path, 'actions', 'press.png'),
os.path.join(scalable_icon_path, 'actions', 'crunch.png'))
# test broken symlink, too
os.mkdir(os.path.join(scalable_icon_path, 'mimetypes'))
os.symlink('../apps/foo.svg',
os.path.join(scalable_icon_path, 'mimetypes', 'text-x-foo.svg'))
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertNotIn('following files are not recognized', o)
f = self.installed_files()
self.assertIn('/usr/share/icons/hicolor/scalable/actions/press.png', f)
self.assertIn('/usr/share/icons/hicolor/scalable/actions/crunch.png', f)
self.assertIn('/usr/share/icons/hicolor/48x48/apps/foo.png', f)
self.assertTrue(os.path.islink(os.path.join(self.install_tree,
'usr/share/icons/hicolor/scalable/actions/crunch.png')))
self.assertTrue(os.path.islink(os.path.join(self.install_tree,
'usr/share/icons/hicolor/scalable/mimetypes/text-x-foo.svg')))
def test_data(self):
'''Auxiliary files in data/'''
# have some explicitly covered files, to check that they don't get
# installed into prefix/share/foo/ again
self._mksrc('setup.py', '''
from DistUtilsExtra.auto import setup
from glob import glob
setup(
name='foo',
version='0.1',
description='Test suite package',
url='https://foo.example.com',
license='GPL v2 or later',
author='Martin Pitt',
author_email='martin.pitt@example.com',
data_files = [
('/lib/udev/rules.d', ['data/40-foo.rules']),
('/etc/foo', glob('data/*.conf')),
]
)
''')
self._mksrc('data/stuff')
self._mksrc('data/handlers/red.py', 'import sys\nprint ("RED")')
self._mksrc('data/handlers/blue.py', 'import sys\nprint ("BLUE")')
self._mksrc('data/40-foo.rules')
self._mksrc('data/blob1.conf')
self._mksrc('data/blob2.conf')
os.symlink('stuff', os.path.join(self.src, 'data', 'stufflink'))
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertNotIn('following files are not recognized', o)
f = self.installed_files()
self.assertIn('/usr/share/foo/stuff', f)
self.assertIn('/usr/share/foo/stufflink', f)
self.assertTrue(os.path.islink(os.path.join(self.install_tree, 'usr',
'share', 'foo', 'stufflink')))
self.assertIn('/usr/share/foo/handlers/red.py', f)
self.assertIn('/usr/share/foo/handlers/blue.py', f)
self.assertIn('/lib/udev/rules.d/40-foo.rules', f)
self.assertIn('/etc/foo/blob1.conf', f)
self.assertIn('/etc/foo/blob2.conf', f)
self.assertNotIn('/usr/share/foo/blob1.conf', f)
self.assertNotIn('/usr/share/foo/40-foo.rules', f)
def test_scripts(self):
'''scripts'''
# these should get autoinstalled
self._mksrc('bin/yell', '#!/bin/sh', True)
self._mksrc('bin/shout', '#!/bin/sh', True)
self._mksrc('bin/foo', b'#!/usr/bin/python\n# \xc2\xa9 copyright'.decode('UTF-8'), True)
os.symlink('shout', os.path.join(self.src, 'bin', 'shoutlink'))
# these shouldn't
self._mksrc('daemon/food', '#!/bin/sh', True) # not in bin/
self._mksrc('foob', '#!/bin/sh', True) # not named like project
# not executable
self._mksrc('bin/whisper', b'#!/usr/bin/python\n# \xc2\xa9 copyright'.decode('UTF-8'))
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertIn('following files are not recognized', o)
self.assertIn('\n foob', o)
self.assertIn('\n bin/whisper', o)
self.assertIn('\n daemon/food', o)
f = self.installed_files()
self.assertIn('/usr/bin/yell', f)
self.assertIn('/usr/bin/shout', f)
self.assertIn('/usr/bin/shoutlink', f)
self.assertTrue(os.path.islink(os.path.join(self.install_tree, 'usr',
'bin', 'shoutlink')))
self.assertIn('/usr/bin/foo', f)
ftext = '\n'.join(f)
self.assertNotIn('food', ftext)
self.assertNotIn('foob', ftext)
self.assertNotIn('whisper', ftext)
# verify that they are executable
binpath = os.path.join(self.install_tree, 'usr', 'bin')
self.assertTrue(os.access(os.path.join(binpath, 'yell'), os.X_OK))
self.assertTrue(os.access(os.path.join(binpath, 'shout'), os.X_OK))
self.assertTrue(os.access(os.path.join(binpath, 'foo'), os.X_OK))
def test_pot_manual(self):
'''PO template creation with manual POTFILES.in'''
self._mk_i18n_source()
self._mksrc('po/foo.pot', '')
# only do a subset here
self._mksrc('po/POTFILES.in', '''
gtk/main.py
gui/foo.desktop.in
[type: gettext/glade]gtk/test.ui''')
(o, e, s) = self.setup_py(['build'])
self.assertEqual(e, '')
self.assertEqual(s, 0)
# POT file should not be shown as not recognized
self.assertNotIn('\n po/foo.pot\n', o)
pot = self._src_contents('po/foo.pot')
self.assertNotIn('msgid "no"', pot)
self.assertIn('msgid "yes1"', pot)
self.assertIn('msgid "yes2 %s"', pot)
self.assertNotIn('msgid "yes5"', pot) # we didn't add helpers.py
self.assertIn('msgid "yes7"', pot) # we did include the desktop file
self.assertNotIn('msgid "yes5"', pot) # we didn't add helpers.py
self.assertIn('msgid "yes11"', pot) # we added one GTKBuilder file
self.assertNotIn('msgid "yes12"', pot) # ... but not the other
def test_pot_auto(self):
'''PO template creation with automatic POTFILES.in'''
self._mk_i18n_source()
(o, e, s) = self.setup_py(['build'])
self.assertEqual(e, '')
self.assertEqual(s, 0)
# POT file should not be shown as not recognized
self.assertNotIn('\n po/foo.pot\n', o)
pot = self._src_contents('po/foo.pot')
self.assertNotIn('msgid "no"', pot)
for i in range(2, 15):
self.assertTrue('msgid "yes%i' % i in pot or
'msgid ""\n"yes%i' % i in pot,
'yes%i' % i)
# above loop would match yes11 to yes1 as well, so test it explicitly
self.assertIn('msgid "yes1"', pot)
def test_pot_auto_explicit(self):
'''PO template creation with automatic POTFILES.in and explicit scripts'''
self._mk_i18n_source()
# add some additional binaries here which aren't caught by default
self._mksrc('cli/client-cli', "#!/usr/bin/python\nprint (_('yes15'))", True)
self._mksrc('gtk/client-gtk', '#!/usr/bin/python\nprint (_("yes16"))', True)
# this is the most tricky case: intltool doesn't consider them Python
# files by default and thus just looks for _(""):
self._mksrc('kde/client-kde', "#!/usr/bin/python\nprint (_('yes17'))", True)
self._mksrc('po/POTFILES.in.in', 'gtk/client-gtk\nkde/client-kde')
self._mksrc('setup.py', '''
from DistUtilsExtra.auto import setup
import warnings
warnings.filterwarnings('ignore', 'pipe2 set errno ENOSYS.*')
setup(
name='foo',
version='0.1',
data_files=[('share/foo', ['gtk/client-gtk', 'kde/client-kde'])],
scripts=['cli/client-cli'],
)
''')
(o, e, s) = self.setup_py(['build'])
self.assertEqual(e, '')
self.assertEqual(s, 0)
# POT file should not be shown as not recognized
self.assertNotIn('\n po/foo.pot\n', o)
pot = self._src_contents('po/foo.pot')
self.assertNotIn('msgid "no"', pot)
for i in range(2, 18):
self.assertTrue('msgid "yes%i' % i in pot or
'msgid ""\n"yes%i' % i in pot,
'yes%i' % i)
# above loop would match yes11 to yes1 as well, so test it explicitly
self.assertIn('msgid "yes1"', pot)
def test_standard_files(self):
'''Standard files (MANIFEST.in, COPYING, etc.)'''
self._mksrc('AUTHORS')
self._mksrc('COPYING')
self._mksrc('LICENSE')
self._mksrc('COPYING.LIB')
self._mksrc('README.txt')
self._mksrc('MANIFEST.in')
self._mksrc('MANIFEST')
self._mksrc('NEWS')
self._mksrc('TODO')
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertNotIn('following files are not recognized', o)
f = self.installed_files()
self.assertIn('/usr/share/doc/foo/README.txt', f)
self.assertIn('/usr/share/doc/foo/NEWS', f)
ftext = '\n'.join(f)
self.assertNotIn('MANIFEST', ftext)
self.assertNotIn('COPYING', ftext)
self.assertNotIn('COPYING', ftext)
self.assertNotIn('AUTHORS', ftext)
self.assertNotIn('TODO', ftext)
# sub-dir READMEs shouldn't be installed by default
self.snapshot = None
self._mksrc('extra/README')
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertIn('following files are not recognized', o)
self.assertIn('\n extra/README\n', o)
def test_sdist(self):
'''default MANIFEST'''
good = ['AUTHORS', 'README.txt', 'COPYING', 'helpers.py',
'foo/__init__.py', 'foo/bar.py', 'tests/all.py',
'gui/x.desktop.in', 'backend/foo.policy.in',
'daemon/backend.conf', 'x/y', 'po/de.po', 'po/foo.pot',
'.quickly', 'data/icons/16x16/apps/foo.png', 'bin/foo',
'backend/food', 'backend/com.example.foo.service',
'gtk/main.glade', 'dist/extra.tar.gz']
bad = ['po/de.mo', '.helpers.py.swp', '.bzr/index', '.svn/index',
'.git/index', 'bin/foo~', 'backend/foo.pyc',
'dist/foo-0.1.tar.gz', '.shelf/1', '.bzr/revs', '.git/config']
for f in good + bad:
self._mksrc(f)
(o, e, s) = self.setup_py(['sdist', '-o'])
self.assertIn("'MANIFEST.in' does not exist", e)
self.assertEqual(s, 0)
manifest = self._src_contents('MANIFEST').splitlines()
for f in good:
self.assertIn(f, manifest)
for f in bad:
self.assertNotIn(f, manifest)
os.unlink(os.path.join(self.src, 'MANIFEST'))
def test_ui(self):
'''GtkBuilder/Qt *.ui'''
self._mksrc('gtk/test.ui', b'''
'''.decode('UTF-8'))
self._mksrc('gtk/settings.ui', '''
''')
self._mksrc('kde/mainwindow.ui', '''
CrashDialog
''')
self._mksrc('someweird.ui')
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertIn('following files are not recognized', o)
self.assertIn('\n someweird.ui\n', o)
f = self.installed_files()
self.assertIn('/usr/share/foo/test.ui', f)
self.assertIn('/usr/share/foo/settings.ui', f)
self.assertIn('/usr/share/foo/mainwindow.ui', f)
ftext = '\n'.join(f)
self.assertNotIn('someweird', ftext)
def test_manpages(self):
'''manpages'''
self._mksrc('man/foo.1', '.TH foo 1 "Jan 01, 1900" "Joe Developer"')
self._mksrc('daemon/food.8', '.\" some comment\n.TH food 8 "Jan 01, 1900" "Joe Developer"')
self._mksrc('cruft/food.1', '')
self._mksrc('daemon/notme.s', '.TH food 8 "Jan 01, 1900" "Joe Developer"')
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertIn('following files are not recognized', o)
self.assertIn('\n cruft/food.1\n', o)
self.assertIn('\n daemon/notme.s\n', o)
f = self.installed_files()
self.assertIn('/usr/share/man/man1/foo.1', f)
self.assertIn('/usr/share/man/man8/food.8', f)
ftext = '\n'.join(f)
self.assertNotIn('food.1', ftext)
self.assertNotIn('notme', ftext)
def test_etc(self):
'''etc/*'''
self._mksrc('etc/cron.daily/foo')
self._mksrc('etc/foo.conf')
self._mksrc('etc/init.d/foo', executable=True)
d = os.path.join(self.src, 'etc', 'cron.weekly')
os.mkdir(d)
os.symlink(os.path.join('..', 'cron.daily', 'foo'),
os.path.join(d, 'foo'))
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertNotIn('following files are not recognized', o)
f = self.installed_files()
self.assertIn('/etc/cron.daily/foo', f)
self.assertIn('/etc/cron.weekly/foo', f)
self.assertIn('/etc/init.d/foo', f)
self.assertIn('/etc/foo.conf', f)
# verify that init script is executable
self.assertTrue(os.access(os.path.join(self.install_tree, 'etc', 'init.d',
'foo'), os.X_OK))
# verify that symlinks get preserved
self.assertTrue(os.path.islink(os.path.join(self.install_tree, 'etc',
'cron.weekly', 'foo')))
# check that we can install again into the same source tree
(o, e, s) = self.setup_py(['install', '--no-compile', '--prefix=/usr',
'--root=' + self.install_tree])
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertNotIn('following files are not recognized', o)
def test_requires_provides(self):
'''automatic requires/provides'''
for needed_pkg in ['pkg_resources','httplib2','gi.repository.GLib']:
try:
__import__(needed_pkg)
except ImportError:
self.fail('You need to have %s installed for this test suite to work' % needed_pkg)
self._mksrc('foo/__init__.py', '')
self._mksrc('foo/stuff.py', '''import xml.parsers.expat
import os, os.path, email.mime, distutils.command.register
from email import header as h
import httplib2.iri2uri, unknown
from . bar import poke
from bar.poke import x
import grab_cli
import broken
''')
self._mksrc('foo/bar/__init__.py', '')
self._mksrc('foo/bar/poke.py', 'from . import broken\ndef x(): pass')
self._mksrc('foo/bar/broken.py', 'raise RuntimeError("cannot initialize system")')
self._mksrc('mymod.py', 'import foo\nfrom foo.bar.poke import x')
# trying to import this will cause setup.py to not process any args any more
self._mksrc('grab_cli.py', 'from optparse import OptionParser\nOptionParser().parse_args()')
# trying to import this will break setup.py
self._mksrc('broken.py', 'raise SystemError("cannot initialize system")')
self._mksrc('pygi.py', 'from gi.repository import GLib\nimport gi.repository.GObject')
self._mksrc('bin/foo-cli', '''#!/usr/bin/python
import sys
import pkg_resources
import foo.bar
from httplib2 import iri2uri
print ('import iamnota.module')
''', executable=True)
# this shouldn't be treated specially
self._mksrc('data/example-code/template.py', 'import example.module')
self._mksrc('data/example-code/mymod/__init__.py', '')
self._mksrc('data/example-code/mymod/shiny.py', 'import example.othermod')
(o, e, s) = self.do_install()
self.assertEqual(s, 0, e)
self.assertEqual(e, 'ERROR: Python module unknown not found\n')
self.assertNotIn('following files are not recognized', o)
inst = self.installed_files()
self.assertIn('/usr/share/foo/example-code/template.py', inst)
self.assertIn('/usr/share/foo/example-code/mymod/shiny.py', inst)
for f in inst:
if 'template.py' in f or 'shiny' in f:
self.assertNotIn('packages', f)
# parse .egg-info
(o, e, s) = self.setup_py(['install_egg_info', '-d', self.install_tree])
self.assertEqual(e, 'ERROR: Python module unknown not found\n')
egg_paths = [x for x in inst if x.endswith('.egg-info')]
self.assertEqual(len(egg_paths), 1)
egg = self._installed_contents(egg_paths[0].strip(os.path.sep)).splitlines()
self.assertIn('Name: foo', egg)
# check provides
prov = [prop.split(' ', 1)[1] for prop in egg if prop.startswith('Provides: ')]
self.assertEqual(set(prov), set(['foo', 'mymod', 'broken', 'grab_cli', 'pygi']))
# check requires
req = [prop.split(' ', 1)[1] for prop in egg if prop.startswith('Requires: ')]
self.assertEqual(set(req), set(['httplib2', 'pkg_resources',
'gi.repository.GLib', 'gi.repository.GObject']))
def test_help_docbook(self):
'''Docbook XML help'''
self._mksrc('help/C/index.docbook')
self._mksrc('help/C/legal.xml')
self._mksrc('help/C/figures/mainscreen.png')
self._mksrc('help/de/index.docbook')
self._mksrc('help/de/legal.xml')
self._mksrc('help/de/figures/mainscreen.png')
self._mksrc('help/weird.xml')
self._mksrc('help/notme.png')
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertIn('following files are not recognized', o)
self.assertIn('\n help/weird.xml\n', o)
self.assertIn('\n help/notme.png\n', o)
f = self.installed_files()
self.assertIn('/usr/share/help/C/foo/index.docbook', f)
self.assertIn('/usr/share/help/C/foo/legal.xml', f)
self.assertIn('/usr/share/help/C/foo/figures/mainscreen.png', f)
self.assertIn('/usr/share/help/de/foo/index.docbook', f)
self.assertIn('/usr/share/help/de/foo/legal.xml', f)
self.assertIn('/usr/share/help/de/foo/figures/mainscreen.png', f)
def test_help_mallard(self):
'''Mallard XML help'''
self._mksrc('help/C/index.page')
self._mksrc('help/C/legal.page')
self._mksrc('help/C/figures/mainscreen.png')
self._mksrc('help/de/index.page')
self._mksrc('help/de/legal.page')
self._mksrc('help/de/figures/mainscreen.png')
self._mksrc('help/weird.page')
self._mksrc('help/notme.png')
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertIn('following files are not recognized', o)
self.assertIn('\n help/weird.page\n', o)
self.assertIn('\n help/notme.png\n', o)
f = self.installed_files()
self.assertIn('/usr/share/help/C/foo/index.page', f)
self.assertIn('/usr/share/help/C/foo/legal.page', f)
self.assertIn('/usr/share/help/C/foo/figures/mainscreen.png', f)
self.assertIn('/usr/share/help/de/foo/index.page', f)
self.assertIn('/usr/share/help/de/foo/legal.page', f)
self.assertIn('/usr/share/help/de/foo/figures/mainscreen.png', f)
def test_binary_files(self):
'''Binary files are ignored'''
with open(os.path.join(self.src, 'binary_trap'), 'wb') as f:
f.write(b'\x00\x01abc\xFF\xFE')
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
self.assertIn('following files are not recognized', o)
self.assertIn('\n binary_trap\n', o)
f = self.installed_files()
self.assertEqual(len(f), 1, f)
self.assertIn('egg-info', f[0])
def test_utf8_filenames(self):
'''UTF-8 file names'''
bin_fname = b'a\xc3\xa4b.bin'.decode('UTF-8')
with open(os.path.join(self.src, bin_fname).encode('UTF-8'), 'wb') as f:
f.write(b'\x00\x01abc\xFF\xFE')
(o, e, s) = self.do_install()
self.assertEqual(e, '')
self.assertEqual(s, 0)
f = self.installed_files()
self.assertEqual(len(f), 1, f)
self.assertIn('egg-info', f[0])
self.assertIn('following files are not recognized', o)
# this might not be the correct file name when the locale is e. g. C
self.assertIn('b.bin\n', o)
#
# helper methods
#
def setup_py(self, args):
'''Run setup.py with given arguments.
For convenience, this snapshots the tree if no snapshot exists yet.
Return (out, err, exitcode) triple.
'''
if not self.snapshot:
self.do_snapshot()
env = os.environ.copy()
oldcwd = os.getcwd()
if 'PYTHONPATH' in env:
env['PYTHONPATH'] = oldcwd + os.pathsep + env['PYTHONPATH']
else:
env['PYTHONPATH'] = oldcwd
# unset envvars that alter results
env.pop('LINGUAS', '')
env.pop('PYTHONDONTWRITEBYTECODE', '')
os.chdir(self.src)
s = subprocess.Popen(['/proc/self/exe', 'setup.py'] + args, env=env,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(out, err) = s.communicate()
out = out.decode()
err = err.decode()
os.chdir(oldcwd)
return (out, err, s.returncode)
def do_install(self):
'''Run setup.py install into temporary tree.
Return (out, err, exitcode) triple.
'''
self.install_tree = tempfile.mkdtemp()
self.setup_py(['build'])
return self.setup_py(['install', '--no-compile', '--skip-build',
'--prefix=/usr', '--root=' + self.install_tree])
def installed_files(self):
'''Return list of file paths in install tree.'''
result = []
for root, _, files in os.walk(self.install_tree):
assert root.startswith(self.install_tree)
r = root[len(self.install_tree):]
for f in files:
result.append(os.path.join(r, f))
return result
def _mksrc(self, path, content=None, executable=False):
'''Create a file in the test source tree.'''
path = os.path.join(self.src, path)
dir = os.path.dirname(path)
if not os.path.isdir(dir):
os.makedirs(dir)
with open(path, 'wb') as f:
if content is None:
# default content, to spot with diff
f.write(b'dummy')
else:
f.write((content + '\n').encode('UTF-8'))
if executable:
os.chmod(path, 0o755)
def do_snapshot(self):
'''Snapshot source tree.
This should be called after a test set up all source files.
'''
assert self.snapshot is None, 'snapshot already taken'
self.snapshot = tempfile.mkdtemp()
shutil.copytree(self.src, os.path.join(self.snapshot, 's'), symlinks=True)
def diff_snapshot(self):
'''Compare source tree to snapshot.
Return diff -Nur output.
'''
assert self.snapshot, 'no snapshot taken'
diff = subprocess.Popen(['diff', '-x', 'foo.pot', '-x', '*.pyc',
'-Nur', os.path.join(self.snapshot, 's'), self.src],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(out, err) = diff.communicate()
out = out.decode('UTF-8')
return out
def _mkpo(self):
'''Create some example po files.'''
self._mksrc('po/POTFILES.in', '')
self._mksrc('po/de.po', '''msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8\\n"
msgid "Good morning"
msgstr "Guten Morgen"
msgid "Hello"
msgstr "Hallo"''')
self._mksrc('po/fr.po', '''msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8\\n"
msgid "Good morning"
msgstr "Bonjour"''')
def _mk_i18n_source(self):
'''Create some example source files with gettext calls'''
self._mksrc('gtk/main.py', '''print (_("yes1"))
print ("no1")
print (__("no2"))
x = _('yes2 %s') % y
def f():
print (_("yes3"))
return _('yes6')''')
self._mksrc('helpers.py', '''
print (f(_("yes4")))
print (_(\'\'\'yes5
even more
lines\'\'\'))
print (_("""yes6
more lines"""))
print (\'\'\'no3
boo\'\'\')
print ("""no4
more""")''')
self._mksrc('gui/foo.desktop.in', '''[Desktop Entry]
_Name=yes7
_Comment=yes8
Icon=no5
Exec=/usr/bin/foo''')
self._mksrc('daemon/com.example.foo.policy.in', '''
<_description>yes9
<_message>yes10
no6
''')
self._mksrc('gtk/test.ui', '''
''')
self._mksrc('data/settings.ui', '''
''')
self._mksrc('Makefile', 'echo _("no7")')
# Executables without *.py extension
self._mksrc('gtk/foo-gtk', '#!/usr/bin/python\nprint (_("yes13"))',
executable=True)
self._mksrc('cli/foo-cli', '#!/usr/bin/env python\nprint (_(\'yes14\'))',
executable=True)
self._mksrc('daemon/foobarize', '#!/usr/bin/flex\np _("no8")',
executable=True)
def _src_contents(self, path):
f = open(os.path.join(self.src, path))
contents = f.read()
f.close()
return contents
def _installed_contents(self, path):
f = open(os.path.join(self.install_tree, path))
contents = f.read()
f.close()
return contents
if __name__ == '__main__':
unittest.main()