Back to index

python-cliapp  1.20120630
app_tests.py
Go to the documentation of this file.
00001 # Copyright (C) 2011  Lars Wirzenius
00002 # 
00003 # This program is free software; you can redistribute it and/or modify
00004 # it under the terms of the GNU General Public License as published by
00005 # the Free Software Foundation; either version 2 of the License, or
00006 # (at your option) any later version.
00007 # 
00008 # This program is distributed in the hope that it will be useful,
00009 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011 # GNU General Public License for more details.
00012 # 
00013 # You should have received a copy of the GNU General Public License along
00014 # with this program; if not, write to the Free Software Foundation, Inc.,
00015 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00016 
00017 
00018 import optparse
00019 import os
00020 import StringIO
00021 import sys
00022 import tempfile
00023 import unittest
00024 
00025 import cliapp
00026 
00027 
00028 def devnull(msg):
00029     pass
00030 
00031 
00032 class AppExceptionTests(unittest.TestCase):
00033 
00034     def setUp(self):
00035         self.e = cliapp.AppException('foo')
00036         
00037     def test_error_message_contains_foo(self):
00038         self.assert_('foo' in str(self.e))
00039 
00040 
00041 class ApplicationTests(unittest.TestCase):
00042 
00043     def setUp(self):
00044         self.app = cliapp.Application()
00045 
00046     def test_creates_settings(self):
00047         self.assert_(isinstance(self.app.settings, cliapp.Settings))
00048         
00049     def test_calls_add_settings_only_in_run(self):
00050     
00051         class Foo(cliapp.Application):
00052             def process_args(self, args):
00053                 pass
00054             def add_settings(self):
00055                 self.settings.string(['foo'], '')
00056         foo = Foo()
00057         self.assertFalse('foo' in foo.settings)
00058         foo.run(args=[])
00059         self.assert_('foo' in foo.settings)
00060 
00061     def test_run_uses_string_list_options_only_once(self):
00062         
00063         class Foo(cliapp.Application):
00064             def add_settings(self):
00065                 self.settings.string_list(['foo'], '')
00066             def process_args(self, args):
00067                 pass
00068         foo = Foo()
00069         foo.run(args=['--foo=yo'])
00070         self.assertEqual(foo.settings['foo'], ['yo'])
00071 
00072     def test_run_sets_up_logging(self):
00073         self.called = False
00074         def setup():
00075             self.called = True
00076         self.app.setup_logging = setup
00077         self.app.process_args = lambda args: None
00078         self.app.run([])
00079         self.assert_(self.called)
00080     
00081     def test_run_sets_progname_from_sysargv0(self):
00082         self.app.process_args = lambda args: None
00083         self.app.run(args=[], sysargv=['foo'])
00084         self.assertEqual(self.app.settings.progname, 'foo')
00085     
00086     def test_run_calls_process_args(self):
00087         self.called = None
00088         self.app.process_args = lambda args: setattr(self, 'called', args)
00089         self.app.run(args=['foo', 'bar'])
00090         self.assertEqual(self.called, ['foo', 'bar'])
00091     
00092     def test_run_processes_input_files(self):
00093         self.inputs = []
00094         self.app.process_input = lambda name: self.inputs.append(name)
00095         self.app.run(args=['foo', 'bar'])
00096         self.assertEqual(self.inputs, ['foo', 'bar'])
00097         
00098     def test_run_sets_output_attribute(self):
00099         self.app.process_args = lambda args: None
00100         self.app.run(args=[])
00101         self.assertEqual(self.app.output, sys.stdout)
00102         
00103     def test_run_sets_output_to_file_if_output_option_is_set(self):
00104         self.app.process_args = lambda args: None
00105         self.app.run(args=['--output=/dev/null'])
00106         self.assertEqual(self.app.output.name, '/dev/null')
00107     
00108     def test_run_calls_parse_args(self):
00109         class DummyOptions(object):
00110             def __init__(self):
00111                 self.output = None
00112                 self.log = None
00113         self.called = None
00114         self.app.parse_args = lambda args, **kw: setattr(self, 'called', args)
00115         self.app.process_args = lambda args: None
00116         self.app.settings.options = DummyOptions()
00117         self.app.run(args=['foo', 'bar'])
00118         self.assertEqual(self.called, ['foo', 'bar'])
00119 
00120     def test_makes_envname_correctly(self):
00121         self.assertEqual(self.app._envname('foo'), 'FOO')
00122         self.assertEqual(self.app._envname('foo.py'), 'FOO')
00123         self.assertEqual(self.app._envname('foo bar'), 'FOO_BAR')
00124         self.assertEqual(self.app._envname('foo-bar'), 'FOO_BAR')
00125         self.assertEqual(self.app._envname('foo/bar'), 'BAR')
00126         self.assertEqual(self.app._envname('foo_bar'), 'FOO_BAR')
00127 
00128     def test_parses_options(self):
00129         self.app.settings.string(['foo'], 'foo help')
00130         self.app.settings.boolean(['bar'], 'bar help')
00131         self.app.parse_args(['--foo=foovalue', '--bar'])
00132         self.assertEqual(self.app.settings['foo'], 'foovalue')
00133         self.assertEqual(self.app.settings['bar'], True)
00134 
00135     def test_calls_setup(self):
00136         class App(cliapp.Application):
00137             def setup(self):
00138                 self.setup_called = True
00139             def process_inputs(self, args):
00140                 pass
00141         app = App()
00142         app.run(args=[])
00143         self.assertTrue(app.setup_called)
00144 
00145     def test_calls_cleanup(self):
00146         class App(cliapp.Application):
00147             def cleanup(self):
00148                 self.cleanup_called = True
00149             def process_inputs(self, args):
00150                 pass
00151         app = App()
00152         app.run(args=[])
00153         self.assertTrue(app.cleanup_called)
00154 
00155     def test_process_args_calls_process_inputs(self):
00156         self.called = False
00157         def process_inputs(args):
00158             self.called = True
00159         self.app.process_inputs = process_inputs
00160         self.app.process_args([])
00161         self.assert_(self.called)
00162 
00163     def test_process_inputs_calls_process_input_for_each_arg(self):
00164         self.args = []
00165         def process_input(arg):
00166             self.args.append(arg)
00167         self.app.process_input = process_input
00168         self.app.process_inputs(['foo', 'bar'])
00169         self.assertEqual(self.args, ['foo', 'bar'])
00170 
00171     def test_process_inputs_calls_process_input_with_dash_if_no_inputs(self):
00172         self.args = []
00173         def process_input(arg):
00174             self.args.append(arg)
00175         self.app.process_input = process_input
00176         self.app.process_inputs([])
00177         self.assertEqual(self.args, ['-'])
00178 
00179     def test_open_input_opens_file(self):
00180         f = self.app.open_input('/dev/null')
00181         self.assert_(isinstance(f, file))
00182         self.assertEqual(f.mode, 'r')
00183         
00184     def test_open_input_opens_file_in_binary_mode_when_requested(self):
00185         f = self.app.open_input('/dev/null', mode='rb')
00186         self.assertEqual(f.mode, 'rb')
00187 
00188     def test_open_input_opens_stdin_if_dash_given(self):
00189         self.assertEqual(self.app.open_input('-'), sys.stdin)
00190 
00191     def test_process_input_calls_open_input(self):
00192         self.called = None
00193         def open_input(name):
00194             self.called = name
00195             return StringIO.StringIO('')
00196         self.app.open_input = open_input
00197         self.app.process_input('foo')
00198         self.assertEqual(self.called, 'foo')
00199         
00200     def test_process_input_does_not_close_stdin(self):
00201         self.closed = False
00202         def close():
00203             self.closed = True
00204         f = StringIO.StringIO('')
00205         f.close = close
00206         def open_input(name):
00207             if name == '-':
00208                 return f
00209         self.app.open_input = open_input
00210         self.app.process_input('-', stdin=f)
00211         self.assertEqual(self.closed, False)
00212 
00213     def test_processes_input_lines(self):
00214 
00215         lines = []
00216         class Foo(cliapp.Application):
00217             def open_input(self, name):
00218                 return StringIO.StringIO(''.join('%s%d\n' % (name, i)
00219                                                  for i in range(2)))
00220             def process_input_line(self, name, line):
00221                 lines.append(line)
00222 
00223         foo = Foo()
00224         foo.run(args=['foo', 'bar'])
00225         self.assertEqual(lines, ['foo0\n', 'foo1\n', 'bar0\n', 'bar1\n'])
00226 
00227     def test_process_input_line_can_access_counters(self):
00228         counters = []
00229         class Foo(cliapp.Application):
00230             def open_input(self, name):
00231                 return StringIO.StringIO(''.join('%s%d\n' % (name, i)
00232                                                  for i in range(2)))
00233             def process_input_line(self, name, line):
00234                 counters.append((self.fileno, self.global_lineno, self.lineno))
00235 
00236         foo = Foo()
00237         foo.run(args=['foo', 'bar'])
00238         self.assertEqual(counters, 
00239                          [(1, 1, 1), 
00240                           (1, 2, 2), 
00241                           (2, 3, 1), 
00242                           (2, 4, 2)])
00243 
00244     def test_run_prints_out_error_for_appexception(self):
00245         def raise_error(args):
00246             raise cliapp.AppException('xxx')
00247         self.app.process_args = raise_error
00248         f = StringIO.StringIO()
00249         self.assertRaises(SystemExit, self.app.run, [], stderr=f, log=devnull)
00250         self.assert_('xxx' in f.getvalue())
00251 
00252     def test_run_prints_out_stack_trace_for_not_appexception(self):
00253         def raise_error(args):
00254             raise Exception('xxx')
00255         self.app.process_args = raise_error
00256         f = StringIO.StringIO()
00257         self.assertRaises(SystemExit, self.app.run, [], stderr=f, log=devnull)
00258         self.assert_('Traceback' in f.getvalue())
00259 
00260     def test_run_raises_systemexit_for_systemexit(self):
00261         def raise_error(args):
00262             raise SystemExit(123)
00263         self.app.process_args = raise_error
00264         f = StringIO.StringIO()
00265         self.assertRaises(SystemExit, self.app.run, [], stderr=f, log=devnull)
00266 
00267     def test_run_raises_systemexit_for_keyboardint(self):
00268         def raise_error(args):
00269             raise KeyboardInterrupt()
00270         self.app.process_args = raise_error
00271         f = StringIO.StringIO()
00272         self.assertRaises(SystemExit, self.app.run, [], stderr=f, log=devnull)
00273     
00274 
00275 class DummySubcommandApp(cliapp.Application):
00276 
00277     def cmd_help(self, args):
00278         self.help_called = True
00279         
00280         
00281 class SubcommandTests(unittest.TestCase):
00282 
00283     def setUp(self):
00284         self.app = DummySubcommandApp()
00285         self.trash = StringIO.StringIO()
00286         
00287     def test_lists_subcommands(self):
00288         self.assertEqual(self.app._subcommand_methodnames(), ['cmd_help'])
00289 
00290     def test_normalizes_subcommand(self):
00291         self.assertEqual(self.app._normalize_cmd('help'), 'cmd_help')
00292         self.assertEqual(self.app._normalize_cmd('foo-bar'), 'cmd_foo_bar')
00293         
00294     def test_raises_error_for_no_subcommand(self):
00295         self.assertRaises(SystemExit, self.app.run, [], 
00296                           stderr=self.trash, log=devnull)
00297         
00298     def test_raises_error_for_unknown_subcommand(self):
00299         self.assertRaises(SystemExit, self.app.run, ['what?'], 
00300                           stderr=self.trash, log=devnull)
00301 
00302     def test_calls_subcommand_method(self):
00303         self.app.run(['help'], stderr=self.trash, log=devnull)
00304         self.assert_(self.app.help_called)
00305 
00306 
00307 class ExtensibleSubcommandTests(unittest.TestCase):
00308 
00309     def setUp(self):
00310         self.app = cliapp.Application()
00311     
00312     def test_lists_no_subcommands(self):
00313         self.assertEqual(self.app.subcommands, {})
00314         
00315     def test_adds_subcommand(self):
00316         help = lambda args: None
00317         self.app.add_subcommand('help', help)
00318         self.assertEqual(self.app.subcommands, {'help': help})
00319