commit 3895bf0e0ec81504ec0a4ea34c8768a80ca910bb Author: Damian Johnson atagar@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.
tor-commits@lists.torproject.org