[tor-commits] [stem/master] Class and function for Tor Versions

atagar at torproject.org atagar at torproject.org
Thu Oct 6 16:54:06 UTC 2011


commit 69083da95c5ab536adcd23f19a424ed9395015bb
Author: Damian Johnson <atagar at torproject.org>
Date:   Thu Oct 6 09:52:04 2011 -0700

    Class and function for Tor Versions
    
    Starting with a simple (but non-trivial) class that will be needed for handling
    PROTOCOLINFO responses. This is partly to establish conventions for
    documentation and unit tests.
---
 run_tests.py      |   13 ++++++
 stem/__init__.py  |    6 +++
 stem/__init__.pyc |  Bin 0 -> 228 bytes
 stem/run_tests.py |   13 ++++++
 stem/types.py     |   99 +++++++++++++++++++++++++++++++++++++++++++
 stem/types.pyc    |  Bin 0 -> 3406 bytes
 test/__init__.py  |    6 +++
 test/__init__.pyc |  Bin 0 -> 237 bytes
 test/version.py   |  121 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 test/version.pyc  |  Bin 0 -> 3982 bytes
 10 files changed, 258 insertions(+), 0 deletions(-)

diff --git a/run_tests.py b/run_tests.py
old mode 100644
new mode 100755
index e69de29..0fe6365
--- a/run_tests.py
+++ b/run_tests.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+"""
+Runs unit and integration tests.
+"""
+
+import unittest
+import test.version
+
+if __name__ == '__main__':
+  suite = unittest.TestLoader().loadTestsFromTestCase(test.version.TestVerionFunctions)
+  unittest.TextTestRunner(verbosity=2).run(suite)
+
diff --git a/stem/__init__.py b/stem/__init__.py
new file mode 100644
index 0000000..01c350c
--- /dev/null
+++ b/stem/__init__.py
@@ -0,0 +1,6 @@
+"""
+Library for working with the tor process.
+"""
+
+__all__ = ["types"]
+
diff --git a/stem/__init__.pyc b/stem/__init__.pyc
new file mode 100644
index 0000000..43c6f7d
Binary files /dev/null and b/stem/__init__.pyc differ
diff --git a/stem/run_tests.py b/stem/run_tests.py
new file mode 100644
index 0000000..ad52f9c
--- /dev/null
+++ b/stem/run_tests.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+"""
+Runs unit and integration tests.
+"""
+
+import unittest
+import test.types
+
+if __name__ == '__main__':
+  suite = unittest.TestLoader().loadTestsFromTestCase(test.types.TestVerionFunctions)
+  unittest.TextTestRunner(verbosity=2).run(suite)
+
diff --git a/stem/types.py b/stem/types.py
new file mode 100644
index 0000000..0f7030b
--- /dev/null
+++ b/stem/types.py
@@ -0,0 +1,99 @@
+"""
+Classes for miscellaneous tor object. This includes...
+
+types.Version - Tor versioning information.
+  * get_version(versionStr)
+    Converts a version string to a types.Version instance.
+"""
+
+import re
+
+class Version:
+  """
+  Comparable tor version, as per the 'new version' of the version-spec...
+  https://gitweb.torproject.org/torspec.git/blob/HEAD:/version-spec.txt
+  
+  Attributes:
+    major (int)  - major version
+    minor (int)  - minor version
+    micro (int)  - micro version
+    patch (int)  - optional patch level (None if undefined)
+    status (str) - optional status tag without the preceding dash such as
+                   'alpha', 'beta-dev', etc (None if undefined)
+  """
+  
+  def __init__(self, major, minor, micro, patch = None, status = None):
+    self.major = major
+    self.minor = minor
+    self.micro = micro
+    self.patch = patch
+    self.status = status
+  
+  def __str__(self):
+    """
+    Provides the normal representation for the version, for instance:
+    "0.2.2.23-alpha"
+    """
+    
+    suffix = ""
+    
+    if self.patch:
+      suffix += ".%i" % self.patch
+    
+    if self.status:
+      suffix += "-%s" % self.status
+    
+    return "%i.%i.%i%s" % (self.major, self.minor, self.micro, suffix)
+  
+  def __cmp__(self, other):
+    """
+    Simple comparision of versions. An undefined patch level is treated as zero
+    and status tags are compared lexically (as per the version spec).
+    """
+    
+    if not isinstance(other, Version):
+      raise ValueError("types.Version can only be compared with other Version instances")
+    
+    for attr in ("major", "minor", "micro", "patch"):
+      myVersion = max(0, self.__dict__[attr])
+      otherVersion = max(0, other.__dict__[attr])
+      
+      if myVersion > otherVersion: return 1
+      elif myVersion < otherVersion: return -1
+    
+    myStatus = self.status if self.status else ""
+    otherStatus = other.status if other.status else ""
+    
+    return cmp(myStatus, otherStatus)
+
+def get_version(versionStr):
+  """
+  Parses a version string, providing back a types.Version instance.
+  
+  Arguments:
+    versionStr (str) - string representing a tor version (ex. "0.2.2.23-alpha")
+  
+  Returns:
+    types.Version instance
+  
+  Throws:
+    ValueError if input isn't a valid tor version
+  """
+  
+  if not isinstance(versionStr, str):
+    raise ValueError("argument is not a string")
+  
+  m = re.match(r'^([0-9]+).([0-9]+).([0-9]+)(.[0-9]+)?(-\S*)?$', versionStr)
+  
+  if m:
+    major, minor, micro, patch, status = m.groups()
+    
+    # The patch and status matches are optional (may be None) and have an extra
+    # proceeding period or dash if they exist. Stripping those off.
+    
+    if patch: patch = int(patch[1:])
+    if status: status = status[1:]
+    
+    return Version(int(major), int(minor), int(micro), patch, status)
+  else: raise ValueError("'%s' isn't a properly formatted tor version" % versionStr)
+
diff --git a/stem/types.pyc b/stem/types.pyc
new file mode 100644
index 0000000..27a5bb1
Binary files /dev/null and b/stem/types.pyc differ
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644
index 0000000..6d651bb
--- /dev/null
+++ b/test/__init__.py
@@ -0,0 +1,6 @@
+"""
+Unit and integration tests for the stem library.
+"""
+
+__all__ = ["version"]
+
diff --git a/test/__init__.pyc b/test/__init__.pyc
new file mode 100644
index 0000000..6b41a10
Binary files /dev/null and b/test/__init__.pyc differ
diff --git a/test/version.py b/test/version.py
new file mode 100644
index 0000000..fd32886
--- /dev/null
+++ b/test/version.py
@@ -0,0 +1,121 @@
+"""
+Unit tests for types functions and classes.
+"""
+
+import unittest
+import stem.types
+
+class TestVerionFunctions(unittest.TestCase):
+  """
+  Tests methods and functions related to 'types.Version'.
+  """
+  
+  def test_parsing(self):
+    """
+    Tests parsing by the 'get_version' function.
+    """
+    
+    # valid versions with various number of compontents to the version
+    version = stem.types.get_version("0.1.2.3-tag")
+    self.assert_versions_match(version, 0, 1, 2, 3, "tag")
+    
+    version = stem.types.get_version("0.1.2.3")
+    self.assert_versions_match(version, 0, 1, 2, 3, None)
+    
+    version = stem.types.get_version("0.1.2-tag")
+    self.assert_versions_match(version, 0, 1, 2, None, "tag")
+    
+    version = stem.types.get_version("0.1.2")
+    self.assert_versions_match(version, 0, 1, 2, None, None)
+    
+    # checks an empty tag
+    version = stem.types.get_version("0.1.2.3-")
+    self.assert_versions_match(version, 0, 1, 2, 3, "")
+    
+    version = stem.types.get_version("0.1.2-")
+    self.assert_versions_match(version, 0, 1, 2, None, "")
+    
+    # checks invalid version strings
+    self.assertRaises(ValueError, stem.types.get_version, "")
+    self.assertRaises(ValueError, stem.types.get_version, "1.2.3.4nodash")
+    self.assertRaises(ValueError, stem.types.get_version, "1.2.3.a")
+    self.assertRaises(ValueError, stem.types.get_version, "1.2.a.4")
+    self.assertRaises(ValueError, stem.types.get_version, "12.3")
+    self.assertRaises(ValueError, stem.types.get_version, "1.-2.3")
+  
+  def test_comparison(self):
+    """
+    Tests comparision between Version instances.
+    """
+    
+    # check for basic incrementing in each portion
+    self.assert_version_is_greater("1.1.2.3-tag", "0.1.2.3-tag")
+    self.assert_version_is_greater("0.2.2.3-tag", "0.1.2.3-tag")
+    self.assert_version_is_greater("0.1.3.3-tag", "0.1.2.3-tag")
+    self.assert_version_is_greater("0.1.2.4-tag", "0.1.2.3-tag")
+    self.assert_version_is_greater("0.1.2.3-ugg", "0.1.2.3-tag")
+    self.assert_version_is_equal("0.1.2.3-tag", "0.1.2.3-tag")
+    
+    # checks that a missing patch level equals zero
+    self.assert_version_is_equal("0.1.2", "0.1.2.0")
+    self.assert_version_is_equal("0.1.2-tag", "0.1.2.0-tag")
+    
+    # checks for missing patch or status
+    self.assert_version_is_greater("0.1.2.3-tag", "0.1.2.3")
+    self.assert_version_is_greater("0.1.2.3-tag", "0.1.2-tag")
+    self.assert_version_is_greater("0.1.2.3-tag", "0.1.2")
+    
+    self.assert_version_is_equal("0.1.2.3", "0.1.2.3")
+    self.assert_version_is_equal("0.1.2", "0.1.2")
+  
+  def test_string(self):
+    """
+    Tests the Version -> string conversion.
+    """
+    
+    # checks conversion with various numbers of arguments
+    
+    self.assert_string_matches("0.1.2.3-tag")
+    self.assert_string_matches("0.1.2.3")
+    self.assert_string_matches("0.1.2")
+  
+  def assert_versions_match(self, version, major, minor, micro, patch, status):
+    """
+    Asserts that the values for a types.Version instance match the given
+    values.
+    """
+    
+    self.assertEqual(version.major, major)
+    self.assertEqual(version.minor, minor)
+    self.assertEqual(version.micro, micro)
+    self.assertEqual(version.patch, patch)
+    self.assertEqual(version.status, status)
+  
+  def assert_version_is_greater(self, firstVersion, secondVersion):
+    """
+    Asserts that the parsed version of the first version is greate than the
+    second (also checking the inverse).
+    """
+    
+    version1 = stem.types.get_version(firstVersion)
+    version2 = stem.types.get_version(secondVersion)
+    self.assertEqual(version1 > version2, True)
+    self.assertEqual(version1 < version2, False)
+  
+  def assert_version_is_equal(self, firstVersion, secondVersion):
+    """
+    Asserts that the parsed version of the first version equals the second.
+    """
+    
+    version1 = stem.types.get_version(firstVersion)
+    version2 = stem.types.get_version(secondVersion)
+    self.assertEqual(version1, version2)
+  
+  def assert_string_matches(self, version):
+    """
+    Parses the given version string then checks that its string representation
+    matches the input.
+    """
+    
+    self.assertEqual(version, str(stem.types.get_version(version)))
+
diff --git a/test/version.pyc b/test/version.pyc
new file mode 100644
index 0000000..c459fa7
Binary files /dev/null and b/test/version.pyc differ



More information about the tor-commits mailing list