~barry/ubuntu/natty/python-distutils-extra/670188-ftbfs

« back to all changes in this revision

Viewing changes to test/auto.py

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2009-07-01 16:39:54 UTC
  • mfrom: (1.1.13 karmic)
  • Revision ID: james.westby@ubuntu.com-20090701163954-3ewvhmu8l9oci2w9
Tags: 2.3
* auto.py: Fix recognition of GtkBuilder *.ui files as glade-3 writes them.
* auto.py: Add automatic calculation of "requires" unless explicitly given.
* auto.py: Add automatic calculation of "provides" unless explicitly given.
* Drop test/testBzrBuild.py, it's specific to Sebastian's computer.
* setup.py: Drop nose.collector, we don't use it.
* Add debian/local/python-mkdebian: Create/update debian packaging
  (debian/*) from python egg-info data. Not terribly pretty, but working and
  reasonably policy compliant.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
 
 
3
# test DistUtilsExtra.auto
 
4
 
 
5
import sys, unittest, shutil, tempfile, os, os.path, subprocess
 
6
 
 
7
class T(unittest.TestCase):
 
8
    def setUp(self):
 
9
        self.src = tempfile.mkdtemp()
 
10
 
 
11
        self._mksrc('setup.py', '''
 
12
from DistUtilsExtra.auto import setup
 
13
 
 
14
setup(
 
15
    name='foo',
 
16
    version='0.1',
 
17
    description='Test suite package',
 
18
    url='https://foo.example.com',
 
19
    license='GPL v2 or later',
 
20
    author='Martin Pitt',
 
21
    author_email='martin.pitt@example.com',
 
22
)
 
23
''')
 
24
        self.snapshot = None
 
25
        self.install_tree = None
 
26
 
 
27
    def tearDown(self):
 
28
        try:
 
29
            # check that setup.py clean removes everything
 
30
            (o, e, s) = self.setup_py(['clean', '-a'])
 
31
            self.assertEqual(e, '')
 
32
            self.assertEqual(s, 0)
 
33
            cruft = self.diff_snapshot()
 
34
            self.assertEqual(cruft, '', 'no cruft after cleaning:\n' + cruft)
 
35
        finally:
 
36
            shutil.rmtree(self.src)
 
37
            if self.snapshot:
 
38
                shutil.rmtree(self.snapshot)
 
39
            if self.install_tree:
 
40
                shutil.rmtree(self.install_tree)
 
41
            self.src = None
 
42
            self.snapshot = None
 
43
            self.install_tree = None
 
44
 
 
45
    #
 
46
    # actual tests come here
 
47
    #
 
48
 
 
49
    def test_empty(self):
 
50
        '''empty source tree (just setup.py)'''
 
51
 
 
52
        (o, e, s) = self.do_install()
 
53
        self.assertEqual(e, '')
 
54
        self.assertEqual(s, 0)
 
55
        self.failIf('following files are not recognized' in o)
 
56
 
 
57
        f = self.installed_files()
 
58
        # just installs the .egg_info
 
59
        self.assertEqual(len(f), 1)
 
60
        self.assert_(f[0].endswith('.egg-info'))
 
61
 
 
62
    def test_modules(self):
 
63
        '''Python modules'''
 
64
 
 
65
        self._mksrc('yesme.py')
 
66
        self._mksrc('stuff/notme.py')
 
67
 
 
68
        (o, e, s) = self.do_install()
 
69
        self.assertEqual(e, '')
 
70
        self.assertEqual(s, 0)
 
71
        self.assert_('following files are not recognized' in o)
 
72
        self.assert_('\n  stuff/notme.py\n' in o)
 
73
 
 
74
        f = '\n'.join(self.installed_files())
 
75
        self.assert_('-packages/yesme.py' in f)
 
76
        self.failIf('notme' in f)
 
77
 
 
78
    def test_packages(self):
 
79
        '''Python packages'''
 
80
 
 
81
        self._mksrc('foopkg/__init__.py', '')
 
82
        self._mksrc('foopkg/bar.py')
 
83
        self._mksrc('foopkg/baz.py')
 
84
        self._mksrc('noinit/notme.py')
 
85
 
 
86
        (o, e, s) = self.do_install()
 
87
        self.assertEqual(e, '')
 
88
        self.assertEqual(s, 0)
 
89
        self.assert_('following files are not recognized' in o)
 
90
        self.assert_('\n  noinit/notme.py\n' in o)
 
91
 
 
92
        f = '\n'.join(self.installed_files())
 
93
        self.assert_('foopkg/__init__.py' in f)
 
94
        self.assert_('foopkg/bar.py' in f)
 
95
        self.failIf('noinit' in f)
 
96
 
 
97
    def test_dbus(self):
 
98
        '''D-BUS configuration and service files'''
 
99
 
 
100
        # D-BUS ACL configuration file
 
101
        self._mksrc('daemon/com.example.foo.conf', '''<!DOCTYPE busconfig PUBLIC
 
102
 "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
 
103
 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
 
104
<busconfig>
 
105
</busconfig>''')
 
106
 
 
107
        # non-D-BUS configuration file
 
108
        self._mksrc('daemon/defaults.conf', 'start = True\nlog = syslog')
 
109
 
 
110
        # D-BUS system service
 
111
        self._mksrc('daemon/com.example.foo.service', '''[D-BUS Service]
 
112
Name=com.example.Foo
 
113
Exec=/usr/lib/foo/foo_daemon
 
114
User=root''')
 
115
 
 
116
        # D-BUS session service
 
117
        self._mksrc('gui/com.example.foo.gui.service', '''[D-BUS Service]
 
118
Name=com.example.Foo.GUI
 
119
Exec=/usr/bin/foo-gtk
 
120
''')
 
121
 
 
122
        # non-D-BUS .service file
 
123
        self._mksrc('stuff/super.service', 'I am a file')
 
124
 
 
125
        (o, e, s) = self.do_install()
 
126
        self.assertEqual(e, '')
 
127
        self.assertEqual(s, 0)
 
128
        self.assert_('following files are not recognized' in o)
 
129
        self.assert_('\n  stuff/super.service\n' in o)
 
130
 
 
131
        f = self.installed_files()
 
132
        self.assertEqual(len(f), 4) # 3 D-BUS files plus .egg-info
 
133
        self.assert_('/etc/dbus-1/system.d/com.example.foo.conf' in f)
 
134
        self.assert_('/usr/share/dbus-1/system-services/com.example.foo.service' in f)
 
135
        self.assert_('/usr/share/dbus-1/services/com.example.foo.gui.service' in f)
 
136
        self.failIf('super.service' in '\n'.join(f))
 
137
 
 
138
    def test_po(self):
 
139
        '''gettext *.po files'''
 
140
 
 
141
        self._mkpo()
 
142
 
 
143
        (o, e, s) = self.do_install()
 
144
        self.assertEqual(e, '')
 
145
        self.assertEqual(s, 0)
 
146
        self.failIf('following files are not recognized' in o)
 
147
        f = self.installed_files()
 
148
        self.assert_('/usr/share/locale/de/LC_MESSAGES/foo.mo' in f)
 
149
        self.assert_('/usr/share/locale/fr/LC_MESSAGES/foo.mo' in f)
 
150
        self.failIf('junk' in '\n'.join(f))
 
151
 
 
152
        msgunfmt = subprocess.Popen(['msgunfmt',
 
153
            os.path.join(self.install_tree,
 
154
            'usr/share/locale/de/LC_MESSAGES/foo.mo')],
 
155
            stdout=subprocess.PIPE)
 
156
        out = msgunfmt.communicate()[0]
 
157
        self.assertEqual(out, open(os.path.join(self.src, 'po/de.po')).read())
 
158
 
 
159
    def test_policykit(self):
 
160
        '''*.policy.in PolicyKit files'''
 
161
 
 
162
        self._mksrc('daemon/com.example.foo.policy.in', '''<?xml version="1.0" encoding="UTF-8"?>
 
163
<!DOCTYPE policyconfig PUBLIC
 
164
 "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
 
165
 "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
 
166
<policyconfig>
 
167
  <vendor>Foo project</vendor>
 
168
  <vendor_url>https://foo.example.com</vendor_url>
 
169
 
 
170
  <action id="com.example.foo.greet">
 
171
    <_description>Good morning</_description>
 
172
    <_message>Hello</_message>
 
173
    <defaults>
 
174
      <allow_active>yes</allow_active>
 
175
    </defaults>
 
176
  </action>
 
177
</policyconfig>''')
 
178
 
 
179
        self._mkpo()
 
180
        (o, e, s) = self.do_install()
 
181
        self.assertEqual(e, '')
 
182
        self.assertEqual(s, 0)
 
183
        self.failIf('following files are not recognized' in o)
 
184
 
 
185
        f = self.installed_files()
 
186
        self.assert_('/usr/share/PolicyKit/policy/com.example.foo.policy' in f)
 
187
        p = open(os.path.join(self.install_tree,
 
188
            'usr/share/PolicyKit/policy/com.example.foo.policy')).read()
 
189
        self.assert_('<description>Good morning</description>' in p)
 
190
        self.assert_('<description xml:lang="de">Guten Morgen</description>' in p)
 
191
        self.assert_('<message>Hello</message>' in p)
 
192
        self.assert_('<message xml:lang="de">Hallo</message>' in p)
 
193
 
 
194
    def test_desktop(self):
 
195
        '''*.desktop.in files'''
 
196
 
 
197
        self._mksrc('gui/foogtk.desktop.in', '''[Desktop Entry]
 
198
_Name=Hello
 
199
_Comment=Good morning
 
200
Exec=/bin/foo''')
 
201
        self._mksrc('gui/autostart/fooapplet.desktop.in', '''[Desktop Entry]
 
202
_Name=Hello
 
203
_Comment=Good morning
 
204
Exec=/usr/bin/fooapplet''')
 
205
        self._mkpo()
 
206
 
 
207
        (o, e, s) = self.do_install()
 
208
        self.assertEqual(e, '')
 
209
        self.assertEqual(s, 0)
 
210
        self.failIf('following files are not recognized' in o)
 
211
 
 
212
        f = self.installed_files()
 
213
        self.assert_('/usr/share/autostart/fooapplet.desktop' in f)
 
214
        self.assert_('/usr/share/applications/foogtk.desktop' in f)
 
215
 
 
216
        p = open(os.path.join(self.install_tree,
 
217
            'usr/share/autostart/fooapplet.desktop')).read()
 
218
        self.assert_('\nName=Hello\n' in p)
 
219
        self.assert_('\nName[de]=Hallo\n' in p)
 
220
        self.assert_('\nComment[fr]=Bonjour\n' in p)
 
221
 
 
222
    def test_icons(self):
 
223
        '''data/icons/'''
 
224
 
 
225
        self._mksrc('data/icons/scalable/actions/press.png')
 
226
        self._mksrc('data/icons/48x48/apps/foo.png')
 
227
        action_icon_path = os.path.join(self.src, 'data', 'icons', 'scalable',
 
228
                'actions')
 
229
        os.symlink(os.path.join(action_icon_path, 'press.png'),
 
230
                os.path.join(action_icon_path, 'crunch.png'))
 
231
 
 
232
        (o, e, s) = self.do_install()
 
233
        self.assertEqual(e, '')
 
234
        self.assertEqual(s, 0)
 
235
        self.failIf('following files are not recognized' in o)
 
236
 
 
237
        f = self.installed_files()
 
238
        self.assert_('/usr/share/icons/hicolor/scalable/actions/press.png' in f)
 
239
        self.assert_('/usr/share/icons/hicolor/scalable/actions/crunch.png' in f)
 
240
        self.assert_('/usr/share/icons/hicolor/48x48/apps/foo.png' in f)
 
241
        # TODO: known to fail right now
 
242
        #self.assert_(os.path.islink(os.path.join(self.install_tree, 
 
243
        #   '/usr/share/icons/hicolor/scalable/actions/crunch.png'))
 
244
 
 
245
    def test_data(self):
 
246
        '''Auxiliary files in data/'''
 
247
 
 
248
        # have some explicitly covered files, to check that they don't get
 
249
        # installed into prefix/share/foo/ again
 
250
        self._mksrc('setup.py', '''
 
251
from DistUtilsExtra.auto import setup
 
252
from glob import glob
 
253
 
 
254
setup(
 
255
    name='foo',
 
256
    version='0.1',
 
257
    description='Test suite package',
 
258
    url='https://foo.example.com',
 
259
    license='GPL v2 or later',
 
260
    author='Martin Pitt',
 
261
    author_email='martin.pitt@example.com',
 
262
 
 
263
    data_files = [
 
264
      ('/lib/udev/rules.d', ['data/40-foo.rules']),
 
265
      ('/etc/foo', glob('data/*.conf')),
 
266
    ]
 
267
)
 
268
''')
 
269
 
 
270
        self._mksrc('data/stuff')
 
271
        self._mksrc('data/handlers/red.py', 'import sys\nprint "RED"')
 
272
        self._mksrc('data/handlers/blue.py', 'import sys\nprint "BLUE"')
 
273
        self._mksrc('data/40-foo.rules')
 
274
        self._mksrc('data/blob1.conf')
 
275
        self._mksrc('data/blob2.conf')
 
276
 
 
277
        (o, e, s) = self.do_install()
 
278
        self.assertEqual(e, '')
 
279
        self.assertEqual(s, 0)
 
280
        self.failIf('following files are not recognized' in o)
 
281
 
 
282
        f = self.installed_files()
 
283
        self.assert_('/usr/share/foo/stuff' in f)
 
284
        self.assert_('/usr/share/foo/handlers/red.py' in f)
 
285
        self.assert_('/usr/share/foo/handlers/blue.py' in f)
 
286
        self.assert_('/lib/udev/rules.d/40-foo.rules' in f)
 
287
        self.assert_('/etc/foo/blob1.conf' in f)
 
288
        self.assert_('/etc/foo/blob2.conf' in f)
 
289
        self.failIf('/usr/share/foo/blob1.conf' in f)
 
290
        self.failIf('/usr/share/foo/40-foo.rules' in f)
 
291
 
 
292
    def test_scripts(self):
 
293
        '''scripts'''
 
294
 
 
295
        # these should get autoinstalled
 
296
        self._mksrc('bin/yell', '#!/bin/sh', True)
 
297
        self._mksrc('bin/shout', '#!/bin/sh', True)
 
298
        self._mksrc('bin/foo', '#!/bin/sh', True)
 
299
 
 
300
        # these shouldn't
 
301
        self._mksrc('daemon/food', '#!/bin/sh', True) # not in bin/
 
302
        self._mksrc('foob', '#!/bin/sh', True) # not named like project
 
303
        self._mksrc('bin/whisper', '#!/bin/sh') # not executable
 
304
 
 
305
        (o, e, s) = self.do_install()
 
306
        self.assertEqual(e, '')
 
307
        self.assertEqual(s, 0)
 
308
        self.assert_('following files are not recognized' in o)
 
309
        self.assert_('\n  foob' in o)
 
310
        self.assert_('\n  bin/whisper' in o)
 
311
        self.assert_('\n  daemon/food' in o)
 
312
 
 
313
        f = self.installed_files()
 
314
        self.assert_('/usr/bin/yell' in f)
 
315
        self.assert_('/usr/bin/shout' in f)
 
316
        self.assert_('/usr/bin/foo' in f)
 
317
        ftext = '\n'.join(f)
 
318
        self.failIf('food' in ftext)
 
319
        self.failIf('foob' in ftext)
 
320
        self.failIf('whisper' in ftext)
 
321
 
 
322
        # verify that they are executable
 
323
        binpath = os.path.join(self.install_tree, 'usr', 'bin')
 
324
        self.assert_(os.access(os.path.join(binpath, 'yell'), os.X_OK))
 
325
        self.assert_(os.access(os.path.join(binpath, 'shout'), os.X_OK))
 
326
        self.assert_(os.access(os.path.join(binpath, 'foo'), os.X_OK))
 
327
 
 
328
    def test_pot_manual(self):
 
329
        '''PO template creation with manual POTFILES.in'''
 
330
 
 
331
        self._mk_i18n_source()
 
332
        self._mksrc('po/foo.pot', '')
 
333
        # only do a subset here
 
334
        self._mksrc('po/POTFILES.in', '''
 
335
gtk/main.py
 
336
gui/foo.desktop.in
 
337
[type: gettext/glade]gtk/test.ui''')
 
338
 
 
339
        (o, e, s) = self.setup_py(['build'])
 
340
        self.assertEqual(e, '')
 
341
        self.assertEqual(s, 0)
 
342
        # POT file should not be shown as not recognized
 
343
        self.failIf('\n  po/foo.pot\n' in o)
 
344
 
 
345
        pot_path = os.path.join(self.src, 'po', 'foo.pot')
 
346
        self.assert_(os.path.exists(pot_path))
 
347
        pot = open(pot_path).read()
 
348
 
 
349
        self.failIf('msgid "no"' in pot)
 
350
        self.assert_('msgid "yes1"' in pot)
 
351
        self.assert_('msgid "yes2 %s"' in pot)
 
352
        self.failIf('msgid "yes5"' in pot) # we didn't add helpers.py
 
353
        self.assert_('msgid "yes7"' in pot) # we did include the desktop file
 
354
        self.failIf('msgid "yes5"' in pot) # we didn't add helpers.py
 
355
        self.assert_('msgid "yes11"' in pot) # we added the GTKBuilder file
 
356
 
 
357
    def test_pot_auto(self):
 
358
        '''PO template creation with automatic POTFILES.in'''
 
359
 
 
360
        self._mk_i18n_source()
 
361
 
 
362
        (o, e, s) = self.setup_py(['build'])
 
363
        self.assertEqual(e, '')
 
364
        self.assertEqual(s, 0)
 
365
        # POT file should not be shown as not recognized
 
366
        self.failIf('\n  po/foo.pot\n' in o)
 
367
 
 
368
        pot_path = os.path.join(self.src, 'po', 'foo.pot')
 
369
        self.assert_(os.path.exists(pot_path))
 
370
        pot = open(pot_path).read()
 
371
 
 
372
        self.failIf('msgid "no"' in pot)
 
373
        for i in range(2, 14):
 
374
            self.assert_('msgid "yes%i' % i in pot or 
 
375
                   'msgid ""\n"yes%i' % i in pot,
 
376
                   'yes%i' % i)
 
377
        # above loop would match yes11 to yes1 as well, so test it explicitly
 
378
        self.assert_('msgid "yes1"' in pot)
 
379
 
 
380
    def test_standard_files(self):
 
381
        '''Standard files (MANIFEST.in, COPYING, etc.)'''
 
382
 
 
383
        self._mksrc('AUTHORS')
 
384
        self._mksrc('COPYING')
 
385
        self._mksrc('LICENSE')
 
386
        self._mksrc('COPYING.LIB')
 
387
        self._mksrc('README.txt')
 
388
        self._mksrc('MANIFEST.in')
 
389
        self._mksrc('MANIFEST')
 
390
        self._mksrc('NEWS')
 
391
        self._mksrc('TODO')
 
392
 
 
393
        (o, e, s) = self.do_install()
 
394
        self.assertEqual(e, '')
 
395
        self.assertEqual(s, 0)
 
396
        self.failIf('following files are not recognized' in o, o)
 
397
 
 
398
        f = self.installed_files()
 
399
        self.assert_('/usr/share/doc/foo/README.txt' in f)
 
400
        self.assert_('/usr/share/doc/foo/NEWS' in f)
 
401
        ftext = '\n'.join(f)
 
402
        self.failIf('MANIFEST' in ftext)
 
403
        self.failIf('COPYING' in ftext)
 
404
        self.failIf('COPYING' in ftext)
 
405
        self.failIf('AUTHORS' in ftext)
 
406
        self.failIf('TODO' in ftext)
 
407
 
 
408
        # sub-dir READMEs shouldn't be installed by default
 
409
        self.snapshot = None
 
410
        self._mksrc('extra/README')
 
411
        (o, e, s) = self.do_install()
 
412
        self.assertEqual(e, '')
 
413
        self.assertEqual(s, 0)
 
414
        self.assert_('following files are not recognized' in o)
 
415
        self.assert_('\n  extra/README\n' in o)
 
416
 
 
417
    def test_sdist(self):
 
418
        '''default MANIFEST'''
 
419
 
 
420
        good = ['AUTHORS', 'README.txt', 'COPYING', 'helpers.py',
 
421
                'foo/__init__.py', 'foo/bar.py', 'tests/all.py',
 
422
                'gui/x.desktop.in', 'backend/foo.policy.in',
 
423
                'daemon/backend.conf', 'x/y', 'po/de.po', 'po/foo.pot',
 
424
                '.quickly', 'data/icons/16x16/apps/foo.png', 'bin/foo',
 
425
                'backend/food', 'backend/com.example.foo.service',
 
426
                'gtk/main.glade', 'dist/extra.tar.gz']
 
427
        bad = ['po/de.mo', '.helpers.py.swp', '.bzr/index', '.svn/index',
 
428
               '.git/index', 'bin/foo~', 'backend/foo.pyc', 
 
429
               'dist/foo-0.1.tar.gz']
 
430
 
 
431
        for f in good + bad:
 
432
            self._mksrc(f)
 
433
 
 
434
        (o, e, s) = self.setup_py(['sdist', '-o'])
 
435
        self.assert_("'MANIFEST.in' does not exist" in e)
 
436
        self.assertEqual(s, 0)
 
437
 
 
438
        manifest = open(os.path.join(self.src, 'MANIFEST')).read().splitlines()
 
439
 
 
440
        for f in good:
 
441
            self.assert_(f in manifest, '%s in manifest' % f)
 
442
        for f in bad:
 
443
            self.failIf(f in manifest, '%s not in manifest' % f)
 
444
        os.unlink(os.path.join(self.src, 'MANIFEST'))
 
445
 
 
446
    def test_gtkbuilder(self):
 
447
        '''GtkBuilder *.ui'''
 
448
 
 
449
        self._mksrc('gtk/test.ui', '''<?xml version="1.0"?>
 
450
<interface>
 
451
  <requires lib="gtk+" version="2.16"/>
 
452
  <object class="GtkWindow" id="window1">
 
453
    <property name="title" translatable="yes">yes11</property>
 
454
    <child><placeholder/></child>
 
455
  </object>
 
456
</interface>''')
 
457
        self._mksrc('someweird.ui')
 
458
 
 
459
        (o, e, s) = self.do_install()
 
460
        self.assertEqual(e, '')
 
461
        self.assertEqual(s, 0)
 
462
        self.assert_('following files are not recognized' in o)
 
463
        self.assert_('\n  someweird.ui\n' in o)
 
464
 
 
465
        f = self.installed_files()
 
466
        self.assert_('/usr/share/foo/test.ui' in f)
 
467
        ftext = '\n'.join(f)
 
468
        self.failIf('someweird' in ftext)
 
469
 
 
470
    def test_manpages(self):
 
471
        '''manpages'''
 
472
 
 
473
        self._mksrc('man/foo.1', '.TH foo 1 "Jan 01, 1900" "Joe Developer"')
 
474
        self._mksrc('daemon/food.8', '.TH food 8 "Jan 01, 1900" "Joe Developer"')
 
475
        self._mksrc('cruft/food.1', '')
 
476
        self._mksrc('daemon/notme.s', '.TH food 8 "Jan 01, 1900" "Joe Developer"')
 
477
 
 
478
        (o, e, s) = self.do_install()
 
479
        self.assertEqual(e, '')
 
480
        self.assertEqual(s, 0)
 
481
        self.assert_('following files are not recognized' in o)
 
482
        self.assert_('\n  cruft/food.1\n' in o)
 
483
        self.assert_('\n  daemon/notme.s\n' in o)
 
484
 
 
485
        f = self.installed_files()
 
486
        self.assert_('/usr/share/man/man1/foo.1' in f)
 
487
        self.assert_('/usr/share/man/man8/food.8' in f)
 
488
        ftext = '\n'.join(f)
 
489
        self.failIf('food.1' in ftext)
 
490
        self.failIf('notme' in ftext)
 
491
 
 
492
    def test_etc(self):
 
493
        '''etc/*'''
 
494
 
 
495
        self._mksrc('etc/cron.daily/foo')
 
496
        self._mksrc('etc/foo.conf')
 
497
        self._mksrc('etc/init.d/foo', executable=True)
 
498
        d = os.path.join(self.src, 'etc', 'cron.weekly')
 
499
        os.mkdir(d)
 
500
        os.symlink(os.path.join('..', 'cron.daily', 'foo'),
 
501
                os.path.join(d, 'foo'))
 
502
 
 
503
        (o, e, s) = self.do_install()
 
504
        self.assertEqual(e, '')
 
505
        self.assertEqual(s, 0)
 
506
        self.failIf('following files are not recognized' in o, o)
 
507
 
 
508
        f = self.installed_files()
 
509
        self.assert_('/etc/cron.daily/foo' in f)
 
510
        self.assert_('/etc/cron.weekly/foo' in f)
 
511
        self.assert_('/etc/init.d/foo' in f)
 
512
        self.assert_('/etc/foo.conf' in f)
 
513
 
 
514
        # verify that init script is executable
 
515
        self.assert_(os.access(os.path.join(self.install_tree, 'etc', 'init.d',
 
516
            'foo'), os.X_OK))
 
517
        # verify that symlinks get preserved
 
518
        self.assert_(os.path.islink(os.path.join(self.install_tree, 'etc',
 
519
            'cron.weekly', 'foo')))
 
520
 
 
521
        # check that we can install again into the same source tree
 
522
        (o, e, s) = self.setup_py(['install', '--no-compile', '--prefix=/usr', 
 
523
            '--root=' + self.install_tree])
 
524
        self.assertEqual(e, '')
 
525
        self.assertEqual(s, 0)
 
526
        self.failIf('following files are not recognized' in o, o)
 
527
 
 
528
    def test_requires_provides(self):
 
529
        '''automatic requires/provides'''
 
530
 
 
531
        try:
 
532
            __import__('Crypto')
 
533
            __import__('dateutil')
 
534
        except ImportError:
 
535
            self.fail('You need to have Crypto and dateutil installed for this test suite to work')
 
536
 
 
537
        self._mksrc('foo/__init__.py', '')
 
538
        self._mksrc('foo/stuff.py', '''import xml.parsers.expat
 
539
import os, os.path, email.mime, distutils.command.register
 
540
from email import header as h
 
541
import Crypto.PublicKey.DSA, unknown
 
542
''')
 
543
 
 
544
        self._mksrc('foo/bar/__init__.py', '')
 
545
        self._mksrc('foo/bar/poke.py', 'def x(): pass')
 
546
 
 
547
        self._mksrc('mymod.py', 'import foo\nfrom foo.bar.poke import x')
 
548
 
 
549
        self._mksrc('bin/foo-cli', '''#!/usr/bin/python
 
550
import sys
 
551
from dateutil import tz
 
552
import foo.bar
 
553
from Crypto import Cipher
 
554
 
 
555
print 'import iamnota.module'
 
556
''', executable=True)
 
557
 
 
558
        self.install_tree = tempfile.mkdtemp()
 
559
        (o, e, s) = self.setup_py(['install_egg_info', '-d', self.install_tree])
 
560
        self.assertEqual(e, 'ERROR: Python module unknown not found\n')
 
561
        self.assertEqual(s, 0)
 
562
        self.failIf('following files are not recognized' in o, o)
 
563
 
 
564
        # parse .egg-info
 
565
        egg = open(os.path.join(self.install_tree,
 
566
            'foo-0.1.egg-info')).read().splitlines()
 
567
        self.assert_('Name: foo' in egg)
 
568
 
 
569
        # check provides
 
570
        prov = [prop.split(' ', 1)[1] for prop in egg if prop.startswith('Provides: ')]
 
571
        self.assertEqual(set(prov), set(['foo', 'mymod']))
 
572
 
 
573
        # check requires
 
574
        req = [prop.split(' ', 1)[1] for prop in egg if prop.startswith('Requires: ')]
 
575
        self.assertEqual(set(req), set(['DistUtilsExtra.auto', 'Crypto', 'dateutil']))
 
576
 
 
577
    #
 
578
    # helper methods
 
579
    #
 
580
 
 
581
    def setup_py(self, args):
 
582
        '''Run setup.py with given arguments.
 
583
 
 
584
        For convenience, this snapshots the tree if no snapshot exists yet.
 
585
 
 
586
        Return (out, err, exitcode) triple.
 
587
        '''
 
588
        if not self.snapshot:
 
589
            self.do_snapshot()
 
590
 
 
591
        env = os.environ
 
592
        oldcwd = os.getcwd()
 
593
        env['PYTHONPATH'] = oldcwd
 
594
        os.chdir(self.src)
 
595
        s = subprocess.Popen(['python', 'setup.py'] + args, env=env,
 
596
            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
597
        (out, err) = s.communicate()
 
598
        os.chdir(oldcwd)
 
599
        return (out, err, s.returncode)
 
600
 
 
601
    def do_install(self):
 
602
        '''Run setup.py install into temporary tree.
 
603
 
 
604
        Return (out, err, exitcode) triple.
 
605
        '''
 
606
        self.install_tree = tempfile.mkdtemp()
 
607
 
 
608
        return self.setup_py(['install', '--no-compile', '--prefix=/usr', 
 
609
            '--root=' + self.install_tree])
 
610
 
 
611
    def installed_files(self):
 
612
        '''Return list of file paths in install tree.'''
 
613
 
 
614
        result = []
 
615
        for root, _, files in os.walk(self.install_tree):
 
616
            assert root.startswith(self.install_tree)
 
617
            r = root[len(self.install_tree):]
 
618
            for f in files:
 
619
                result.append(os.path.join(r, f))
 
620
        return result
 
621
 
 
622
    def _mksrc(self, path, content=None, executable=False):
 
623
        '''Create a file in the test source tree.'''
 
624
 
 
625
        path = os.path.join(self.src, path)
 
626
        dir = os.path.dirname(path)
 
627
        if not os.path.isdir(dir):
 
628
            os.makedirs(dir)
 
629
        f = open(path, 'w')
 
630
        if content is None:
 
631
            # default content, to spot with diff
 
632
            print >> f, 'dummy'
 
633
        else:
 
634
            print >> f, content
 
635
        f.close()
 
636
 
 
637
        if executable:
 
638
            os.chmod(path, 0755)
 
639
 
 
640
    def do_snapshot(self):
 
641
        '''Snapshot source tree.
 
642
 
 
643
        This should be called after a test set up all source files.
 
644
        '''
 
645
        assert self.snapshot is None, 'snapshot already taken'
 
646
 
 
647
        self.snapshot = tempfile.mkdtemp()
 
648
        shutil.copytree(self.src, os.path.join(self.snapshot, 's'))
 
649
 
 
650
    def diff_snapshot(self):
 
651
        '''Compare source tree to snapshot.
 
652
 
 
653
        Return diff -Nur output.
 
654
        '''
 
655
        assert self.snapshot, 'no snapshot taken'
 
656
        diff = subprocess.Popen(['diff', '-x', 'foo.pot', '-Nur', os.path.join(self.snapshot, 's'), 
 
657
            self.src], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
658
        (out, err) = diff.communicate()
 
659
        self.assertEqual(err, '', 'diff error messages')
 
660
        return out
 
661
 
 
662
    def _mkpo(self):
 
663
        '''Create some example po files.'''
 
664
 
 
665
        self._mksrc('po/POTFILES.in', '')
 
666
        self._mksrc('po/de.po', '''msgid ""
 
667
msgstr "Content-Type: text/plain; charset=UTF-8\\n"
 
668
 
 
669
msgid "Good morning"
 
670
msgstr "Guten Morgen"
 
671
 
 
672
msgid "Hello"
 
673
msgstr "Hallo"''')
 
674
        self._mksrc('po/fr.po', '''msgid ""
 
675
msgstr "Content-Type: text/plain; charset=UTF-8\\n"
 
676
        
 
677
msgid "Good morning"
 
678
msgstr "Bonjour"''')
 
679
 
 
680
    def _mk_i18n_source(self):
 
681
        '''Create some example source files with gettext calls'''
 
682
 
 
683
        self._mksrc('gtk/main.py', '''print _("yes1")
 
684
print "no1"
 
685
print __("no2")
 
686
x = _('yes2 %s') % y
 
687
 
 
688
def f():
 
689
    print _(u"yes3")
 
690
    return _(u'yes6')''')
 
691
 
 
692
        self._mksrc('helpers.py', '''
 
693
print f(_(u"yes4"))
 
694
print _(\'\'\'yes5
 
695
even more
 
696
lines\'\'\')
 
697
print _("""yes6
 
698
more lines""")
 
699
print \'\'\'no3
 
700
boo\'\'\'
 
701
print """no4
 
702
more"""''')
 
703
 
 
704
        self._mksrc('gui/foo.desktop.in', '''[Desktop Entry]
 
705
_Name=yes7
 
706
_Comment=yes8
 
707
Icon=no5
 
708
Exec=/usr/bin/foo''')
 
709
 
 
710
        self._mksrc('daemon/com.example.foo.policy.in', '''<?xml version="1.0" encoding="UTF-8"?>
 
711
<!DOCTYPE policyconfig PUBLIC
 
712
 "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
 
713
 "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
 
714
<policyconfig>
 
715
  <action id="com.example.foo.greet">
 
716
    <_description>yes9</_description>
 
717
    <_message>yes10</_message>
 
718
    <defaults>
 
719
      <allow_active>no6</allow_active>
 
720
    </defaults>
 
721
  </action>
 
722
</policyconfig>''')
 
723
 
 
724
        self._mksrc('gtk/test.ui', '''<?xml version="1.0"?>
 
725
<interface>
 
726
  <requires lib="gtk+" version="2.16"/>
 
727
  <object class="GtkWindow" id="window1">
 
728
    <property name="title" translatable="yes">yes11</property>
 
729
    <child><placeholder/></child>
 
730
  </object>
 
731
</interface>''')
 
732
 
 
733
        self._mksrc('Makefile', 'echo _("no7")')
 
734
 
 
735
        # Executables without *.py extension
 
736
        self._mksrc('gtk/foo-gtk', '#!/usr/bin/python\nprint _("yes12")',
 
737
                executable=True)
 
738
        self._mksrc('cli/foo-cli', '#!/usr/bin/env python\nprint _("yes13")',
 
739
                executable=True)
 
740
        self._mksrc('daemon/foobarize', '#!/usr/bin/flex\np _("no8")',
 
741
                executable=True)
 
742
 
 
743
unittest.main()