98ffbc9b20bb9bee5ed3f16f30cede615b8bc352
[tools/git-buildpackage.git] / tests / 20_test_rpm.py
1 # vim: set fileencoding=utf-8 :
2 #
3 # (C) 2012 Intel Corporation <markus.lehtonen@linux.intel.com>
4 #    This program is free software; you can redistribute it and/or modify
5 #    it under the terms of the GNU General Public License as published by
6 #    the Free Software Foundation; either version 2 of the License, or
7 #    (at your option) any later version.
8 #
9 #    This program is distributed in the hope that it will be useful,
10 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 #    GNU General Public License for more details.
13 #
14 #    You should have received a copy of the GNU General Public License
15 #    along with this program; if not, write to the Free Software
16 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 """Test the classes under L{gbp.rpm}"""
18
19 import filecmp
20 import os
21 import shutil
22 import tempfile
23 from nose.tools import assert_raises, eq_, ok_   # pylint: disable=E0611
24
25 from gbp.errors import GbpError
26 from gbp.rpm import (SrcRpmFile, SpecFile, parse_srpm, NoSpecError, guess_spec,
27                      guess_spec_repo, spec_from_repo)
28 from gbp.git.repository import GitRepository
29
30 # Disable "Method could be a function"
31 #   pylint: disable=R0201
32
33
34 DATA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data',
35                         'rpm')
36 SRPM_DIR = os.path.join(DATA_DIR, 'srpms')
37 SPEC_DIR = os.path.join(DATA_DIR, 'specs')
38
39
40 class SpecFileTester(SpecFile):
41     """Helper class for testing"""
42
43     def protected(self, name):
44         """Get a protected member"""
45         return super(SpecFileTester, self).__getattribute__(name)
46
47
48 class RpmTestBase(object):
49     """Test base class"""
50     def __init__(self):
51         self.tmpdir = None
52
53     def setup(self):
54         """Test case setup"""
55         self.tmpdir = tempfile.mkdtemp(prefix='gbp_%s_' % __name__, dir='.')
56
57     def teardown(self):
58         """Test case teardown"""
59         shutil.rmtree(self.tmpdir)
60
61 class TestSrcRpmFile(RpmTestBase):
62     """Test L{gbp.rpm.SrcRpmFile}"""
63
64     def test_srpm(self):
65         """Test parsing of a source rpm"""
66         srpm = SrcRpmFile(os.path.join(SRPM_DIR, 'gbp-test-1.0-1.src.rpm'))
67         eq_(srpm.version, {'release': '1', 'upstreamversion': '1.0'})
68         eq_(srpm.name, 'gbp-test')
69         eq_(srpm.upstreamversion, '1.0')
70         eq_(srpm.packager, None)
71
72     def test_srpm_2(self):
73         """Test parsing of another source rpm"""
74         srpm = SrcRpmFile(os.path.join(SRPM_DIR, 'gbp-test2-3.0-0.src.rpm'))
75         eq_(srpm.version, {'release': '0', 'upstreamversion': '3.0',
76                            'epoch': '2'})
77         eq_(srpm.packager, 'Markus Lehtonen <markus.lehtonen@linux.intel.com>')
78
79     def test_unpack_srpm(self):
80         """Test unpacking of a source rpm"""
81         srpm = SrcRpmFile(os.path.join(SRPM_DIR, 'gbp-test-1.0-1.src.rpm'))
82         srpm.unpack(self.tmpdir)
83         for fn in ['gbp-test-1.0.tar.bz2', 'foo.txt', 'bar.tar.gz', 'my.patch',
84                    'my2.patch', 'my3.patch']:
85             ok_(os.path.exists(os.path.join(self.tmpdir, fn)),
86                     "%s not found" % fn)
87
88 class TestSpecFile(RpmTestBase):
89     """Test L{gbp.rpm.SpecFile}"""
90
91     def test_spec(self):
92         """Test parsing of a valid spec file"""
93         spec_filepath = os.path.join(SPEC_DIR, 'gbp-test.spec')
94         spec = SpecFileTester(spec_filepath)
95
96         # Test basic properties
97         eq_(spec.specfile, os.path.basename(spec_filepath))
98         eq_(spec.specdir, os.path.dirname(spec_filepath))
99         eq_(spec.specpath, spec_filepath)
100
101         eq_(spec.name, 'gbp-test')
102         eq_(spec.packager, None)
103
104         eq_(spec.upstreamversion, '1.0')
105         eq_(spec.release, '1')
106         eq_(spec.epoch, None)
107         eq_(spec.version, {'release': '1', 'upstreamversion': '1.0'})
108
109         orig = spec.orig_src
110         eq_(orig['filename'], 'gbp-test-1.0.tar.bz2')
111         eq_(orig['uri'], 'gbp-test-1.0.tar.bz2')
112         eq_(orig['filename_base'], 'gbp-test-1.0')
113         eq_(orig['archive_fmt'], 'tar')
114         eq_(orig['compression'], 'bzip2')
115         eq_(orig['prefix'], 'gbp-test/')
116
117     def test_spec_2(self):
118         """Test parsing of another valid spec file"""
119         spec_filepath = os.path.join(SPEC_DIR, 'gbp-test2.spec')
120         spec = SpecFile(spec_filepath)
121
122         # Test basic properties
123         eq_(spec.name, 'gbp-test2')
124         eq_(spec.packager, 'Markus Lehtonen <markus.lehtonen@linux.intel.com>')
125
126         eq_(spec.epoch, '2')
127         eq_(spec.version, {'release': '0', 'upstreamversion': '3.0',
128                            'epoch': '2'})
129
130         orig = spec.orig_src
131         eq_(orig['filename'], 'gbp-test2-3.0.tar.gz')
132         eq_(orig['uri'], 'ftp://ftp.host.com/gbp-test2-3.0.tar.gz')
133         eq_(orig['archive_fmt'], 'tar')
134         eq_(orig['compression'], 'gzip')
135         eq_(orig['prefix'], '')
136
137     def test_spec_3(self):
138         """Test parsing of yet another valid spec file"""
139         spec_filepath = os.path.join(SPEC_DIR, 'gbp-test-native.spec')
140         spec = SpecFile(spec_filepath)
141
142         # Test basic properties
143         eq_(spec.name, 'gbp-test-native')
144         orig = spec.orig_src
145         eq_(orig['filename'], 'gbp-test-native-1.0.zip')
146         eq_(orig['archive_fmt'], 'zip')
147         eq_(orig['compression'], None)
148         eq_(orig['prefix'], 'gbp-test-native-1.0/')
149
150     def test_spec_4(self):
151         """Test parsing of spec without orig tarball"""
152         spec_filepath = os.path.join(SPEC_DIR, 'gbp-test-native2.spec')
153         spec = SpecFile(spec_filepath)
154
155         # Test basic properties
156         eq_(spec.name, 'gbp-test-native2')
157         eq_(spec.orig_src, None)
158
159     def test_parse_raw(self):
160         """Test parsing of a valid spec file"""
161         with assert_raises(NoSpecError):
162             SpecFile(None, None)
163         with assert_raises(NoSpecError):
164             SpecFile('filename', 'filedata')
165
166         spec_filepath = os.path.join(SPEC_DIR, 'gbp-test.spec')
167         with open(spec_filepath, 'r') as spec_fd:
168             spec_data = spec_fd.read()
169         spec = SpecFile(filedata=spec_data)
170
171         # Test basic properties
172         eq_(spec.specfile, None)
173         eq_(spec.specdir, None)
174         eq_(spec.name, 'gbp-test')
175
176     def test_update_spec(self):
177         """Test spec autoupdate functionality"""
178         # Create temporary spec file
179         tmp_spec = os.path.join(self.tmpdir, 'gbp-test.spec')
180         shutil.copy2(os.path.join(SPEC_DIR, 'gbp-test.spec'), tmp_spec)
181
182         reference_spec = os.path.join(SPEC_DIR, 'gbp-test-reference.spec')
183         spec = SpecFile(tmp_spec)
184         spec.update_patches(['new.patch'], {})
185         spec.write_spec_file()
186         eq_(filecmp.cmp(tmp_spec, reference_spec), True)
187
188         # Test adding the VCS tag and adding changelog
189         reference_spec = os.path.join(SPEC_DIR, 'gbp-test-reference2.spec')
190         spec.set_tag('VCS', None, 'myvcstag')
191         spec.set_changelog("* Wed Feb 05 2014 Name <email> 1\n- New entry\n")
192         spec.write_spec_file()
193         eq_(filecmp.cmp(tmp_spec, reference_spec), True)
194
195     def test_update_spec2(self):
196         """Another test for spec autoupdate functionality"""
197         tmp_spec = os.path.join(self.tmpdir, 'gbp-test2.spec')
198         shutil.copy2(os.path.join(SPEC_DIR, 'gbp-test2.spec'), tmp_spec)
199
200         reference_spec = os.path.join(SPEC_DIR, 'gbp-test2-reference2.spec')
201         spec = SpecFile(tmp_spec)
202         spec.update_patches(['1.patch', '2.patch'],
203                             {'1.patch': {'if': 'true'},
204                              '2.patch': {'ifarch': '%ix86'}})
205         spec.set_tag('VCS', None, 'myvcstag')
206         spec.write_spec_file()
207         eq_(filecmp.cmp(tmp_spec, reference_spec), True)
208
209         # Test updating patches again, removing the VCS tag and re-writing
210         # changelog
211         reference_spec = os.path.join(SPEC_DIR, 'gbp-test2-reference.spec')
212         spec.update_patches(['new.patch'], {'new.patch': {'if': '1'}})
213         spec.set_tag('VCS', None, '')
214         spec.set_changelog("* Wed Feb 05 2014 Name <email> 2\n- New entry\n\n")
215         spec.write_spec_file()
216         eq_(filecmp.cmp(tmp_spec, reference_spec), True)
217
218     def test_modifying(self):
219         """Test updating/deleting of tags and macros"""
220         tmp_spec = os.path.join(self.tmpdir, 'gbp-test.spec')
221         shutil.copy2(os.path.join(SPEC_DIR, 'gbp-test-updates.spec'), tmp_spec)
222         reference_spec = os.path.join(SPEC_DIR,
223                                       'gbp-test-updates-reference.spec')
224         spec = SpecFileTester(tmp_spec)
225
226         # Mangle tags
227         prev = spec.protected('_delete_tag')('Vendor', None)
228         spec.protected('_set_tag')('License', None, 'new license', prev)
229         spec.protected('_delete_tag')('source', 0)
230         eq_(spec.sources(), {})
231         spec.protected('_delete_tag')('patch', 0)
232         spec.protected('_delete_tag')('patch', -1)
233         eq_(spec.protected('_patches')(), {})
234         prev = spec.protected('_delete_tag')('invalidtag', None)
235
236         with assert_raises(GbpError):
237             # Check that setting empty value fails
238             spec.protected('_set_tag')('Version', None, '', prev)
239         with assert_raises(GbpError):
240             # Check that setting invalid tag with public method fails
241             spec.set_tag('invalidtag', None, 'value')
242
243         # Mangle macros
244         prev = spec.protected('_delete_special_macro')('patch', -1)
245         spec.protected('_delete_special_macro')('patch', 123)
246         spec.protected('_set_special_macro')('patch', 0, 'my new args', prev)
247         with assert_raises(GbpError):
248             spec.protected('_delete_special_macro')('invalidmacro', 0)
249         with assert_raises(GbpError):
250             spec.protected('_set_special_macro')('invalidmacro', 0, 'args',
251                            prev)
252
253         # Check resulting spec file
254         spec.write_spec_file()
255         eq_(filecmp.cmp(tmp_spec, reference_spec), True)
256
257     def test_modifying_err(self):
258         """Test error conditions of modification methods"""
259         spec_filepath = os.path.join(SPEC_DIR, 'gbp-test2.spec')
260         spec = SpecFileTester(spec_filepath)
261
262         # Unknown/invalid section name
263         with assert_raises(GbpError):
264             spec.protected('_set_section')('patch', 'new content\n')
265
266         # Multiple sections with the same name
267         with assert_raises(GbpError):
268             spec.protected('_set_section')('files', '%{_sysconfdir}/foo\n')
269
270     def test_changelog(self):
271         """Test changelog methods"""
272         spec_filepath = os.path.join(SPEC_DIR, 'gbp-test2.spec')
273         spec = SpecFile(spec_filepath)
274
275         # Read changelog
276         eq_(spec.get_changelog(),
277             "* Tue Feb 04 2014 Name <email> 1\n- My change\n\n\n")
278
279         # Set changelog and check again
280         new_text = "* Wed Feb 05 2014 Name <email> 2\n- New entry\n\n\n"
281         spec.set_changelog(new_text)
282         eq_(spec.get_changelog(), new_text)
283
284     def test_quirks(self):
285         """Test spec that is broken/has anomalities"""
286         spec_filepath = os.path.join(SPEC_DIR, 'gbp-test-quirks.spec')
287         spec = SpecFile(spec_filepath)
288
289         # Check that we quess orig source and prefix correctly
290         eq_(spec.orig_src['prefix'], 'foobar/')
291
292     def test_tags(self):
293         """Test parsing of all the different tags of spec file"""
294         spec_filepath = os.path.join(SPEC_DIR, 'gbp-test-tags.spec')
295         spec = SpecFileTester(spec_filepath)
296
297         # Check all the tags
298         for name, val in spec.protected('_tags').iteritems():
299             rval = None
300             if name in ('version', 'release', 'epoch'):
301                 rval = '0'
302             elif name in ('autoreq', 'autoprov', 'autoreqprov'):
303                 rval = 'No'
304             elif name not in spec.protected('_listtags'):
305                 rval = 'my_%s' % name
306             if rval:
307                 eq_(val['value'], rval, ("'%s:' is '%s', expecting '%s'" %
308                                               (name, val['value'], rval)))
309             eq_(spec.ignorepatches, [])
310             # Check patch numbers and patch filenames
311             patches = {}
312             for patch in spec.protected('_tags')['patch']['lines']:
313                 patches[patch['num']] = patch['linevalue']
314
315             eq_(patches, {0: 'my_patch0', -1: 'my_patch'})
316
317     def test_patch_series(self):
318         """Test the getting the patches as a patchseries"""
319         spec_filepath = os.path.join(SPEC_DIR, 'gbp-test-native.spec')
320         spec = SpecFileTester(spec_filepath)
321
322         eq_(len(spec.patchseries()), 0)
323         spec.update_patches(['1.patch', '2.patch', '3.patch'], {})
324         eq_(len(spec.patchseries()), 3)
325         spec.protected('_gbp_tags')['ignore-patches'].append({'args': "0"})
326         spec.update_patches(['4.patch'], {})
327         eq_(len(spec.patchseries()), 1)
328         eq_(len(spec.patchseries(ignored=True)), 2)
329         spec.protected('_delete_special_macro')('patch', 0)
330         eq_(len(spec.patchseries(ignored=True)), 1)
331         series = spec.patchseries(unapplied=True, ignored=True)
332         eq_(len(series), 2)
333         eq_(os.path.basename(series[-1].path), '1.patch')
334
335     def test_patch_series_quirks(self):
336         """Patches are applied in order different from the patch numbering"""
337         spec_filepath = os.path.join(SPEC_DIR, 'gbp-test-quirks.spec')
338         spec = SpecFileTester(spec_filepath)
339
340         # Check series is returned in the order the patches are applied
341         files = [os.path.basename(patch.path) for patch in spec.patchseries()]
342         eq_(files, ['05.patch', '01.patch'])
343         # Also ignored patches are returned in the correct order
344         files = [os.path.basename(patch.path) for patch in
345                     spec.patchseries(ignored=True)]
346         eq_(files, ['05.patch', '02.patch', '01.patch'])
347         # Unapplied patches are added to the end of the series
348         files = [os.path.basename(patch.path) for patch in
349                     spec.patchseries(unapplied=True)]
350         eq_(files, ['05.patch', '01.patch', '03.patch'])
351         # Return all patches (for which tag is found)
352         files = [os.path.basename(patch.path) for patch in
353                     spec.patchseries(unapplied=True, ignored=True)]
354         eq_(files, ['05.patch', '02.patch', '01.patch', '03.patch', '04.patch'])
355
356
357 class TestUtilityFunctions(RpmTestBase):
358     """Test utility functions of L{gbp.rpm}"""
359
360     def test_guess_spec(self):
361         """Test guess_spec() function"""
362         # Spec not found
363         with assert_raises(NoSpecError):
364             guess_spec(DATA_DIR, recursive=False)
365         # Multiple spec files
366         with assert_raises(NoSpecError):
367             guess_spec(DATA_DIR, recursive=True)
368         with assert_raises(NoSpecError):
369             guess_spec(SPEC_DIR, recursive=False)
370         # Spec found
371         spec = guess_spec(SPEC_DIR, recursive=False,
372                              preferred_name = 'gbp-test2.spec')
373         eq_(spec.specfile, 'gbp-test2.spec')
374         eq_(spec.specdir, SPEC_DIR)
375
376     def test_guess_spec_repo(self):
377         """Test guess_spec_repo() and spec_from_repo() functions"""
378         # Create dummy repository with some commits
379         repo = GitRepository.create(self.tmpdir)
380         with open(os.path.join(repo.path, 'foo.txt'), 'w') as fobj:
381             fobj.write('bar\n')
382         repo.add_files('foo.txt')
383         repo.commit_all('Add dummy file')
384         os.mkdir(os.path.join(repo.path, 'packaging'))
385         shutil.copy(os.path.join(SPEC_DIR, 'gbp-test.spec'),
386                     os.path.join(repo.path, 'packaging'))
387         repo.add_files('packaging/gbp-test.spec')
388         repo.commit_all('Add spec file')
389
390         # Spec not found
391         with assert_raises(NoSpecError):
392             guess_spec_repo(repo, 'HEAD~1', recursive=True)
393         with assert_raises(NoSpecError):
394             guess_spec_repo(repo, 'HEAD', recursive=False)
395         # Spec found
396         spec = guess_spec_repo(repo, 'HEAD', 'packaging', recursive=False)
397         spec = guess_spec_repo(repo, 'HEAD', recursive=True)
398         eq_(spec.specfile, 'gbp-test.spec')
399         eq_(spec.specdir, 'packaging')
400         eq_(spec.specpath, 'packaging/gbp-test.spec')
401
402         # Test spec_from_repo()
403         with assert_raises(NoSpecError):
404             spec_from_repo(repo, 'HEAD~1', 'packaging/gbp-test.spec')
405         spec = spec_from_repo(repo, 'HEAD', 'packaging/gbp-test.spec')
406         eq_(spec.specfile, 'gbp-test.spec')
407
408 # vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·: