import unittest import logging import os import uuid import string from collections import OrderedDict import copy from vers import Spin from vers import Rc from vers import Vendor from vers import Package from vers import packageFromStr from datetime import datetime from dateutil.relativedelta import relativedelta class Series(object): """ A Series represents a package candidate, snapshot, or release and includes version, package, date, and spin information. """ series = ['candidate', 'snapshot', 'release'] serieslongupper = ['Release-Candidate', 'Snapshot', 'Release'] # @ seriestype - 'type' of either 'candidate', 'snapshot', or 'release' # @ package - String or Package object representing the series package # name with version string, e.g., 'GCC-5.3.1' # @ vendor - String or Vendor object representing the vendor that is # tagged on this series. # @ date - String or 'datetime' object representing the YYYY.MM of this # series. It defaults to 'today' # @ spin - Optional Spin, str, or int # @ rc - Optional Rc, str, or int # This will make sure snapshot doesn't have an rc and release doesn't have # an rc for instance. def __init__(self, seriestype, vendor=None, package=None, date=datetime.today(), spin=None, rc=None, strict=True): """ Create a Series to store the details on a package release, snapshot, or release-candidate. Parameters ---------- seriestype : str "candidate", "release", or "snapshot" vendor : str A String representing the vendor for the series. The default is inherited from the Vendor class. package : str or Package Designate the package that this Series represents, for example, 'gcc'. The default is inherited from the Package class. date : str or datetime "YYYY.MM", "YY.MM", or a datetime object representing the Series. spin : Spin A spin number for the Series. rc : Rc An rc number for the Series. strict=True : bool Enforce rules regarding whether candidates can/cannot have Rcs. """ if isinstance(spin, Spin): self.spin = spin else: # Spin will raise an exception if spin is the wrong type. # A None parameter will create a Spin with an internal value of # None. self.spin = Spin(spin) if isinstance(rc, Rc): self.rc = rc else: # Rc will raise an exception if rc is the wrong type. A None parameter # will create an Rc with an internal value of None. self.rc = Rc(rc) if isinstance(date, datetime): # Force all of the days of the month to 15 to unify comparisons. date.replace(day=15) self.date = date else: # 'date' is a string. If the input date can't be parsed by datetime # it will throw an exception. We can't recover from it so just pass # it up. if len(date) < 10: tmpdate = datetime.strptime(date, "%Y.%m") else: tmpdate = datetime.strptime(date, "%Y.%m.%d") # Force all of the days of the month to 15 to unify comparisons. self.date = tmpdate.replace(day=15) # If the input vendor=None just instantiate the default vendor. if not vendor: self.vendor = Vendor() else: self.vendor = vendor if not package: raise TypeError('Series requires an input package.') elif isinstance(package, Package): self.package = package elif isinstance(package, basestring): self.package = packageFromStr(package) else: # There can't be a defaut package because it requires a version. raise TypeError( "Series 'package' unrecognized type " + str(type(package))) # We might want to uniquely identify a particular Series object. self.uniqueid = str(uuid.uuid4()) # We store a seriestype as an integer into an enumeration array # so that the names can be changed as desired. try: self.seriestype = Series.series.index(seriestype.lower()) except ValueError: self.seriestype = -1 # no match raise TypeError('Invalid series type %s.' % seriestype) # rc can only be non-None for release-candidates. if strict: if self.seriestype == Series.series.index("snapshot"): if self.rc.val != 0: raise ValueError('A snapshot series cannot have an rc.') elif self.seriestype == Series.series.index("release"): if self.rc.val != 0: raise ValueError('A release series cannot have an rc.') if self.seriestype == Series.series.index( "candidate") and self.rc.val is 0: raise TypeError('A candidate series must have an rc specified.') # We need an OrderedDict because we want to parse/capture -%X and .%X # before %X and a regular dict won't guarantee that iterkeys returns in # the inserted order.. This dict starts out empty and we'll populate it # in the same way we will udpate it later. This is used for the # __format__ function for quick formatting. self.fmt = OrderedDict() self.fmt['%N'] = None self.fmt['-%L'] = None self.fmt['.%L'] = None self.fmt['%L'] = None self.fmt['%P'] = None self.fmt['%l'] = None self.fmt['%V'] = None self.fmt['%v'] = None self.fmt['%E'] = None self.fmt['%e'] = None self.fmt['%M'] = None self.fmt['%m'] = None self.fmt['%p'] = None self.fmt['%D'] = None self.fmt['%h'] = None self.fmt['%d'] = None # Match and strip the key '-' as Spin might be empty. self.fmt['-%S'] = None # Match and strip the key '.' as Spin might be empty. self.fmt['.%S'] = None self.fmt['%S'] = None # Match and strip the key '-' as Rc might be empty. self.fmt['-%R'] = None # Match and strip the key '.' as Rc might be empty. self.fmt['.%R'] = None self.fmt['%R'] = None # Fill in the values. for key in self.fmt.iterkeys(): self.update_fmtdict(key) def update_fmtdict(self, key): if key == '%N': self.fmt['%N'] = self.get_namespace() elif key == '-%L': self.fmt['-%L'] = self.serieslabel() return elif key == '.%L': # Only prepend '.' if there's actually a series label. label = self.serieslabel() self.fmt['.%L'] = string.replace(label, '-', '.') return elif key == '%L': self.fmt['%L'] = self.serieslabel().strip('-') return elif key == '%P': self.fmt['%P'] = self.package.package elif key == '%l': self.fmt['%l'] = self.package.package.lower() elif key == '%V': self.fmt['%V'] = str(self.vendor) elif key == '%v': self.fmt['%v'] = self.vendor.lower() elif key == '%E': self.fmt['%E'] = self.package.version.strfversion("%M%m") elif key == '%e': self.fmt['%e'] = self.package.version.strfversion("%M%m%p") elif key == '%M': self.fmt['%M'] = self.package.version.strfversion("%M") elif key == '%m': self.fmt['%m'] = self.package.version.strfversion("%m") elif key == '%p': self.fmt['%p'] = self.package.version.strfversion("%p") elif key == '%D': self.fmt['%D'] = self.date.strftime("%Y.%m") elif key == '%h': self.fmt['%h'] = self.get_server() elif key == '%d': self.fmt['%d'] = self.get_dir() elif key == '-%S': # This will include the '-' if present. self.fmt['-%S'] = str(self.spin) elif key == '.%S': spin = str(self.spin) self.fmt['.%S'] = string.replace(spin, '-', '.') elif key == '%S': self.fmt['%S'] = str(self.spin).strip('-') elif key == '-%R': # This will include the '-' is present. self.fmt['-%R'] = str(self.rc) elif key == '.%R': rc = str(self.rc) self.fmt['.%R'] = string.replace(rc, '-', '.') elif key == '%R': self.fmt['%R'] = str(self.rc).strip('-') else: raise KeyError('Unknown format key.') def __format__(self, format_spec): """ Format an output string based on format spec. This function will take a format spec which includes mixture of text to be preserved as well as format designators which indicate series information to be substituted in place. Take the following format_spec: "%V released this product" This would be formatted as "Linaro released this product" call using: formattedstr=format(series_instance, 'string_with_delimiters') delimiters ---------- %N : git repository branch namespace. %L : series label This is only valid for snapshots. Releases and candidates will result in an empty string. Note: This is special. If -%L or .%L is in the format_spec and there is no series label (for release and candidates) then the leading '-' or '.' will be stripped as well. %P : package name %l : lowercase package name %V : vendor %v : lowercase vendor %E : Version Major.Minor Note: if Minor doen't exist the . delimiter won't be displayed. %E : Version Major.Minor.Point Note: if Minor or Point don't exist the . delimiters won't be displayed. %M : Version Major %m : Version Minor Note: if Minor doesn't exist the . delimiter won't be displayed. %p : Version Point Note: if Point doesn't exist the . delimiter won't be displayed. %D : Series date in YYYY.MM format. %h : snapshot or release server. %d : Series dir representation. %S : Spin number Note: This is special. If -%S or .%S is in the format_spec and there is no Spin then the leading '-' or '.' will be stripped as well. %R : Rc number Note: This is special. If -%R or .%R is in the format_spec and there is no Rc then the leading '-' or '.' will be stripped as well. """ ret = '' # Iterate across the dictionary and for each key found replace the key # with the contents of the fmt dictionary. ret = format_spec for key in self.fmt.iterkeys(): if key in ret: # Update the relevant keys everytime through because the user # might have changed things. self.update_fmtdict(key) replac = self.fmt[key] ret = string.replace(ret, key, replac) return ret def serieslabel(self): # Only the 'snapshot-' is used in a label. if self.seriestype == Series.series.index("snapshot"): return u'-' + Series.series[self.seriestype] else: return u'' def shorttype(self): return Series.series[self.seriestype] def longlowertype(self): return Series.serieslongupper[self.seriestype].lower() def longuppertype(self): return Series.serieslongupper[self.seriestype] def __str__(self): return Series.series[self.seriestype] + "_" + self.uniqueid def label(self): label = str(self.vendor) + self.serieslabel() + \ u'-' + str(self.package) label = label + u'-' + self.date.strftime("%Y.%m") label = label + str(self.spin) + str(self.rc) return label def incrementMonth(self): self.date = self.date + relativedelta(months=1) def get_dir(self): dirstr = self.package.version.strfversion("%M%m") dirstr = dirstr + u'-' + self.date.strftime("%Y.%m") dirstr = dirstr + str(self.spin) dirstr = dirstr + str(self.rc) return dirstr def get_server(self): namespace = u'snapshots' if self.seriestype == Series.series.index("release"): namespace = u'releases' return namespace def get_namespace(self): namespace = u'releases' if self.seriestype == Series.series.index("snapshot"): namespace = u'snapshots' return namespace # TODO: Document what a conformant branch name looks like. # Return a conformant branch name from the Series information. def branchname(self): """ Return a branchname in the form of: /---- return value ------------ string represensting the branch name. """ branchname = u'' if self.seriestype == Series.series.index("snapshot"): branchname = branchname + u'snapshots/' else: branchname = branchname + u'releases/' branchname = branchname + self.vendor.lower() branchname = branchname + u'-' + \ self.package.version.strfversion("%M%m") branchname = branchname + u'-' + self.date.strftime("%Y.%m") branchname = branchname + str(self.spin) branchname = branchname + str(self.rc) return branchname def series_from_branchname(branch=None): """ Create a Series from a branch name string. Parameters ---------- branch : str The branchname of a package as it lives in a git repository. A properly formed branch name has the following elements, where options in brackets are optional: /-.[-spin][-rcN] Snapshot -------- snapshots/[!-rcN] Candidate --------- releases/-rcN Release ------- releases/[!-rcN] """ logging.info("Branch is %s." % branch) if not isinstance(branch, basestring): raise TypeError( 'series_from_branchname requires a basestring as input') if not branch: raise TypeError( 'series_from_branchname requires a non-empty string as input') # Get the part before the '/'. That's the namespace try: namespace = branch.rsplit('/', 1)[0] except IndexError: raise ValueError( 'string must have a namespace, e.g., /') # Get everything after the '/'. try: seriesstr = branch.rsplit('/', 1)[1] except IndexError: raise ValueError( "string must delimit the namespace from the right branch with a" " '/' char, e.g., /-") # TODO: Are these necessary or did the previous cases cover it? # if namespace == u'': # raise ValueError("Couldn't parse a namespace from input string") # This will catch where there's a namespace/ but no right hand value after # the /. if seriesstr == u'': raise ValueError( "Couldn't parse a series from input string. Missing a branch name.") keys = ['vendor', 'version', 'date', 'spin', 'rc'] # This might generate key errors, depending on whether anything can be # parsed from the right-hand values. values = seriesstr.split('-') dictionary = dict(zip(keys, values)) # if there is no spin but there is an 'rcX' in the spin key:value pair # it means that there's really no spin but should be in the rc key:value # pair. if "spin" in dictionary and "rc" in dictionary["spin"]: dictionary["rc"] = dictionary["spin"] dictionary.pop("spin", None) # We need to have None fields in the missing keys for when we call the # Series constructor. if "rc" not in dictionary: dictionary["rc"] = None else: # strip the "rc" and just leave the int. if dictionary["rc"].startswith("rc"): try: dictionary["rc"] = int(dictionary["rc"][2:]) except ValueError: raise TypeError("The rc value must be an integer.") # We need to have None fields in the missing keys for when we call the # Series constructor. if "spin" not in dictionary: dictionary["spin"] = None else: if not dictionary["spin"].isnumeric(): raise TypeError("The spin must be numeric.") seriesdate = datetime.today if dictionary["date"]: datekeys = ['year', 'month', 'day'] datevalues = dictionary["date"].split('.') datefields = dict(zip(datekeys, datevalues)) if "day" not in datefields: datefields["day"] = "15" seriesdate = datetime(int(datefields["year"]), int( datefields["month"]), int(datefields["day"])) if "snapshots" in namespace and dictionary["rc"]: raise ValueError( 'A snapshots namespace can not have an rc. This is a non-conforming input.') elif "releases" not in namespace and dictionary["rc"]: raise ValueError( 'An rc must have a "releases" namespace. This is a non-conforming input.') elif dictionary["rc"]: seriesname = "candidate" elif "snapshots" in namespace: seriesname = "snapshot" elif "releases" in namespace: seriesname = "release" elif "snapshot" in namespace: raise ValueError( '"snapshot" is not a complete namespace. The conforming namespace is "snapshots".') else: # TODO test for unknown namespace. raise ValueError('Unknown namespace in input string.') package = Package(package="GCC", version=dictionary["version"]) series = Series(seriesname, package=package, date=seriesdate, spin=dictionary[ "spin"], rc=dictionary["rc"], strict=True) return series def series_from_tag(tag=None): """ Create a Series from a git tag name Parameters ---------- tag : str The git tag of a package as it lives in a git repository. A properly formed tag will take the following form: Snapshot -------- linaro-snapshot-Maj.min-YYYY.MM[-spin] Candidate --------- linaro-Maj.min-YYYY.MM[-spin]-rcN Release ------- linaro-Maj.min-YYYY.MM[-spin] """ # TODO Test this. if not tag: raise ValueError('series_from_tag requires a tag') # TODO Test this. if not isinstance(tag, basestring): raise TypeError('series_from_tag requires a basestring as input') # This is default, we'll replace with 'snapshots' if we detect that the # tag is indeed a snapshot. namespace = "releases" # since 'snapshots' tags aren't the same as release and rc tags we need # to force conformance, i.e., remove "-snapshot", but record the # namespace as snapshots while we're at it. if "snapshot" in tag: tag = string.replace(tag, "-snapshot", '') namespace = "snapshots" # Now we're going to cheat and fabricate a false branchname. branchname = namespace + '/' + tag # ... in which case we can reuse this function. return series_from_branchname(branchname) from vers import versionFromStr class TestSeries(unittest.TestCase): """Test the Series class using the unittest framework.""" def test_missing_seriestype(self): with self.assertRaises(TypeError): spin = Spin() rc = Rc() candidate = Series(package="GCC-5.3.1", spin=spin, rc=rc) def test_no_match(self): with self.assertRaises(TypeError): candidate = Series("foobar", package="GCC-5.3.1") def test_partial_seriestype_match(self): with self.assertRaises(TypeError): candidate = Series("candid", package="GCC-5.3.1") def test_excessive_seriestype_match(self): with self.assertRaises(TypeError): candidate = Series("candidate2", package="GCC-5.3.1") def test_match_release(self): release = Series("release", package="GCC-5.3.1") self.assertEqual(str(release).split("_")[0], "release") def test_match_candidate_wrongcase(self): candidate = Series("Candidate", package="GCC-5.3.1", rc="1") self.assertEqual(str(candidate).split("_")[0], "candidate") def test_match_snapshot_wrongcase(self): snapshot = Series("SNAPSHOT", package="GCC-5.3.1") self.assertEqual(str(snapshot).split("_")[0], "snapshot") def test_longlowertype_candidate(self): candidate = Series("candidate", package="GCC-5.3.1", rc="1") self.assertEqual(candidate.longlowertype(), "release-candidate") def test_longuppertype_candidate(self): candidate = Series("candidate", package="GCC-5.3.1", rc="4") self.assertEqual(candidate.longuppertype(), "Release-Candidate") def test_shorttype_candidate(self): candidate = Series("candidate", package="GCC-5.3.1", rc="1") self.assertEqual(candidate.shorttype(), "candidate") def test_serieslabel_candidate(self): candidate = Series("candidate", package="GCC-5.3.1", rc="1") self.assertEqual(candidate.serieslabel(), "") def test_serieslabel_release(self): candidate = Series("release", package="GCC-5.3.1") self.assertEqual(candidate.serieslabel(), "") def test_serieslabel_snapshot(self): candidate = Series("snapshot", package="GCC-5.3.1") self.assertEqual(candidate.serieslabel(), "-snapshot") def test_empty_rc(self): rc = Rc(7) candidate = Series("candidate", package="GCC-5.3.1", rc=rc) self.assertEqual(candidate.rc.vers, 7) self.assertEqual(str(candidate.rc), "FOO") def test_empty_rc(self): rc = Rc() release = Series("release", package="GCC-5.3.1", rc=rc) self.assertEqual(str(release.rc), "") release2 = Series("release", package="GCC-5.3.1") self.assertEqual(str(release2.rc), "") def test_specified_rc(self): rc = Rc(7) candidate = Series("candidate", package="GCC-5.3.1", rc=rc) self.assertEqual(candidate.rc.val, 7) self.assertEqual(str(candidate.rc), "-rc7") def test_candidate_with_no_rc(self): with self.assertRaises(TypeError): candidate = Series("candidate", package="GCC-5.3.1") with self.assertRaises(TypeError): rc = Rc() candidate2 = Series("candidate", package="GCC-5.3.1", rc=rc) def test_rc_as_string(self): candidate = Series("candidate", package="GCC-5.3.1", rc="7") self.assertEqual(candidate.rc.val, 7) self.assertEqual(str(candidate.rc), "-rc7") def test_rc_as_int(self): candidate = Series("candidate", package="GCC-5.3.1", rc=7) self.assertEqual(candidate.rc.val, 7) self.assertEqual(str(candidate.rc), "-rc7") def test_rc_as_typeerror(self): floatrc = 7.0 with self.assertRaises(TypeError): snapshot = Series("snapshot", package="GCC-5.3.1", rc=floatrc) def test_rc_as_negative(self): with self.assertRaises(ValueError): snapshot = Series("snapshot", package="GCC-5.3.1", rc="-1") def test_missing_spin(self): snapshot = Series("snapshot", package="GCC-5.3.1") self.assertEqual(snapshot.spin.val, 0) self.assertEqual(str(snapshot.spin), "") def test_specified_spin(self): spin = Spin(7) snapshot = Series("snapshot", package="GCC-5.3.1", spin=spin) self.assertEqual(snapshot.spin.val, 7) self.assertEqual(str(snapshot.spin), "-7") def test_empty_spin(self): spin = Spin() snapshot = Series("snapshot", package="GCC-5.3.1", spin=spin) self.assertEqual(str(snapshot.spin), "") def test_spin_as_string(self): snapshot = Series("snapshot", spin="7", package="GCC-5.3.1") self.assertEqual(snapshot.spin.val, 7) self.assertEqual(str(snapshot.spin), "-7") def test_spin_as_int(self): snapshot = Series("snapshot", spin=7, package="GCC-5.3.1") self.assertEqual(snapshot.spin.val, 7) self.assertEqual(str(snapshot.spin), "-7") def test_spin_as_typeerror(self): floatspin = 7.0 with self.assertRaises(TypeError): snapshot = Series("snapshot", spin=floatspin, package="GCC-5.3.1") def test_spin_as_negative(self): with self.assertRaises(ValueError): snapshot = Series("snapshot", spin="-1", package="GCC-5.3.1") def test_empty_spin_and_rc(self): release = Series("release", package="GCC-5.3.1") self.assertEqual(release.spin.val, 0) self.assertEqual(release.rc.val, 0) self.assertEqual(str(release.spin), "") self.assertEqual(str(release.rc), "") def test_package_as_Package(self): package = Package("GCC", "5.3.1") release = Series("release", package) self.assertEqual(str(release.package), "GCC-5.3.1") def test_package_as_Package(self): # Create a Version instead of a package package = versionFromStr("5.3.1") with self.assertRaises(TypeError): candidate = Series("candidate", package) def test_package_as_None(self): package = None with self.assertRaises(TypeError): candidate = Series("candidate", package) def test_getbranchname(self): candidate = Series("candidate", package="GCC-5.3.1", date=datetime(2016, 0o5, 15), spin="1", rc="1") self.assertEqual(candidate.branchname(), "releases/linaro-5.3-2016.05-1-rc1") release = Series("release", package="GCC-5.3.1", date=datetime(2016, 0o5, 15), spin="1", rc=None) self.assertEqual(release.branchname(), "releases/linaro-5.3-2016.05-1") def test_date_string(self): candidate = Series("candidate", package="GCC-5.3.1", date="2016.05.27", spin="1", rc="1") self.assertEqual(datetime(2016, 0o5, 15), candidate.date) candidate2 = Series("candidate", package="GCC-5.3.1", date="2016.05", spin="1", rc="1") self.assertEqual(datetime(2016, 0o5, 15), candidate2.date) with self.assertRaises(TypeError): candidate3 = Series("candidate", package="GCC-5.3.1", date=datetime("20161034234"), spin="1", rc="1") def test_series_strict_true(self): with self.assertRaises(ValueError): snapshot = Series("snapshot", package="GCC-5.3.1", date=datetime(2016, 0o5, 15), spin="1", rc="1", strict=True) with self.assertRaises(ValueError): snapshot = Series("snapshot", package="GCC-5.3.1", date=datetime(2016, 0o5, 15), spin="1", rc="1") with self.assertRaises(ValueError): release = Series("release", package="GCC-5.3.1", date=datetime(2016, 0o5, 15), spin="1", rc="1", strict=True) with self.assertRaises(ValueError): release = Series("release", package="GCC-5.3.1", date=datetime(2016, 0o5, 15), spin="1", rc="1") def test_series_strict_false(self): snapshot = Series("snapshot", package="GCC-5.3.1", date=datetime(2016, 0o5, 15), spin="6", rc="1", strict=False) self.assertEqual(snapshot.branchname(), "snapshots/linaro-5.3-2016.05-6-rc1") release = Series("release", package="GCC-5.3.1", date=datetime(2016, 0o5, 15), spin="1", rc="1", strict=False) self.assertEqual(release.branchname(), "releases/linaro-5.3-2016.05-1-rc1") release = Series("release", package="GCC-5.3.1", date=datetime(2016, 0o5, 15), rc="1", strict=False) self.assertEqual(release.branchname(), "releases/linaro-5.3-2016.05-rc1") # These tests will verify that a branchname can be read, a series created, # and then the same branch name recreated. def test_series_from_branchname(self): branch2 = u'snapshots/linaro-5.3-2016.05-6' series2 = series_from_branchname(branch=branch2) self.assertEqual(series2.branchname(), u'snapshots/linaro-5.3-2016.05-6') branch3 = u'snapshots/linaro-5.3-2016.05' series3 = series_from_branchname(branch=branch3) self.assertEqual(series3.branchname(), u'snapshots/linaro-5.3-2016.05') branch4 = u'releases/linaro-5.3-2016.05-6-rc1' series4 = series_from_branchname(branch=branch4) self.assertEqual(series4.branchname(), u'releases/linaro-5.3-2016.05-6-rc1') branch5 = u'releases/linaro-5.3-2016.05-rc1' series5 = series_from_branchname(branch=branch5) self.assertEqual(series5.branchname(), u'releases/linaro-5.3-2016.05-rc1') branch6 = u'releases/linaro-5.3-2016.05-6' series6 = series_from_branchname(branch=branch6) self.assertEqual(series6.branchname(), u'releases/linaro-5.3-2016.05-6') branch7 = u'releases/linaro-5.3-2016.05' series7 = series_from_branchname(branch=branch7) self.assertEqual(series7.branchname(), u'releases/linaro-5.3-2016.05') # -rc1 is invalid with a snapshots namespace. branch8 = u'snapshots/linaro-5.3-2016.05-6-rc1' with self.assertRaises(ValueError): series8 = series_from_branchname(branch=branch8) # Wrong branchname.. it should be 'snapshots' branch9 = u'snapshot/linaro-5.3-2016.05-6-rc1' with self.assertRaises(ValueError): series9 = series_from_branchname(branch=branch9) # Wrong branchname.. it should be 'snapshots' branch10 = u'snapshot/linaro-5.3-2016.05-6' with self.assertRaises(ValueError): series10 = series_from_branchname(branch=branch10) # namespace required. branch11 = u'linaro-5.3-2016.05-6' with self.assertRaises(ValueError): series11 = series_from_branchname(branch=branch11) # it should complain about missing the right-hand values. branch12 = u'snapshots/' with self.assertRaises(ValueError): series12 = series_from_branchname(branch=branch12) # It won't parse because of a missing namespace / branch13 = u'snapshots' with self.assertRaises(ValueError): series13 = series_from_branchname(branch=branch13) branch14 = u'snapshots/foo' with self.assertRaises(KeyError): series14 = series_from_branchname(branch=branch14) # unknown namespace. branch15 = u'foobarnamespace/linaro-5.3-2016.05-6' with self.assertRaises(ValueError): series15 = series_from_branchname(branch=branch15) # This will fail on a non-datetime input. branch16 = u'snapshots/linaro-5.3-asdf' with self.assertRaises(ValueError): series16 = series_from_branchname(branch=branch16) # This will fail with an invalid spin. branch17 = u'snapshots/linaro-5.3-2016.05-a' with self.assertRaises(TypeError): series17 = series_from_branchname(branch=branch17) # This will fail with an invalid rc. branch18 = u'snapshots/linaro-5.3-2016.05-rcasdfn' with self.assertRaises(TypeError): series18 = series_from_branchname(branch=branch18) # This will fail with an invalid rc. branch19 = u'snapshots/linaro-5.3-2016.05-9-rcasdfn' with self.assertRaises(TypeError): series19 = series_from_branchname(branch=branch19) # TODO: Test series.label (as there was a runtime bug) def test_snapshot_series_from_tag(self): tag = u'linaro-snapshot-5.3-2016.05-6' series = series_from_tag(tag=tag) self.assertEqual(series.branchname(), u'snapshots/linaro-5.3-2016.05-6') def test_invalid_snapshot_series_from_tag(self): # We can't have -rc1 on a snapshot. tag = u'linaro-snapshot-5.3-2016.05-6-rc1' with self.assertRaises(ValueError): series = series_from_tag(tag=tag) def test_candidate_series_from_tag(self): tag = u'linaro-5.3-2016.05-6-rc1' series = series_from_tag(tag=tag) self.assertEqual(series.branchname(), u'releases/linaro-5.3-2016.05-6-rc1') def test_release_series_from_tag(self): tag = u'linaro-5.3-2016.05-6' series = series_from_tag(tag=tag) self.assertEqual(series.branchname(), u'releases/linaro-5.3-2016.05-6') def test_series_from_tag_invalid_spin(self): tag = u'linaro-5.3-2016.05-abc' with self.assertRaises(TypeError): series = series_from_tag(tag=tag) def test_series_from_tag_invalid_rc(self): tag = u'linaro-5.3-2016.05-rcabf' with self.assertRaises(TypeError): series = series_from_tag(tag=tag) def test_series_from_tag_invalid_rc_with_valid_spin(self): tag = u'linaro-5.3-2016.05-9-rcabf' with self.assertRaises(TypeError): series = series_from_tag(tag=tag) class TestSeriesFormat(unittest.TestCase): @classmethod def setUpClass(cls): snaptag = u'linaro-snapshot-5.3-2016.05-6' reltag = u'linaro-5.3-2016.05-6' rctag = u'linaro-5.3-2016.05-6-rc1' cls.snapseries = series_from_tag(tag=snaptag) cls.relseries = series_from_tag(tag=reltag) cls.rcseries = series_from_tag(tag=rctag) def test_format_N_snap(self): f = format(self.snapseries, '%N') self.assertEquals(f, 'snapshots') def test_format_N_rel(self): f = format(self.relseries, '%N') self.assertEquals(f, 'releases') def test_format_N_rc(self): f = format(self.rcseries, '%N') self.assertEquals(f, 'releases') def test_format_dashL_snap(self): f = format(self.snapseries, 'foo-%L-bar') self.assertEquals(f, 'foo-snapshot-bar') def test_format_dashL_rel(self): f = format(self.relseries, 'foo-%L-bar') self.assertEquals(f, 'foo-bar') def test_format_dashL_rc(self): f = format(self.rcseries, 'foo-%L-bar') self.assertEquals(f, 'foo-bar') def test_format_dotL_snap(self): f = format(self.snapseries, 'foo.%L.bar') self.assertEquals(f, 'foo.snapshot.bar') def test_format_dotL_rel(self): f = format(self.relseries, 'foo.%L.bar') self.assertEquals(f, 'foo.bar') def test_format_dotL_rc(self): f = format(self.rcseries, 'foo.%L.bar') self.assertEquals(f, 'foo.bar') def test_format_L_snap(self): f = format(self.snapseries, 'foo%Lbar') self.assertEquals(f, 'foosnapshotbar') def test_format_L_rel(self): f = format(self.relseries, 'foo%Lbar') self.assertEquals(f, 'foobar') def test_format_L_rc(self): f = format(self.rcseries, 'foo%Lbar') self.assertEquals(f, 'foobar') def test_format_dashP(self): f = format(self.snapseries, 'foo-%P-bar') self.assertEquals(f, 'foo-GCC-bar') def test_format_dotP(self): f = format(self.snapseries, 'foo.%P.bar') self.assertEquals(f, 'foo.GCC.bar') def test_format_P(self): f = format(self.snapseries, 'foo%Pbar') self.assertEquals(f, 'fooGCCbar') def test_format_dashl(self): f = format(self.snapseries, 'foo-%l-bar') self.assertEquals(f, 'foo-gcc-bar') def test_format_dotl(self): f = format(self.snapseries, 'foo.%l.bar') self.assertEquals(f, 'foo.gcc.bar') def test_format_l(self): f = format(self.snapseries, 'foo%lbar') self.assertEquals(f, 'foogccbar') def test_format_V(self): f = format(self.snapseries, 'foo.%V.bar') self.assertEquals(f, 'foo.Linaro.bar') def test_format_v(self): f = format(self.snapseries, 'foo.%v.bar') self.assertEquals(f, 'foo.linaro.bar') def test_format_E(self): f = format(self.snapseries, 'gcc-%E') self.assertEquals(f, 'gcc-5.3') # This shouldn't show a trailing - after the minor because of a point. def test_format_E_with_fabricated_minor(self): minorseries = copy.deepcopy(self.snapseries) minorseries.package.version.point = 9 f = format(minorseries, 'gcc-%E') self.assertEquals(f, 'gcc-5.3') def test_format_e(self): f = format(self.snapseries, 'gcc-%e') self.assertEquals(f, 'gcc-5.3') def test_format_e_fabricate_minor(self): minorseries = copy.deepcopy(self.snapseries) minorseries.package.version.point = 9 f = format(minorseries, 'gcc-%e') self.assertEquals(f, 'gcc-5.3.9') def test_format_M(self): f = format(self.snapseries, 'gcc-%M') self.assertEquals(f, 'gcc-5') def test_format_m(self): f = format(self.snapseries, 'gcc-X.%m') self.assertEquals(f, 'gcc-X.3') def test_format_p(self): f = format(self.snapseries, 'gcc-X.Y.%p') self.assertEquals(f, 'gcc-X.Y.') def test_format_p_fabricate_minor(self): minorseries = copy.deepcopy(self.snapseries) minorseries.package.version.point = 9 f = format(minorseries, 'gcc-X.Y.%p') self.assertEquals(f, 'gcc-X.Y.9') def test_format_D(self): f = format(self.snapseries, 'foo-%D-bar') self.assertEquals(f, 'foo-2016.05-bar') def test_format_snap_h(self): f = format(self.snapseries, 'http://%h.') self.assertEquals(f, 'http://snapshots.') def test_format_release_h(self): f = format(self.relseries, 'http://%h.') self.assertEquals(f, 'http://releases.') def test_format_snap_h(self): f = format(self.snapseries, 'http://%h.') self.assertEquals(f, 'http://snapshots.') def test_format_snap_d(self): f = format( self.snapseries, 'http://%h.linaro.org/components/toolchain/binaries/%d') self.assertEquals( f, 'http://snapshots.linaro.org/components/' 'toolchain/binaries/5.3-2016.05-6') def test_format_rel_d(self): f = format( self.relseries, 'http://%h.linaro.org/components/toolchain/binaries/%d') self.assertEquals( f, 'http://releases.linaro.org/components/' 'toolchain/binaries/5.3-2016.05-6') def test_format_rc_d(self): f = format( self.rcseries, 'http://%h.linaro.org/components/toolchain/binaries/%d') self.assertEquals( f, 'http://snapshots.linaro.org/components/' 'toolchain/binaries/5.3-2016.05-6-rc1') def test_format_snap_d(self): f = format( self.snapseries, 'http://%h.linaro.org/components/toolchain/binaries/%d') self.assertEquals( f, 'http://snapshots.linaro.org/components/' 'toolchain/binaries/5.3-2016.05-6') def test_format_rel_d(self): f = format( self.relseries, 'http://%h.linaro.org/components/toolchain/binaries/%d') self.assertEquals( f, 'http://releases.linaro.org/components/' 'toolchain/binaries/5.3-2016.05-6') def test_format_rc_d(self): f = format( self.rcseries, 'http://%h.linaro.org/components/toolchain/binaries/%d') self.assertEquals( f, 'http://snapshots.linaro.org/components/' 'toolchain/binaries/5.3-2016.05-6-rc1') def test_format_snap_d(self): f = format( self.snapseries, 'http://%h.linaro.org/components/toolchain/binaries/%d') self.assertEquals( f, 'http://snapshots.linaro.org/components/' 'toolchain/binaries/5.3-2016.05-6') def test_format_rel_d(self): f = format( self.relseries, 'http://%h.linaro.org/components/toolchain/binaries/%d') self.assertEquals( f, 'http://releases.linaro.org/components/' 'toolchain/binaries/5.3-2016.05-6') def test_format_rc_d(self): f = format( self.rcseries, 'http://%h.linaro.org/components/toolchain/binaries/%d') self.assertEquals( f, 'http://snapshots.linaro.org/components/' 'toolchain/binaries/5.3-2016.05-6-rc1') def test_format_dashS(self): f = format(self.snapseries, 'foo-%S-bar') self.assertEquals(f, 'foo-6-bar') def test_format_dashS_no_spin(self): nospinseries = copy.deepcopy(self.snapseries) nospinseries.spin = Spin() f = format(nospinseries, 'foo-%S-bar') self.assertEquals(f, 'foo-bar') def test_format_dotS(self): f = format(self.snapseries, 'foo.%S.bar') self.assertEquals(f, 'foo.6.bar') def test_format_dotS_no_spin(self): nospinseries = copy.deepcopy(self.snapseries) nospinseries.spin = Spin() f = format(nospinseries, 'foo.%S.bar') self.assertEquals(f, 'foo.bar') def test_format_S(self): f = format(self.snapseries, 'foo%Sbar') self.assertEquals(f, 'foo6bar') def test_format_S_no_spin(self): nospinseries = copy.deepcopy(self.snapseries) nospinseries.spin = Spin() f = format(nospinseries, 'foo%Sbar') self.assertEquals(f, 'foobar') def test_format_dashR(self): f = format(self.rcseries, '2016.05-6-%R') self.assertEquals(f, '2016.05-6-rc1') def test_format_dashR_fabricate_norc(self): norcseries = copy.deepcopy(self.rcseries) norcseries.rc = Rc() f = format(norcseries, '2016.05-6-%R') self.assertEquals(f, '2016.05-6') def test_format_dotR(self): f = format(self.rcseries, '2016.05-6.%R') self.assertEquals(f, '2016.05-6.rc1') def test_format_dotR_fabricate_norc(self): norcseries = copy.deepcopy(self.rcseries) norcseries.rc = Rc() f = format(norcseries, '2016.05-6.%R') self.assertEquals(f, '2016.05-6') def test_format_R(self): f = format(self.rcseries, '2016.05-6-%R') self.assertEquals(f, '2016.05-6-rc1') def test_format_R_fabricate_norc(self): norcseries = copy.deepcopy(self.rcseries) norcseries.rc = Rc() f = format(norcseries, '2016.05-6#%R') self.assertEquals(f, '2016.05-6#') if __name__ == '__main__': # logging.basicConfig(level="INFO") unittest.main()