#!/usr/bin/env python # # 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 argparse import os import textwrap from io import StringIO from unittest import mock from cliff.formatters import table from cliff.tests import base from cliff.tests import test_columns class args(object): def __init__(self, max_width=0, print_empty=False, fit_width=False): self.fit_width = fit_width if max_width > 0: self.max_width = max_width else: # Envvar is only taken into account iff CLI parameter not given self.max_width = int(os.environ.get('CLIFF_MAX_TERM_WIDTH', 0)) self.print_empty = print_empty def _table_tester_helper(tags, data, extra_args=None): """Get table output as a string, formatted according to CLI arguments, environment variables and terminal size tags - tuple of strings for data tags (column headers or fields) data - tuple of strings for single data row - list of tuples of strings for multiple rows of data extra_args - an instance of class args - a list of strings for CLI arguments """ sf = table.TableFormatter() if extra_args is None: # Default to no CLI arguments parsed_args = args() elif type(extra_args) == args: # Use the given CLI arguments parsed_args = extra_args else: # Parse arguments as if passed on the command-line parser = argparse.ArgumentParser(description='Testing...') sf.add_argument_group(parser) parsed_args = parser.parse_args(extra_args) output = StringIO() emitter = sf.emit_list if type(data) is list else sf.emit_one emitter(tags, data, output, parsed_args) return output.getvalue() 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(fit_width=True)), ) @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, extra_args=['--fit-width']), ) @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, extra_args=['--fit-width']), ) @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, extra_args=['--fit-width']), ) 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 width = tw.return_value = 80 self.assertEqual( self._expected_mv[width], _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 width = tw.return_value = 50 actual = _table_tester_helper(self._col_names, self._col_data, extra_args=['--fit-width']) self.assertEqual(self._expected_mv[width], actual) self.assertEqual(width, len(actual.splitlines()[0])) @mock.patch('cliff.utils.terminal_width') def test_max_width_45(self, tw): # resize 2 columns width = tw.return_value = 45 actual = _table_tester_helper(self._col_names, self._col_data, extra_args=['--fit-width']) self.assertEqual(self._expected_mv[width], actual) self.assertEqual(width, len(actual.splitlines()[0])) @mock.patch('cliff.utils.terminal_width') def test_max_width_40(self, tw): # resize all columns width = tw.return_value = 40 actual = _table_tester_helper(self._col_names, self._col_data, extra_args=['--fit-width']) self.assertEqual(self._expected_mv[width], actual) self.assertEqual(width, 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 width = tw.return_value = 10 actual = _table_tester_helper(self._col_names, self._col_data, extra_args=['--fit-width']) self.assertEqual(self._expected_mv[width], 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)))