[asterisk-dev] Change in testsuite[master]: python/asterisk/version: Update version handling for Git mig...

Matt Jordan (Code Review) asteriskteam at digium.com
Mon Apr 13 16:12:49 CDT 2015


Matt Jordan has uploaded a new change for review.

  https://gerrit.asterisk.org/91

Change subject: python/asterisk/version: Update version handling for Git migration
......................................................................

python/asterisk/version: Update version handling for Git migration

This patch updates the Asterisk version parsing module to handle
versions derived from a Git repository. It leaves SVN and tag version
parsing alone.

All Git versions are assumed to be in the following form:
  Asterisk GIT-{branch}-{obj_blob}
Where branch must be 'master' or one of the canonical mainline branches.

Since the version tag no longer reflects individual team or local
branches, this makes it far easier to map a team/local branch to an
upstream Asterisk version.

ASTERISK-24954

Change-Id: Idbb0ef92fec6d4e8e39da105c9c635f9ecdd4ee4
---
M lib/python/asterisk/version.py
1 file changed, 228 insertions(+), 86 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/testsuite refs/changes/91/91/2

diff --git a/lib/python/asterisk/version.py b/lib/python/asterisk/version.py
index 3b7bf5b..520f5a0 100644
--- a/lib/python/asterisk/version.py
+++ b/lib/python/asterisk/version.py
@@ -22,20 +22,22 @@
 
 LOGGER = logging.getLogger(__name__)
 
-def parse_branch_name(branch_tokens):
-    """Parse an Asterisk branch version"""
+
+def parse_svn_branch_name(branch_tokens):
+    """Parse an Asterisk SVN branch version"""
     name = branch_tokens[0]
     munched = 0
     for i in range(1, len(branch_tokens)):
         # Stop when we hit the revision
         if branch_tokens[i][0] == 'r':
-            candidate = branch_tokens[i].replace('r','')
-            candidate = candidate.replace('M','').replace('m','')
+            candidate = branch_tokens[i].replace('r', '')
+            candidate = candidate.replace('M', '').replace('m', '')
             if candidate.isdigit():
                 break
         name += '-' + branch_tokens[i]
         munched += 1
     return (name, munched)
+
 
 def parse_version(version_string):
     """Parse a 'standard' Asterisk version"""
@@ -52,13 +54,15 @@
         count += 1
     return (parsed_numbers, True)
 
+
 def parse_revision(revision_string):
     """Parse a modified version of Asterisk"""
     candidate = revision_string.replace('M', '')
-    candidate = candidate.replace('r','').replace('m','')
+    candidate = candidate.replace('r', '').replace('m', '')
     if candidate.isdigit():
         return (int(candidate), True)
     return (0, False)
+
 
 def parse_feature(feature_string):
     """Parse a feature from a version"""
@@ -71,6 +75,7 @@
             return (feature, iteration, True)
     return ('', -1, False)
 
+
 def parse_version_modifier(version_modifier):
     """Parse a version modifier"""
     for modifier in AsteriskVersion.supported_modifiers:
@@ -82,77 +87,42 @@
             return (modifier, iteration, True)
     return ('', -1, False)
 
+
 def parse_parent_branch(parent_branch):
     """Parse a parent branch out of a version branch"""
     # Parent branch can be just about anything, so just accept it.
     # This should be the last thing called.
     return (parent_branch, True)
 
-def parse_version_string(raw_version):
-    """Parse a raw version string into its parts"""
-    branch = False
-    svn = False
-    feature = ''
-    parsed_numbers = [0, 0, 0]
-    name = ''
-    revision = 0
-    parent = ''
-    iteration = 0
-    modifier = ''
-
-    raw_version = raw_version.replace('Asterisk ', '')
-
-    tokens = re.split('[-~]', raw_version)
-    count = 0
-    while (count < len(tokens)):
-        token = tokens[count]
-        # Determine if we're a subversion branch
-        if 'SVN' == token:
-            svn = True
-        elif 'branch' == token:
-            branch = True
-        else:
-            if svn and not branch and not name:
-                # Team branch or trunk.  This will modify the current
-                # position based on the number of tokens consumed
-                (name, munched) = parse_branch_name(tokens[count:])
-                count += munched
-            else:
-                handled = False
-                if (len([num for num in parsed_numbers if num != 0]) == 0):
-                    (parsed_numbers, handled) = parse_version(token)
-                if not handled and revision == 0:
-                    (revision, handled) = parse_revision(token)
-                if not handled and not feature:
-                    # If a feature returns back a number, its actually the
-                    # 'patch' version number (e.g., 1.8.11-cert3)
-                    (feature, temp, handled) = parse_feature(token)
-                    if (temp > 0):
-                        parsed_numbers[2] = temp
-                if not handled and not modifier:
-                    (modifier,
-                     iteration,
-                     handled) = parse_version_modifier(token)
-                if not handled and not parent:
-                    (parent, handled) = parse_parent_branch(token)
-                if not handled:
-                    LOGGER.error("Unable to parse token '%s' in version " \
-                                 "string '%s'" % (token, raw_version))
-        count += 1
-    return (parsed_numbers[0], parsed_numbers[1], parsed_numbers[2],
-            iteration, revision, branch, svn, name, feature, modifier,
-            parent)
-
 
 class AsteriskVersion(object):
     """An Asterisk Version.
 
     This class handles Asterisk version strings.
+
+    Attributes:
+    raw_version - The pre-parsed version string
+    branch      - If true, this is a branch and not a tag. Note that
+                  if svn is True, then this implies that we think this
+                  must be 'trunk'. This is always True if git is True.
+    svn         - The Asterisk version is derived from Subversion
+    git         - The Asterisk version is derived from Git
+    major       - The major version number
+    minor       - The minor version number
+    patch       - The patch version number
+    feature     - Asterisk specific branch/tag features, e.g., 'cert'
+    modifier    - Asterisk tag release modifiers, e.g., 'rc'
+    iteration   - Iteration of the modifier, e.g., 1 for 'rc1'
+    parent      - If a parent SVN branch exists, what branch this was
+                  derived from
+    name        - The name of the team branch or 'trunk' for SVN, or
+                  'master' for Git. If None, then a major/minor/patch
+                  version should be available.
     """
 
-    supported_features = [ 'cert', 'digiumphones', 'dfsg' ]
+    supported_features = ['cert', 'digiumphones', 'dfsg']
 
-    supported_modifiers = [ 'rc', 'beta' ]
+    supported_modifiers = ['rc', 'beta']
 
     def __init__(self, version=None):
         """Construct an Asterisk Version parser.
@@ -165,18 +135,79 @@
             version = AsteriskVersion.get_version_from_binary()
 
         self.raw_version = version
+        self.branch = False
+        self.svn = False
+        self.git = False
+        self.major = 0
+        self.minor = 0
+        self.patch = 0
+        self.iteration = 0
+        self.revision = None
+        self.feature = None
+        self.modifier = None
+        self.parent = None
+        self.name = None
 
-        (self.major,
-         self.minor,
-         self.patch,
-         self.iteration,
-         self.revision,
-         self.branch,
-         self.svn,
-         self.name,
-         self.feature,
-         self.modifier,
-         self.parent) = parse_version_string(self.raw_version)
+        self.parse_version_string(self.raw_version)
+
+    def parse_version_string(self, raw_version):
+        """Parse a raw version string into its parts"""
+        parsed_numbers = [0, 0, 0]
+        raw_version = raw_version.replace('Asterisk ', '')
+
+        tokens = re.split('[-~]', raw_version)
+        count = 0
+        while (count < len(tokens)):
+            token = tokens[count]
+            # Determine if we're a subversion branch
+            if 'SVN' == token:
+                self.svn = True
+            elif 'GIT' == token:
+                # All Git versions are branches
+                self.git = True
+                self.branch = True
+            elif 'branch' == token:
+                self.branch = True
+            else:
+                if self.svn and not self.branch and not self.name:
+                    # Team branch or trunk.  This will modify the current
+                    # position based on the number of tokens consumed
+                    (self.name,
+                     munched) = parse_svn_branch_name(tokens[count:])
+                    count += munched
+                elif self.git and token == 'master':
+                    # It's a Git branch! This should contain our upstream
+                    # major branch, so we only care if the current token
+                    # says this is master.
+                    self.name = token
+                else:
+                    handled = False
+                    if (len([num for num in parsed_numbers if num != 0]) == 0):
+                        (parsed_numbers, handled) = parse_version(token)
+                        self.major = parsed_numbers[0]
+                        self.minor = parsed_numbers[1]
+                        self.patch = parsed_numbers[2]
+                    if not handled and not self.feature:
+                        # If a feature returns back a number, its actually the
+                        # 'patch' version number (e.g., 1.8.11-cert3)
+                        (self.feature, temp, handled) = parse_feature(token)
+                        if (temp > 0):
+                            self.patch = temp
+                    if not handled and not self.modifier:
+                        (self.modifier,
+                         self.iteration,
+                         handled) = parse_version_modifier(token)
+                    if not handled and not self.revision:
+                        if not self.git:
+                            (self.revision, handled) = parse_revision(token)
+                        else:
+                            self.revision = token
+                    if not handled and not self.parent:
+                        (self.parent, handled) = parse_parent_branch(token)
+                    if not handled:
+                        LOGGER.error("Unable to parse token '%s' in version "
+                                     "string '%s'" % (token, raw_version))
+            count += 1
 
     def __str__(self):
         """Return the raw Asterisk version as a string"""
@@ -205,14 +236,58 @@
             return (self._modifier_weight() + self.patch * 1000 +
                     self.minor * 100000 + self.major * 100000000)
 
-    def __cmp__(self, other):
-        """Compare this AsteriskVersion instance to another"""
-        res = cmp(int(self), int(other))
-        if res == 0:
-            if self.svn and other.svn:
-                res = cmp(self.revision, other.revision)
+    def __lt__(self, other):
+        """Test if self < other """
+        if int(self) < int(other):
+            return True
+        elif self.svn and other.svn:
+            return self.revision < other.revision
+        else:
+            return False
 
-        return res
+    def __le__(self, other):
+        """Test if self <= other"""
+        if int(self) <= int(other):
+            return True
+        elif self.svn and other.svn:
+            return self.revision <= other.revision
+        else:
+            return False
+
+    def __eq__(self, other):
+        """Test if self == other"""
+        if int(self) != int(other):
+            return False
+        if (self.svn and other.svn) or (self.git and other.git):
+            return self.revision == other.revision
+        return True
+
+    def __ne__(self, other):
+        """Test if self != other"""
+        if int(self) == int(other):
+            if (self.svn and other.svn) or (self.git and other.git):
+                return self.revision != other.revision
+            else:
+                return False
+        return True
+
+    def __gt__(self, other):
+        """Test if self > other"""
+        if int(self) > int(other):
+            return True
+        elif self.svn and other.svn:
+            return self.revision > other.revision
+        else:
+            return False
+
+    def __ge__(self, other):
+        """Test if self >= other"""
+        if int(self) >= int(other):
+            return True
+        elif self.svn and other.svn:
+            return self.revision >= other.revision
+        else:
+            return False
 
     def _modifier_weight(self):
         """Determine the relative weight due to a modifier"""
@@ -240,7 +315,14 @@
 
     @classmethod
     def get_version_from_binary(cls):
-        """Obtain the version from Asterisk and return a cached version of it"""
+        """Obtain the version from the installed instance of Asterisk
+
+        This method will invoke Asterisk, get the version, parse the
+        result, and cache it. Once cached, the cached version will
+        always be returned.
+
+        Returns: The installed Asterisk version
+        """
         if not hasattr(cls, "_asterisk_version_from_binary"):
             version = ""
             ast_binary = (test_suite_utils.which("asterisk") or
@@ -252,14 +334,15 @@
 
             try:
                 process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
-                        stderr=None)
+                                           stderr=None)
                 version = process.stdout.read()
             except OSError as o_excep:
                 LOGGER.error("OSError [%d]: %s" % (o_excep.errno,
                                                    o_excep.strerror))
                 raise
             process.wait()
-            cls._asterisk_version_from_binary = version.replace("Asterisk ", "")
+            cls._asterisk_version_from_binary = version.replace(
+                "Asterisk ", "")
         return cls._asterisk_version_from_binary
 
 
@@ -386,7 +469,8 @@
 
     def test_svn_branch_18_features_1(self):
         """Test parsing a 1.8 branch with features"""
-        version = AsteriskVersion("SVN-branch-1.8-digiumphones-r357808-/branches/1.8")
+        ver = "SVN-branch-1.8-digiumphones-r357808-/branches/1.8"
+        version = AsteriskVersion(ver)
         self.assertTrue(version.svn)
         self.assertTrue(version.branch)
         self.assertEqual(version.major, 8)
@@ -398,7 +482,8 @@
 
     def test_svn_branch_10_features_1(self):
         """Test parsing a 10 branch with features"""
-        version = AsteriskVersion("SVN-branch-10-digiumphones-r365402-/branches/10")
+        ver = "SVN-branch-10-digiumphones-r365402-/branches/10"
+        version = AsteriskVersion(ver)
         self.assertTrue(version.svn)
         self.assertTrue(version.branch)
         self.assertEqual(version.major, 10)
@@ -410,7 +495,8 @@
 
     def test_svn_branch_10_features_2(self):
         """Test parsing another 10 feature branch"""
-        version = AsteriskVersion("Asterisk SVN-branch-10-digiumphones-r365402")
+        ver = "Asterisk SVN-branch-10-digiumphones-r365402"
+        version = AsteriskVersion(ver)
         self.assertTrue(version.svn)
         self.assertTrue(version.branch)
         self.assertEqual(version.major, 10)
@@ -473,6 +559,40 @@
         self.assertEqual(version.patch, 0)
         self.assertEqual(version.feature, 'cert')
         self.assertEqual(version.revision, 368608)
+
+    def test_git_11_branch(self):
+        """Test a Git checkout from master"""
+        version = AsteriskVersion("Asterisk GIT-11-a987f3")
+        self.assertFalse(version.svn)
+        self.assertTrue(version.git)
+        self.assertTrue(version.branch)
+        self.assertEqual(version.major, 11)
+        self.assertEqual(version.minor, 0)
+        self.assertEqual(version.patch, 0)
+        self.assertEqual(version.revision, "a987f3")
+
+    def test_git_116_certified_branch(self):
+        """Test a Git checkout from master"""
+        version = AsteriskVersion("Asterisk GIT-11.6-cert-a987f3")
+        self.assertFalse(version.svn)
+        self.assertTrue(version.git)
+        self.assertTrue(version.branch)
+        self.assertTrue(version.branch)
+        self.assertEqual(version.major, 11)
+        self.assertEqual(version.minor, 6)
+        self.assertEqual(version.patch, 0)
+        self.assertEqual(version.feature, 'cert')
+        self.assertEqual(version.name, None)
+        self.assertEqual(version.revision, "a987f3")
+
+    def test_git_master(self):
+        """Test a Git checkout from master"""
+        version = AsteriskVersion("Asterisk GIT-master-a987f3")
+        self.assertFalse(version.svn)
+        self.assertTrue(version.git)
+        self.assertTrue(version.branch)
+        self.assertEqual(version.name, "master")
+        self.assertEqual(version.revision, "a987f3")
 
     def test_cmp1(self):
         """Compare two trunk versions, an 11 tag, and a 1.8 branch"""
@@ -598,6 +718,28 @@
         version2 = AsteriskVersion("Asterisk SVN-branch-1.8.11-cert-r368608")
         self.assertTrue(version1 < version2)
 
+    def test_cmp_git_18_11(self):
+        """Compare a Git 1.8 branch to an 11 branch"""
+        version1 = AsteriskVersion("Asterisk GIT-1.8-18has09")
+        version2 = AsteriskVersion("Asterisk GIT-11-81yhas90")
+        self.assertTrue(version1 < version2)
+
+    def test_cmp_git_11(self):
+        """Compare two Git 11 branch versions"""
+        version1 = AsteriskVersion("Asterisk GIT-11-a9suh193")
+        version2 = AsteriskVersion("Asterisk GIT-11-aj981bnd")
+        self.assertTrue(version1 != version2)
+        self.assertFalse(version1 < version2)
+        self.assertFalse(version1 > version2)
+        self.assertFalse(version1 == version2)
+
+    def test_cmp_git_1811_1811branch(self):
+        """Compare a CA version against a Git CA branch"""
+        version1 = AsteriskVersion("1.8.11-cert2")
+        version2 = AsteriskVersion("Asterisk GIT-1.8.11-cert-89haskljh")
+        self.assertTrue(version1 < version2)
+
+
 def main():
     """Run the unit tests"""
     unittest.main()

-- 
To view, visit https://gerrit.asterisk.org/91
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Idbb0ef92fec6d4e8e39da105c9c635f9ecdd4ee4
Gerrit-PatchSet: 2
Gerrit-Project: testsuite
Gerrit-Branch: master
Gerrit-Owner: Matt Jordan <mjordan at digium.com>



More information about the asterisk-dev mailing list