[tor-commits] [stem/master] Loosen 'tor --version' parsing

atagar at torproject.org atagar at torproject.org
Sat Feb 2 19:58:01 UTC 2019


commit 3895bf0e0ec81504ec0a4ea34c8768a80ca910bb
Author: Damian Johnson <atagar at torproject.org>
Date:   Sat Feb 2 11:49:02 2019 -0800

    Loosen 'tor --version' parsing
    
    Nick formally documented the 'tor --version' output we rely on (thanks!)...
    
      https://gitweb.torproject.org/tor.git/commit/?id=0e6e902
    
    Stem asserted the version is the last line of the output but that is not
    specified, and I can see scenarios where we might add more text. Loosening our
    parser to look for the version in any line of the output and expanding our
    tests.
---
 stem/version.py      | 16 +++++++---------
 test/unit/version.py | 47 +++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 50 insertions(+), 13 deletions(-)

diff --git a/stem/version.py b/stem/version.py
index 38101694..cafe37a6 100644
--- a/stem/version.py
+++ b/stem/version.py
@@ -129,23 +129,21 @@ def get_system_tor_version(tor_cmd = 'tor'):
 
       raise IOError(exc)
 
-    if version_output:
+    for line in version_output:
       # output example:
       # Oct 21 07:19:27.438 [notice] Tor v0.2.1.30. This is experimental software. Do not rely on it for strong anonymity. (Running on Linux i686)
       # Tor version 0.2.1.30.
 
-      last_line = version_output[-1]
-
-      if last_line.startswith('Tor version ') and last_line.endswith('.'):
+      if line.startswith('Tor version ') and line.endswith('.'):
         try:
-          version_str = last_line[12:-1]
+          version_str = line[12:-1]
           VERSION_CACHE[tor_cmd] = Version(version_str)
+          break
         except ValueError as exc:
           raise IOError(exc)
-      else:
-        raise IOError("Unexpected response from '%s': %s" % (version_cmd, last_line))
-    else:
-      raise IOError("'%s' didn't have any output" % version_cmd)
+
+    if tor_cmd not in VERSION_CACHE:
+      raise IOError("'%s' didn't provide a parseable version:\n\n%s" % (version_cmd, '\n'.join(version_output)))
 
   return VERSION_CACHE[tor_cmd]
 
diff --git a/test/unit/version.py b/test/unit/version.py
index f8c51f95..3c21855c 100644
--- a/test/unit/version.py
+++ b/test/unit/version.py
@@ -11,14 +11,24 @@ from stem.version import Version
 
 try:
   # added in python 3.3
-  from unittest.mock import patch
+  from unittest.mock import Mock, patch
 except ImportError:
-  from mock import patch
+  from mock import Mock, patch
 
-TOR_VERSION_OUTPUT = """Mar 22 23:09:37.088 [notice] Tor v0.2.2.35 \
+VERSION_CMD_OUTPUT = """Mar 22 23:09:37.088 [notice] Tor v0.2.2.35 \
 (git-73ff13ab3cc9570d). This is experimental software. Do not rely on it for \
 strong anonymity. (Running on Linux i686)
-Tor version 0.2.2.35 (git-73ff13ab3cc9570d)."""
+%s"""
+
+TOR_VERSION_OUTPUT = VERSION_CMD_OUTPUT % 'Tor version 0.2.2.35 (git-73ff13ab3cc9570d).'
+MALFORMED_TOR_VERSION = VERSION_CMD_OUTPUT % 'Tor version 0.2.blah (git-73ff13ab3cc9570d).'
+MISSING_TOR_VERSION = VERSION_CMD_OUTPUT % ''
+
+TOR_VERSION_EXTRA_LINES = VERSION_CMD_OUTPUT % """
+This is an extra line before the version
+Tor version 0.2.2.35 (git-73ff13ab3cc9570d).
+And an extra line afterward too
+"""
 
 
 class TestVersion(unittest.TestCase):
@@ -35,6 +45,35 @@ class TestVersion(unittest.TestCase):
 
     self.assertEqual(stem.version.VERSION_CACHE['tor_unit'], version)
 
+  @patch('stem.util.system.call', Mock(return_value = TOR_VERSION_EXTRA_LINES.splitlines()))
+  @patch.dict(stem.version.VERSION_CACHE)
+  def test_get_system_tor_version_extra_lines(self):
+    """
+    Include extra text before and after the version.
+    """
+
+    version = stem.version.get_system_tor_version('tor_unit')
+    self.assert_versions_match(version, 0, 2, 2, 35, None, 'git-73ff13ab3cc9570d')
+
+  @patch('stem.util.system.call', Mock(return_value = MISSING_TOR_VERSION.splitlines()))
+  @patch.dict(stem.version.VERSION_CACHE)
+  def test_get_system_tor_version_missing(self):
+    """
+    Tor version output that doesn't include a version within it.
+    """
+
+    self.assertRaisesRegexp(IOError, "'tor_unit --version' didn't provide a parseable version", stem.version.get_system_tor_version, 'tor_unit')
+
+  @patch('stem.util.system.call', Mock(return_value = MALFORMED_TOR_VERSION.splitlines()))
+  @patch.dict(stem.version.VERSION_CACHE)
+  def test_get_system_tor_version_malformed(self):
+    """
+    Tor version output that has the correct basic formatting, but an invalid
+    version.
+    """
+
+    self.assertRaisesWith(IOError, "'0.2.blah (git-73ff13ab3cc9570d)' isn't a properly formatted tor version", stem.version.get_system_tor_version, 'tor_unit')
+
   def test_parsing(self):
     """
     Tests parsing by the Version class constructor.



More information about the tor-commits mailing list