diff --git a/.gitignore b/.gitignore index ba52b1e..28420f9 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ ChangeLog # Editors *~ .*.swp +/.testrepository/ +/cover/ +.coverage.* \ No newline at end of file diff --git a/.testr.conf b/.testr.conf new file mode 100644 index 0000000..faf55cf --- /dev/null +++ b/.testr.conf @@ -0,0 +1,7 @@ +[DEFAULT] +test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ + OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ + OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \ + ${PYTHON:-python} -m subunit.run discover -t ./ ./cliff $LISTOPT $IDOPTION +test_id_option=--load-list $IDFILE +test_list_option=--list diff --git a/cliff/tests/base.py b/cliff/tests/base.py new file mode 100644 index 0000000..7afce02 --- /dev/null +++ b/cliff/tests/base.py @@ -0,0 +1,29 @@ +# -*- encoding: utf-8 -*- +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import testtools + +import fixtures + + +class TestBase(testtools.TestCase): + + def setUp(self): + super(TestBase, self).setUp() + self._stdout_fixture = fixtures.StringStream('stdout') + self.stdout = self.useFixture(self._stdout_fixture).stream + self.useFixture(fixtures.MonkeyPatch('sys.stdout', self.stdout)) + self._stderr_fixture = fixtures.StringStream('stderr') + self.stderr = self.useFixture(self._stderr_fixture).stream + self.useFixture(fixtures.MonkeyPatch('sys.stderr', self.stderr)) diff --git a/cliff/tests/test_app.py b/cliff/tests/test_app.py index 26149a2..4430d77 100644 --- a/cliff/tests/test_app.py +++ b/cliff/tests/test_app.py @@ -13,20 +13,21 @@ # under the License. import argparse +import codecs +import locale try: from StringIO import StringIO except ImportError: from io import StringIO +import sys -import codecs -import locale import mock import six -import sys from cliff import app as application from cliff import command as c_cmd from cliff import commandmanager +from cliff.tests import base from cliff.tests import utils as test_utils from cliff import utils @@ -59,440 +60,441 @@ def make_app(**kwargs): return app, command -def test_no_args_triggers_interactive_mode(): - app, command = make_app() - app.interact = mock.MagicMock(name='inspect') - app.run([]) - app.interact.assert_called_once_with() +class TestInteractiveMode(base.TestBase): + + def test_no_args_triggers_interactive_mode(self): + app, command = make_app() + app.interact = mock.MagicMock(name='inspect') + app.run([]) + app.interact.assert_called_once_with() + + def test_interactive_mode_cmdloop(self): + app, command = make_app() + app.interactive_app_factory = mock.MagicMock( + name='interactive_app_factory' + ) + self.assertIs(None, app.interpreter) + app.run([]) + self.assertIsNot(None, app.interpreter) + cmdloop = app.interactive_app_factory.return_value.cmdloop + cmdloop.assert_called_once_with() -def test_interactive_mode_cmdloop(): - app, command = make_app() - app.interactive_app_factory = mock.MagicMock( - name='interactive_app_factory' - ) - assert app.interpreter is None - app.run([]) - assert app.interpreter is not None - app.interactive_app_factory.return_value.cmdloop.assert_called_once_with() +class TestInitAndCleanup(base.TestBase): + + def test_initialize_app(self): + app, command = make_app() + app.initialize_app = mock.MagicMock(name='initialize_app') + app.run(['mock']) + app.initialize_app.assert_called_once_with(['mock']) + + def test_prepare_to_run_command(self): + app, command = make_app() + app.prepare_to_run_command = mock.MagicMock( + name='prepare_to_run_command', + ) + app.run(['mock']) + app.prepare_to_run_command.assert_called_once_with(command()) + + def test_clean_up_success(self): + app, command = make_app() + app.clean_up = mock.MagicMock(name='clean_up') + app.run(['mock']) + app.clean_up.assert_called_once_with(command.return_value, 0, None) + + def test_clean_up_error(self): + app, command = make_app() + + app.clean_up = mock.MagicMock(name='clean_up') + app.run(['error']) + + app.clean_up.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY) + call_args = app.clean_up.call_args_list[0] + self.assertEqual(mock.call(mock.ANY, 1, mock.ANY), call_args) + args, kwargs = call_args + self.assertIsInstance(args[2], RuntimeError) + self.assertEqual(('test exception',), args[2].args) + + def test_clean_up_error_debug(self): + app, command = make_app() + + app.clean_up = mock.MagicMock(name='clean_up') + try: + app.run(['--debug', 'error']) + except RuntimeError as err: + self.assertIs(err, app.clean_up.call_args_list[0][0][2]) + else: + self.fail('Should have had an exception') + + self.assertTrue(app.clean_up.called) + call_args = app.clean_up.call_args_list[0] + self.assertEqual(mock.call(mock.ANY, 1, mock.ANY), call_args) + args, kwargs = call_args + self.assertIsInstance(args[2], RuntimeError) + self.assertEqual(('test exception',), args[2].args) + + def test_error_handling_clean_up_raises_exception(self): + app, command = make_app() + + app.clean_up = mock.MagicMock( + name='clean_up', + side_effect=RuntimeError('within clean_up'), + ) + app.run(['error']) + + self.assertTrue(app.clean_up.called) + call_args = app.clean_up.call_args_list[0] + self.assertEqual(mock.call(mock.ANY, 1, mock.ANY), call_args) + args, kwargs = call_args + self.assertIsInstance(args[2], RuntimeError) + self.assertEqual(('test exception',), args[2].args) + + def test_error_handling_clean_up_raises_exception_debug(self): + app, command = make_app() + + app.clean_up = mock.MagicMock( + name='clean_up', + side_effect=RuntimeError('within clean_up'), + ) + try: + app.run(['--debug', 'error']) + except RuntimeError as err: + if not hasattr(err, '__context__'): + # The exception passed to clean_up is not the exception + # caused *by* clean_up. This test is only valid in python + # 2 because under v3 the original exception is re-raised + # with the new one as a __context__ attribute. + self.assertIsNot(err, app.clean_up.call_args_list[0][0][2]) + else: + self.fail('Should have had an exception') + + self.assertTrue(app.clean_up.called) + call_args = app.clean_up.call_args_list[0] + self.assertEqual(mock.call(mock.ANY, 1, mock.ANY), call_args) + args, kwargs = call_args + self.assertIsInstance(args[2], RuntimeError) + self.assertEqual(('test exception',), args[2].args) + + def test_normal_clean_up_raises_exception(self): + app, command = make_app() + + app.clean_up = mock.MagicMock( + name='clean_up', + side_effect=RuntimeError('within clean_up'), + ) + app.run(['mock']) + + self.assertTrue(app.clean_up.called) + call_args = app.clean_up.call_args_list[0] + self.assertEqual(mock.call(mock.ANY, 0, None), call_args) + + def test_normal_clean_up_raises_exception_debug(self): + app, command = make_app() + + app.clean_up = mock.MagicMock( + name='clean_up', + side_effect=RuntimeError('within clean_up'), + ) + app.run(['--debug', 'mock']) + + self.assertTrue(app.clean_up.called) + call_args = app.clean_up.call_args_list[0] + self.assertEqual(mock.call(mock.ANY, 0, None), call_args) -def test_initialize_app(): - app, command = make_app() - app.initialize_app = mock.MagicMock(name='initialize_app') - app.run(['mock']) - app.initialize_app.assert_called_once_with(['mock']) +class TestOptionParser(base.TestBase): + def test_conflicting_option_should_throw(self): + class MyApp(application.App): + def __init__(self): + super(MyApp, self).__init__( + description='testing', + version='0.1', + command_manager=commandmanager.CommandManager('tests'), + ) -def test_prepare_to_run_command(): - app, command = make_app() - app.prepare_to_run_command = mock.MagicMock(name='prepare_to_run_command') - app.run(['mock']) - app.prepare_to_run_command.assert_called_once_with(command()) + def build_option_parser(self, description, version): + parser = super(MyApp, self).build_option_parser(description, + version) + parser.add_argument( + '-h', '--help', + default=self, # tricky + help="Show help message and exit.", + ) + self.assertRaises( + argparse.ArgumentError, + MyApp, + ) -def test_clean_up_success(): - app, command = make_app() - app.clean_up = mock.MagicMock(name='clean_up') - app.run(['mock']) - app.clean_up.assert_called_once_with(command.return_value, 0, None) + def test_conflicting_option_custom_arguments_should_not_throw(self): + class MyApp(application.App): + def __init__(self): + super(MyApp, self).__init__( + description='testing', + version='0.1', + command_manager=commandmanager.CommandManager('tests'), + ) + def build_option_parser(self, description, version): + argparse_kwargs = {'conflict_handler': 'resolve'} + parser = super(MyApp, self).build_option_parser( + description, + version, + argparse_kwargs=argparse_kwargs) + parser.add_argument( + '-h', '--help', + default=self, # tricky + help="Show help message and exit.", + ) -def test_clean_up_error(): - app, command = make_app() - - app.clean_up = mock.MagicMock(name='clean_up') - app.run(['error']) - - app.clean_up.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY) - call_args = app.clean_up.call_args_list[0] - assert call_args == mock.call(mock.ANY, 1, mock.ANY) - args, kwargs = call_args - assert isinstance(args[2], RuntimeError) - assert args[2].args == ('test exception',) - - -def test_clean_up_error_debug(): - app, command = make_app() - - app.clean_up = mock.MagicMock(name='clean_up') - try: - app.run(['--debug', 'error']) - except RuntimeError as err: - assert app.clean_up.call_args_list[0][0][2] is err - else: - assert False, 'Should have had an exception' - - assert app.clean_up.called - call_args = app.clean_up.call_args_list[0] - assert call_args == mock.call(mock.ANY, 1, mock.ANY) - args, kwargs = call_args - assert isinstance(args[2], RuntimeError) - assert args[2].args == ('test exception',) - - -def test_error_handling_clean_up_raises_exception(): - app, command = make_app() - - app.clean_up = mock.MagicMock( - name='clean_up', - side_effect=RuntimeError('within clean_up'), - ) - app.run(['error']) - - assert app.clean_up.called - call_args = app.clean_up.call_args_list[0] - assert call_args == mock.call(mock.ANY, 1, mock.ANY) - args, kwargs = call_args - assert isinstance(args[2], RuntimeError) - assert args[2].args == ('test exception',) - - -def test_error_handling_clean_up_raises_exception_debug(): - app, command = make_app() - - app.clean_up = mock.MagicMock( - name='clean_up', - side_effect=RuntimeError('within clean_up'), - ) - try: - app.run(['--debug', 'error']) - except RuntimeError as err: - if not hasattr(err, '__context__'): - # The exception passed to clean_up is not the exception - # caused *by* clean_up. This test is only valid in python - # 2 because under v3 the original exception is re-raised - # with the new one as a __context__ attribute. - assert app.clean_up.call_args_list[0][0][2] is not err - else: - assert False, 'Should have had an exception' - - assert app.clean_up.called - call_args = app.clean_up.call_args_list[0] - assert call_args == mock.call(mock.ANY, 1, mock.ANY) - args, kwargs = call_args - assert isinstance(args[2], RuntimeError) - assert args[2].args == ('test exception',) - - -def test_normal_clean_up_raises_exception(): - app, command = make_app() - - app.clean_up = mock.MagicMock( - name='clean_up', - side_effect=RuntimeError('within clean_up'), - ) - app.run(['mock']) - - assert app.clean_up.called - call_args = app.clean_up.call_args_list[0] - assert call_args == mock.call(mock.ANY, 0, None) - - -def test_normal_clean_up_raises_exception_debug(): - app, command = make_app() - - app.clean_up = mock.MagicMock( - name='clean_up', - side_effect=RuntimeError('within clean_up'), - ) - app.run(['--debug', 'mock']) - - assert app.clean_up.called - call_args = app.clean_up.call_args_list[0] - assert call_args == mock.call(mock.ANY, 0, None) - - -def test_build_option_parser_conflicting_option_should_throw(): - class MyApp(application.App): - def __init__(self): - super(MyApp, self).__init__( - description='testing', - version='0.1', - command_manager=commandmanager.CommandManager('tests'), - ) - - def build_option_parser(self, description, version): - parser = super(MyApp, self).build_option_parser(description, - version) - parser.add_argument( - '-h', '--help', - default=self, # tricky - help="Show help message and exit.", - ) - - # TODO: tests should really use unittest2. - try: MyApp() - except argparse.ArgumentError: - pass - else: - raise Exception('Exception was not thrown') + + def test_option_parser_abbrev_issue(self): + class MyCommand(c_cmd.Command): + def get_parser(self, prog_name): + parser = super(MyCommand, self).get_parser(prog_name) + parser.add_argument("--end") + return parser + + def take_action(self, parsed_args): + assert(parsed_args.end == '123') + + class MyCommandManager(commandmanager.CommandManager): + def load_commands(self, namespace): + self.add_command("mycommand", MyCommand) + + class MyApp(application.App): + def __init__(self): + super(MyApp, self).__init__( + description='testing', + version='0.1', + command_manager=MyCommandManager(None), + ) + + def build_option_parser(self, description, version): + parser = super(MyApp, self).build_option_parser( + description, + version, + argparse_kwargs={'allow_abbrev': False}) + parser.add_argument('--endpoint') + return parser + + app = MyApp() + # NOTE(jd) --debug is necessary so assert in take_action() + # raises correctly here + app.run(['--debug', 'mycommand', '--end', '123']) -def test_option_parser_conflicting_option_custom_arguments_should_not_throw(): - class MyApp(application.App): - def __init__(self): - super(MyApp, self).__init__( - description='testing', - version='0.1', - command_manager=commandmanager.CommandManager('tests'), +class TestHelpHandling(base.TestBase): + + def _test_help(self, deferred_help): + app, _ = make_app(deferred_help=deferred_help) + with mock.patch.object(app, 'initialize_app') as init: + with mock.patch('cliff.help.HelpAction.__call__', + side_effect=SystemExit(0)) as helper: + self.assertRaises( + SystemExit, + app.run, + ['--help'], + ) + self.assertTrue(helper.called) + self.assertEqual(deferred_help, init.called) + + def test_help(self): + self._test_help(False) + + def test_deferred_help(self): + self._test_help(True) + + def test_subcommand_help(self): + app, _ = make_app(deferred_help=False) + + # Help is called immediately + with mock.patch('cliff.help.HelpAction.__call__') as helper: + app.run(['show', 'files', '--help']) + + self.assertTrue(helper.called) + + def test_subcommand_deferred_help(self): + app, _ = make_app(deferred_help=True) + + # Show that provide_help_if_requested() did not show help and exit + with mock.patch.object(app, 'run_subcommand') as helper: + app.run(['show', 'files', '--help']) + + helper.assert_called_once_with(['help', 'show', 'files']) + + +class TestCommandLookup(base.TestBase): + + def test_unknown_cmd(self): + app, command = make_app() + self.assertEqual(2, app.run(['hell'])) + + def test_unknown_cmd_debug(self): + app, command = make_app() + try: + self.assertEqual(2, app.run(['--debug', 'hell'])) + except ValueError as err: + self.assertIn("['hell']", str(err)) + + def test_list_matching_commands(self): + stdout = StringIO() + app = application.App('testing', '1', + test_utils.TestCommandManager( + test_utils.TEST_NAMESPACE), + stdout=stdout) + app.NAME = 'test' + try: + self.assertEqual(2, app.run(['t'])) + except SystemExit: + pass + output = stdout.getvalue() + self.assertIn("test: 't' is not a test command. See 'test --help'.", + output) + self.assertIn('Did you mean one of these?', output) + self.assertIn('three word command\n two words\n', output) + + def test_fuzzy_no_commands(self): + cmd_mgr = commandmanager.CommandManager('cliff.fuzzy') + app = application.App('test', '1.0', cmd_mgr) + cmd_mgr.commands = {} + matches = app.get_fuzzy_matches('foo') + self.assertEqual([], matches) + + def test_fuzzy_common_prefix(self): + # searched string is a prefix of all commands + cmd_mgr = commandmanager.CommandManager('cliff.fuzzy') + app = application.App('test', '1.0', cmd_mgr) + cmd_mgr.commands = {} + cmd_mgr.add_command('user list', test_utils.TestCommand) + cmd_mgr.add_command('user show', test_utils.TestCommand) + matches = app.get_fuzzy_matches('user') + self.assertEqual(['user list', 'user show'], matches) + + def test_fuzzy_same_distance(self): + # searched string has the same distance to all commands + cmd_mgr = commandmanager.CommandManager('cliff.fuzzy') + app = application.App('test', '1.0', cmd_mgr) + cmd_mgr.add_command('user', test_utils.TestCommand) + for cmd in cmd_mgr.commands.keys(): + self.assertEqual( + 8, + utils.damerau_levenshtein('node', cmd, utils.COST), ) + matches = app.get_fuzzy_matches('node') + self.assertEqual(['complete', 'help', 'user'], matches) - def build_option_parser(self, description, version): - argparse_kwargs = {'conflict_handler': 'resolve'} - parser = super(MyApp, self).build_option_parser( - description, - version, - argparse_kwargs=argparse_kwargs) - parser.add_argument( - '-h', '--help', - default=self, # tricky - help="Show help message and exit.", - ) - - MyApp() + def test_fuzzy_no_prefix(self): + # search by distance, no common prefix with any command + cmd_mgr = commandmanager.CommandManager('cliff.fuzzy') + app = application.App('test', '1.0', cmd_mgr) + cmd_mgr.add_command('user', test_utils.TestCommand) + matches = app.get_fuzzy_matches('uesr') + self.assertEqual(['user'], matches) -def test_option_parser_abbrev_issue(): - class MyCommand(c_cmd.Command): - def get_parser(self, prog_name): - parser = super(MyCommand, self).get_parser(prog_name) - parser.add_argument("--end") - return parser +class TestVerboseMode(base.TestBase): - def take_action(self, parsed_args): - assert(parsed_args.end == '123') - - class MyCommandManager(commandmanager.CommandManager): - def load_commands(self, namespace): - self.add_command("mycommand", MyCommand) - - class MyApp(application.App): - def __init__(self): - super(MyApp, self).__init__( - description='testing', - version='0.1', - command_manager=MyCommandManager(None), - ) - - def build_option_parser(self, description, version): - parser = super(MyApp, self).build_option_parser( - description, - version, - argparse_kwargs={'allow_abbrev': False}) - parser.add_argument('--endpoint') - return parser - - app = MyApp() - # NOTE(jd) --debug is necessary so assert in take_action() raises correctly - # here - app.run(['--debug', 'mycommand', '--end', '123']) + def test_verbose(self): + app, command = make_app() + app.clean_up = mock.MagicMock(name='clean_up') + app.run(['--verbose', 'mock']) + app.clean_up.assert_called_once_with(command.return_value, 0, None) + app.clean_up.reset_mock() + app.run(['--quiet', 'mock']) + app.clean_up.assert_called_once_with(command.return_value, 0, None) + self.assertRaises( + SystemExit, + app.run, + ['--verbose', '--quiet', 'mock'], + ) -def _test_help(deferred_help): - app, _ = make_app(deferred_help=deferred_help) - with mock.patch.object(app, 'initialize_app') as init: - with mock.patch('cliff.help.HelpAction.__call__', - side_effect=SystemExit(0)) as helper: +class TestIO(base.TestBase): + + def test_io_streams(self): + cmd_mgr = commandmanager.CommandManager('cliff.tests') + io = mock.Mock() + + if six.PY2: + stdin_save = sys.stdin + stdout_save = sys.stdout + stderr_save = sys.stderr + encoding = locale.getpreferredencoding() or 'utf-8' + + app = application.App('no io streams', 1, cmd_mgr) + self.assertIsInstance(app.stdin, codecs.StreamReader) + self.assertIsInstance(app.stdout, codecs.StreamWriter) + self.assertIsInstance(app.stderr, codecs.StreamWriter) + + app = application.App('with stdin io stream', 1, cmd_mgr, stdin=io) + self.assertIs(io, app.stdin) + self.assertIsInstance(app.stdout, codecs.StreamWriter) + self.assertIsInstance(app.stderr, codecs.StreamWriter) + + app = application.App('with stdout io stream', 1, cmd_mgr, + stdout=io) + self.assertIsInstance(app.stdin, codecs.StreamReader) + self.assertIs(io, app.stdout) + self.assertIsInstance(app.stderr, codecs.StreamWriter) + + app = application.App('with stderr io stream', 1, cmd_mgr, + stderr=io) + self.assertIsInstance(app.stdin, codecs.StreamReader) + self.assertIsInstance(app.stdout, codecs.StreamWriter) + self.assertIs(io, app.stderr) + try: - app.run(['--help']) - except SystemExit: - pass - else: - raise Exception('Exception was not thrown') - assert helper.called - assert init.called == deferred_help + sys.stdin = codecs.getreader(encoding)(sys.stdin) + app = application.App( + 'with wrapped sys.stdin io stream', 1, cmd_mgr) + self.assertIs(sys.stdin, app.stdin) + self.assertIsInstance(app.stdout, codecs.StreamWriter) + self.assertIsInstance(app.stderr, codecs.StreamWriter) + finally: + sys.stdin = stdin_save + try: + sys.stdout = codecs.getwriter(encoding)(sys.stdout) + app = application.App('with wrapped stdout io stream', 1, + cmd_mgr) + self.assertIsInstance(app.stdin, codecs.StreamReader) + self.assertIs(sys.stdout, app.stdout) + self.assertIsInstance(app.stderr, codecs.StreamWriter) + finally: + sys.stdout = stdout_save -def test_help(): - _test_help(False) + try: + sys.stderr = codecs.getwriter(encoding)(sys.stderr) + app = application.App('with wrapped stderr io stream', 1, + cmd_mgr) + self.assertIsInstance(app.stdin, codecs.StreamReader) + self.assertIsInstance(app.stdout, codecs.StreamWriter) + self.assertIs(sys.stderr, app.stderr) + finally: + sys.stderr = stderr_save + else: + app = application.App('no io streams', 1, cmd_mgr) + self.assertIs(sys.stdin, app.stdin) + self.assertIs(sys.stdout, app.stdout) + self.assertIs(sys.stderr, app.stderr) -def test_deferred_help(): - _test_help(True) + app = application.App('with stdin io stream', 1, cmd_mgr, stdin=io) + self.assertIs(io, app.stdin) + self.assertIs(sys.stdout, app.stdout) + self.assertIs(sys.stderr, app.stderr) + app = application.App('with stdout io stream', 1, cmd_mgr, + stdout=io) + self.assertIs(sys.stdin, app.stdin) + self.assertIs(io, app.stdout) + self.assertIs(sys.stderr, app.stderr) -def test_subcommand_help(): - app, _ = make_app(deferred_help=False) - - # Help is called immediately - with mock.patch('cliff.help.HelpAction.__call__') as helper: - app.run(['show', 'files', '--help']) - - assert helper.called - - -def test_subcommand_deferred_help(): - app, _ = make_app(deferred_help=True) - - # Show that provide_help_if_requested() did not show help and exit - with mock.patch.object(app, 'run_subcommand') as helper: - app.run(['show', 'files', '--help']) - - helper.assert_called_once_with(['help', 'show', 'files']) - - -def test_unknown_cmd(): - app, command = make_app() - assert app.run(['hell']) == 2 - - -def test_unknown_cmd_debug(): - app, command = make_app() - try: - app.run(['--debug', 'hell']) == 2 - except ValueError as err: - assert "['hell']" in ('%s' % err) - - -def test_list_matching_commands(): - stdout = StringIO() - app = application.App('testing', '1', - test_utils.TestCommandManager( - test_utils.TEST_NAMESPACE), - stdout=stdout) - app.NAME = 'test' - try: - assert app.run(['t']) == 2 - except SystemExit: - pass - output = stdout.getvalue() - assert "test: 't' is not a test command. See 'test --help'." in output - assert 'Did you mean one of these?' in output - assert 'three word command\n two words\n' in output - - -def test_fuzzy_no_commands(): - cmd_mgr = commandmanager.CommandManager('cliff.fuzzy') - app = application.App('test', '1.0', cmd_mgr) - cmd_mgr.commands = {} - matches = app.get_fuzzy_matches('foo') - assert matches == [] - - -def test_fuzzy_common_prefix(): - # searched string is a prefix of all commands - cmd_mgr = commandmanager.CommandManager('cliff.fuzzy') - app = application.App('test', '1.0', cmd_mgr) - cmd_mgr.commands = {} - cmd_mgr.add_command('user list', test_utils.TestCommand) - cmd_mgr.add_command('user show', test_utils.TestCommand) - matches = app.get_fuzzy_matches('user') - assert matches == ['user list', 'user show'] - - -def test_fuzzy_same_distance(): - # searched string has the same distance to all commands - cmd_mgr = commandmanager.CommandManager('cliff.fuzzy') - app = application.App('test', '1.0', cmd_mgr) - cmd_mgr.add_command('user', test_utils.TestCommand) - for cmd in cmd_mgr.commands.keys(): - assert utils.damerau_levenshtein('node', cmd, utils.COST) == 8 - matches = app.get_fuzzy_matches('node') - assert matches == ['complete', 'help', 'user'] - - -def test_fuzzy_no_prefix(): - # search by distance, no common prefix with any command - cmd_mgr = commandmanager.CommandManager('cliff.fuzzy') - app = application.App('test', '1.0', cmd_mgr) - cmd_mgr.add_command('user', test_utils.TestCommand) - matches = app.get_fuzzy_matches('uesr') - assert matches == ['user'] - - -def test_verbose(): - app, command = make_app() - app.clean_up = mock.MagicMock(name='clean_up') - app.run(['--verbose', 'mock']) - app.clean_up.assert_called_once_with(command.return_value, 0, None) - app.clean_up.reset_mock() - app.run(['--quiet', 'mock']) - app.clean_up.assert_called_once_with(command.return_value, 0, None) - try: - app.run(['--verbose', '--quiet', 'mock']) - except SystemExit: - pass - else: - raise Exception('Exception was not thrown') - - -def test_io_streams(): - cmd_mgr = commandmanager.CommandManager('cliff.tests') - io = mock.Mock() - - if six.PY2: - stdin_save = sys.stdin - stdout_save = sys.stdout - stderr_save = sys.stderr - encoding = locale.getpreferredencoding() or 'utf-8' - - app = application.App('no io streams', 1, cmd_mgr) - assert isinstance(app.stdin, codecs.StreamReader) - assert isinstance(app.stdout, codecs.StreamWriter) - assert isinstance(app.stderr, codecs.StreamWriter) - - app = application.App('with stdin io stream', 1, cmd_mgr, stdin=io) - assert app.stdin is io - assert isinstance(app.stdout, codecs.StreamWriter) - assert isinstance(app.stderr, codecs.StreamWriter) - - app = application.App('with stdout io stream', 1, cmd_mgr, stdout=io) - assert isinstance(app.stdin, codecs.StreamReader) - assert app.stdout is io - assert isinstance(app.stderr, codecs.StreamWriter) - - app = application.App('with stderr io stream', 1, cmd_mgr, stderr=io) - assert isinstance(app.stdin, codecs.StreamReader) - assert isinstance(app.stdout, codecs.StreamWriter) - assert app.stderr is io - - try: - sys.stdin = codecs.getreader(encoding)(sys.stdin) - app = application.App( - 'with wrapped sys.stdin io stream', 1, cmd_mgr) - assert app.stdin is sys.stdin - assert isinstance(app.stdout, codecs.StreamWriter) - assert isinstance(app.stderr, codecs.StreamWriter) - finally: - sys.stdin = stdin_save - - try: - sys.stdout = codecs.getwriter(encoding)(sys.stdout) - app = application.App('with wrapped stdout io stream', 1, cmd_mgr) - assert isinstance(app.stdin, codecs.StreamReader) - assert app.stdout is sys.stdout - assert isinstance(app.stderr, codecs.StreamWriter) - finally: - sys.stdout = stdout_save - - try: - sys.stderr = codecs.getwriter(encoding)(sys.stderr) - app = application.App('with wrapped stderr io stream', 1, cmd_mgr) - assert isinstance(app.stdin, codecs.StreamReader) - assert isinstance(app.stdout, codecs.StreamWriter) - assert app.stderr is sys.stderr - finally: - sys.stderr = stderr_save - - else: - app = application.App('no io streams', 1, cmd_mgr) - assert app.stdin is sys.stdin - assert app.stdout is sys.stdout - assert app.stderr is sys.stderr - - app = application.App('with stdin io stream', 1, cmd_mgr, stdin=io) - assert app.stdin is io - assert app.stdout is sys.stdout - assert app.stderr is sys.stderr - - app = application.App('with stdout io stream', 1, cmd_mgr, stdout=io) - assert app.stdin is sys.stdin - assert app.stdout is io - assert app.stderr is sys.stderr - - app = application.App('with stderr io stream', 1, cmd_mgr, stderr=io) - assert app.stdin is sys.stdin - assert app.stdout is sys.stdout - assert app.stderr is io + app = application.App('with stderr io stream', 1, cmd_mgr, + stderr=io) + self.assertIs(sys.stdin, app.stdin) + self.assertIs(sys.stdout, app.stdout) + self.assertIs(io, app.stderr) diff --git a/cliff/tests/test_columns.py b/cliff/tests/test_columns.py index d7bb2b0..fef1128 100644 --- a/cliff/tests/test_columns.py +++ b/cliff/tests/test_columns.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +import unittest + from cliff import columns @@ -19,12 +21,15 @@ class FauxColumn(columns.FormattableColumn): return u'I made this string myself: {}'.format(self._value) -def test_faux_column_machine(): - c = FauxColumn(['list', 'of', 'values']) - assert c.machine_readable() == ['list', 'of', 'values'] +class TestColumns(unittest.TestCase): + def test_faux_column_machine(self): + c = FauxColumn(['list', 'of', 'values']) + self.assertEqual(['list', 'of', 'values'], c.machine_readable()) -def test_faux_column_human(): - c = FauxColumn(['list', 'of', 'values']) - assert c.human_readable() == \ - u"I made this string myself: ['list', 'of', 'values']" + def test_faux_column_human(self): + c = FauxColumn(['list', 'of', 'values']) + self.assertEqual( + u"I made this string myself: ['list', 'of', 'values']", + c.human_readable(), + ) diff --git a/cliff/tests/test_command.py b/cliff/tests/test_command.py index 55bb61c..4bc03aa 100644 --- a/cliff/tests/test_command.py +++ b/cliff/tests/test_command.py @@ -11,6 +11,7 @@ # under the License. from cliff import command +from cliff.tests import base class TestCommand(command.Command): @@ -51,47 +52,44 @@ class TestCommandNoDocstring(command.Command): return 42 -def test_get_description_docstring(): - cmd = TestCommand(None, None) - desc = cmd.get_description() - assert desc == "Description of command.\n " +class TestDescription(base.TestBase): + + def test_get_description_docstring(self): + cmd = TestCommand(None, None) + desc = cmd.get_description() + assert desc == "Description of command.\n " + + def test_get_description_attribute(self): + cmd = TestCommand(None, None) + # Artificially inject a value for _description to verify that it + # overrides the docstring. + cmd._description = 'this is not the default' + desc = cmd.get_description() + assert desc == 'this is not the default' + + def test_get_description_default(self): + cmd = TestCommandNoDocstring(None, None) + desc = cmd.get_description() + assert desc == '' -def test_get_description_attribute(): - cmd = TestCommand(None, None) - # Artificially inject a value for _description to verify that it - # overrides the docstring. - cmd._description = 'this is not the default' - desc = cmd.get_description() - assert desc == 'this is not the default' +class TestBasicValues(base.TestBase): + + def test_get_parser(self): + cmd = TestCommand(None, None) + parser = cmd.get_parser('NAME') + assert parser.prog == 'NAME' + + def test_get_name(self): + cmd = TestCommand(None, None, cmd_name='object action') + assert cmd.cmd_name == 'object action' + + def test_run_return(self): + cmd = TestCommand(None, None, cmd_name='object action') + assert cmd.run(None) == 42 -def test_get_description_default(): - cmd = TestCommandNoDocstring(None, None) - desc = cmd.get_description() - assert desc == '' - - -def test_get_parser(): - cmd = TestCommand(None, None) - parser = cmd.get_parser('NAME') - assert parser.prog == 'NAME' - - -def test_get_name(): - cmd = TestCommand(None, None, cmd_name='object action') - assert cmd.cmd_name == 'object action' - - -def test_run_return(): - cmd = TestCommand(None, None, cmd_name='object action') - assert cmd.run(None) == 42 - - -def test_smart_help_formatter(): - cmd = TestCommand(None, None) - parser = cmd.get_parser('NAME') - expected_help_message = """ +expected_help_message = """ long_help_argument Create a NIC on the server. Specify option multiple times to create multiple NICs. Either net-id or port-id must be provided, but not @@ -108,4 +106,11 @@ def test_smart_help_formatter(): regular_help_argument The quick brown fox jumps over the lazy dog. """ - assert expected_help_message in parser.format_help() + + +class TestHelp(base.TestBase): + + def test_smart_help_formatter(self): + cmd = TestCommand(None, None) + parser = cmd.get_parser('NAME') + self.assertIn(expected_help_message, parser.format_help()) diff --git a/cliff/tests/test_commandmanager.py b/cliff/tests/test_commandmanager.py index 7f5ce0c..e4ba225 100644 --- a/cliff/tests/test_commandmanager.py +++ b/cliff/tests/test_commandmanager.py @@ -11,122 +11,142 @@ # under the License. import mock +import testscenarios from cliff import commandmanager +from cliff.tests import base from cliff.tests import utils -def test_lookup_and_find(): - def check(mgr, argv): - cmd, name, remaining = mgr.find_command(argv) - assert cmd - assert name == ' '.join(argv) - assert not remaining - mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) - for expected in [['one'], - ['two', 'words'], - ['three', 'word', 'command'], - ]: - yield check, mgr, expected - return +load_tests = testscenarios.load_tests_apply_scenarios -def test_lookup_with_remainder(): - def check(mgr, argv): - cmd, name, remaining = mgr.find_command(argv) - assert cmd - assert remaining == ['--opt'] - mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) - for expected in [['one', '--opt'], - ['two', 'words', '--opt'], - ['three', 'word', 'command', '--opt'], - ]: - yield check, mgr, expected - return +class TestLookupAndFind(base.TestBase): + + scenarios = [ + ('one-word', {'argv': ['one']}), + ('two-words', {'argv': ['two', 'words']}), + ('three-words', {'argv': ['three', 'word', 'command']}), + ] + + def test(self): + mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) + cmd, name, remaining = mgr.find_command(self.argv) + self.assertTrue(cmd) + self.assertEqual(' '.join(self.argv), name) + self.assertFalse(remaining) -def test_find_invalid_command(): - mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) +class TestLookupWithRemainder(base.TestBase): - def check_one(argv): + scenarios = [ + ('one', {'argv': ['one', '--opt']}), + ('two', {'argv': ['two', 'words', '--opt']}), + ('three', {'argv': ['three', 'word', 'command', '--opt']}), + ] + + def test(self): + mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) + cmd, name, remaining = mgr.find_command(self.argv) + self.assertTrue(cmd) + self.assertEqual(['--opt'], remaining) + + +class TestFindInvalidCommand(base.TestBase): + + scenarios = [ + ('no-such-command', {'argv': ['a', '-b']}), + ('no-command-given', {'argv': ['-b']}), + ] + + def test(self): + mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) try: - mgr.find_command(argv) + mgr.find_command(self.argv) except ValueError as err: # make sure err include 'a' when ['a', '-b'] - assert argv[0] in ('%s' % err) - assert '-b' in ('%s' % err) + self.assertIn(self.argv[0], str(err)) + self.assertIn('-b', str(err)) else: - assert False, 'expected a failure' - for argv in [['a', '-b'], - ['-b'], - ]: - yield check_one, argv + self.fail('expected a failure') -def test_find_unknown_command(): - mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) - try: - mgr.find_command(['a', 'b']) - except ValueError as err: - assert "['a', 'b']" in ('%s' % err) - else: - assert False, 'expected a failure' +class TestFindUnknownCommand(base.TestBase): + + def test(self): + mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) + try: + mgr.find_command(['a', 'b']) + except ValueError as err: + self.assertIn("['a', 'b']", str(err)) + else: + self.fail('expected a failure') -def test_add_command(): - mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) - mock_cmd = mock.Mock() - mgr.add_command('mock', mock_cmd) - found_cmd, name, args = mgr.find_command(['mock']) - assert found_cmd is mock_cmd +class TestDynamicCommands(base.TestBase): + + def test_add(self): + mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) + mock_cmd = mock.Mock() + mgr.add_command('mock', mock_cmd) + found_cmd, name, args = mgr.find_command(['mock']) + self.assertIs(mock_cmd, found_cmd) + + def test_intersected_commands(self): + def foo(arg): + pass + + def foo_bar(): + pass + + mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) + mgr.add_command('foo', foo) + mgr.add_command('foo bar', foo_bar) + + self.assertIs(foo_bar, mgr.find_command(['foo', 'bar'])[0]) + self.assertIs( + foo, + mgr.find_command(['foo', 'arg0'])[0], + ) -def test_intersected_commands(): - def foo(arg): - pass +class TestLoad(base.TestBase): - def foo_bar(): - pass + def test_load_commands(self): + testcmd = mock.Mock(name='testcmd') + testcmd.name.replace.return_value = 'test' + mock_pkg_resources = mock.Mock(return_value=[testcmd]) + with mock.patch('pkg_resources.iter_entry_points', + mock_pkg_resources) as iter_entry_points: + mgr = commandmanager.CommandManager('test') + iter_entry_points.assert_called_once_with('test') + names = [n for n, v in mgr] + self.assertEqual(['test'], names) - mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) - mgr.add_command('foo', foo) - mgr.add_command('foo bar', foo_bar) + def test_load_commands_keep_underscores(self): + testcmd = mock.Mock() + testcmd.name = 'test_cmd' + mock_pkg_resources = mock.Mock(return_value=[testcmd]) + with mock.patch('pkg_resources.iter_entry_points', + mock_pkg_resources) as iter_entry_points: + mgr = commandmanager.CommandManager( + 'test', + convert_underscores=False, + ) + iter_entry_points.assert_called_once_with('test') + names = [n for n, v in mgr] + self.assertEqual(['test_cmd'], names) - assert mgr.find_command(['foo', 'bar'])[0] is foo_bar - assert mgr.find_command(['foo', 'arg0'])[0] is foo - - -def test_load_commands(): - testcmd = mock.Mock(name='testcmd') - testcmd.name.replace.return_value = 'test' - mock_pkg_resources = mock.Mock(return_value=[testcmd]) - with mock.patch('pkg_resources.iter_entry_points', - mock_pkg_resources) as iter_entry_points: - mgr = commandmanager.CommandManager('test') - iter_entry_points.assert_called_once_with('test') - names = [n for n, v in mgr] - assert names == ['test'] - - -def test_load_commands_keep_underscores(): - testcmd = mock.Mock() - testcmd.name = 'test_cmd' - mock_pkg_resources = mock.Mock(return_value=[testcmd]) - with mock.patch('pkg_resources.iter_entry_points', - mock_pkg_resources) as iter_entry_points: - mgr = commandmanager.CommandManager('test', convert_underscores=False) - iter_entry_points.assert_called_once_with('test') - names = [n for n, v in mgr] - assert names == ['test_cmd'] - - -def test_load_commands_replace_underscores(): - testcmd = mock.Mock() - testcmd.name = 'test_cmd' - mock_pkg_resources = mock.Mock(return_value=[testcmd]) - with mock.patch('pkg_resources.iter_entry_points', - mock_pkg_resources) as iter_entry_points: - mgr = commandmanager.CommandManager('test', convert_underscores=True) - iter_entry_points.assert_called_once_with('test') - names = [n for n, v in mgr] - assert names == ['test cmd'] + def test_load_commands_replace_underscores(self): + testcmd = mock.Mock() + testcmd.name = 'test_cmd' + mock_pkg_resources = mock.Mock(return_value=[testcmd]) + with mock.patch('pkg_resources.iter_entry_points', + mock_pkg_resources) as iter_entry_points: + mgr = commandmanager.CommandManager( + 'test', + convert_underscores=True, + ) + iter_entry_points.assert_called_once_with('test') + names = [n for n, v in mgr] + self.assertEqual(['test cmd'], names) diff --git a/cliff/tests/test_complete.py b/cliff/tests/test_complete.py index b1523ec..a4f8dc4 100644 --- a/cliff/tests/test_complete.py +++ b/cliff/tests/test_complete.py @@ -18,50 +18,52 @@ import mock from cliff import app as application from cliff import commandmanager from cliff import complete +from cliff.tests import base -def test_complete_dictionary(): - sot = complete.CompleteDictionary() - sot.add_command("image delete".split(), - [mock.Mock(option_strings=["1"])]) - sot.add_command("image list".split(), - [mock.Mock(option_strings=["2"])]) - sot.add_command("image create".split(), - [mock.Mock(option_strings=["3"])]) - sot.add_command("volume type create".split(), - [mock.Mock(option_strings=["4"])]) - sot.add_command("volume type delete".split(), - [mock.Mock(option_strings=["5"])]) - assert "image volume" == sot.get_commands() - result = sot.get_data() - assert "image" == result[0][0] - assert "create delete list" == result[0][1] - assert "image_create" == result[1][0] - assert "3" == result[1][1] - assert "image_delete" == result[2][0] - assert "1" == result[2][1] - assert "image_list" == result[3][0] - assert "2" == result[3][1] +class TestCompletion(base.TestBase): + def test_dictionary(self): + sot = complete.CompleteDictionary() + sot.add_command("image delete".split(), + [mock.Mock(option_strings=["1"])]) + sot.add_command("image list".split(), + [mock.Mock(option_strings=["2"])]) + sot.add_command("image create".split(), + [mock.Mock(option_strings=["3"])]) + sot.add_command("volume type create".split(), + [mock.Mock(option_strings=["4"])]) + sot.add_command("volume type delete".split(), + [mock.Mock(option_strings=["5"])]) + self.assertEqual("image volume", sot.get_commands()) + result = sot.get_data() + self.assertEqual("image", result[0][0]) + self.assertEqual("create delete list", result[0][1]) + self.assertEqual("image_create", result[1][0]) + self.assertEqual("3", result[1][1]) + self.assertEqual("image_delete", result[2][0]) + self.assertEqual("1", result[2][1]) + self.assertEqual("image_list", result[3][0]) + self.assertEqual("2", result[3][1]) -def test_complete_dictionary_subcmd(): - sot = complete.CompleteDictionary() - sot.add_command("image delete".split(), - [mock.Mock(option_strings=["1"])]) - sot.add_command("image list".split(), - [mock.Mock(option_strings=["2"])]) - sot.add_command("image list better".split(), - [mock.Mock(option_strings=["3"])]) - assert "image" == sot.get_commands() - result = sot.get_data() - assert "image" == result[0][0] - assert "delete list list_better" == result[0][1] - assert "image_delete" == result[1][0] - assert "1" == result[1][1] - assert "image_list" == result[2][0] - assert "2 better" == result[2][1] - assert "image_list_better" == result[3][0] - assert "3" == result[3][1] + def test_complete_dictionary_subcmd(self): + sot = complete.CompleteDictionary() + sot.add_command("image delete".split(), + [mock.Mock(option_strings=["1"])]) + sot.add_command("image list".split(), + [mock.Mock(option_strings=["2"])]) + sot.add_command("image list better".split(), + [mock.Mock(option_strings=["3"])]) + self.assertEqual("image", sot.get_commands()) + result = sot.get_data() + self.assertEqual("image", result[0][0]) + self.assertEqual("delete list list_better", result[0][1]) + self.assertEqual("image_delete", result[1][0]) + self.assertEqual("1", result[1][1]) + self.assertEqual("image_list", result[2][0]) + self.assertEqual("2 better", result[2][1]) + self.assertEqual("image_list_better", result[3][0]) + self.assertEqual("3", result[3][1]) class FakeStdout: @@ -78,96 +80,92 @@ class FakeStdout: return result -def given_cmdo_data(): - cmdo = "image server" - data = [("image", "create"), - ("image_create", "--eolus"), - ("server", "meta ssh"), - ("server_meta_delete", "--wilson"), - ("server_ssh", "--sunlight")] - return cmdo, data +class TestCompletionAlternatives(base.TestBase): + + def given_cmdo_data(self): + cmdo = "image server" + data = [("image", "create"), + ("image_create", "--eolus"), + ("server", "meta ssh"), + ("server_meta_delete", "--wilson"), + ("server_ssh", "--sunlight")] + return cmdo, data + + def then_data(self, content): + self.assertIn(" cmds='image server'\n", content) + self.assertIn(" cmds_image='create'\n", content) + self.assertIn(" cmds_image_create='--eolus'\n", content) + self.assertIn(" cmds_server='meta ssh'\n", content) + self.assertIn(" cmds_server_meta_delete='--wilson'\n", content) + self.assertIn(" cmds_server_ssh='--sunlight'\n", content) + + def test_complete_no_code(self): + output = FakeStdout() + sot = complete.CompleteNoCode("doesNotMatter", output) + sot.write(*self.given_cmdo_data()) + self.then_data(output.content) + + def test_complete_bash(self): + output = FakeStdout() + sot = complete.CompleteBash("openstack", output) + sot.write(*self.given_cmdo_data()) + self.then_data(output.content) + self.assertIn("_openstack()\n", output.content[0]) + self.assertIn("complete -F _openstack openstack\n", output.content[-1]) + + def test_complete_command_parser(self): + sot = complete.CompleteCommand(mock.Mock(), mock.Mock()) + parser = sot.get_parser('nothing') + self.assertEqual("nothing", parser.prog) + self.assertEqual("print bash completion command\n ", + parser.description) -def then_data(content): - assert " cmds='image server'\n" in content - assert " cmds_image='create'\n" in content - assert " cmds_image_create='--eolus'\n" in content - assert " cmds_server='meta ssh'\n" in content - assert " cmds_server_meta_delete='--wilson'\n" in content - assert " cmds_server_ssh='--sunlight'\n" in content +class TestCompletionAction(base.TestBase): + def given_complete_command(self): + cmd_mgr = commandmanager.CommandManager('cliff.tests') + app = application.App('testing', '1', cmd_mgr, stdout=FakeStdout()) + sot = complete.CompleteCommand(app, mock.Mock()) + cmd_mgr.add_command('complete', complete.CompleteCommand) + return sot, app, cmd_mgr -def test_complete_no_code(): - output = FakeStdout() - sot = complete.CompleteNoCode("doesNotMatter", output) - sot.write(*given_cmdo_data()) - then_data(output.content) + def then_actions_equal(self, actions): + optstr = ' '.join(opt for action in actions + for opt in action.option_strings) + self.assertEqual('-h --help --name --shell', optstr) + def test_complete_command_get_actions(self): + sot, app, cmd_mgr = self.given_complete_command() + app.interactive_mode = False + actions = sot.get_actions(["complete"]) + self.then_actions_equal(actions) -def test_complete_bash(): - output = FakeStdout() - sot = complete.CompleteBash("openstack", output) - sot.write(*given_cmdo_data()) - then_data(output.content) - assert "_openstack()\n" in output.content[0] - assert "complete -F _openstack openstack\n" in output.content[-1] + def test_complete_command_get_actions_interactive(self): + sot, app, cmd_mgr = self.given_complete_command() + app.interactive_mode = True + actions = sot.get_actions(["complete"]) + self.then_actions_equal(actions) + def test_complete_command_take_action(self): + sot, app, cmd_mgr = self.given_complete_command() + parsed_args = mock.Mock() + parsed_args.name = "test_take" + parsed_args.shell = "bash" + content = app.stdout.content + self.assertEqual(0, sot.take_action(parsed_args)) + self.assertIn("_test_take()\n", content[0]) + self.assertIn("complete -F _test_take test_take\n", content[-1]) + self.assertIn(" cmds='complete help'\n", content) + self.assertIn(" cmds_complete='-h --help --name --shell'\n", content) + self.assertIn(" cmds_help='-h --help'\n", content) -def test_complete_command_parser(): - sot = complete.CompleteCommand(mock.Mock(), mock.Mock()) - parser = sot.get_parser('nothing') - assert "nothing" == parser.prog - assert "print bash completion command\n " == parser.description - - -def given_complete_command(): - cmd_mgr = commandmanager.CommandManager('cliff.tests') - app = application.App('testing', '1', cmd_mgr, stdout=FakeStdout()) - sot = complete.CompleteCommand(app, mock.Mock()) - cmd_mgr.add_command('complete', complete.CompleteCommand) - return sot, app, cmd_mgr - - -def then_actions_equal(actions): - optstr = ' '.join(opt for action in actions - for opt in action.option_strings) - assert '-h --help --name --shell' == optstr - - -def test_complete_command_get_actions(): - sot, app, cmd_mgr = given_complete_command() - app.interactive_mode = False - actions = sot.get_actions(["complete"]) - then_actions_equal(actions) - - -def test_complete_command_get_actions_interactive(): - sot, app, cmd_mgr = given_complete_command() - app.interactive_mode = True - actions = sot.get_actions(["complete"]) - then_actions_equal(actions) - - -def test_complete_command_take_action(): - sot, app, cmd_mgr = given_complete_command() - parsed_args = mock.Mock() - parsed_args.name = "test_take" - parsed_args.shell = "bash" - content = app.stdout.content - assert 0 == sot.take_action(parsed_args) - assert "_test_take()\n" in content[0] - assert "complete -F _test_take test_take\n" in content[-1] - assert " cmds='complete help'\n" in content - assert " cmds_complete='-h --help --name --shell'\n" in content - assert " cmds_help='-h --help'\n" in content - - -def test_complete_command_remove_dashes(): - sot, app, cmd_mgr = given_complete_command() - parsed_args = mock.Mock() - parsed_args.name = "test-take" - parsed_args.shell = "bash" - content = app.stdout.content - assert 0 == sot.take_action(parsed_args) - assert "_test_take()\n" in content[0] - assert "complete -F _test_take test-take\n" in content[-1] + def test_complete_command_remove_dashes(self): + sot, app, cmd_mgr = self.given_complete_command() + parsed_args = mock.Mock() + parsed_args.name = "test-take" + parsed_args.shell = "bash" + content = app.stdout.content + self.assertEqual(0, sot.take_action(parsed_args)) + self.assertIn("_test_take()\n", content[0]) + self.assertIn("complete -F _test_take test-take\n", content[-1]) diff --git a/cliff/tests/test_formatters_csv.py b/cliff/tests/test_formatters_csv.py index 27c6f84..001f169 100644 --- a/cliff/tests/test_formatters_csv.py +++ b/cliff/tests/test_formatters_csv.py @@ -13,73 +13,74 @@ # License for the specific language governing permissions and limitations # under the License. -import mock import argparse +import unittest + +import mock import six from cliff.formatters import commaseparated from cliff.tests import test_columns -def test_commaseparated_list_formatter(): - sf = commaseparated.CSVLister() - c = ('a', 'b', 'c') - d1 = ('A', 'B', 'C') - d2 = ('D', 'E', 'F') - data = [d1, d2] - expected = 'a,b,c\nA,B,C\nD,E,F\n' - output = six.StringIO() - parsed_args = mock.Mock() - parsed_args.quote_mode = 'none' - sf.emit_list(c, data, output, parsed_args) - actual = output.getvalue() - assert expected == actual +class TestCSVFormatter(unittest.TestCase): + def test_commaseparated_list_formatter(self): + sf = commaseparated.CSVLister() + c = ('a', 'b', 'c') + d1 = ('A', 'B', 'C') + d2 = ('D', 'E', 'F') + data = [d1, d2] + expected = 'a,b,c\nA,B,C\nD,E,F\n' + output = six.StringIO() + parsed_args = mock.Mock() + parsed_args.quote_mode = 'none' + sf.emit_list(c, data, output, parsed_args) + actual = output.getvalue() + self.assertEqual(expected, actual) -def test_commaseparated_list_formatter_quoted(): - sf = commaseparated.CSVLister() - c = ('a', 'b', 'c') - d1 = ('A', 'B', 'C') - d2 = ('D', 'E', 'F') - data = [d1, d2] - expected = '"a","b","c"\n"A","B","C"\n"D","E","F"\n' - output = six.StringIO() - # Parse arguments as if passed on the command-line - parser = argparse.ArgumentParser(description='Testing...') - sf.add_argument_group(parser) - parsed_args = parser.parse_args(['--quote', 'all']) - sf.emit_list(c, data, output, parsed_args) - actual = output.getvalue() - assert expected == actual + def test_commaseparated_list_formatter_quoted(self): + sf = commaseparated.CSVLister() + c = ('a', 'b', 'c') + d1 = ('A', 'B', 'C') + d2 = ('D', 'E', 'F') + data = [d1, d2] + expected = '"a","b","c"\n"A","B","C"\n"D","E","F"\n' + output = six.StringIO() + # Parse arguments as if passed on the command-line + parser = argparse.ArgumentParser(description='Testing...') + sf.add_argument_group(parser) + parsed_args = parser.parse_args(['--quote', 'all']) + sf.emit_list(c, data, output, parsed_args) + actual = output.getvalue() + self.assertEqual(expected, actual) + def test_commaseparated_list_formatter_formattable_column(self): + sf = commaseparated.CSVLister() + c = ('a', 'b', 'c') + d1 = ('A', 'B', test_columns.FauxColumn(['the', 'value'])) + data = [d1] + expected = 'a,b,c\nA,B,[\'the\'\\, \'value\']\n' + output = six.StringIO() + parsed_args = mock.Mock() + parsed_args.quote_mode = 'none' + sf.emit_list(c, data, output, parsed_args) + actual = output.getvalue() + self.assertEqual(expected, actual) -def test_commaseparated_list_formatter_formattable_column(): - sf = commaseparated.CSVLister() - c = ('a', 'b', 'c') - d1 = ('A', 'B', test_columns.FauxColumn(['the', 'value'])) - data = [d1] - expected = 'a,b,c\nA,B,[\'the\'\\, \'value\']\n' - output = six.StringIO() - parsed_args = mock.Mock() - parsed_args.quote_mode = 'none' - sf.emit_list(c, data, output, parsed_args) - actual = output.getvalue() - assert expected == actual - - -def test_commaseparated_list_formatter_unicode(): - sf = commaseparated.CSVLister() - c = (u'a', u'b', u'c') - d1 = (u'A', u'B', u'C') - happy = u'高兴' - d2 = (u'D', u'E', happy) - data = [d1, d2] - expected = u'a,b,c\nA,B,C\nD,E,%s\n' % happy - output = six.StringIO() - parsed_args = mock.Mock() - parsed_args.quote_mode = 'none' - sf.emit_list(c, data, output, parsed_args) - actual = output.getvalue() - if six.PY2: - actual = actual.decode('utf-8') - assert expected == actual + def test_commaseparated_list_formatter_unicode(self): + sf = commaseparated.CSVLister() + c = (u'a', u'b', u'c') + d1 = (u'A', u'B', u'C') + happy = u'高兴' + d2 = (u'D', u'E', happy) + data = [d1, d2] + expected = u'a,b,c\nA,B,C\nD,E,%s\n' % happy + output = six.StringIO() + parsed_args = mock.Mock() + parsed_args.quote_mode = 'none' + sf.emit_list(c, data, output, parsed_args) + actual = output.getvalue() + if six.PY2: + actual = actual.decode('utf-8') + self.assertEqual(expected, actual) diff --git a/cliff/tests/test_formatters_json.py b/cliff/tests/test_formatters_json.py index eb7397d..2f3a6e9 100644 --- a/cliff/tests/test_formatters_json.py +++ b/cliff/tests/test_formatters_json.py @@ -13,117 +13,117 @@ # under the License. import json -import six from cliff.formatters import json_format +from cliff.tests import base from cliff.tests import test_columns import mock +import six -def test_json_format_one(): - sf = json_format.JSONFormatter() - c = ('a', 'b', 'c', 'd') - d = ('A', 'B', 'C', '"escape me"') - expected = { - 'a': 'A', - 'b': 'B', - 'c': 'C', - 'd': '"escape me"' - } - args = mock.Mock() - sf.add_argument_group(args) +class TestJSONFormatter(base.TestBase): - args.noindent = True - output = six.StringIO() - sf.emit_one(c, d, output, args) - value = output.getvalue() - print(len(value.splitlines())) - assert 1 == len(value.splitlines()) - actual = json.loads(value) - assert expected == actual + def test_one(self): + sf = json_format.JSONFormatter() + c = ('a', 'b', 'c', 'd') + d = ('A', 'B', 'C', '"escape me"') + expected = { + 'a': 'A', + 'b': 'B', + 'c': 'C', + 'd': '"escape me"' + } + args = mock.Mock() + sf.add_argument_group(args) - args.noindent = False - output = six.StringIO() - sf.emit_one(c, d, output, args) - value = output.getvalue() - assert 6 == len(value.splitlines()) - actual = json.loads(value) - assert expected == actual + args.noindent = True + output = six.StringIO() + sf.emit_one(c, d, output, args) + value = output.getvalue() + print(len(value.splitlines())) + self.assertEqual(1, len(value.splitlines())) + actual = json.loads(value) + self.assertEqual(expected, actual) + args.noindent = False + output = six.StringIO() + sf.emit_one(c, d, output, args) + value = output.getvalue() + self.assertEqual(6, len(value.splitlines())) + actual = json.loads(value) + self.assertEqual(expected, actual) -def test_json_format_formattablecolumn_one(): - sf = json_format.JSONFormatter() - c = ('a', 'b', 'c', 'd') - d = ('A', 'B', 'C', test_columns.FauxColumn(['the', 'value'])) - expected = { - 'a': 'A', - 'b': 'B', - 'c': 'C', - 'd': ['the', 'value'], - } - args = mock.Mock() - sf.add_argument_group(args) + def test_formattablecolumn_one(self): + sf = json_format.JSONFormatter() + c = ('a', 'b', 'c', 'd') + d = ('A', 'B', 'C', test_columns.FauxColumn(['the', 'value'])) + expected = { + 'a': 'A', + 'b': 'B', + 'c': 'C', + 'd': ['the', 'value'], + } + args = mock.Mock() + sf.add_argument_group(args) - args.noindent = True - output = six.StringIO() - sf.emit_one(c, d, output, args) - value = output.getvalue() - print(len(value.splitlines())) - assert 1 == len(value.splitlines()) - actual = json.loads(value) - assert expected == actual + args.noindent = True + output = six.StringIO() + sf.emit_one(c, d, output, args) + value = output.getvalue() + print(len(value.splitlines())) + self.assertEqual(1, len(value.splitlines())) + actual = json.loads(value) + self.assertEqual(expected, actual) + def test_list(self): + sf = json_format.JSONFormatter() + c = ('a', 'b', 'c') + d = ( + ('A1', 'B1', 'C1'), + ('A2', 'B2', 'C2'), + ('A3', 'B3', 'C3') + ) + expected = [ + {'a': 'A1', 'b': 'B1', 'c': 'C1'}, + {'a': 'A2', 'b': 'B2', 'c': 'C2'}, + {'a': 'A3', 'b': 'B3', 'c': 'C3'} + ] + args = mock.Mock() + sf.add_argument_group(args) -def test_json_format_list(): - sf = json_format.JSONFormatter() - c = ('a', 'b', 'c') - d = ( - ('A1', 'B1', 'C1'), - ('A2', 'B2', 'C2'), - ('A3', 'B3', 'C3') - ) - expected = [ - {'a': 'A1', 'b': 'B1', 'c': 'C1'}, - {'a': 'A2', 'b': 'B2', 'c': 'C2'}, - {'a': 'A3', 'b': 'B3', 'c': 'C3'} - ] - args = mock.Mock() - sf.add_argument_group(args) + args.noindent = True + output = six.StringIO() + sf.emit_list(c, d, output, args) + value = output.getvalue() + self.assertEqual(1, len(value.splitlines())) + actual = json.loads(value) + self.assertEqual(expected, actual) - args.noindent = True - output = six.StringIO() - sf.emit_list(c, d, output, args) - value = output.getvalue() - assert 1 == len(value.splitlines()) - actual = json.loads(value) - assert expected == actual + args.noindent = False + output = six.StringIO() + sf.emit_list(c, d, output, args) + value = output.getvalue() + self.assertEqual(17, len(value.splitlines())) + actual = json.loads(value) + self.assertEqual(expected, actual) - args.noindent = False - output = six.StringIO() - sf.emit_list(c, d, output, args) - value = output.getvalue() - assert 17 == len(value.splitlines()) - actual = json.loads(value) - assert expected == actual + def test_formattablecolumn_list(self): + sf = json_format.JSONFormatter() + c = ('a', 'b', 'c') + d = ( + ('A1', 'B1', test_columns.FauxColumn(['the', 'value'])), + ) + expected = [ + {'a': 'A1', 'b': 'B1', 'c': ['the', 'value']}, + ] + args = mock.Mock() + sf.add_argument_group(args) - -def test_json_format_formattablecolumn_list(): - sf = json_format.JSONFormatter() - c = ('a', 'b', 'c') - d = ( - ('A1', 'B1', test_columns.FauxColumn(['the', 'value'])), - ) - expected = [ - {'a': 'A1', 'b': 'B1', 'c': ['the', 'value']}, - ] - args = mock.Mock() - sf.add_argument_group(args) - - args.noindent = True - output = six.StringIO() - sf.emit_list(c, d, output, args) - value = output.getvalue() - assert 1 == len(value.splitlines()) - actual = json.loads(value) - assert expected == actual + args.noindent = True + output = six.StringIO() + sf.emit_list(c, d, output, args) + value = output.getvalue() + self.assertEqual(1, len(value.splitlines())) + actual = json.loads(value) + self.assertEqual(expected, actual) diff --git a/cliff/tests/test_formatters_shell.py b/cliff/tests/test_formatters_shell.py index 7689f73..a6929a3 100644 --- a/cliff/tests/test_formatters_shell.py +++ b/cliff/tests/test_formatters_shell.py @@ -16,81 +16,81 @@ import argparse import six from cliff.formatters import shell +from cliff.tests import base from cliff.tests import test_columns import mock -def test_shell_formatter(): - sf = shell.ShellFormatter() - c = ('a', 'b', 'c', 'd') - d = ('A', 'B', 'C', '"escape me"') - expected = 'a="A"\nb="B"\nd="\\"escape me\\""\n' - output = six.StringIO() - args = mock.Mock() - args.variables = ['a', 'b', 'd'] - args.prefix = '' - sf.emit_one(c, d, output, args) - actual = output.getvalue() - assert expected == actual +class TestShellFormatter(base.TestBase): + def test(self): + sf = shell.ShellFormatter() + c = ('a', 'b', 'c', 'd') + d = ('A', 'B', 'C', '"escape me"') + expected = 'a="A"\nb="B"\nd="\\"escape me\\""\n' + output = six.StringIO() + args = mock.Mock() + args.variables = ['a', 'b', 'd'] + args.prefix = '' + sf.emit_one(c, d, output, args) + actual = output.getvalue() + self.assertEqual(expected, actual) -def test_shell_formatter_args(): - sf = shell.ShellFormatter() - c = ('a', 'b', 'c', 'd') - d = ('A', 'B', 'C', '"escape me"') - expected = 'Xd="\\"escape me\\""\n' - output = six.StringIO() - # Parse arguments as if passed on the command-line - parser = argparse.ArgumentParser(description='Testing...') - sf.add_argument_group(parser) - parsed_args = parser.parse_args(['--variable', 'd', '--prefix', 'X']) - sf.emit_one(c, d, output, parsed_args) - actual = output.getvalue() - assert expected == actual + def test_args(self): + sf = shell.ShellFormatter() + c = ('a', 'b', 'c', 'd') + d = ('A', 'B', 'C', '"escape me"') + expected = 'Xd="\\"escape me\\""\n' + output = six.StringIO() + # Parse arguments as if passed on the command-line + parser = argparse.ArgumentParser(description='Testing...') + sf.add_argument_group(parser) + parsed_args = parser.parse_args(['--variable', 'd', '--prefix', 'X']) + sf.emit_one(c, d, output, parsed_args) + actual = output.getvalue() + self.assertEqual(expected, actual) + def test_formattable_column(self): + sf = shell.ShellFormatter() + c = ('a', 'b', 'c') + d = ('A', 'B', test_columns.FauxColumn(['the', 'value'])) + expected = '\n'.join([ + 'a="A"', + 'b="B"', + 'c="[\'the\', \'value\']"\n', + ]) + output = six.StringIO() + args = mock.Mock() + args.variables = ['a', 'b', 'c'] + args.prefix = '' + sf.emit_one(c, d, output, args) + actual = output.getvalue() + self.assertEqual(expected, actual) -def test_shell_formatter_formattable_column(): - sf = shell.ShellFormatter() - c = ('a', 'b', 'c') - d = ('A', 'B', test_columns.FauxColumn(['the', 'value'])) - expected = '\n'.join([ - 'a="A"', - 'b="B"', - 'c="[\'the\', \'value\']"\n', - ]) - output = six.StringIO() - args = mock.Mock() - args.variables = ['a', 'b', 'c'] - args.prefix = '' - sf.emit_one(c, d, output, args) - actual = output.getvalue() - assert expected == actual + def test_non_string_values(self): + sf = shell.ShellFormatter() + c = ('a', 'b', 'c', 'd', 'e') + d = (True, False, 100, '"esc"', six.text_type('"esc"')) + expected = ('a="True"\nb="False"\nc="100"\n' + 'd="\\"esc\\""\ne="\\"esc\\""\n') + output = six.StringIO() + args = mock.Mock() + args.variables = ['a', 'b', 'c', 'd', 'e'] + args.prefix = '' + sf.emit_one(c, d, output, args) + actual = output.getvalue() + self.assertEqual(expected, actual) - -def test_shell_formatter_with_non_string_values(): - sf = shell.ShellFormatter() - c = ('a', 'b', 'c', 'd', 'e') - d = (True, False, 100, '"esc"', six.text_type('"esc"')) - expected = 'a="True"\nb="False"\nc="100"\nd="\\"esc\\""\ne="\\"esc\\""\n' - output = six.StringIO() - args = mock.Mock() - args.variables = ['a', 'b', 'c', 'd', 'e'] - args.prefix = '' - sf.emit_one(c, d, output, args) - actual = output.getvalue() - assert expected == actual - - -def test_shell_formatter_with_non_bash_friendly_values(): - sf = shell.ShellFormatter() - c = ('a', 'foo-bar', 'provider:network_type') - d = (True, 'baz', 'vxlan') - expected = 'a="True"\nfoo_bar="baz"\nprovider_network_type="vxlan"\n' - output = six.StringIO() - args = mock.Mock() - args.variables = ['a', 'foo-bar', 'provider:network_type'] - args.prefix = '' - sf.emit_one(c, d, output, args) - actual = output.getvalue() - assert expected == actual + def test_non_bash_friendly_values(self): + sf = shell.ShellFormatter() + c = ('a', 'foo-bar', 'provider:network_type') + d = (True, 'baz', 'vxlan') + expected = 'a="True"\nfoo_bar="baz"\nprovider_network_type="vxlan"\n' + output = six.StringIO() + args = mock.Mock() + args.variables = ['a', 'foo-bar', 'provider:network_type'] + args.prefix = '' + sf.emit_one(c, d, output, args) + actual = output.getvalue() + self.assertEqual(expected, actual) diff --git a/cliff/tests/test_formatters_table.py b/cliff/tests/test_formatters_table.py index 7e8cf97..e1b2f09 100644 --- a/cliff/tests/test_formatters_table.py +++ b/cliff/tests/test_formatters_table.py @@ -12,12 +12,15 @@ # License for the specific language governing permissions and limitations # under the License. +import argparse +import os +import textwrap + import mock from six import StringIO -import os -import argparse from cliff.formatters import table +from cliff.tests import base from cliff.tests import test_columns @@ -61,487 +64,548 @@ def _table_tester_helper(tags, data, extra_args=None): return output.getvalue() -@mock.patch('cliff.utils.terminal_width') -def test_table_formatter(tw): - tw.return_value = 80 - c = ('a', 'b', 'c', 'd') - d = ('A', 'B', 'C', 'test\rcarriage\r\nreturn') - expected = '''\ -+-------+---------------+ -| Field | Value | -+-------+---------------+ -| a | A | -| b | B | -| c | C | -| d | test carriage | -| | return | -+-------+---------------+ -''' - assert expected == _table_tester_helper(c, d) - - -# Multi-line output when width is restricted to 42 columns -expected_ml_val = '''\ -+-------+--------------------------------+ -| Field | Value | -+-------+--------------------------------+ -| a | A | -| b | B | -| c | C | -| d | dddddddddddddddddddddddddddddd | -| | dddddddddddddddddddddddddddddd | -| | ddddddddddddddddd | -+-------+--------------------------------+ -''' - -# Multi-line output when width is restricted to 80 columns -expected_ml_80_val = '''\ -+-------+----------------------------------------------------------------------+ -| Field | Value | -+-------+----------------------------------------------------------------------+ -| a | A | -| b | B | -| c | C | -| d | dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd | -| | ddddddddd | -+-------+----------------------------------------------------------------------+ -''' # noqa - -# Single-line output, for when no line length restriction apply -expected_sl_val = '''\ -+-------+-------------------------------------------------------------------------------+ -| Field | Value | -+-------+-------------------------------------------------------------------------------+ -| a | A | -| b | B | -| c | C | -| d | ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd | -+-------+-------------------------------------------------------------------------------+ -''' # noqa - - -@mock.patch('cliff.utils.terminal_width') -def test_table_formatter_no_cli_param(tw): - tw.return_value = 80 - c = ('a', 'b', 'c', 'd') - d = ('A', 'B', 'C', 'd' * 77) - assert expected_ml_80_val == _table_tester_helper(c, d, extra_args=args()) - - -@mock.patch('cliff.utils.terminal_width') -def test_table_formatter_cli_param(tw): - tw.return_value = 80 - c = ('a', 'b', 'c', 'd') - d = ('A', 'B', 'C', 'd' * 77) - assert (expected_ml_val == - _table_tester_helper(c, d, extra_args=['--max-width', '42'])) - - -@mock.patch('cliff.utils.terminal_width') -def test_table_formatter_no_cli_param_unlimited_tw(tw): - tw.return_value = 0 - c = ('a', 'b', 'c', 'd') - d = ('A', 'B', 'C', 'd' * 77) - # output should not be wrapped to multiple lines - assert expected_sl_val == _table_tester_helper(c, d, extra_args=args()) - - -@mock.patch('cliff.utils.terminal_width') -def test_table_formatter_cli_param_unlimited_tw(tw): - tw.return_value = 0 - c = ('a', 'b', 'c', 'd') - d = ('A', 'B', 'C', 'd' * 77) - assert (expected_ml_val == - _table_tester_helper(c, d, extra_args=['--max-width', '42'])) - - -@mock.patch('cliff.utils.terminal_width') -@mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '666'}) -def test_table_formatter_cli_param_envvar_big(tw): - tw.return_value = 80 - c = ('a', 'b', 'c', 'd') - d = ('A', 'B', 'C', 'd' * 77) - assert (expected_ml_val == - _table_tester_helper(c, d, extra_args=['--max-width', '42'])) - - -@mock.patch('cliff.utils.terminal_width') -@mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '23'}) -def test_table_formatter_cli_param_envvar_tiny(tw): - tw.return_value = 80 - c = ('a', 'b', 'c', 'd') - d = ('A', 'B', 'C', 'd' * 77) - assert (expected_ml_val == - _table_tester_helper(c, d, extra_args=['--max-width', '42'])) - - -@mock.patch('cliff.utils.terminal_width') -def test_table_formatter_max_width(tw): - tw.return_value = 80 - c = ('field_name', 'a_really_long_field_name') - d = ('the value', 'a value significantly longer than the field') - expected = '''\ -+--------------------------+---------------------------------------------+ -| Field | Value | -+--------------------------+---------------------------------------------+ -| field_name | the value | -| a_really_long_field_name | a value significantly longer than the field | -+--------------------------+---------------------------------------------+ -''' - assert expected == _table_tester_helper(c, d) - - # resize value column - tw.return_value = 70 - expected = '''\ -+--------------------------+-----------------------------------------+ -| Field | Value | -+--------------------------+-----------------------------------------+ -| field_name | the value | -| a_really_long_field_name | a value significantly longer than the | -| | field | -+--------------------------+-----------------------------------------+ -''' - assert expected == _table_tester_helper(c, d) - - # resize both columns - tw.return_value = 50 - expected = '''\ -+-----------------------+------------------------+ -| Field | Value | -+-----------------------+------------------------+ -| field_name | the value | -| a_really_long_field_n | a value significantly | -| ame | longer than the field | -+-----------------------+------------------------+ -''' - assert expected == _table_tester_helper(c, d) - - # resize all columns limited by min_width=16 - tw.return_value = 10 - expected = '''\ -+------------------+------------------+ -| Field | Value | -+------------------+------------------+ -| field_name | the value | -| a_really_long_fi | a value | -| eld_name | significantly | -| | longer than the | -| | field | -+------------------+------------------+ -''' - assert expected == _table_tester_helper(c, d) - - -@mock.patch('cliff.utils.terminal_width') -def test_table_list_formatter(tw): - tw.return_value = 80 - c = ('a', 'b', 'c') - d1 = ('A', 'B', 'C') - d2 = ('D', 'E', 'test\rcarriage\r\nreturn') - data = [d1, d2] - expected = '''\ -+---+---+---------------+ -| a | b | c | -+---+---+---------------+ -| A | B | C | -| D | E | test carriage | -| | | return | -+---+---+---------------+ -''' - assert expected == _table_tester_helper(c, data) - - -@mock.patch('cliff.utils.terminal_width') -def test_table_formatter_formattable_column(tw): - tw.return_value = 0 - c = ('a', 'b', 'c', 'd') - d = ('A', 'B', 'C', test_columns.FauxColumn(['the', 'value'])) - expected = '''\ -+-------+---------------------------------------------+ -| Field | Value | -+-------+---------------------------------------------+ -| a | A | -| b | B | -| c | C | -| d | I made this string myself: ['the', 'value'] | -+-------+---------------------------------------------+ -''' - assert expected == _table_tester_helper(c, d) - - -_col_names = ('one', 'two', 'three') -_col_data = [( - 'one one one one one', - 'two two two two', - 'three three')] - -_expected_mv = { - 80: '''\ -+---------------------+-----------------+-------------+ -| one | two | three | -+---------------------+-----------------+-------------+ -| one one one one one | two two two two | three three | -+---------------------+-----------------+-------------+ -''', - - 50: '''\ -+----------------+-----------------+-------------+ -| one | two | three | -+----------------+-----------------+-------------+ -| one one one | two two two two | three three | -| one one | | | -+----------------+-----------------+-------------+ -''', - - 47: '''\ -+---------------+---------------+-------------+ -| one | two | three | -+---------------+---------------+-------------+ -| one one one | two two two | three three | -| one one | two | | -+---------------+---------------+-------------+ -''', - - 45: '''\ -+--------------+--------------+-------------+ -| one | two | three | -+--------------+--------------+-------------+ -| one one one | two two two | three three | -| one one | two | | -+--------------+--------------+-------------+ -''', - - 40: '''\ -+------------+------------+------------+ -| one | two | three | -+------------+------------+------------+ -| one one | two two | three | -| one one | two two | three | -| one | | | -+------------+------------+------------+ -''', - - 10: '''\ -+----------+----------+----------+ -| one | two | three | -+----------+----------+----------+ -| one one | two two | three | -| one one | two two | three | -| one | | | -+----------+----------+----------+ -''', -} - - -@mock.patch('cliff.utils.terminal_width') -def test_table_list_formatter_formattable_column(tw): - tw.return_value = 80 - c = ('a', 'b', 'c') - d1 = ('A', 'B', test_columns.FauxColumn(['the', 'value'])) - data = [d1] - expected = '''\ -+---+---+---------------------------------------------+ -| a | b | c | -+---+---+---------------------------------------------+ -| A | B | I made this string myself: ['the', 'value'] | -+---+---+---------------------------------------------+ -''' - assert expected == _table_tester_helper(c, data) - - -@mock.patch('cliff.utils.terminal_width') -def test_table_list_formatter_max_width(tw): - # no resize - l = tw.return_value = 80 - assert _expected_mv[l] == _table_tester_helper(_col_names, _col_data) - - # resize 1 column - l = tw.return_value = 50 - actual = _table_tester_helper(_col_names, _col_data) - assert _expected_mv[l] == actual - assert len(actual.splitlines()[0]) == l - - # resize 2 columns - l = tw.return_value = 45 - actual = _table_tester_helper(_col_names, _col_data) - assert _expected_mv[l] == actual - assert len(actual.splitlines()[0]) == l - - # resize all columns - l = tw.return_value = 40 - actual = _table_tester_helper(_col_names, _col_data) - assert _expected_mv[l] == actual - assert len(actual.splitlines()[0]) == l - - # resize all columns limited by min_width=8 - l = tw.return_value = 10 - actual = _table_tester_helper(_col_names, _col_data) - assert _expected_mv[l] == actual - # 3 columns each 8 wide, plus table spacing and borders - expected_width = 11 * 3 + 1 - assert len(actual.splitlines()[0]) == expected_width - - -# Force a wide terminal by overriding its width with envvar -@mock.patch('cliff.utils.terminal_width') -@mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '666'}) -def test_table_list_formatter_max_width_and_envvar_max(tw): - # no resize - tw.return_value = 80 - assert _expected_mv[80] == _table_tester_helper(_col_names, _col_data) - - # resize 1 column - tw.return_value = 50 - assert _expected_mv[80] == _table_tester_helper(_col_names, _col_data) - - # resize 2 columns - tw.return_value = 45 - assert _expected_mv[80] == _table_tester_helper(_col_names, _col_data) - - # resize all columns - tw.return_value = 40 - assert _expected_mv[80] == _table_tester_helper(_col_names, _col_data) - - # resize all columns limited by min_width=8 - tw.return_value = 10 - assert _expected_mv[80] == _table_tester_helper(_col_names, _col_data) - - -# Force a narrow terminal by overriding its width with envvar -@mock.patch('cliff.utils.terminal_width') -@mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '47'}) -def test_table_list_formatter_max_width_and_envvar_mid(tw): - # no resize - tw.return_value = 80 - assert _expected_mv[47] == _table_tester_helper(_col_names, _col_data) - - # resize 1 column - tw.return_value = 50 - actual = _table_tester_helper(_col_names, _col_data) - assert _expected_mv[47] == actual - assert len(actual.splitlines()[0]) == 47 - - # resize 2 columns - tw.return_value = 45 - actual = _table_tester_helper(_col_names, _col_data) - assert _expected_mv[47] == actual - assert len(actual.splitlines()[0]) == 47 - - # resize all columns - tw.return_value = 40 - actual = _table_tester_helper(_col_names, _col_data) - assert _expected_mv[47] == actual - assert len(actual.splitlines()[0]) == 47 - - # resize all columns limited by min_width=8 - tw.return_value = 10 - actual = _table_tester_helper(_col_names, _col_data) - assert _expected_mv[47] == actual - assert len(actual.splitlines()[0]) == 47 - - -@mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '80'}) -def test_table_list_formatter_env_maxwidth_noresize(): - # no resize - assert _expected_mv[80] == _table_tester_helper(_col_names, _col_data) - - -@mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '50'}) -def test_table_list_formatter_env_maxwidth_resize_one(): - # resize 1 column - actual = _table_tester_helper(_col_names, _col_data) - assert _expected_mv[50] == actual - assert len(actual.splitlines()[0]) == 50 - - -@mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '45'}) -def test_table_list_formatter_env_maxwidth_resize_two(): - # resize 2 columns - actual = _table_tester_helper(_col_names, _col_data) - assert _expected_mv[45] == actual - assert len(actual.splitlines()[0]) == 45 - - -@mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '40'}) -def test_table_list_formatter_env_maxwidth_resize_all(): - # resize all columns - actual = _table_tester_helper(_col_names, _col_data) - assert _expected_mv[40] == actual - assert len(actual.splitlines()[0]) == 40 - - -@mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '8'}) -def test_table_list_formatter_env_maxwidth_resize_all_tiny(): - # resize all columns limited by min_width=8 - actual = _table_tester_helper(_col_names, _col_data) - assert _expected_mv[10] == actual - # 3 columns each 8 wide, plus table spacing and borders - expected_width = 11 * 3 + 1 - assert len(actual.splitlines()[0]) == expected_width - - -@mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '42'}) -def test_table_list_formatter_env_maxwidth_args_big(): - assert _expected_mv[80] == _table_tester_helper(_col_names, _col_data, - extra_args=args(666)) - - -@mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '42'}) -def test_table_list_formatter_env_maxwidth_args_tiny(): - assert _expected_mv[40] == _table_tester_helper(_col_names, _col_data, - extra_args=args(40)) - - -@mock.patch('cliff.utils.terminal_width') -def test_table_list_formatter_empty(tw): - tw.return_value = 80 - c = ('a', 'b', 'c') - data = [] - expected = '\n' - assert expected == _table_tester_helper(c, data) - - -@mock.patch('cliff.utils.terminal_width') -def test_table_list_formatter_empty_table(tw): - tw.return_value = 80 - c = ('a', 'b', 'c') - data = [] - expected = '''\ -+---+---+---+ -| a | b | c | -+---+---+---+ -+---+---+---+ -''' - assert expected == _table_tester_helper(c, data, - extra_args=['--print-empty']) - - -def test_field_widths(): - tf = table.TableFormatter - assert { - 'a': 1, - 'b': 2, - 'c': 3, - 'd': 10 - } == tf._field_widths( - ('a', 'b', 'c', 'd'), - '+---+----+-----+------------+') - - -def test_field_widths_zero(): - tf = table.TableFormatter - assert { - 'a': 0, - 'b': 0, - 'c': 0 - } == tf._field_widths( - ('a', 'b', 'c'), - '+--+-++') - - -def test_width_info(): - tf = table.TableFormatter - assert (49, 4) == (tf._width_info(80, 10)) - assert (76, 76) == (tf._width_info(80, 1)) - assert (79, 0) == (tf._width_info(80, 0)) - assert (0, 0) == (tf._width_info(0, 80)) +class TestTableFormatter(base.TestBase): + + @mock.patch('cliff.utils.terminal_width') + def test(self, tw): + tw.return_value = 80 + c = ('a', 'b', 'c', 'd') + d = ('A', 'B', 'C', 'test\rcarriage\r\nreturn') + expected = textwrap.dedent('''\ + +-------+---------------+ + | Field | Value | + +-------+---------------+ + | a | A | + | b | B | + | c | C | + | d | test carriage | + | | return | + +-------+---------------+ + ''') + self.assertEqual(expected, _table_tester_helper(c, d)) + + +class TestTerminalWidth(base.TestBase): + + # Multi-line output when width is restricted to 42 columns + expected_ml_val = textwrap.dedent('''\ + +-------+--------------------------------+ + | Field | Value | + +-------+--------------------------------+ + | a | A | + | b | B | + | c | C | + | d | dddddddddddddddddddddddddddddd | + | | dddddddddddddddddddddddddddddd | + | | ddddddddddddddddd | + +-------+--------------------------------+ + ''') + + # Multi-line output when width is restricted to 80 columns + expected_ml_80_val = textwrap.dedent('''\ + +-------+----------------------------------------------------------------------+ + | Field | Value | + +-------+----------------------------------------------------------------------+ + | a | A | + | b | B | + | c | C | + | d | dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd | + | | ddddddddd | + +-------+----------------------------------------------------------------------+ + ''') # noqa + + # Single-line output, for when no line length restriction apply + expected_sl_val = textwrap.dedent('''\ + +-------+-------------------------------------------------------------------------------+ + | Field | Value | + +-------+-------------------------------------------------------------------------------+ + | a | A | + | b | B | + | c | C | + | d | ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd | + +-------+-------------------------------------------------------------------------------+ + ''') # noqa + + @mock.patch('cliff.utils.terminal_width') + def test_table_formatter_no_cli_param(self, tw): + tw.return_value = 80 + c = ('a', 'b', 'c', 'd') + d = ('A', 'B', 'C', 'd' * 77) + self.assertEqual( + self.expected_ml_80_val, + _table_tester_helper(c, d, extra_args=args()), + ) + + @mock.patch('cliff.utils.terminal_width') + def test_table_formatter_cli_param(self, tw): + tw.return_value = 80 + c = ('a', 'b', 'c', 'd') + d = ('A', 'B', 'C', 'd' * 77) + self.assertEqual( + self.expected_ml_val, + _table_tester_helper(c, d, extra_args=['--max-width', '42']), + ) + + @mock.patch('cliff.utils.terminal_width') + def test_table_formatter_no_cli_param_unlimited_tw(self, tw): + tw.return_value = 0 + c = ('a', 'b', 'c', 'd') + d = ('A', 'B', 'C', 'd' * 77) + # output should not be wrapped to multiple lines + self.assertEqual( + self.expected_sl_val, + _table_tester_helper(c, d, extra_args=args()), + ) + + @mock.patch('cliff.utils.terminal_width') + def test_table_formatter_cli_param_unlimited_tw(self, tw): + tw.return_value = 0 + c = ('a', 'b', 'c', 'd') + d = ('A', 'B', 'C', 'd' * 77) + self.assertEqual( + self.expected_ml_val, + _table_tester_helper(c, d, extra_args=['--max-width', '42']), + ) + + @mock.patch('cliff.utils.terminal_width') + @mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '666'}) + def test_table_formatter_cli_param_envvar_big(self, tw): + tw.return_value = 80 + c = ('a', 'b', 'c', 'd') + d = ('A', 'B', 'C', 'd' * 77) + self.assertEqual( + self.expected_ml_val, + _table_tester_helper(c, d, extra_args=['--max-width', '42']), + ) + + @mock.patch('cliff.utils.terminal_width') + @mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '23'}) + def test_table_formatter_cli_param_envvar_tiny(self, tw): + tw.return_value = 80 + c = ('a', 'b', 'c', 'd') + d = ('A', 'B', 'C', 'd' * 77) + self.assertEqual( + self.expected_ml_val, + _table_tester_helper(c, d, extra_args=['--max-width', '42']), + ) + + +class TestMaxWidth(base.TestBase): + + expected_80 = textwrap.dedent('''\ + +--------------------------+---------------------------------------------+ + | Field | Value | + +--------------------------+---------------------------------------------+ + | field_name | the value | + | a_really_long_field_name | a value significantly longer than the field | + +--------------------------+---------------------------------------------+ + ''') + + @mock.patch('cliff.utils.terminal_width') + def test_80(self, tw): + tw.return_value = 80 + c = ('field_name', 'a_really_long_field_name') + d = ('the value', 'a value significantly longer than the field') + self.assertEqual(self.expected_80, _table_tester_helper(c, d)) + + @mock.patch('cliff.utils.terminal_width') + def test_70(self, tw): + # resize value column + tw.return_value = 70 + c = ('field_name', 'a_really_long_field_name') + d = ('the value', 'a value significantly longer than the field') + expected = textwrap.dedent('''\ + +--------------------------+-----------------------------------------+ + | Field | Value | + +--------------------------+-----------------------------------------+ + | field_name | the value | + | a_really_long_field_name | a value significantly longer than the | + | | field | + +--------------------------+-----------------------------------------+ + ''') + self.assertEqual(expected, _table_tester_helper(c, d)) + + @mock.patch('cliff.utils.terminal_width') + def test_50(self, tw): + # resize both columns + tw.return_value = 50 + c = ('field_name', 'a_really_long_field_name') + d = ('the value', 'a value significantly longer than the field') + expected = textwrap.dedent('''\ + +-----------------------+------------------------+ + | Field | Value | + +-----------------------+------------------------+ + | field_name | the value | + | a_really_long_field_n | a value significantly | + | ame | longer than the field | + +-----------------------+------------------------+ + ''') + self.assertEqual(expected, _table_tester_helper(c, d)) + + @mock.patch('cliff.utils.terminal_width') + def test_10(self, tw): + # resize all columns limited by min_width=16 + tw.return_value = 10 + c = ('field_name', 'a_really_long_field_name') + d = ('the value', 'a value significantly longer than the field') + expected = textwrap.dedent('''\ + +------------------+------------------+ + | Field | Value | + +------------------+------------------+ + | field_name | the value | + | a_really_long_fi | a value | + | eld_name | significantly | + | | longer than the | + | | field | + +------------------+------------------+ + ''') + self.assertEqual(expected, _table_tester_helper(c, d)) + + +class TestListFormatter(base.TestBase): + + _col_names = ('one', 'two', 'three') + _col_data = [( + 'one one one one one', + 'two two two two', + 'three three')] + + _expected_mv = { + 80: textwrap.dedent('''\ + +---------------------+-----------------+-------------+ + | one | two | three | + +---------------------+-----------------+-------------+ + | one one one one one | two two two two | three three | + +---------------------+-----------------+-------------+ + '''), + + 50: textwrap.dedent('''\ + +----------------+-----------------+-------------+ + | one | two | three | + +----------------+-----------------+-------------+ + | one one one | two two two two | three three | + | one one | | | + +----------------+-----------------+-------------+ + '''), + + 47: textwrap.dedent('''\ + +---------------+---------------+-------------+ + | one | two | three | + +---------------+---------------+-------------+ + | one one one | two two two | three three | + | one one | two | | + +---------------+---------------+-------------+ + '''), + + 45: textwrap.dedent('''\ + +--------------+--------------+-------------+ + | one | two | three | + +--------------+--------------+-------------+ + | one one one | two two two | three three | + | one one | two | | + +--------------+--------------+-------------+ + '''), + + 40: textwrap.dedent('''\ + +------------+------------+------------+ + | one | two | three | + +------------+------------+------------+ + | one one | two two | three | + | one one | two two | three | + | one | | | + +------------+------------+------------+ + '''), + + 10: textwrap.dedent('''\ + +----------+----------+----------+ + | one | two | three | + +----------+----------+----------+ + | one one | two two | three | + | one one | two two | three | + | one | | | + +----------+----------+----------+ + '''), + } + + @mock.patch('cliff.utils.terminal_width') + def test_table_list_formatter(self, tw): + tw.return_value = 80 + c = ('a', 'b', 'c') + d1 = ('A', 'B', 'C') + d2 = ('D', 'E', 'test\rcarriage\r\nreturn') + data = [d1, d2] + expected = textwrap.dedent('''\ + +---+---+---------------+ + | a | b | c | + +---+---+---------------+ + | A | B | C | + | D | E | test carriage | + | | | return | + +---+---+---------------+ + ''') + self.assertEqual(expected, _table_tester_helper(c, data)) + + @mock.patch('cliff.utils.terminal_width') + def test_table_formatter_formattable_column(self, tw): + tw.return_value = 0 + c = ('a', 'b', 'c', 'd') + d = ('A', 'B', 'C', test_columns.FauxColumn(['the', 'value'])) + expected = textwrap.dedent('''\ + +-------+---------------------------------------------+ + | Field | Value | + +-------+---------------------------------------------+ + | a | A | + | b | B | + | c | C | + | d | I made this string myself: ['the', 'value'] | + +-------+---------------------------------------------+ + ''') + self.assertEqual(expected, _table_tester_helper(c, d)) + + @mock.patch('cliff.utils.terminal_width') + def test_formattable_column(self, tw): + tw.return_value = 80 + c = ('a', 'b', 'c') + d1 = ('A', 'B', test_columns.FauxColumn(['the', 'value'])) + data = [d1] + expected = textwrap.dedent('''\ + +---+---+---------------------------------------------+ + | a | b | c | + +---+---+---------------------------------------------+ + | A | B | I made this string myself: ['the', 'value'] | + +---+---+---------------------------------------------+ + ''') + self.assertEqual(expected, _table_tester_helper(c, data)) + + @mock.patch('cliff.utils.terminal_width') + def test_max_width_80(self, tw): + # no resize + l = tw.return_value = 80 + self.assertEqual( + self._expected_mv[l], + _table_tester_helper(self._col_names, self._col_data), + ) + + @mock.patch('cliff.utils.terminal_width') + def test_max_width_50(self, tw): + # resize 1 column + l = tw.return_value = 50 + actual = _table_tester_helper(self._col_names, self._col_data) + self.assertEqual(self._expected_mv[l], actual) + self.assertEqual(l, len(actual.splitlines()[0])) + + @mock.patch('cliff.utils.terminal_width') + def test_max_width_45(self, tw): + # resize 2 columns + l = tw.return_value = 45 + actual = _table_tester_helper(self._col_names, self._col_data) + self.assertEqual(self._expected_mv[l], actual) + self.assertEqual(l, len(actual.splitlines()[0])) + + @mock.patch('cliff.utils.terminal_width') + def test_max_width_40(self, tw): + # resize all columns + l = tw.return_value = 40 + actual = _table_tester_helper(self._col_names, self._col_data) + self.assertEqual(self._expected_mv[l], actual) + self.assertEqual(l, len(actual.splitlines()[0])) + + @mock.patch('cliff.utils.terminal_width') + def test_max_width_10(self, tw): + # resize all columns limited by min_width=8 + l = tw.return_value = 10 + actual = _table_tester_helper(self._col_names, self._col_data) + self.assertEqual(self._expected_mv[l], actual) + # 3 columns each 8 wide, plus table spacing and borders + expected_width = 11 * 3 + 1 + self.assertEqual(expected_width, len(actual.splitlines()[0])) + + # Force a wide terminal by overriding its width with envvar + @mock.patch('cliff.utils.terminal_width') + @mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '666'}) + def test_max_width_and_envvar_max(self, tw): + # no resize + tw.return_value = 80 + self.assertEqual( + self._expected_mv[80], + _table_tester_helper(self._col_names, self._col_data), + ) + + # resize 1 column + tw.return_value = 50 + self.assertEqual( + self._expected_mv[80], + _table_tester_helper(self._col_names, self._col_data), + ) + + # resize 2 columns + tw.return_value = 45 + self.assertEqual( + self._expected_mv[80], + _table_tester_helper(self._col_names, self._col_data), + ) + + # resize all columns + tw.return_value = 40 + self.assertEqual( + self._expected_mv[80], + _table_tester_helper(self._col_names, self._col_data), + ) + + # resize all columns limited by min_width=8 + tw.return_value = 10 + self.assertEqual( + self._expected_mv[80], + _table_tester_helper(self._col_names, self._col_data), + ) + + # Force a narrow terminal by overriding its width with envvar + @mock.patch('cliff.utils.terminal_width') + @mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '47'}) + def test_max_width_and_envvar_mid(self, tw): + # no resize + tw.return_value = 80 + self.assertEqual( + self._expected_mv[47], + _table_tester_helper(self._col_names, self._col_data), + ) + + # resize 1 column + tw.return_value = 50 + actual = _table_tester_helper(self._col_names, self._col_data) + self.assertEqual(self._expected_mv[47], actual) + self.assertEqual(47, len(actual.splitlines()[0])) + + # resize 2 columns + tw.return_value = 45 + actual = _table_tester_helper(self._col_names, self._col_data) + self.assertEqual(self._expected_mv[47], actual) + self.assertEqual(47, len(actual.splitlines()[0])) + + # resize all columns + tw.return_value = 40 + actual = _table_tester_helper(self._col_names, self._col_data) + self.assertEqual(self._expected_mv[47], actual) + self.assertEqual(47, len(actual.splitlines()[0])) + + # resize all columns limited by min_width=8 + tw.return_value = 10 + actual = _table_tester_helper(self._col_names, self._col_data) + self.assertEqual(self._expected_mv[47], actual) + self.assertEqual(47, len(actual.splitlines()[0])) + + @mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '80'}) + def test_env_maxwidth_noresize(self): + # no resize + self.assertEqual( + self._expected_mv[80], + _table_tester_helper(self._col_names, self._col_data), + ) + + @mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '50'}) + def test_env_maxwidth_resize_one(self): + # resize 1 column + actual = _table_tester_helper(self._col_names, self._col_data) + self.assertEqual(self._expected_mv[50], actual) + self.assertEqual(50, len(actual.splitlines()[0])) + + @mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '45'}) + def test_env_maxwidth_resize_two(self): + # resize 2 columns + actual = _table_tester_helper(self._col_names, self._col_data) + self.assertEqual(self._expected_mv[45], actual) + self.assertEqual(45, len(actual.splitlines()[0])) + + @mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '40'}) + def test_env_maxwidth_resize_all(self): + # resize all columns + actual = _table_tester_helper(self._col_names, self._col_data) + self.assertEqual(self._expected_mv[40], actual) + self.assertEqual(40, len(actual.splitlines()[0])) + + @mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '8'}) + def test_env_maxwidth_resize_all_tiny(self): + # resize all columns limited by min_width=8 + actual = _table_tester_helper(self._col_names, self._col_data) + self.assertEqual(self._expected_mv[10], actual) + # 3 columns each 8 wide, plus table spacing and borders + expected_width = 11 * 3 + 1 + self.assertEqual(expected_width, len(actual.splitlines()[0])) + + @mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '42'}) + def test_env_maxwidth_args_big(self): + self.assertEqual( + self._expected_mv[80], + _table_tester_helper(self._col_names, self._col_data, + extra_args=args(666)), + ) + + @mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '42'}) + def test_env_maxwidth_args_tiny(self): + self.assertEqual( + self._expected_mv[40], + _table_tester_helper(self._col_names, self._col_data, + extra_args=args(40)), + ) + + @mock.patch('cliff.utils.terminal_width') + def test_empty(self, tw): + tw.return_value = 80 + c = ('a', 'b', 'c') + data = [] + expected = '\n' + self.assertEqual(expected, _table_tester_helper(c, data)) + + @mock.patch('cliff.utils.terminal_width') + def test_empty_table(self, tw): + tw.return_value = 80 + c = ('a', 'b', 'c') + data = [] + expected = textwrap.dedent('''\ + +---+---+---+ + | a | b | c | + +---+---+---+ + +---+---+---+ + ''') + self.assertEqual( + expected, + _table_tester_helper(c, data, + extra_args=['--print-empty']), + ) + + +class TestFieldWidths(base.TestBase): + + def test(self): + tf = table.TableFormatter + self.assertEqual( + { + 'a': 1, + 'b': 2, + 'c': 3, + 'd': 10 + }, + tf._field_widths( + ('a', 'b', 'c', 'd'), + '+---+----+-----+------------+'), + ) + + def test_zero(self): + tf = table.TableFormatter + self.assertEqual( + { + 'a': 0, + 'b': 0, + 'c': 0 + }, + tf._field_widths( + ('a', 'b', 'c'), + '+--+-++'), + ) + + def test_info(self): + tf = table.TableFormatter + self.assertEqual((49, 4), (tf._width_info(80, 10))) + self.assertEqual((76, 76), (tf._width_info(80, 1))) + self.assertEqual((79, 0), (tf._width_info(80, 0))) + self.assertEqual((0, 0), (tf._width_info(0, 80))) diff --git a/cliff/tests/test_formatters_value.py b/cliff/tests/test_formatters_value.py index f704b08..6053dd3 100644 --- a/cliff/tests/test_formatters_value.py +++ b/cliff/tests/test_formatters_value.py @@ -15,51 +15,51 @@ import six from cliff.formatters import value +from cliff.tests import base from cliff.tests import test_columns -def test_value_formatter(): - sf = value.ValueFormatter() - c = ('a', 'b', 'c', 'd') - d = ('A', 'B', 'C', '"no escape me"') - expected = 'A\nB\nC\n"no escape me"\n' - output = six.StringIO() - sf.emit_one(c, d, output, None) - actual = output.getvalue() - assert expected == actual +class TestValueFormatter(base.TestBase): + def test(self): + sf = value.ValueFormatter() + c = ('a', 'b', 'c', 'd') + d = ('A', 'B', 'C', '"no escape me"') + expected = 'A\nB\nC\n"no escape me"\n' + output = six.StringIO() + sf.emit_one(c, d, output, None) + actual = output.getvalue() + self.assertEqual(expected, actual) -def test_value_formatter_formattable_column(): - sf = value.ValueFormatter() - c = ('a', 'b', 'c', 'd') - d = ('A', 'B', 'C', test_columns.FauxColumn(['the', 'value'])) - expected = "A\nB\nC\n['the', 'value']\n" - output = six.StringIO() - sf.emit_one(c, d, output, None) - actual = output.getvalue() - assert expected == actual + def test_formattable_column(self): + sf = value.ValueFormatter() + c = ('a', 'b', 'c', 'd') + d = ('A', 'B', 'C', test_columns.FauxColumn(['the', 'value'])) + expected = "A\nB\nC\n['the', 'value']\n" + output = six.StringIO() + sf.emit_one(c, d, output, None) + actual = output.getvalue() + self.assertEqual(expected, actual) + def test_list_formatter(self): + sf = value.ValueFormatter() + c = ('a', 'b', 'c') + d1 = ('A', 'B', 'C') + d2 = ('D', 'E', 'F') + data = [d1, d2] + expected = 'A B C\nD E F\n' + output = six.StringIO() + sf.emit_list(c, data, output, None) + actual = output.getvalue() + self.assertEqual(expected, actual) -def test_value_list_formatter(): - sf = value.ValueFormatter() - c = ('a', 'b', 'c') - d1 = ('A', 'B', 'C') - d2 = ('D', 'E', 'F') - data = [d1, d2] - expected = 'A B C\nD E F\n' - output = six.StringIO() - sf.emit_list(c, data, output, None) - actual = output.getvalue() - assert expected == actual - - -def test_value_list_formatter_formattable_column(): - sf = value.ValueFormatter() - c = ('a', 'b', 'c') - d1 = ('A', 'B', test_columns.FauxColumn(['the', 'value'])) - data = [d1] - expected = "A B ['the', 'value']\n" - output = six.StringIO() - sf.emit_list(c, data, output, None) - actual = output.getvalue() - assert expected == actual + def test_list_formatter_formattable_column(self): + sf = value.ValueFormatter() + c = ('a', 'b', 'c') + d1 = ('A', 'B', test_columns.FauxColumn(['the', 'value'])) + data = [d1] + expected = "A B ['the', 'value']\n" + output = six.StringIO() + sf.emit_list(c, data, output, None) + actual = output.getvalue() + self.assertEqual(expected, actual) diff --git a/cliff/tests/test_formatters_yaml.py b/cliff/tests/test_formatters_yaml.py index 61db5d8..b6147d8 100644 --- a/cliff/tests/test_formatters_yaml.py +++ b/cliff/tests/test_formatters_yaml.py @@ -16,85 +16,85 @@ import six import yaml from cliff.formatters import yaml_format +from cliff.tests import base from cliff.tests import test_columns import mock -def test_yaml_format_one(): - sf = yaml_format.YAMLFormatter() - c = ('a', 'b', 'c', 'd') - d = ('A', 'B', 'C', '"escape me"') - expected = { - 'a': 'A', - 'b': 'B', - 'c': 'C', - 'd': '"escape me"' - } - output = six.StringIO() - args = mock.Mock() - sf.emit_one(c, d, output, args) - actual = yaml.safe_load(output.getvalue()) - assert expected == actual +class TestYAMLFormatter(base.TestBase): + def test_format_one(self): + sf = yaml_format.YAMLFormatter() + c = ('a', 'b', 'c', 'd') + d = ('A', 'B', 'C', '"escape me"') + expected = { + 'a': 'A', + 'b': 'B', + 'c': 'C', + 'd': '"escape me"' + } + output = six.StringIO() + args = mock.Mock() + sf.emit_one(c, d, output, args) + actual = yaml.safe_load(output.getvalue()) + self.assertEqual(expected, actual) -def test_yaml_format_formattablecolumn_one(): - sf = yaml_format.YAMLFormatter() - c = ('a', 'b', 'c', 'd') - d = ('A', 'B', 'C', test_columns.FauxColumn(['the', 'value'])) - expected = { - 'a': 'A', - 'b': 'B', - 'c': 'C', - 'd': ['the', 'value'], - } - args = mock.Mock() - sf.add_argument_group(args) + def test_formattablecolumn_one(self): + sf = yaml_format.YAMLFormatter() + c = ('a', 'b', 'c', 'd') + d = ('A', 'B', 'C', test_columns.FauxColumn(['the', 'value'])) + expected = { + 'a': 'A', + 'b': 'B', + 'c': 'C', + 'd': ['the', 'value'], + } + args = mock.Mock() + sf.add_argument_group(args) - args.noindent = True - output = six.StringIO() - sf.emit_one(c, d, output, args) - value = output.getvalue() - print(len(value.splitlines())) - actual = yaml.safe_load(output.getvalue()) - assert expected == actual + args.noindent = True + output = six.StringIO() + sf.emit_one(c, d, output, args) + value = output.getvalue() + print(len(value.splitlines())) + actual = yaml.safe_load(output.getvalue()) + self.assertEqual(expected, actual) + def test_list(self): + sf = yaml_format.YAMLFormatter() + c = ('a', 'b', 'c') + d = ( + ('A1', 'B1', 'C1'), + ('A2', 'B2', 'C2'), + ('A3', 'B3', 'C3') + ) + expected = [ + {'a': 'A1', 'b': 'B1', 'c': 'C1'}, + {'a': 'A2', 'b': 'B2', 'c': 'C2'}, + {'a': 'A3', 'b': 'B3', 'c': 'C3'} + ] + output = six.StringIO() + args = mock.Mock() + sf.add_argument_group(args) + sf.emit_list(c, d, output, args) + actual = yaml.safe_load(output.getvalue()) + self.assertEqual(expected, actual) -def test_yaml_format_list(): - sf = yaml_format.YAMLFormatter() - c = ('a', 'b', 'c') - d = ( - ('A1', 'B1', 'C1'), - ('A2', 'B2', 'C2'), - ('A3', 'B3', 'C3') - ) - expected = [ - {'a': 'A1', 'b': 'B1', 'c': 'C1'}, - {'a': 'A2', 'b': 'B2', 'c': 'C2'}, - {'a': 'A3', 'b': 'B3', 'c': 'C3'} - ] - output = six.StringIO() - args = mock.Mock() - sf.add_argument_group(args) - sf.emit_list(c, d, output, args) - actual = yaml.safe_load(output.getvalue()) - assert expected == actual + def test_formattablecolumn_list(self): + sf = yaml_format.YAMLFormatter() + c = ('a', 'b', 'c') + d = ( + ('A1', 'B1', test_columns.FauxColumn(['the', 'value'])), + ) + expected = [ + {'a': 'A1', 'b': 'B1', 'c': ['the', 'value']}, + ] + args = mock.Mock() + sf.add_argument_group(args) - -def test_yaml_format_formattablecolumn_list(): - sf = yaml_format.YAMLFormatter() - c = ('a', 'b', 'c') - d = ( - ('A1', 'B1', test_columns.FauxColumn(['the', 'value'])), - ) - expected = [ - {'a': 'A1', 'b': 'B1', 'c': ['the', 'value']}, - ] - args = mock.Mock() - sf.add_argument_group(args) - - args.noindent = True - output = six.StringIO() - sf.emit_list(c, d, output, args) - actual = yaml.safe_load(output.getvalue()) - assert expected == actual + args.noindent = True + output = six.StringIO() + sf.emit_list(c, d, output, args) + actual = yaml.safe_load(output.getvalue()) + self.assertEqual(expected, actual) diff --git a/cliff/tests/test_help.py b/cliff/tests/test_help.py index 23659a2..8b66fbe 100644 --- a/cliff/tests/test_help.py +++ b/cliff/tests/test_help.py @@ -22,159 +22,153 @@ import mock from cliff import app as application from cliff import commandmanager from cliff import help +from cliff.tests import base from cliff.tests import utils -def test_show_help_for_command(): - # FIXME(dhellmann): Are commands tied too closely to the app? Or - # do commands know too much about apps by using them to get to the - # command manager? - stdout = StringIO() - app = application.App('testing', '1', - utils.TestCommandManager(utils.TEST_NAMESPACE), - stdout=stdout) - app.NAME = 'test' - help_cmd = help.HelpCommand(app, mock.Mock()) - parser = help_cmd.get_parser('test') - parsed_args = parser.parse_args(['one']) - try: - help_cmd.run(parsed_args) - except SystemExit: - pass - assert stdout.getvalue() == 'TestParser' +class TestHelp(base.TestBase): + def test_show_help_for_command(self): + # FIXME(dhellmann): Are commands tied too closely to the app? Or + # do commands know too much about apps by using them to get to the + # command manager? + stdout = StringIO() + app = application.App('testing', '1', + utils.TestCommandManager(utils.TEST_NAMESPACE), + stdout=stdout) + app.NAME = 'test' + help_cmd = help.HelpCommand(app, mock.Mock()) + parser = help_cmd.get_parser('test') + parsed_args = parser.parse_args(['one']) + try: + help_cmd.run(parsed_args) + except SystemExit: + pass + self.assertEqual('TestParser', stdout.getvalue()) -def test_list_matching_commands(): - # FIXME(dhellmann): Are commands tied too closely to the app? Or - # do commands know too much about apps by using them to get to the - # command manager? - stdout = StringIO() - app = application.App('testing', '1', - utils.TestCommandManager(utils.TEST_NAMESPACE), - stdout=stdout) - app.NAME = 'test' - help_cmd = help.HelpCommand(app, mock.Mock()) - parser = help_cmd.get_parser('test') - parsed_args = parser.parse_args(['t']) - try: - help_cmd.run(parsed_args) - except SystemExit: - pass - help_output = stdout.getvalue() - assert 'Command "t" matches:' in help_output - assert 'three word command\n two words\n' in help_output + def test_list_matching_commands(self): + # FIXME(dhellmann): Are commands tied too closely to the app? Or + # do commands know too much about apps by using them to get to the + # command manager? + stdout = StringIO() + app = application.App('testing', '1', + utils.TestCommandManager(utils.TEST_NAMESPACE), + stdout=stdout) + app.NAME = 'test' + help_cmd = help.HelpCommand(app, mock.Mock()) + parser = help_cmd.get_parser('test') + parsed_args = parser.parse_args(['t']) + try: + help_cmd.run(parsed_args) + except SystemExit: + pass + help_output = stdout.getvalue() + self.assertIn('Command "t" matches:', help_output) + self.assertIn('three word command\n two words\n', help_output) + def test_list_matching_commands_no_match(self): + # FIXME(dhellmann): Are commands tied too closely to the app? Or + # do commands know too much about apps by using them to get to the + # command manager? + stdout = StringIO() + app = application.App('testing', '1', + utils.TestCommandManager(utils.TEST_NAMESPACE), + stdout=stdout) + app.NAME = 'test' + help_cmd = help.HelpCommand(app, mock.Mock()) + parser = help_cmd.get_parser('test') + parsed_args = parser.parse_args(['z']) + self.assertRaises( + ValueError, + help_cmd.run, + parsed_args, + ) -def test_list_matching_commands_no_match(): - # FIXME(dhellmann): Are commands tied too closely to the app? Or - # do commands know too much about apps by using them to get to the - # command manager? - stdout = StringIO() - app = application.App('testing', '1', - utils.TestCommandManager(utils.TEST_NAMESPACE), - stdout=stdout) - app.NAME = 'test' - help_cmd = help.HelpCommand(app, mock.Mock()) - parser = help_cmd.get_parser('test') - parsed_args = parser.parse_args(['z']) - try: - help_cmd.run(parsed_args) - except SystemExit: - pass - except ValueError: - pass - else: - assert False, 'Should have seen a ValueError' + def test_show_help_for_help(self): + # FIXME(dhellmann): Are commands tied too closely to the app? Or + # do commands know too much about apps by using them to get to the + # command manager? + stdout = StringIO() + app = application.App('testing', '1', + utils.TestCommandManager(utils.TEST_NAMESPACE), + stdout=stdout) + app.NAME = 'test' + app.options = mock.Mock() + help_cmd = help.HelpCommand(app, mock.Mock()) + parser = help_cmd.get_parser('test') + parsed_args = parser.parse_args([]) + try: + help_cmd.run(parsed_args) + except SystemExit: + pass + help_text = stdout.getvalue() + basecommand = os.path.split(sys.argv[0])[1] + self.assertIn('usage: %s [--version]' % basecommand, help_text) + self.assertIn('optional arguments:\n --version', help_text) + expected = ( + ' one Test command.\n' + ' three word command Test command.\n' + ) + self.assertIn(expected, help_text) + def test_list_deprecated_commands(self): + # FIXME(dhellmann): Are commands tied too closely to the app? Or + # do commands know too much about apps by using them to get to the + # command manager? + stdout = StringIO() + app = application.App('testing', '1', + utils.TestCommandManager(utils.TEST_NAMESPACE), + stdout=stdout) + app.NAME = 'test' + try: + app.run(['--help']) + except SystemExit: + pass + help_output = stdout.getvalue() + self.assertIn('two words', help_output) + self.assertIn('three word command', help_output) + self.assertNotIn('old cmd', help_output) -def test_show_help_for_help(): - # FIXME(dhellmann): Are commands tied too closely to the app? Or - # do commands know too much about apps by using them to get to the - # command manager? - stdout = StringIO() - app = application.App('testing', '1', - utils.TestCommandManager(utils.TEST_NAMESPACE), - stdout=stdout) - app.NAME = 'test' - app.options = mock.Mock() - help_cmd = help.HelpCommand(app, mock.Mock()) - parser = help_cmd.get_parser('test') - parsed_args = parser.parse_args([]) - try: - help_cmd.run(parsed_args) - except SystemExit: - pass - help_text = stdout.getvalue() - basecommand = os.path.split(sys.argv[0])[1] - assert 'usage: %s [--version]' % basecommand in help_text - assert 'optional arguments:\n --version' in help_text - expected = ( - ' one Test command.\n' - ' three word command Test command.\n' - ) - assert expected in help_text + @mock.patch.object(commandmanager.EntryPointWrapper, 'load', + side_effect=Exception('Could not load EntryPoint')) + def test_show_help_with_ep_load_fail(self, mock_load): + stdout = StringIO() + app = application.App('testing', '1', + utils.TestCommandManager(utils.TEST_NAMESPACE), + stdout=stdout) + app.NAME = 'test' + app.options = mock.Mock() + app.options.debug = False + help_cmd = help.HelpCommand(app, mock.Mock()) + parser = help_cmd.get_parser('test') + parsed_args = parser.parse_args([]) + try: + help_cmd.run(parsed_args) + except SystemExit: + pass + help_output = stdout.getvalue() + self.assertIn('Commands:', help_output) + self.assertIn('Could not load', help_output) + self.assertNotIn('Exception: Could not load EntryPoint', help_output) - -def test_list_deprecated_commands(): - # FIXME(dhellmann): Are commands tied too closely to the app? Or - # do commands know too much about apps by using them to get to the - # command manager? - stdout = StringIO() - app = application.App('testing', '1', - utils.TestCommandManager(utils.TEST_NAMESPACE), - stdout=stdout) - app.NAME = 'test' - try: - app.run(['--help']) - except SystemExit: - pass - help_output = stdout.getvalue() - assert 'two words' in help_output - assert 'three word command' in help_output - assert 'old cmd' not in help_output - - -@mock.patch.object(commandmanager.EntryPointWrapper, 'load', - side_effect=Exception('Could not load EntryPoint')) -def test_show_help_with_ep_load_fail(mock_load): - stdout = StringIO() - app = application.App('testing', '1', - utils.TestCommandManager(utils.TEST_NAMESPACE), - stdout=stdout) - app.NAME = 'test' - app.options = mock.Mock() - app.options.debug = False - help_cmd = help.HelpCommand(app, mock.Mock()) - parser = help_cmd.get_parser('test') - parsed_args = parser.parse_args([]) - try: - help_cmd.run(parsed_args) - except SystemExit: - pass - help_output = stdout.getvalue() - assert 'Commands:' in help_output - assert 'Could not load' in help_output - assert 'Exception: Could not load EntryPoint' not in help_output - - -@mock.patch.object(commandmanager.EntryPointWrapper, 'load', - side_effect=Exception('Could not load EntryPoint')) -def test_show_help_print_exc_with_ep_load_fail(mock_load): - stdout = StringIO() - app = application.App('testing', '1', - utils.TestCommandManager(utils.TEST_NAMESPACE), - stdout=stdout) - app.NAME = 'test' - app.options = mock.Mock() - app.options.debug = True - help_cmd = help.HelpCommand(app, mock.Mock()) - parser = help_cmd.get_parser('test') - parsed_args = parser.parse_args([]) - try: - help_cmd.run(parsed_args) - except SystemExit: - pass - help_output = stdout.getvalue() - assert 'Commands:' in help_output - assert 'Could not load' in help_output - assert 'Exception: Could not load EntryPoint' in help_output + @mock.patch.object(commandmanager.EntryPointWrapper, 'load', + side_effect=Exception('Could not load EntryPoint')) + def test_show_help_print_exc_with_ep_load_fail(self, mock_load): + stdout = StringIO() + app = application.App('testing', '1', + utils.TestCommandManager(utils.TEST_NAMESPACE), + stdout=stdout) + app.NAME = 'test' + app.options = mock.Mock() + app.options.debug = True + help_cmd = help.HelpCommand(app, mock.Mock()) + parser = help_cmd.get_parser('test') + parsed_args = parser.parse_args([]) + try: + help_cmd.run(parsed_args) + except SystemExit: + pass + help_output = stdout.getvalue() + self.assertIn('Commands:', help_output) + self.assertIn('Could not load', help_output) + self.assertIn('Exception: Could not load EntryPoint', help_output) diff --git a/cliff/tests/test_interactive.py b/cliff/tests/test_interactive.py index c18e7b3..8dfb5e9 100644 --- a/cliff/tests/test_interactive.py +++ b/cliff/tests/test_interactive.py @@ -15,69 +15,65 @@ import cmd2 from cliff.interactive import InteractiveApp +from cliff.tests import base class FakeApp(object): NAME = 'Fake' -def make_interactive_app(*command_names): - fake_command_manager = [(x, None) for x in command_names] - return InteractiveApp(FakeApp, fake_command_manager, - stdin=None, stdout=None) +class TestInteractive(base.TestBase): + def make_interactive_app(self, *command_names): + fake_command_manager = [(x, None) for x in command_names] + return InteractiveApp(FakeApp, fake_command_manager, + stdin=None, stdout=None) -def _test_completenames(expecteds, prefix): - app = make_interactive_app('hips', 'hippo', 'nonmatching') - assert set(app.completenames(prefix)) == set(expecteds) + def _test_completenames(self, expecteds, prefix): + app = self.make_interactive_app('hips', 'hippo', 'nonmatching') + self.assertEqual(set(app.completenames(prefix)), set(expecteds)) + def test_cmd2_completenames(self): + # cmd2.Cmd define do_help method + self._test_completenames(['help'], 'he') -def test_cmd2_completenames(): - # cmd2.Cmd define do_help method - _test_completenames(['help'], 'he') + def test_cliff_completenames(self): + self._test_completenames(['hips', 'hippo'], 'hip') + def test_no_completenames(self): + self._test_completenames([], 'taz') -def test_cliff_completenames(): - _test_completenames(['hips', 'hippo'], 'hip') + def test_both_completenames(self): + # cmd2.Cmd define do_history method + # NOTE(dtroyer): Before release 0.7.0 do_hi was also defined so we need + # to account for that in the list of possible responses. + # Remove this check after cmd2 0.7.0 is the minimum + # requirement. + if hasattr(cmd2.Cmd, "do_hi"): + self._test_completenames(['hi', 'history', 'hips', 'hippo'], 'hi') + else: + self._test_completenames(['history', 'hips', 'hippo'], 'hi') + def _test_completedefault(self, expecteds, line, begidx): + command_names = set(['show file', 'show folder', 'show long', + 'list all']) + app = self.make_interactive_app(*command_names) + observeds = app.completedefault(None, line, begidx, None) + self.assertEqual(set(expecteds), set(observeds)) + self.assertTrue( + set([line[:begidx] + x for x in observeds]) <= command_names + ) -def test_no_completenames(): - _test_completenames([], 'taz') + def test_empty_text_completedefault(self): + # line = 'show ' + begidx = 5 implies text = '' + self._test_completedefault(['file', 'folder', ' long'], 'show ', 5) + def test_nonempty_text_completedefault2(self): + # line = 'show f' + begidx = 6 implies text = 'f' + self._test_completedefault(['file', 'folder'], 'show f', 5) -def test_both_completenames(): - # cmd2.Cmd define do_history method - # NOTE(dtroyer): Before release 0.7.0 do_hi was also defined so we need - # to account for that in the list of possible responses. - # Remove this check after cmd2 0.7.0 is the minimum - # requirement. - if hasattr(cmd2.Cmd, "do_hi"): - _test_completenames(['hi', 'history', 'hips', 'hippo'], 'hi') - else: - _test_completenames(['history', 'hips', 'hippo'], 'hi') + def test_long_completedefault(self): + self._test_completedefault(['long'], 'show ', 6) - -def _test_completedefault(expecteds, line, begidx): - command_names = set(['show file', 'show folder', 'show long', 'list all']) - app = make_interactive_app(*command_names) - observeds = app.completedefault(None, line, begidx, None) - assert set(observeds) == set(expecteds) - assert set([line[:begidx] + x for x in observeds]) <= command_names - - -def test_empty_text_completedefault(): - # line = 'show ' + begidx = 5 implies text = '' - _test_completedefault(['file', 'folder', ' long'], 'show ', 5) - - -def test_nonempty_text_completedefault2(): - # line = 'show f' + begidx = 6 implies text = 'f' - _test_completedefault(['file', 'folder'], 'show f', 5) - - -def test_long_completedefault(): - _test_completedefault(['long'], 'show ', 6) - - -def test_no_completedefault(): - _test_completedefault([], 'taz ', 4) + def test_no_completedefault(self): + self._test_completedefault([], 'taz ', 4) diff --git a/cliff/tests/test_lister.py b/cliff/tests/test_lister.py index f2e4a0d..9434ce6 100644 --- a/cliff/tests/test_lister.py +++ b/cliff/tests/test_lister.py @@ -15,6 +15,7 @@ import weakref from cliff import lister +from cliff.tests import base import mock @@ -43,33 +44,33 @@ class ExerciseLister(lister.Lister): ) -def test_formatter_args(): - app = mock.Mock() - test_lister = ExerciseLister(app, []) +class TestLister(base.TestBase): - parsed_args = mock.Mock() - parsed_args.columns = ('Col1', 'Col2') - parsed_args.formatter = 'test' + def test_formatter_args(self): + app = mock.Mock() + test_lister = ExerciseLister(app, []) - test_lister.run(parsed_args) - f = test_lister._formatter_plugins['test'] - assert len(f.args) == 1 - args = f.args[0] - assert args[0] == list(parsed_args.columns) - data = list(args[1]) - assert data == [['a', 'A'], ['b', 'B']] + parsed_args = mock.Mock() + parsed_args.columns = ('Col1', 'Col2') + parsed_args.formatter = 'test' + test_lister.run(parsed_args) + f = test_lister._formatter_plugins['test'] + self.assertEqual(1, len(f.args)) + args = f.args[0] + self.assertEqual(list(parsed_args.columns), args[0]) + data = list(args[1]) + self.assertEqual([['a', 'A'], ['b', 'B']], data) -def test_no_exist_column(): - test_lister = ExerciseLister(mock.Mock(), []) - parsed_args = mock.Mock() - parsed_args.columns = ('no_exist_column',) - parsed_args.formatter = 'test' - with mock.patch.object(test_lister, 'take_action') as mock_take_action: - mock_take_action.return_value = (('Col1', 'Col2', 'Col3'), []) - try: - test_lister.run(parsed_args) - except ValueError: - pass - else: - assert False, 'Should have had an exception' + def test_no_exist_column(self): + test_lister = ExerciseLister(mock.Mock(), []) + parsed_args = mock.Mock() + parsed_args.columns = ('no_exist_column',) + parsed_args.formatter = 'test' + with mock.patch.object(test_lister, 'take_action') as mock_take_action: + mock_take_action.return_value = (('Col1', 'Col2', 'Col3'), []) + self.assertRaises( + ValueError, + test_lister.run, + parsed_args, + ) diff --git a/cliff/tests/test_show.py b/cliff/tests/test_show.py index 9e46c54..ab185e2 100644 --- a/cliff/tests/test_show.py +++ b/cliff/tests/test_show.py @@ -15,6 +15,7 @@ import weakref from cliff import show +from cliff.tests import base import mock @@ -43,42 +44,41 @@ class ExerciseShowOne(show.ShowOne): ) -def test_formatter_args(): - app = mock.Mock() - test_show = ExerciseShowOne(app, []) +class TestShow(base.TestBase): - parsed_args = mock.Mock() - parsed_args.columns = ('Col1', 'Col2') - parsed_args.formatter = 'test' + def test_formatter_args(self): + app = mock.Mock() + test_show = ExerciseShowOne(app, []) - test_show.run(parsed_args) - f = test_show._formatter_plugins['test'] - assert len(f.args) == 1 - args = f.args[0] - assert args[0] == list(parsed_args.columns) - data = list(args[1]) - assert data == [('a', 'A'), ('b', 'B')] + parsed_args = mock.Mock() + parsed_args.columns = ('Col1', 'Col2') + parsed_args.formatter = 'test' + test_show.run(parsed_args) + f = test_show._formatter_plugins['test'] + self.assertEqual(1, len(f.args)) + args = f.args[0] + self.assertEqual(list(parsed_args.columns), args[0]) + data = list(args[1]) + self.assertEqual([('a', 'A'), ('b', 'B')], data) -def test_dict2columns(): - app = mock.Mock() - test_show = ExerciseShowOne(app, []) - d = {'a': 'A', 'b': 'B', 'c': 'C'} - expected = [('a', 'b', 'c'), ('A', 'B', 'C')] - actual = list(test_show.dict2columns(d)) - assert expected == actual + def test_dict2columns(self): + app = mock.Mock() + test_show = ExerciseShowOne(app, []) + d = {'a': 'A', 'b': 'B', 'c': 'C'} + expected = [('a', 'b', 'c'), ('A', 'B', 'C')] + actual = list(test_show.dict2columns(d)) + self.assertEqual(expected, actual) - -def test_no_exist_column(): - test_show = ExerciseShowOne(mock.Mock(), []) - parsed_args = mock.Mock() - parsed_args.columns = ('no_exist_column',) - parsed_args.formatter = 'test' - with mock.patch.object(test_show, 'take_action') as mock_take_action: - mock_take_action.return_value = (('Col1', 'Col2', 'Col3'), []) - try: - test_show.run(parsed_args) - except ValueError: - pass - else: - assert False, 'Should have had an exception' + def test_no_exist_column(self): + test_show = ExerciseShowOne(mock.Mock(), []) + parsed_args = mock.Mock() + parsed_args.columns = ('no_exist_column',) + parsed_args.formatter = 'test' + with mock.patch.object(test_show, 'take_action') as mock_take_action: + mock_take_action.return_value = (('Col1', 'Col2', 'Col3'), []) + self.assertRaises( + ValueError, + test_show.run, + parsed_args, + ) diff --git a/cliff/tests/test_sphinxext.py b/cliff/tests/test_sphinxext.py index fe271fa..2720d22 100644 --- a/cliff/tests/test_sphinxext.py +++ b/cliff/tests/test_sphinxext.py @@ -15,92 +15,91 @@ import argparse import textwrap -from nose.tools import assert_equals - from cliff import sphinxext +from cliff.tests import base -def test_empty_help(): - """Handle positional and optional actions without help messages.""" - parser = argparse.ArgumentParser(prog='hello-world', add_help=False) - parser.add_argument('name', action='store') - parser.add_argument('--language', dest='lang') +class TestSphinxExtension(base.TestBase): - output = '\n'.join(sphinxext._format_parser(parser)) - assert_equals(textwrap.dedent(""" - .. program:: hello-world - .. code-block:: shell + def test_empty_help(self): + """Handle positional and optional actions without help messages.""" + parser = argparse.ArgumentParser(prog='hello-world', add_help=False) + parser.add_argument('name', action='store') + parser.add_argument('--language', dest='lang') - hello-world [--language LANG] name + output = '\n'.join(sphinxext._format_parser(parser)) + self.assertEqual(textwrap.dedent(""" + .. program:: hello-world + .. code-block:: shell - .. option:: --language + hello-world [--language LANG] name - .. option:: name - """).lstrip(), output) + .. option:: --language + + .. option:: name + """).lstrip(), output) + + def test_nonempty_help(self): + """Handle positional and optional actions with help messages.""" + parser = argparse.ArgumentParser(prog='hello-world', add_help=False) + parser.add_argument('name', help='user name') + parser.add_argument('--language', dest='lang', + help='greeting language') + + output = '\n'.join(sphinxext._format_parser(parser)) + self.assertEqual(textwrap.dedent(""" + .. program:: hello-world + .. code-block:: shell + + hello-world [--language LANG] name + + .. option:: --language + + greeting language + + .. option:: name + + user name + """).lstrip(), output) + + def test_flag(self): + """Handle a boolean argparse action.""" + parser = argparse.ArgumentParser(prog='hello-world', add_help=False) + parser.add_argument('name', help='user name') + parser.add_argument('--translate', action='store_true', + help='translate to local language') + + output = '\n'.join(sphinxext._format_parser(parser)) + self.assertEqual(textwrap.dedent(""" + .. program:: hello-world + .. code-block:: shell + + hello-world [--translate] name + + .. option:: --translate + + translate to local language + + .. option:: name + + user name + """).lstrip(), output) + + def test_supressed(self): + """Handle a supressed action.""" + parser = argparse.ArgumentParser(prog='hello-world', add_help=False) + parser.add_argument('name', help='user name') + parser.add_argument('--variable', help=argparse.SUPPRESS) + + output = '\n'.join(sphinxext._format_parser(parser)) + self.assertEqual(textwrap.dedent(""" + .. program:: hello-world + .. code-block:: shell + + hello-world name -def test_nonempty_help(): - """Handle positional and optional actions with help messages.""" - parser = argparse.ArgumentParser(prog='hello-world', add_help=False) - parser.add_argument('name', help='user name') - parser.add_argument('--language', dest='lang', help='greeting language') + .. option:: name - output = '\n'.join(sphinxext._format_parser(parser)) - assert_equals(textwrap.dedent(""" - .. program:: hello-world - .. code-block:: shell - - hello-world [--language LANG] name - - .. option:: --language - - greeting language - - .. option:: name - - user name - """).lstrip(), output) - - -def test_flag(): - """Handle a boolean argparse action.""" - parser = argparse.ArgumentParser(prog='hello-world', add_help=False) - parser.add_argument('name', help='user name') - parser.add_argument('--translate', action='store_true', - help='translate to local language') - - output = '\n'.join(sphinxext._format_parser(parser)) - assert_equals(textwrap.dedent(""" - .. program:: hello-world - .. code-block:: shell - - hello-world [--translate] name - - .. option:: --translate - - translate to local language - - .. option:: name - - user name - """).lstrip(), output) - - -def test_supressed(): - """Handle a supressed action.""" - parser = argparse.ArgumentParser(prog='hello-world', add_help=False) - parser.add_argument('name', help='user name') - parser.add_argument('--variable', help=argparse.SUPPRESS) - - output = '\n'.join(sphinxext._format_parser(parser)) - assert_equals(textwrap.dedent(""" - .. program:: hello-world - .. code-block:: shell - - hello-world name - - - .. option:: name - - user name - """).lstrip(), output) + user name + """).lstrip(), output) diff --git a/cliff/tests/test_utils.py b/cliff/tests/test_utils.py index 23c46c4..9d6b1f6 100644 --- a/cliff/tests/test_utils.py +++ b/cliff/tests/test_utils.py @@ -15,65 +15,63 @@ import os import struct import sys +import unittest import mock -import nose from cliff import utils +from cliff.tests import base -def test_utils_terminal_width(): - width = utils.terminal_width(sys.stdout) - # Results are specific to the execution environment, so only assert - # that no error is raised. - assert width is None or isinstance(width, int) +class TestTerminalWidth(base.TestBase): + def test(self): + width = utils.terminal_width(sys.stdout) + # Results are specific to the execution environment, so only assert + # that no error is raised. + if width is not None: + self.assertIsInstance(width, int) -@mock.patch('cliff.utils.os') -def test_utils_terminal_width_get_terminal_size(mock_os): - if not hasattr(os, 'get_terminal_size'): - raise nose.SkipTest('only needed for python 3.3 onwards') - ts = os.terminal_size((10, 5)) - mock_os.get_terminal_size.return_value = ts - width = utils.terminal_width(sys.stdout) - assert width == 10 + @unittest.skipIf(not hasattr(os, 'get_terminal_size'), + 'only needed for python 3.3 onwards') + @mock.patch('cliff.utils.os') + def test_get_terminal_size(self, mock_os): + ts = os.terminal_size((10, 5)) + mock_os.get_terminal_size.return_value = ts + width = utils.terminal_width(sys.stdout) + self.assertEqual(10, width) + mock_os.get_terminal_size.side_effect = OSError() + width = utils.terminal_width(sys.stdout) + self.assertIs(None, width) - mock_os.get_terminal_size.side_effect = OSError() - width = utils.terminal_width(sys.stdout) - assert width is None + @unittest.skipIf(hasattr(os, 'get_terminal_size'), + 'only needed for python 3.2 and before') + @mock.patch('fcntl.ioctl') + def test_ioctl(self, mock_ioctl): + mock_ioctl.return_value = struct.pack('hhhh', 57, 101, 0, 0) + width = utils.terminal_width(sys.stdout) + self.assertEqual(101, width) + mock_ioctl.side_effect = IOError() + width = utils.terminal_width(sys.stdout) + self.assertIs(None, width) + @unittest.skipIf(hasattr(os, 'get_terminal_size'), + 'only needed for python 3.2 and before') + @mock.patch('cliff.utils.ctypes') + @mock.patch('sys.platform', 'win32') + def test_windows(self, mock_ctypes): + mock_ctypes.create_string_buffer.return_value.raw = struct.pack( + 'hhhhHhhhhhh', 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + mock_ctypes.windll.kernel32.GetStdHandle.return_value = -11 + mock_ctypes.windll.kernel32.GetConsoleScreenBufferInfo.return_value = 1 -@mock.patch('fcntl.ioctl') -def test_utils_terminal_width_ioctl(mock_ioctl): - if hasattr(os, 'get_terminal_size'): - raise nose.SkipTest('only needed for python 3.2 and before') - mock_ioctl.return_value = struct.pack('hhhh', 57, 101, 0, 0) - width = utils.terminal_width(sys.stdout) - assert width == 101 + width = utils.terminal_width(sys.stdout) + self.assertEqual(101, width) - mock_ioctl.side_effect = IOError() - width = utils.terminal_width(sys.stdout) - assert width is None + mock_ctypes.windll.kernel32.GetConsoleScreenBufferInfo.return_value = 0 + width = utils.terminal_width(sys.stdout) + self.assertIs(None, width) -@mock.patch('cliff.utils.ctypes') -@mock.patch('sys.platform', 'win32') -def test_utils_terminal_width_windows(mock_ctypes): - if hasattr(os, 'get_terminal_size'): - raise nose.SkipTest('only needed for python 3.2 and before') - - mock_ctypes.create_string_buffer.return_value.raw = struct.pack( - 'hhhhHhhhhhh', 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - mock_ctypes.windll.kernel32.GetStdHandle.return_value = -11 - mock_ctypes.windll.kernel32.GetConsoleScreenBufferInfo.return_value = 1 - - width = utils.terminal_width(sys.stdout) - assert width == 101 - - mock_ctypes.windll.kernel32.GetConsoleScreenBufferInfo.return_value = 0 - - width = utils.terminal_width(sys.stdout) - assert width is None - - width = utils.terminal_width('foo') - assert width is None + width = utils.terminal_width('foo') + self.assertIs(None, width) diff --git a/test-requirements.txt b/test-requirements.txt index a80bfd6..ee61629 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,9 +1,15 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -nose # LGPL + +python-subunit>=0.0.18 # Apache-2.0/BSD +testrepository>=0.0.18 # Apache-2.0/BSD +testtools>=1.4.0 # MIT mock>=2.0 # BSD +testscenarios>=0.4 # Apache-2.0/BSD + coverage>=4.0 # Apache-2.0 + # this is required for the docs build jobs sphinx>=1.5.1 # BSD oslosphinx>=4.7.0 # Apache-2.0 diff --git a/tox.ini b/tox.ini index 5740170..470da53 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] minversion = 2.0 -envlist = py35,py27,pypy,pep8 +envlist = py35,py27,pep8 [testenv] setenv = @@ -11,7 +11,9 @@ passenv = ZUUL_CACHE_DIR distribute = False install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages} -commands = nosetests -d --with-coverage --cover-inclusive --cover-package cliff [] +commands = + python setup.py test --coverage --coverage-package-name=cliff --slowest --testr-args='{posargs}' + coverage report --show-missing deps = -r{toxinidir}/test-requirements.txt [testenv:pep8]