diff options
Diffstat (limited to 'cliapp/app_tests.py')
-rw-r--r-- | cliapp/app_tests.py | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/cliapp/app_tests.py b/cliapp/app_tests.py new file mode 100644 index 00000000..080a6007 --- /dev/null +++ b/cliapp/app_tests.py @@ -0,0 +1,331 @@ +# Copyright (C) 2011 Lars Wirzenius +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +import optparse +import os +import StringIO +import sys +import tempfile +import unittest + +import cliapp + + +def devnull(msg): + pass + + +class AppExceptionTests(unittest.TestCase): + + def setUp(self): + self.e = cliapp.AppException('foo') + + def test_error_message_contains_foo(self): + self.assert_('foo' in str(self.e)) + + +class ApplicationTests(unittest.TestCase): + + def setUp(self): + self.app = cliapp.Application() + + def test_creates_settings(self): + self.assert_(isinstance(self.app.settings, cliapp.Settings)) + + def test_calls_add_settings_only_in_run(self): + + class Foo(cliapp.Application): + def process_args(self, args): + pass + def add_settings(self): + self.settings.string(['foo'], '') + foo = Foo() + self.assertFalse('foo' in foo.settings) + foo.run(args=[]) + self.assert_('foo' in foo.settings) + + def test_run_uses_string_list_options_only_once(self): + + class Foo(cliapp.Application): + def add_settings(self): + self.settings.string_list(['foo'], '') + def process_args(self, args): + pass + foo = Foo() + foo.run(args=['--foo=yo']) + self.assertEqual(foo.settings['foo'], ['yo']) + + def test_run_sets_up_logging(self): + self.called = False + def setup(): + self.called = True + self.app.setup_logging = setup + self.app.process_args = lambda args: None + self.app.run([]) + self.assert_(self.called) + + def test_run_sets_progname_from_sysargv0(self): + self.app.process_args = lambda args: None + self.app.run(args=[], sysargv=['foo']) + self.assertEqual(self.app.settings.progname, 'foo') + + def test_run_calls_process_args(self): + self.called = None + self.app.process_args = lambda args: setattr(self, 'called', args) + self.app.run(args=['foo', 'bar']) + self.assertEqual(self.called, ['foo', 'bar']) + + def test_run_processes_input_files(self): + self.inputs = [] + self.app.process_input = lambda name: self.inputs.append(name) + self.app.run(args=['foo', 'bar']) + self.assertEqual(self.inputs, ['foo', 'bar']) + + def test_run_sets_output_attribute(self): + self.app.process_args = lambda args: None + self.app.run(args=[]) + self.assertEqual(self.app.output, sys.stdout) + + def test_run_sets_output_to_file_if_output_option_is_set(self): + self.app.process_args = lambda args: None + self.app.run(args=['--output=/dev/null']) + self.assertEqual(self.app.output.name, '/dev/null') + + def test_run_calls_parse_args(self): + class DummyOptions(object): + def __init__(self): + self.output = None + self.log = None + self.called = None + self.app.parse_args = lambda args, **kw: setattr(self, 'called', args) + self.app.process_args = lambda args: None + self.app.settings.options = DummyOptions() + self.app.run(args=['foo', 'bar']) + self.assertEqual(self.called, ['foo', 'bar']) + + def test_makes_envname_correctly(self): + self.assertEqual(self.app._envname('foo'), 'FOO') + self.assertEqual(self.app._envname('foo.py'), 'FOO') + self.assertEqual(self.app._envname('foo bar'), 'FOO_BAR') + self.assertEqual(self.app._envname('foo-bar'), 'FOO_BAR') + self.assertEqual(self.app._envname('foo/bar'), 'BAR') + self.assertEqual(self.app._envname('foo_bar'), 'FOO_BAR') + + def test_parses_options(self): + self.app.settings.string(['foo'], 'foo help') + self.app.settings.boolean(['bar'], 'bar help') + self.app.parse_args(['--foo=foovalue', '--bar']) + self.assertEqual(self.app.settings['foo'], 'foovalue') + self.assertEqual(self.app.settings['bar'], True) + + def test_calls_setup(self): + class App(cliapp.Application): + def setup(self): + self.setup_called = True + def process_inputs(self, args): + pass + app = App() + app.run(args=[]) + self.assertTrue(app.setup_called) + + def test_calls_cleanup(self): + class App(cliapp.Application): + def cleanup(self): + self.cleanup_called = True + def process_inputs(self, args): + pass + app = App() + app.run(args=[]) + self.assertTrue(app.cleanup_called) + + def test_process_args_calls_process_inputs(self): + self.called = False + def process_inputs(args): + self.called = True + self.app.process_inputs = process_inputs + self.app.process_args([]) + self.assert_(self.called) + + def test_process_inputs_calls_process_input_for_each_arg(self): + self.args = [] + def process_input(arg): + self.args.append(arg) + self.app.process_input = process_input + self.app.process_inputs(['foo', 'bar']) + self.assertEqual(self.args, ['foo', 'bar']) + + def test_process_inputs_calls_process_input_with_dash_if_no_inputs(self): + self.args = [] + def process_input(arg): + self.args.append(arg) + self.app.process_input = process_input + self.app.process_inputs([]) + self.assertEqual(self.args, ['-']) + + def test_open_input_opens_file(self): + f = self.app.open_input('/dev/null') + self.assert_(isinstance(f, file)) + self.assertEqual(f.mode, 'r') + + def test_open_input_opens_file_in_binary_mode_when_requested(self): + f = self.app.open_input('/dev/null', mode='rb') + self.assertEqual(f.mode, 'rb') + + def test_open_input_opens_stdin_if_dash_given(self): + self.assertEqual(self.app.open_input('-'), sys.stdin) + + def test_process_input_calls_open_input(self): + self.called = None + def open_input(name): + self.called = name + return StringIO.StringIO('') + self.app.open_input = open_input + self.app.process_input('foo') + self.assertEqual(self.called, 'foo') + + def test_process_input_does_not_close_stdin(self): + self.closed = False + def close(): + self.closed = True + f = StringIO.StringIO('') + f.close = close + def open_input(name): + if name == '-': + return f + self.app.open_input = open_input + self.app.process_input('-', stdin=f) + self.assertEqual(self.closed, False) + + def test_processes_input_lines(self): + + lines = [] + class Foo(cliapp.Application): + def open_input(self, name): + return StringIO.StringIO(''.join('%s%d\n' % (name, i) + for i in range(2))) + def process_input_line(self, name, line): + lines.append(line) + + foo = Foo() + foo.run(args=['foo', 'bar']) + self.assertEqual(lines, ['foo0\n', 'foo1\n', 'bar0\n', 'bar1\n']) + + def test_process_input_line_can_access_counters(self): + counters = [] + class Foo(cliapp.Application): + def open_input(self, name): + return StringIO.StringIO(''.join('%s%d\n' % (name, i) + for i in range(2))) + def process_input_line(self, name, line): + counters.append((self.fileno, self.global_lineno, self.lineno)) + + foo = Foo() + foo.run(args=['foo', 'bar']) + self.assertEqual(counters, + [(1, 1, 1), + (1, 2, 2), + (2, 3, 1), + (2, 4, 2)]) + + def test_run_prints_out_error_for_appexception(self): + def raise_error(args): + raise cliapp.AppException('xxx') + self.app.process_args = raise_error + f = StringIO.StringIO() + self.assertRaises(SystemExit, self.app.run, [], stderr=f, log=devnull) + self.assert_('xxx' in f.getvalue()) + + def test_run_prints_out_stack_trace_for_not_appexception(self): + def raise_error(args): + raise Exception('xxx') + self.app.process_args = raise_error + f = StringIO.StringIO() + self.assertRaises(SystemExit, self.app.run, [], stderr=f, log=devnull) + self.assert_('Traceback' in f.getvalue()) + + def test_run_raises_systemexit_for_systemexit(self): + def raise_error(args): + raise SystemExit(123) + self.app.process_args = raise_error + f = StringIO.StringIO() + self.assertRaises(SystemExit, self.app.run, [], stderr=f, log=devnull) + + def test_run_raises_systemexit_for_keyboardint(self): + def raise_error(args): + raise KeyboardInterrupt() + self.app.process_args = raise_error + f = StringIO.StringIO() + self.assertRaises(SystemExit, self.app.run, [], stderr=f, log=devnull) + + +class DummySubcommandApp(cliapp.Application): + + def cmd_foo(self, args): + self.foo_called = True + + +class SubcommandTests(unittest.TestCase): + + def setUp(self): + self.app = DummySubcommandApp() + self.trash = StringIO.StringIO() + + def test_lists_subcommands(self): + self.assertEqual(self.app._subcommand_methodnames(), ['cmd_foo']) + + def test_normalizes_subcommand(self): + self.assertEqual(self.app._normalize_cmd('foo'), 'cmd_foo') + self.assertEqual(self.app._normalize_cmd('foo-bar'), 'cmd_foo_bar') + + def test_raises_error_for_no_subcommand(self): + self.assertRaises(SystemExit, self.app.run, [], + stderr=self.trash, log=devnull) + + def test_raises_error_for_unknown_subcommand(self): + self.assertRaises(SystemExit, self.app.run, ['what?'], + stderr=self.trash, log=devnull) + + def test_calls_subcommand_method(self): + self.app.run(['foo'], stderr=self.trash, log=devnull) + self.assert_(self.app.foo_called) + + def test_calls_subcommand_method_via_alias(self): + self.bar_called = False + def bar(*args): + self.bar_called = True + self.app.add_subcommand('bar', bar, aliases=['yoyo']) + self.app.run(['yoyo'], stderr=self.trash, log=devnull) + self.assertTrue(self.bar_called) + + def test_adds_default_subcommand_help(self): + self.app.run(['foo'], stderr=self.trash, log=devnull) + self.assertTrue('help' in self.app.subcommands) + + +class ExtensibleSubcommandTests(unittest.TestCase): + + def setUp(self): + self.app = cliapp.Application() + + def test_lists_no_subcommands(self): + self.assertEqual(self.app.subcommands, {}) + + def test_adds_subcommand(self): + help = lambda args: None + self.app.add_subcommand('foo', help) + self.assertEqual(self.app.subcommands, {'foo': help}) + |