[tor-commits] [stem/master] Autocompletion unit tests

atagar at torproject.org atagar at torproject.org
Tue May 6 01:21:13 UTC 2014


commit 6478057edcf83b1cfda1107dd78e290b1c102d0a
Author: Damian Johnson <atagar at torproject.org>
Date:   Sat Apr 19 21:04:29 2014 -0700

    Autocompletion unit tests
    
    Unit tests for our autocompletion class and helper funtion for determine
    options from tor. This didn't uncover any issues with that, but it *did* reveal
    a bug with our util module. We didn't make a shallow copy of list config
    values, so in place modifications edited the configuration's master value.
---
 stem/interpretor/autocomplete.py      |    3 +
 stem/util/conf.py                     |    2 +-
 test/settings.cfg                     |    1 +
 test/unit/interpretor/__init__.py     |    1 +
 test/unit/interpretor/autocomplete.py |  130 +++++++++++++++++++++++++++++++++
 5 files changed, 136 insertions(+), 1 deletion(-)

diff --git a/stem/interpretor/autocomplete.py b/stem/interpretor/autocomplete.py
index 7007be6..f42084e 100644
--- a/stem/interpretor/autocomplete.py
+++ b/stem/interpretor/autocomplete.py
@@ -19,6 +19,9 @@ def _get_commands(controller, config):
 
   commands = config.get('autocomplete', [])
 
+  if controller is None:
+    return commands
+
   # GETINFO commands. Lines are of the form '[option] -- [description]'. This
   # strips '*' from options that accept values.
 
diff --git a/stem/util/conf.py b/stem/util/conf.py
index c232a2a..254e2a4 100644
--- a/stem/util/conf.py
+++ b/stem/util/conf.py
@@ -678,7 +678,7 @@ class Config(object):
         log.debug("Config entry '%s' is expected to be a float, defaulting to '%f'" % (key, default))
         val = default
     elif isinstance(default, list):
-      pass  # nothing special to do (already a list)
+      val = list(val)  # make a shallow copy
     elif isinstance(default, tuple):
       val = tuple(val)
     elif isinstance(default, dict):
diff --git a/test/settings.cfg b/test/settings.cfg
index 684d40a..cf05541 100644
--- a/test/settings.cfg
+++ b/test/settings.cfg
@@ -184,6 +184,7 @@ test.unit_tests
 |test.unit.connection.connect.TestConnect
 |test.unit.control.controller.TestControl
 |test.unit.interpretor.arguments.TestArgumentParsing
+|test.unit.interpretor.autocomplete.TestAutocompletion
 |test.unit.doctest.TestDocumentation
 
 test.integ_tests
diff --git a/test/unit/interpretor/__init__.py b/test/unit/interpretor/__init__.py
index e2ad715..62db05a 100644
--- a/test/unit/interpretor/__init__.py
+++ b/test/unit/interpretor/__init__.py
@@ -4,4 +4,5 @@ Unit tests for the stem's interpretor prompt.
 
 __all__ = [
   "arguments",
+  "autocomplete",
 ]
diff --git a/test/unit/interpretor/autocomplete.py b/test/unit/interpretor/autocomplete.py
new file mode 100644
index 0000000..579f177
--- /dev/null
+++ b/test/unit/interpretor/autocomplete.py
@@ -0,0 +1,130 @@
+import unittest
+
+from stem.interpretor.autocomplete import _get_commands, Autocompleter
+
+try:
+  # added in python 3.3
+  from unittest.mock import Mock
+except ImportError:
+  from mock import Mock
+
+GETINFO_NAMES = """
+info/names -- List of GETINFO options, types, and documentation.
+ip-to-country/* -- Perform a GEOIP lookup
+md/id/* -- Microdescriptors by ID
+""".strip()
+
+GETCONF_NAMES = """
+ExitNodes RouterList
+ExitPolicy LineList
+ExitPolicyRejectPrivate Boolean
+""".strip()
+
+
+class TestAutocompletion(unittest.TestCase):
+  def test_autocomplete_results_from_config(self):
+    """
+    Check that we load autocompletion results from our configuration.
+    """
+
+    commands = _get_commands(None)
+    self.assertTrue('PROTOCOLINFO' in commands)
+    self.assertTrue('/quit' in commands)
+
+  def test_autocomplete_results_from_tor(self):
+    """
+    Check our ability to determine autocompletion results based on our tor
+    instance's capabilities.
+    """
+
+    # Check that when GETINFO requests fail we have base commands, but nothing
+    # with arguments.
+
+    controller = Mock()
+    controller.get_info.return_value = None
+    commands = _get_commands(controller)
+
+    self.assertTrue('GETINFO ' in commands)
+    self.assertTrue('GETCONF ' in commands)
+    self.assertTrue('SIGNAL ' in commands)
+
+    self.assertFalse('GETINFO info/names' in commands)
+    self.assertFalse('GETCONF ExitPolicy' in commands)
+    self.assertFalse('SIGNAL SHUTDOWN' in commands)
+
+    # Now check where we should be able to determine tor's capabilities.
+
+    controller.get_info.side_effect = lambda arg, _: {
+      'info/names': GETINFO_NAMES,
+      'config/names': GETCONF_NAMES,
+      'events/names': 'BW DEBUG INFO NOTICE',
+      'features/names': 'VERBOSE_NAMES EXTENDED_EVENTS',
+      'signal/names': 'RELOAD HUP SHUTDOWN',
+    }[arg]
+
+    commands = _get_commands(controller)
+
+    expected = (
+      'GETINFO info/names',
+      'GETINFO ip-to-country/',
+      'GETINFO md/id/',
+
+      'GETCONF ExitNodes',
+      'GETCONF ExitPolicy',
+      'SETCONF ExitPolicy',
+      'RESETCONF ExitPolicy',
+
+      'SETEVENTS BW',
+      'SETEVENTS INFO',
+      'USEFEATURE VERBOSE_NAMES',
+      'USEFEATURE EXTENDED_EVENTS',
+      'SIGNAL RELOAD',
+      'SIGNAL SHUTDOWN',
+    )
+
+    for result in expected:
+      self.assertTrue(result in commands)
+
+    # We shouldn't include the base commands since we have results with
+    # their arguments.
+
+    self.assertFalse('GETINFO ' in commands)
+    self.assertFalse('GETCONF ' in commands)
+    self.assertFalse('SIGNAL ' in commands)
+
+  def test_autocompleter_match(self):
+    """
+    Exercise our Autocompleter's match method.
+    """
+
+    autocompleter = Autocompleter(None)
+
+    self.assertEqual(['/help'], autocompleter.matches('/help'))
+    self.assertEqual(['/help'], autocompleter.matches('/hel'))
+    self.assertEqual(['/help'], autocompleter.matches('/he'))
+    self.assertEqual(['/help'], autocompleter.matches('/h'))
+    self.assertEqual(['/help', '/events', '/info', '/quit'], autocompleter.matches('/'))
+
+    # check case sensitivity
+
+    self.assertEqual(['/help'], autocompleter.matches('/HELP'))
+    self.assertEqual(['/help'], autocompleter.matches('/HeLp'))
+
+    # check when we shouldn't have any matches
+
+    self.assertEqual([], autocompleter.matches('blarg'))
+
+  def test_autocompleter_complete(self):
+    """
+    Exercise our Autocompleter's complete method.
+    """
+
+    autocompleter = Autocompleter(None)
+
+    self.assertEqual('/help', autocompleter.complete('/', 0))
+    self.assertEqual('/events', autocompleter.complete('/', 1))
+    self.assertEqual('/info', autocompleter.complete('/', 2))
+    self.assertEqual('/quit', autocompleter.complete('/', 3))
+    self.assertEqual(None, autocompleter.complete('/', 4))
+
+    self.assertEqual(None, autocompleter.complete('blarg', 0))





More information about the tor-commits mailing list