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