commander: add tests and fix exposed bugs
2 files changed, 57 insertions(+), 9 deletions(-)

M test/test_cli_commands.py
M vanity/commander.py
M test/test_cli_commands.py +49 -2
@@ 22,11 22,22 @@ def cmd3():
 CMDTABLE_A = [
     ('foo', cmd1, '', [], 'foo fixes everything'),
     ('bar', cmd2, '', [], 'bar breaks it'),
-    ('baz', cmd3, '', [
+    ('baz', cmd3, 'wibble|hamper', [
         ('shrimp', '', None, 'jumbo shrimp'),
         ], 'something helpful'),
     ]
 
+CMDTABLE_B = [
+    {'name': 'snorkel', 'target': cmd1, 'opts': [], 'help': 'dive in'},
+    {'name': 'fish', 'target': cmd2, 'opts': [], 'help': 'the angler'},
+    commander.Command('net', cmd3, opts=[], help='nab it'),
+    ]
+
+CMDTABLE_C = {
+    'snorkel': {'target': cmd1, 'opts': [], 'help': 'magic stuff'},
+    'fish': {'target': cmd2, 'opts': [], 'help': 'other stuff'},
+    }
+
 class TestCliCommands(unittest.TestCase):
 
     def test_parse_cli_cmd(self):

          
@@ 49,7 60,29 @@ class TestCliCommands(unittest.TestCase)
         self.assertEqual(args, ['test.txt'])
         self.assertEqual(opts, dict(verbose=True, quiet=True))
 
-    def test_parse_cli_cmd4(self):
+    def test_partial_command_name(self):
+        arguments = 'fo xxx.txt'.split()
+        cmd, opts, args = commander.parse(GOPT_A, CMDTABLE_A, arguments)
+        self.assertEqual(cmd.target, cmd1)
+        arguments = 'f xxx.txt'.split()
+        cmd, opts, args = commander.parse(GOPT_A, CMDTABLE_A, arguments)
+        self.assertEqual(cmd.target, cmd1)
+        arguments = 'wi xxx.txt'.split()
+        cmd, opts, args = commander.parse(GOPT_A, CMDTABLE_A, arguments)
+        self.assertEqual(cmd.target, cmd3)
+
+    def test_ambiguous_command(self):
+        arguments = 'b zapp.txt'.split()
+        self.assertRaises(commander.AmbiguousCommand,
+            commander.parse, GOPT_A, CMDTABLE_A, arguments)
+
+    def test_invalid_command(self):
+        arguments = 'morton zapp.txt'.split()
+        self.assertRaises(commander.InvalidCommand,
+            commander.parse, GOPT_A, CMDTABLE_A, arguments)
+
+
+    def test_cmd_with_option(self):
         arguments = '-v baz test.txt --shrimp'.split()
         cmd, opts, args = commander.parse(GOPT_A, CMDTABLE_A, arguments)
         self.assertEqual(cmd.target, cmd3)

          
@@ 59,6 92,20 @@ class TestCliCommands(unittest.TestCase)
         cmd, opts, args = commander.parse(GOPT_A, CMDTABLE_A, arguments)
         self.assertEqual(opts, dict(verbose=True, quiet=None, shrimp=None))
 
+    def test_table_mixed_format(self):
+        arguments = 'fish -v yes yes'.split()
+        cmd, opts, args = commander.parse(GOPT_A, CMDTABLE_B, arguments)
+        self.assertEqual(cmd.target, cmd2)
+        self.assertEqual(args, ['yes', 'yes'])
+        self.assertEqual(opts, dict(verbose=True, quiet=None))
+
+    def test_dict_table_style(self):
+        arguments = 'fish -v yes yes'.split()
+        cmd, opts, args = commander.parse(GOPT_A, CMDTABLE_C, arguments)
+        self.assertEqual(cmd.target, cmd2)
+        self.assertEqual(args, ['yes', 'yes'])
+        self.assertEqual(opts, dict(verbose=True, quiet=None))
+
 
 if __name__ == '__main__':
     unittest.main()

          
M vanity/commander.py +8 -7
@@ 29,7 29,7 @@ class AmbiguousCommand(cli.CliError):
     """The given command was ambigouous"""
     def __init__(self, matches):
         msg = 'possible matches: %s' % ' '.join(matches)
-        CliError.__init__(self, msg)
+        cli.CliError.__init__(self, msg)
         self.matches = matches
 
 

          
@@ 118,9 118,9 @@ class CommandTable(object):
 
     def __init__(self, table):
         self._table = {}
-        if hasattr(table, 'keys'):
+        if hasattr(table, 'keys') and hasattr(table, 'items'):
             # if a user is giving us a dict, the entries **must** be dicts
-            for name, entry in table:
+            for name, entry in table.items():
                 self._table[name] = Command(name, **entry)
         else:
             for entry in table:

          
@@ 138,9 138,9 @@ class CommandTable(object):
         AmbiguousCommand exception is raised.
         """
         namemap = dict((k,k) for k in self._table)
-        for key in self._table:
-            for alias in self._table[key].aliaslist():
-                namemap[alias] = name
+        for key, cmd in self._table.iteritems():
+            for alias in cmd.aliaslist():
+                namemap[alias] = key
         if name in namemap:
             key = namemap[name]
             return self._table[key]

          
@@ 150,7 150,8 @@ class CommandTable(object):
             if alias.startswith(name):
                 canidates.add(alias)
         if len(canidates) == 1:
-            key = namemap[canidates[0]]
+            key = namemap[canidates.pop()]
+            print namemap
             return self._table[key]
         if not canidates:
             raise InvalidCommand(name)