| | 1 | # Copyright (c) 2009 Twisted Matrix Laboratories. |
| | 2 | # See LICENSE for details. |
| | 3 | |
| | 4 | """ |
| | 5 | Tests for L{twisted.scripts.tap2rpm}. |
| | 6 | """ |
| | 7 | from os.path import exists |
| | 8 | from twisted.trial.unittest import TestCase, SkipTest |
| | 9 | from twisted.python import log |
| | 10 | from twisted.internet import utils |
| | 11 | from twisted.scripts import tap2rpm |
| | 12 | |
| | 13 | # When we query the RPM metadata, we get back a string we'll have to parse, so |
| | 14 | # we'll use suitably rare delimiter characters to split on. Luckily, ASCII |
| | 15 | # defines some for us! |
| | 16 | RECORD_SEPARATOR = "\x1E" |
| | 17 | UNIT_SEPARATOR = "\x1F" |
| | 18 | |
| | 19 | |
| | 20 | |
| | 21 | def _makeRPMs(tapfile=None, maintainer=None, protocol=None, description=None, |
| | 22 | longDescription=None, setVersion=None, rpmfile=None, type_=None): |
| | 23 | """ |
| | 24 | Helper function to invoke tap2rpm with the given parameters. |
| | 25 | """ |
| | 26 | args = [] |
| | 27 | |
| | 28 | if not tapfile: |
| | 29 | tapfile = "dummy-tap-file" |
| | 30 | handle = open(tapfile, "w") |
| | 31 | handle.write("# Dummy TAP file\n") |
| | 32 | handle.close() |
| | 33 | |
| | 34 | args.extend(["--tapfile", tapfile]) |
| | 35 | |
| | 36 | if maintainer: |
| | 37 | args.extend(["--maintainer", maintainer]) |
| | 38 | if protocol: |
| | 39 | args.extend(["--protocol", protocol]) |
| | 40 | if description: |
| | 41 | args.extend(["--description", description]) |
| | 42 | if longDescription: |
| | 43 | args.extend(["--long_description", longDescription]) |
| | 44 | if setVersion: |
| | 45 | args.extend(["--set-version", setVersion]) |
| | 46 | if rpmfile: |
| | 47 | args.extend(["--rpmfile", rpmfile]) |
| | 48 | if type_: |
| | 49 | args.extend(["--type", type_]) |
| | 50 | |
| | 51 | return tap2rpm.run(args) |
| | 52 | |
| | 53 | |
| | 54 | |
| | 55 | def _queryRPMTags(rpmfile, taglist): |
| | 56 | """ |
| | 57 | Helper function to read the given header tags from the given RPM file. |
| | 58 | |
| | 59 | Returns a Deferred that fires with dictionary mapping a tag name to a list |
| | 60 | of the associated values in the RPM header. If a tag has only a single |
| | 61 | value in the header (like NAME or VERSION), it will be returned as a 1-item |
| | 62 | list. |
| | 63 | |
| | 64 | Run "rpm --querytags" to see what tags can be queried. |
| | 65 | """ |
| | 66 | |
| | 67 | # Build a query format string that will return appropriately delimited |
| | 68 | # results. Every field is treated as an array field, so single-value tags |
| | 69 | # like VERSION will be returned as 1-item lists. |
| | 70 | queryFormat = RECORD_SEPARATOR.join([ |
| | 71 | "[%%{%s}%s]" % (tag, UNIT_SEPARATOR) for tag in taglist |
| | 72 | ]) |
| | 73 | |
| | 74 | def parseTagValues(output): |
| | 75 | res = {} |
| | 76 | |
| | 77 | for tag, values in zip(taglist, output.split(RECORD_SEPARATOR)): |
| | 78 | values = values.strip(UNIT_SEPARATOR).split(UNIT_SEPARATOR) |
| | 79 | res[tag] = values |
| | 80 | |
| | 81 | return res |
| | 82 | |
| | 83 | d = utils.getProcessOutput("rpm", |
| | 84 | ("-q", "--queryformat", queryFormat, "-p", rpmfile)) |
| | 85 | d.addCallback(parseTagValues) |
| | 86 | return d |
| | 87 | |
| | 88 | |
| | 89 | |
| | 90 | class TestTap2RPM(TestCase): |
| | 91 | |
| | 92 | |
| | 93 | def setUp(self): |
| | 94 | return self._checkForRpmbuild() |
| | 95 | |
| | 96 | |
| | 97 | def _checkForRpmbuild(self): |
| | 98 | """ |
| | 99 | tap2rpm requires rpmbuild; skip tests if rpmbuild is not present. |
| | 100 | """ |
| | 101 | skipMsg = "rpmbuild must be present to test tap2rpm" |
| | 102 | def skipTestIfNotFound(result): |
| | 103 | out, err, code = result |
| | 104 | # If there was a problem running rpmbuild, or it looks like we've |
| | 105 | # accidentally found some unrelated script with the same name, skip |
| | 106 | # these tests. |
| | 107 | if code != 0 or not out.startswith("RPM version"): |
| | 108 | raise SkipTest(skipMsg) |
| | 109 | |
| | 110 | def skipTestIfError(failure): |
| | 111 | # According to the spawnProcess docs, OSError is raised if new |
| | 112 | # proceses can't be created for some reason. According to #4184, |
| | 113 | # OSError is raised if Windows can't find the program to be run. |
| | 114 | failure.trap(OSError) |
| | 115 | raise SkipTest(skipMsg) |
| | 116 | |
| | 117 | d = utils.getProcessOutputAndValue("rpmbuild", ("--version",)) |
| | 118 | d.addCallbacks(skipTestIfNotFound, skipTestIfError) |
| | 119 | return d |
| | 120 | |
| | 121 | |
| | 122 | def _makeTapFile(self, basename="dummy"): |
| | 123 | """ |
| | 124 | Makes a temporary .tap file and returns the absolute path. |
| | 125 | """ |
| | 126 | path = basename + ".tap" |
| | 127 | handle = open(path, "w") |
| | 128 | handle.write("# Dummy .tap file") |
| | 129 | handle.close() |
| | 130 | return path |
| | 131 | |
| | 132 | |
| | 133 | def _verifyRPMTags(self, rpmfile, **tags): |
| | 134 | """ |
| | 135 | Checks the given file has the given tags set to the given values. |
| | 136 | """ |
| | 137 | |
| | 138 | d = _queryRPMTags(rpmfile, tags.keys()) |
| | 139 | d.addCallback(self.failUnlessEqual, tags) |
| | 140 | return d |
| | 141 | |
| | 142 | |
| | 143 | def test_basicOperation(self): |
| | 144 | """ |
| | 145 | Calling tap2rpm should produce an RPM and SRPM with default metadata. |
| | 146 | """ |
| | 147 | basename = "frenchtoast" |
| | 148 | |
| | 149 | # Create RPMs based on a TAP file with this name. |
| | 150 | rpm, srpm = _makeRPMs(tapfile = self._makeTapFile(basename)) |
| | 151 | |
| | 152 | # Verify the resulting RPMs have the correct tags. |
| | 153 | d = self._verifyRPMTags(rpm, |
| | 154 | NAME=["twisted-%s" % (basename,)], |
| | 155 | VERSION=["1.0"], |
| | 156 | RELEASE=["1"], |
| | 157 | SUMMARY=["A TCP server for %s" % (basename,)], |
| | 158 | DESCRIPTION=["Automatically created by tap2rpm"], |
| | 159 | ) |
| | 160 | d.addCallback(lambda _: self._verifyRPMTags(srpm, |
| | 161 | NAME=["twisted-%s" % (basename,)], |
| | 162 | VERSION=["1.0"], |
| | 163 | RELEASE=["1"], |
| | 164 | SUMMARY=["A TCP server for %s" % (basename,)], |
| | 165 | DESCRIPTION=["Automatically created by tap2rpm"], |
| | 166 | )) |
| | 167 | |
| | 168 | return d |
| | 169 | |
| | 170 | |
| | 171 | def test_protocolOverride(self): |
| | 172 | """ |
| | 173 | Setting 'protocol' should change the name of the resulting package. |
| | 174 | """ |
| | 175 | basename = "acorn" |
| | 176 | protocol = "banana" |
| | 177 | |
| | 178 | # Create RPMs based on a TAP file with this name. |
| | 179 | rpm, srpm = _makeRPMs(tapfile = self._makeTapFile(basename), |
| | 180 | protocol=protocol) |
| | 181 | |
| | 182 | # Verify the resulting RPMs have the correct tags. |
| | 183 | d = self._verifyRPMTags(rpm, |
| | 184 | NAME=["twisted-%s" % (protocol,)], |
| | 185 | SUMMARY=["A TCP server for %s" % (protocol,)], |
| | 186 | ) |
| | 187 | d.addCallback(lambda _: self._verifyRPMTags(srpm, |
| | 188 | NAME=["twisted-%s" % (protocol,)], |
| | 189 | SUMMARY=["A TCP server for %s" % (protocol,)], |
| | 190 | )) |
| | 191 | |
| | 192 | return d |
| | 193 | |
| | 194 | |
| | 195 | def test_rpmfileOverride(self): |
| | 196 | """ |
| | 197 | Setting 'rpmfile' should change the name of the resulting package. |
| | 198 | """ |
| | 199 | basename = "cherry" |
| | 200 | rpmfile = "donut" |
| | 201 | |
| | 202 | # Create RPMs based on a TAP file with this name. |
| | 203 | rpm, srpm = _makeRPMs(tapfile = self._makeTapFile(basename), |
| | 204 | rpmfile=rpmfile) |
| | 205 | |
| | 206 | # Verify the resulting RPMs have the correct tags. |
| | 207 | d = self._verifyRPMTags(rpm, |
| | 208 | NAME=[rpmfile], |
| | 209 | SUMMARY=["A TCP server for %s" % (basename,)], |
| | 210 | ) |
| | 211 | d.addCallback(lambda _: self._verifyRPMTags(srpm, |
| | 212 | NAME=[rpmfile], |
| | 213 | SUMMARY=["A TCP server for %s" % (basename,)], |
| | 214 | )) |
| | 215 | |
| | 216 | return d |
| | 217 | |
| | 218 | |
| | 219 | def test_descriptionOverride(self): |
| | 220 | """ |
| | 221 | Setting 'description' should change the SUMMARY tag. |
| | 222 | """ |
| | 223 | description="eggplant" |
| | 224 | |
| | 225 | # Create RPMs based on a TAP file with this name. |
| | 226 | rpm, srpm = _makeRPMs(tapfile = self._makeTapFile(), |
| | 227 | description=description) |
| | 228 | |
| | 229 | # Verify the resulting RPMs have the correct tags. |
| | 230 | d = self._verifyRPMTags(rpm, |
| | 231 | SUMMARY=[description], |
| | 232 | ) |
| | 233 | d.addCallback(lambda _: self._verifyRPMTags(srpm, |
| | 234 | SUMMARY=[description], |
| | 235 | )) |
| | 236 | |
| | 237 | return d |
| | 238 | |
| | 239 | |
| | 240 | def test_longDescriptionOverride(self): |
| | 241 | """ |
| | 242 | Setting 'longDescription' should change the DESCRIPTION tag. |
| | 243 | """ |
| | 244 | longDescription="fig" |
| | 245 | |
| | 246 | # Create RPMs based on a TAP file with this name. |
| | 247 | rpm, srpm = _makeRPMs(tapfile = self._makeTapFile(), |
| | 248 | longDescription=longDescription) |
| | 249 | |
| | 250 | # Verify the resulting RPMs have the correct tags. |
| | 251 | d = self._verifyRPMTags(rpm, |
| | 252 | DESCRIPTION=[longDescription], |
| | 253 | ) |
| | 254 | d.addCallback(lambda _: self._verifyRPMTags(srpm, |
| | 255 | DESCRIPTION=[longDescription], |
| | 256 | )) |
| | 257 | |
| | 258 | return d |
| | 259 | |
| | 260 | |
| | 261 | def test_setVersionOverride(self): |
| | 262 | """ |
| | 263 | Setting 'setVersion' should change the RPM's version info. |
| | 264 | """ |
| | 265 | version="123.456" |
| | 266 | |
| | 267 | # Create RPMs based on a TAP file with this name. |
| | 268 | rpm, srpm = _makeRPMs(tapfile = self._makeTapFile(), |
| | 269 | setVersion=version) |
| | 270 | |
| | 271 | # Verify the resulting RPMs have the correct tags. |
| | 272 | d = self._verifyRPMTags(rpm, |
| | 273 | VERSION=["123.456"], |
| | 274 | RELEASE=["1"], |
| | 275 | ) |
| | 276 | d.addCallback(lambda _: self._verifyRPMTags(srpm, |
| | 277 | VERSION=["123.456"], |
| | 278 | RELEASE=["1"], |
| | 279 | )) |
| | 280 | |
| | 281 | return d |