summaryrefslogtreecommitdiff
path: root/linaropy/rn/linaroseries.py
diff options
context:
space:
mode:
Diffstat (limited to 'linaropy/rn/linaroseries.py')
-rw-r--r--linaropy/rn/linaroseries.py361
1 files changed, 361 insertions, 0 deletions
diff --git a/linaropy/rn/linaroseries.py b/linaropy/rn/linaroseries.py
new file mode 100644
index 0000000..f978655
--- /dev/null
+++ b/linaropy/rn/linaroseries.py
@@ -0,0 +1,361 @@
+import unittest
+import copy
+
+from datetime import datetime
+from dateutil.relativedelta import relativedelta
+from ..vers import Spin
+from ..vers import Rc
+from ..series import Series
+from ..series import seriesFromBranchname
+
+# Progression:
+# The Linaro release progression follows.
+#
+# Snapshot 2016.01
+# spin 1 2016.01-1
+# spin 2 2016.01-2
+# Candidate 2016.01-rc1
+# rc 2 2016.01-rc2
+# Release 2016.01
+#
+# Note: Remember, if a snapshot is respun to fix a
+# bug then the fix should be cherry-picked into the
+# release branch and a release-candidate spun from
+# from the release branch.
+#
+# Candidate 2016.01-1-rc1
+# rc 2 2016.01-1-rc2
+# Release 2016.01-1
+
+# Inherit Series in order to provide Linaro specific rules on transitions
+# between series types. The baseclass Series doesn't have the toNext*
+# functions defined.
+class LinaroSeries(Series):
+ def __init__(self, seriestype, vendor=None, package=None, date=datetime.today(), spin=None, rc=None, strict=True):
+
+ # This is a dispatch table so that we can use a 'toNext' function based
+ # on an input type and it will call the correct toNextFoo function.
+ # This will work if the key is an integer or a string. Ideally this
+ # would be added to Series and we wouldn't need to derive, but I
+ # couldn't figure out how to get the pointer tables correct.
+ self.dispatchnext = {
+ Series.series.index("candidate"): self.toNextCandidate,
+ 'candidate': self.toNextCandidate,
+ Series.series.index("snapshot"): self.toNextSnapshot,
+ 'snapshot': self.toNextSnapshot,
+ Series.series.index("release"): self.toNextRelease,
+ 'release': self.toNextRelease,
+ }
+
+ super(LinaroSeries,self).__init__(seriestype, vendor, package, date,spin,rc, strict)
+
+ def toNextCandidate(self, date=None, strict=False):
+ if self.seriestype < 0 or self.seriestype >= len(Series.series):
+ raise TypeError('toNextCandidate on an unknown series type.')
+
+ candidate=copy.deepcopy(self)
+ candidate.seriestype = Series.series.index("candidate")
+
+ if date:
+ if not isinstance(date,datetime):
+ raise TypeError('date is not of type datetime.')
+ candidate.date=date
+
+ if self.seriestype == Series.series.index("candidate"):
+ candidate.rc.increment()
+ elif self.seriestype == Series.series.index("release"):
+ rc=Rc(1)
+ candidate.rc=rc
+ candidate.spin.increment()
+ elif self.seriestype == Series.series.index("snapshot"):
+ spin=Spin(None)
+ rc=Rc(1)
+ candidate.rc=rc
+ candidate.spin=spin
+
+ # If the user hasn't specified a date, the implicit behavior is to
+ # increment the date by 1 month when moving from a snapshot to a
+ # candidate.
+ if not date:
+ candidate.incrementMonth()
+ # If the user did specify a date and we're in strict mode we need to
+ # verify that the date they chose is exactly one month difference.
+ elif strict:
+ if candidate.date != self.date + relativedelta(months=1):
+ raise ValueError('toNextCandidate invoked with strict=True. Snapshot date to candidate date can only be +1 month difference.')
+ return candidate
+
+ if strict and candidate.date != self.date:
+ raise ValueError('Candidate date can only change if current series is a Snapshot when toNextCandidate is invoked with strict=True.')
+
+ return candidate
+
+ def toNextRelease(self, date=None, strict=False):
+ if self.seriestype < 0 or self.seriestype >= len(Series.series):
+ raise TypeError('toNextRelease called on an unknown series type.')
+ elif self.seriestype == Series.series.index("release"):
+ raise TypeError('A release series can not be the basis for another release. Move to a release candidate first.')
+ elif self.seriestype == Series.series.index("snapshot"):
+ raise TypeError('A snapshot series can not be the basis for a release. Move to a release candidate first.')
+
+ # Only a candidate can turn into a release.
+ release=copy.deepcopy(self)
+ # if we have a candidate and ask for a release rc needs to be None.
+ rc=Rc()
+ release.rc=rc
+ release.seriestype = Series.series.index("release")
+
+ if date:
+ if not isinstance(date,datetime):
+ raise TypeError('date is not of type datetime.')
+ release.date=date
+
+ if strict and release.date != self.date:
+ raise ValueError('Release date can not change as toNextRelease was invoked with strict=True')
+
+ return release
+
+ def toNextSnapshot(self, date=None, strict=False):
+ if self.seriestype < 0 or self.seriestype >= len(Series.series):
+ raise TypeError('toNextRelease called on an unknown series type.')
+ elif self.seriestype == Series.series.index("candidate"):
+ raise TypeError('Series of type candidate can not be the basis for a snapshot.')
+ if self.seriestype == Series.series.index("release"):
+ raise TypeError('Series of type release can not be the basis for a snapshot.')
+
+ # Only snapshots can be snapshots next.
+ snapshot=copy.deepcopy(self)
+ snapshot.seriestype = Series.series.index("snapshot")
+ snapshot.spin.increment()
+
+ if date:
+ if not isinstance(date,datetime):
+ raise TypeError('date is not of type datetime.')
+ snapshot.date=date
+
+ if strict and snapshot.date != self.date:
+ raise ValueError('Snapshot date can not change as toNextSnapshot was invoked with strict=True')
+
+ return snapshot
+
+ # toNext will take a 'key' as either a string representing the
+ # Series.series type or the index of the Series.series type, e.g.,
+ # 'candidate' or Series.series.index("candidate") and it will forward
+ # to the correct toNext* function as mapped in the dispatchnext
+ # dictionary.
+ def toNext(self, seriestype, date=None, strict=False):
+ return self.dispatchnext[seriestype](date, strict)
+
+# Helper function which creates a LinaroSeries from a properly formed branch name
+# input string.
+def linaroSeriesFromBranchname(branch=None):
+
+ # Create a Series using the helper function and then populate a
+ # LinaroSeries. TODO: There's probably a better way to do this such
+ # as a SeriesFactory that is overridden.
+ series=seriesFromBranchname(branch)
+ linaroseries=LinaroSeries(Series.series[series.seriestype], vendor="linaro", package=series.package ,date=series.date , spin=series.spin, rc=series.rc, strict=True )
+ return linaroseries
+
+class TestLinaroSeries(unittest.TestCase):
+
+ def test_candidate_to_candidate(self):
+ candidate=LinaroSeries("candidate", package="GCC-5.3.1", date=datetime(2016,05,15), spin="1", rc="1")
+ candidate2=candidate.toNextCandidate()
+ self.assertEqual(candidate2.getBranchname(), "releases/linaro-5.3-2016.05-1-rc2")
+ self.assertEqual(candidate2.seriestype, Series.series.index("candidate"))
+ self.assertEqual(candidate.date, candidate2.date)
+
+ #TODO test date and strict=True
+
+ def test_release_to_candidate(self):
+ # Test where spin is None and needs to be incremented.
+ release=LinaroSeries("release", package="GCC-5.3.1", date=datetime(2016,05,15))
+ candidate=release.toNextCandidate()
+ self.assertEqual(candidate.getBranchname(), "releases/linaro-5.3-2016.05-1-rc1")
+ self.assertEqual(candidate.seriestype, Series.series.index("candidate"))
+ self.assertEqual(candidate.date, release.date)
+
+ # Now test where spin is already non-zero.
+ release2=LinaroSeries("release", package="GCC-5.3.1", date=datetime(2016,05,15), spin="1")
+ candidate2=release2.toNextCandidate()
+ self.assertEqual(candidate2.getBranchname(), "releases/linaro-5.3-2016.05-2-rc1")
+ self.assertEqual(candidate2.seriestype, Series.series.index("candidate"))
+
+ #TODO test date and strict=True
+
+ def test_snapshot_to_candidate(self):
+ snapshot=LinaroSeries("snapshot", package="GCC-5.3.1", date=datetime(2016,05,15), spin="6")
+ candidate=snapshot.toNextCandidate()
+ self.assertEqual(candidate.getBranchname(), "releases/linaro-5.3-2016.06-rc1")
+ self.assertEqual(candidate.seriestype, Series.series.index("candidate"))
+ self.assertNotEqual(candidate.date, snapshot.date)
+ # Verify that it's one month apart.
+ self.assertEqual(candidate.date, snapshot.date + relativedelta(months=1))
+
+ # Make sure that the default date increment is just one month.
+ candidate2=snapshot.toNextCandidate(strict=True)
+ self.assertEqual(candidate2.date, snapshot.date + relativedelta(months=1))
+
+ # whether strict=True or not.
+ candidate3=snapshot.toNextCandidate(strict=False)
+ self.assertEqual(candidate3.date, snapshot.date + relativedelta(months=1))
+
+ # If the user passed a date and strict=yes, make sure the increment is only one month.
+ candidate4=snapshot.toNextCandidate(date=datetime.strptime("2016.06.15", "%Y.%m.%d"), strict=True)
+ plusonem=snapshot.date + relativedelta(months=1)
+ self.assertEqual(candidate4.date,plusonem)
+
+ # Make sure the date can be set without error when strict is False
+ candidate5=snapshot.toNextCandidate(date=datetime.strptime("2016.07.15", "%Y.%m.%d"), strict=False)
+ self.assertEqual(candidate5.date, snapshot.date + relativedelta(months=2))
+
+ with self.assertRaises(ValueError):
+ # Attempting to increment the date when strict=True should raise an exception.
+ candidate6=snapshot.toNextCandidate(date=datetime.strptime("2016.07.15", "%Y.%m.%d"), strict=True)
+
+ def test_unknown_to_candidate(self):
+ unknown=LinaroSeries("candidate", package="GCC-5.3.1", date=datetime(2016,05,15), spin="1", rc="1")
+ # Purposely override with an incorrect seriestype.
+ unknown.seriestype=99
+ with self.assertRaises(TypeError):
+ candidate=unknown.toNextCandidate()
+
+ def test_candidate_to_release(self):
+ candidate=LinaroSeries("candidate", package="GCC-5.3.1", date=datetime(2016,05,15), spin="1", rc="1")
+ release=candidate.toNextRelease()
+ self.assertEqual(release.getBranchname(), "releases/linaro-5.3-2016.05-1")
+ self.assertEqual(release.seriestype, Series.series.index("release"))
+ self.assertEqual(candidate.date, release.date)
+
+ release2=candidate.toNextRelease(strict=True)
+ self.assertEqual(release2.getBranchname(), "releases/linaro-5.3-2016.05-1")
+ self.assertEqual(release2.date, candidate.date)
+
+ with self.assertRaises(ValueError):
+ # Attempting to increment the date when strict=True should raise an exception.
+ release3=candidate.toNextRelease(date=datetime.strptime("2016.06.15", "%Y.%m.%d"), strict=True)
+
+ # If strict=False and the date is changed, go ahead and change it.
+ release4=candidate.toNextRelease(date=datetime.strptime("2016.06.15", "%Y.%m.%d"), strict=False)
+ self.assertEqual(release4.getBranchname(), "releases/linaro-5.3-2016.06-1")
+ self.assertNotEqual(release4.date, release.date)
+
+ with self.assertRaises(TypeError):
+ release5=candidate.toNextRelease("2016.06", strict=False)
+
+ def test_snapshot_to_release(self):
+ snapshot=LinaroSeries("snapshot", package="GCC-5.3.1", date=datetime(2016,05,15), spin="6")
+ with self.assertRaises(TypeError):
+ release=snapshot.toNextRelease()
+
+ def test_release_to_release(self):
+ release=LinaroSeries("release", package="GCC-5.3.1", date=datetime(2016,05,15), spin="6")
+ with self.assertRaises(TypeError):
+ release2=release.toNextRelease()
+
+ def test_unknown_to_release(self):
+ unknown=LinaroSeries("release", package="GCC-5.3.1", date=datetime(2016,05,15), spin="1")
+ # Purposely override with an incorrect seriestype.
+ unknown.seriestype=99
+ with self.assertRaises(TypeError):
+ release=unknown.toNextRelease()
+
+ def test_candidate_to_snapshot(self):
+ candidate=LinaroSeries("candidate", package="GCC-5.3.1", date=datetime(2016,05,15), spin="1", rc="1")
+ with self.assertRaises(TypeError):
+ snapshot=candidate.toNextSnapshot()
+ # This would/should be the case if this wasn't and invalid option.
+ # self.assertEqual(snapshot.seriestype, "snapshot")
+
+ def test_release_to_snapshot(self):
+ release=LinaroSeries("release", package="GCC-5.3.1", date=datetime(2016,05,15), spin="1")
+ with self.assertRaises(TypeError):
+ snapshot=release.toNextSnapshot()
+
+ def test_snapshot_to_snapshot(self):
+ snapshot=LinaroSeries("snapshot", package="GCC-5.3.1", date=datetime(2016,05,15), spin="6")
+ snapshot2=snapshot.toNextSnapshot()
+
+ self.assertEqual(snapshot2.getBranchname(), "snapshots/linaro-5.3-2016.05-7")
+ self.assertEqual(snapshot2.seriestype, Series.series.index("snapshot"))
+ self.assertEqual(snapshot.date, snapshot2.date)
+
+ # Verify that the original hasn't changed. This verifies that a
+ # deepcopy is used on the toNext* functions.
+ self.assertEqual(snapshot.getBranchname(), "snapshots/linaro-5.3-2016.05-6")
+
+ snapshot3=snapshot.toNextSnapshot(strict=True)
+ self.assertEqual(snapshot3.getBranchname(), "snapshots/linaro-5.3-2016.05-7")
+ self.assertEqual(snapshot3.seriestype, Series.series.index("snapshot"))
+ self.assertEqual(snapshot.date, snapshot3.date)
+
+ with self.assertRaises(ValueError):
+ # Attempting to increment the date when strict=True should raise an exception.
+ snapshot4=snapshot.toNextSnapshot(date=datetime.strptime("2016.06.15", "%Y.%m.%d"), strict=True)
+
+ # If strict=False and the date is changed, go ahead and change it.
+ snapshot5=snapshot.toNextSnapshot(date=datetime.strptime("2016.06.15", "%Y.%m.%d"), strict=False)
+ self.assertEqual(snapshot5.getBranchname(), "snapshots/linaro-5.3-2016.06-7")
+ self.assertNotEqual(snapshot5.date, snapshot.date)
+
+ # Verify that a date as a string generates an exception.
+ with self.assertRaises(TypeError):
+ snapshot6=snapshot.toNextSnapshot("2016.06.15", strict=False)
+
+ def test_unknown_to_snapshot(self):
+ unknown=LinaroSeries("snapshot", package="GCC-5.3.1", date=datetime(2016,05,15), spin="1")
+ # Purposely override with an incorrect seriestype.
+ unknown.seriestype=99
+ with self.assertRaises(TypeError):
+ snapshot=unknown.toNextSnapshot()
+
+ def test_snapshot_toNext(self):
+ snapshot=LinaroSeries("snapshot", package="GCC-5.3.1", date=datetime(2016,05,15), spin="1")
+ self.assertEqual(snapshot.getBranchname(), "snapshots/linaro-5.3-2016.05-1")
+
+ candidate=snapshot.toNext("candidate")
+ # A Snapshot -> Candidate traversal always results in spin being set to zero
+ # and the first release candidate being created.
+ self.assertEqual(candidate.getBranchname(), "releases/linaro-5.3-2016.06-rc1")
+ self.assertEqual(candidate.seriestype, Series.series.index("candidate"))
+
+ # Make sure toNext works with 'index' as well as the type string.
+ candidate2=snapshot.toNext(Series.series.index("candidate"))
+ # A Snapshot -> Candidate traversal always results in spin being set to zero
+ # and the first release candidate being created.
+ self.assertEqual(candidate2.getBranchname(), "releases/linaro-5.3-2016.06-rc1")
+ self.assertEqual(candidate2.seriestype, Series.series.index("candidate"))
+
+ with self.assertRaises(TypeError):
+ candidate=snapshot.toNext("release")
+ candidate=snapshot.toNext(Series.series.index("release"))
+
+ def test_toNext_incorrect_keys(self):
+ snapshot=LinaroSeries("snapshot", package="GCC-5.3.1", date=datetime(2016,05,15), spin="1")
+ self.assertEqual(snapshot.getBranchname(), "snapshots/linaro-5.3-2016.05-1")
+ with self.assertRaises(KeyError):
+ candidate=snapshot.toNext("candidat")
+ candidate=snapshot.toNext("snapsho")
+ candidate=snapshot.toNext("releas")
+ candidate=snapshot.toNext(-1)
+ candidate=snapshot.toNext(len(Series.series)+1)
+ # Index out of range of the Series.series array.
+
+ # Test to see if the objects returned from the toNext* functions are
+ # new instances and not references to the input Series.
+ def test_toNextFOO_return_new_objects(self):
+ candidate=LinaroSeries("candidate", package="GCC-5.3.1", date=datetime(2016,05,15), spin="1", rc="1")
+ candidate2=candidate.toNext("candidate")
+ release=candidate.toNext("release")
+
+ self.assertIsNot(candidate,candidate2)
+ self.assertIsNot(candidate,release)
+
+ snapshot=LinaroSeries("snapshot", package="GCC-5.3.1", date=datetime(2016,05,15), spin="1")
+ snapshot2=snapshot.toNext("snapshot")
+ self.assertIsNot(snapshot,snapshot2)
+
+if __name__ == '__main__':
+ #logging.basicConfig(level="INFO")
+ unittest.main()