Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / scripts / test / test_tap2rpm.py
1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
3
4 """
5 Tests for L{twisted.scripts.tap2rpm}.
6 """
7 import os
8
9 from twisted.trial.unittest import TestCase, SkipTest
10 from twisted.python import procutils
11 from twisted.python import versions
12 from twisted.python import deprecate
13 from twisted.python.failure import Failure
14 from twisted.internet import utils
15 from twisted.scripts import tap2rpm
16
17 # When we query the RPM metadata, we get back a string we'll have to parse, so
18 # we'll use suitably rare delimiter characters to split on. Luckily, ASCII
19 # defines some for us!
20 RECORD_SEPARATOR = "\x1E"
21 UNIT_SEPARATOR = "\x1F"
22
23
24
25 def _makeRPMs(tapfile=None, maintainer=None, protocol=None, description=None,
26         longDescription=None, setVersion=None, rpmfile=None, type_=None):
27     """
28     Helper function to invoke tap2rpm with the given parameters.
29     """
30     args = []
31
32     if not tapfile:
33         tapfile = "dummy-tap-file"
34         handle = open(tapfile, "w")
35         handle.write("# Dummy TAP file\n")
36         handle.close()
37
38     args.extend(["--quiet", "--tapfile", tapfile])
39
40     if maintainer:
41         args.extend(["--maintainer", maintainer])
42     if protocol:
43         args.extend(["--protocol", protocol])
44     if description:
45         args.extend(["--description", description])
46     if longDescription:
47         args.extend(["--long_description", longDescription])
48     if setVersion:
49         args.extend(["--set-version", setVersion])
50     if rpmfile:
51         args.extend(["--rpmfile", rpmfile])
52     if type_:
53         args.extend(["--type", type_])
54
55     return tap2rpm.run(args)
56
57
58
59 def _queryRPMTags(rpmfile, taglist):
60     """
61     Helper function to read the given header tags from the given RPM file.
62
63     Returns a Deferred that fires with dictionary mapping a tag name to a list
64     of the associated values in the RPM header. If a tag has only a single
65     value in the header (like NAME or VERSION), it will be returned as a 1-item
66     list.
67
68     Run "rpm --querytags" to see what tags can be queried.
69     """
70
71     # Build a query format string that will return appropriately delimited
72     # results. Every field is treated as an array field, so single-value tags
73     # like VERSION will be returned as 1-item lists.
74     queryFormat = RECORD_SEPARATOR.join([
75             "[%%{%s}%s]" % (tag, UNIT_SEPARATOR) for tag in taglist
76            ])
77
78     def parseTagValues(output):
79         res = {}
80
81         for tag, values in zip(taglist, output.split(RECORD_SEPARATOR)):
82             values = values.strip(UNIT_SEPARATOR).split(UNIT_SEPARATOR)
83             res[tag] = values
84
85         return res
86
87     def checkErrorResult(failure):
88         # The current rpm packages on Debian and Ubuntu don't properly set up
89         # the RPM database, which causes rpm to print a harmless warning to
90         # stderr. Unfortunately, .getProcessOutput() assumes all warnings are
91         # catastrophic and panics whenever it sees one.
92         #
93         # See also:
94         #   http://twistedmatrix.com/trac/ticket/3292#comment:42
95         #   http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=551669
96         #   http://rpm.org/ticket/106
97
98         failure.trap(IOError)
99
100         # Depending on kernel scheduling, we might read the whole error
101         # message, or only the first few bytes.
102         if str(failure.value).startswith("got stderr: 'error: "):
103             newFailure = Failure(SkipTest("rpm is missing its package "
104                     "database. Run 'sudo rpm -qa > /dev/null' to create one."))
105         else:
106             # Not the exception we were looking for; we should report the
107             # original failure.
108             newFailure = failure
109
110         # We don't want to raise the exception right away; we want to wait for
111         # the process to exit, otherwise we'll get extra useless errors
112         # reported.
113         d = failure.value.processEnded
114         d.addBoth(lambda _: newFailure)
115         return d
116
117     d = utils.getProcessOutput("rpm",
118             ("-q", "--queryformat", queryFormat, "-p", rpmfile))
119     d.addCallbacks(parseTagValues, checkErrorResult)
120     return d
121
122
123
124 class TestTap2RPM(TestCase):
125
126
127     def setUp(self):
128         return self._checkForRpmbuild()
129
130
131     def _checkForRpmbuild(self):
132         """
133         tap2rpm requires rpmbuild; skip tests if rpmbuild is not present.
134         """
135         if not procutils.which("rpmbuild"):
136             raise SkipTest("rpmbuild must be present to test tap2rpm")
137
138
139     def _makeTapFile(self, basename="dummy"):
140         """
141         Make a temporary .tap file and returns the absolute path.
142         """
143         path = basename + ".tap"
144         handle = open(path, "w")
145         handle.write("# Dummy .tap file")
146         handle.close()
147         return path
148
149
150     def _verifyRPMTags(self, rpmfile, **tags):
151         """
152         Check the given file has the given tags set to the given values.
153         """
154
155         d = _queryRPMTags(rpmfile, tags.keys())
156         d.addCallback(self.assertEqual, tags)
157         return d
158
159
160     def test_optionDefaults(self):
161         """
162         Commandline options should default to sensible values.
163
164         "sensible" here is defined as "the same values that previous versions
165         defaulted to".
166         """
167         config = tap2rpm.MyOptions()
168         config.parseOptions([])
169
170         self.assertEqual(config['tapfile'], 'twistd.tap')
171         self.assertEqual(config['maintainer'], 'tap2rpm')
172         self.assertEqual(config['protocol'], 'twistd')
173         self.assertEqual(config['description'], 'A TCP server for twistd')
174         self.assertEqual(config['long_description'],
175                 'Automatically created by tap2rpm')
176         self.assertEqual(config['set-version'], '1.0')
177         self.assertEqual(config['rpmfile'], 'twisted-twistd')
178         self.assertEqual(config['type'], 'tap')
179         self.assertEqual(config['quiet'], False)
180         self.assertEqual(config['twistd_option'], 'file')
181         self.assertEqual(config['release-name'], 'twisted-twistd-1.0')
182
183
184     def test_protocolCalculatedFromTapFile(self):
185         """
186         The protocol name defaults to a value based on the tapfile value.
187         """
188         config = tap2rpm.MyOptions()
189         config.parseOptions(['--tapfile', 'pancakes.tap'])
190
191         self.assertEqual(config['tapfile'], 'pancakes.tap')
192         self.assertEqual(config['protocol'], 'pancakes')
193
194
195     def test_optionsDefaultToProtocolValue(self):
196         """
197         Many options default to a value calculated from the protocol name.
198         """
199         config = tap2rpm.MyOptions()
200         config.parseOptions([
201                 '--tapfile', 'sausages.tap',
202                 '--protocol', 'eggs',
203             ])
204
205         self.assertEqual(config['tapfile'], 'sausages.tap')
206         self.assertEqual(config['maintainer'], 'tap2rpm')
207         self.assertEqual(config['protocol'], 'eggs')
208         self.assertEqual(config['description'], 'A TCP server for eggs')
209         self.assertEqual(config['long_description'],
210                 'Automatically created by tap2rpm')
211         self.assertEqual(config['set-version'], '1.0')
212         self.assertEqual(config['rpmfile'], 'twisted-eggs')
213         self.assertEqual(config['type'], 'tap')
214         self.assertEqual(config['quiet'], False)
215         self.assertEqual(config['twistd_option'], 'file')
216         self.assertEqual(config['release-name'], 'twisted-eggs-1.0')
217
218
219     def test_releaseNameDefaultsToRpmfileValue(self):
220         """
221         The release-name option is calculated from rpmfile and set-version.
222         """
223         config = tap2rpm.MyOptions()
224         config.parseOptions([
225                 "--rpmfile", "beans",
226                 "--set-version", "1.2.3",
227             ])
228
229         self.assertEqual(config['release-name'], 'beans-1.2.3')
230
231
232     def test_basicOperation(self):
233         """
234         Calling tap2rpm should produce an RPM and SRPM with default metadata.
235         """
236         basename = "frenchtoast"
237
238         # Create RPMs based on a TAP file with this name.
239         rpm, srpm = _makeRPMs(tapfile=self._makeTapFile(basename))
240
241         # Verify the resulting RPMs have the correct tags.
242         d = self._verifyRPMTags(rpm,
243                 NAME=["twisted-%s" % (basename,)],
244                 VERSION=["1.0"],
245                 RELEASE=["1"],
246                 SUMMARY=["A TCP server for %s" % (basename,)],
247                 DESCRIPTION=["Automatically created by tap2rpm"],
248             )
249         d.addCallback(lambda _: self._verifyRPMTags(srpm,
250                 NAME=["twisted-%s" % (basename,)],
251                 VERSION=["1.0"],
252                 RELEASE=["1"],
253                 SUMMARY=["A TCP server for %s" % (basename,)],
254                 DESCRIPTION=["Automatically created by tap2rpm"],
255             ))
256
257         return d
258
259
260     def test_protocolOverride(self):
261         """
262         Setting 'protocol' should change the name of the resulting package.
263         """
264         basename = "acorn"
265         protocol = "banana"
266
267         # Create RPMs based on a TAP file with this name.
268         rpm, srpm = _makeRPMs(tapfile=self._makeTapFile(basename),
269                 protocol=protocol)
270
271         # Verify the resulting RPMs have the correct tags.
272         d = self._verifyRPMTags(rpm,
273                 NAME=["twisted-%s" % (protocol,)],
274                 SUMMARY=["A TCP server for %s" % (protocol,)],
275             )
276         d.addCallback(lambda _: self._verifyRPMTags(srpm,
277                 NAME=["twisted-%s" % (protocol,)],
278                 SUMMARY=["A TCP server for %s" % (protocol,)],
279             ))
280
281         return d
282
283
284     def test_rpmfileOverride(self):
285         """
286         Setting 'rpmfile' should change the name of the resulting package.
287         """
288         basename = "cherry"
289         rpmfile = "donut"
290
291         # Create RPMs based on a TAP file with this name.
292         rpm, srpm = _makeRPMs(tapfile=self._makeTapFile(basename),
293                 rpmfile=rpmfile)
294
295         # Verify the resulting RPMs have the correct tags.
296         d = self._verifyRPMTags(rpm,
297                 NAME=[rpmfile],
298                 SUMMARY=["A TCP server for %s" % (basename,)],
299             )
300         d.addCallback(lambda _: self._verifyRPMTags(srpm,
301                 NAME=[rpmfile],
302                 SUMMARY=["A TCP server for %s" % (basename,)],
303             ))
304
305         return d
306
307
308     def test_descriptionOverride(self):
309         """
310         Setting 'description' should change the SUMMARY tag.
311         """
312         description = "eggplant"
313
314         # Create RPMs based on a TAP file with this name.
315         rpm, srpm = _makeRPMs(tapfile=self._makeTapFile(),
316                 description=description)
317
318         # Verify the resulting RPMs have the correct tags.
319         d = self._verifyRPMTags(rpm,
320                 SUMMARY=[description],
321             )
322         d.addCallback(lambda _: self._verifyRPMTags(srpm,
323                 SUMMARY=[description],
324             ))
325
326         return d
327
328
329     def test_longDescriptionOverride(self):
330         """
331         Setting 'longDescription' should change the DESCRIPTION tag.
332         """
333         longDescription = "fig"
334
335         # Create RPMs based on a TAP file with this name.
336         rpm, srpm = _makeRPMs(tapfile=self._makeTapFile(),
337                 longDescription=longDescription)
338
339         # Verify the resulting RPMs have the correct tags.
340         d = self._verifyRPMTags(rpm,
341                 DESCRIPTION=[longDescription],
342             )
343         d.addCallback(lambda _: self._verifyRPMTags(srpm,
344                 DESCRIPTION=[longDescription],
345             ))
346
347         return d
348
349
350     def test_setVersionOverride(self):
351         """
352         Setting 'setVersion' should change the RPM's version info.
353         """
354         version = "123.456"
355
356         # Create RPMs based on a TAP file with this name.
357         rpm, srpm = _makeRPMs(tapfile=self._makeTapFile(),
358                 setVersion=version)
359
360         # Verify the resulting RPMs have the correct tags.
361         d = self._verifyRPMTags(rpm,
362                 VERSION=["123.456"],
363                 RELEASE=["1"],
364             )
365         d.addCallback(lambda _: self._verifyRPMTags(srpm,
366                 VERSION=["123.456"],
367                 RELEASE=["1"],
368             ))
369
370         return d
371
372
373     def test_tapInOtherDirectory(self):
374         """
375         tap2rpm handles tapfiles outside the current directory.
376         """
377         # Make a tapfile outside the current directory.
378         tempdir = self.mktemp()
379         os.mkdir(tempdir)
380         tapfile = self._makeTapFile(os.path.join(tempdir, "bacon"))
381
382         # Try and make an RPM from that tapfile.
383         _makeRPMs(tapfile=tapfile)
384
385
386     def test_unsignedFlagDeprecationWarning(self):
387         """
388         The 'unsigned' flag in tap2rpm should be deprecated, and its use
389         should raise a warning as such.
390         """
391         config = tap2rpm.MyOptions()
392         config.parseOptions(['--unsigned'])
393         warnings = self.flushWarnings()
394         self.assertEqual(DeprecationWarning, warnings[0]['category'])
395         self.assertEqual(
396             deprecate.getDeprecationWarningString(
397                 config.opt_unsigned, versions.Version("Twisted", 12, 1, 0)),
398             warnings[0]['message'])
399         self.assertEqual(1, len(warnings))