# Copyright 2021 The Pigweed Authors # # 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 # # https://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. """Tests for pw_console.text_formatting""" import unittest from parameterized import parameterized # type: ignore from prompt_toolkit.formatted_text import ANSI from pw_console.text_formatting import ( get_line_height, insert_linebreaks, split_lines, ) class TestTextFormatting(unittest.TestCase): """Tests for manipulating prompt_toolkit formatted text tuples.""" def setUp(self): self.maxDiff = None # pylint: disable=invalid-name @parameterized.expand([ ( 'with short prefix height 2', len('LINE that should be wrapped'), # text_width len('| |'), # screen_width len('--->'), # prefix_width ( 'LINE that should b\n' '--->e wrapped \n').count('\n'), # expected_height len( '_____'), # expected_trailing_characters ), ( 'with short prefix height 3', len('LINE that should be wrapped three times.'), # text_width len('| |'), # screen_width len('--->'), # prefix_width ( 'LINE that should b\n' '--->e wrapped thre\n' '--->e times. \n').count('\n'), # expected_height len( '______'), # expected_trailing_characters ), ( 'with short prefix height 4', len('LINE that should be wrapped even more times, say four.'), len('| |'), # screen_width len('--->'), # prefix_width ( 'LINE that should b\n' '--->e wrapped even\n' '---> more times, s\n' '--->ay four. \n').count('\n'), # expected_height len( '______'), # expected_trailing_characters ), ( 'no wrapping needed', len('LINE wrapped'), # text_width len('| |'), # screen_width len('--->'), # prefix_width ( 'LINE wrapped \n').count('\n'), # expected_height len( '______'), # expected_trailing_characters ), ( 'prefix is > screen width', len('LINE that should be wrapped'), # text_width len('| |'), # screen_width len('------------------>'), # prefix_width ( 'LINE that should b\n' 'e wrapped \n').count('\n'), # expected_height len( '_________'), # expected_trailing_characters ), ( 'prefix is == screen width', len('LINE that should be wrapped'), # text_width len('| |'), # screen_width len('----------------->'), # prefix_width ( 'LINE that should b\n' 'e wrapped \n').count('\n'), # expected_height len( '_________'), # expected_trailing_characters ), ]) # yapf: disable def test_get_line_height(self, _name, text_width, screen_width, prefix_width, expected_height, expected_trailing_characters) -> None: """Test line height calculations.""" height, remaining_width = get_line_height(text_width, screen_width, prefix_width) self.assertEqual(height, expected_height) self.assertEqual(remaining_width, expected_trailing_characters) # pylint: disable=line-too-long @parameterized.expand([ ( 'One line with ANSI escapes and no included breaks', 12, # screen_width False, # truncate_long_lines 'Lorem ipsum \x1b[34m\x1b[1mdolor sit amet\x1b[0m, consectetur adipiscing elit.', # message ANSI( # Line 1 'Lorem ipsum \n' # Line 2 '\x1b[34m\x1b[1m' # zero width 'dolor sit am\n' # Line 3 'et' '\x1b[0m' # zero width ', consecte\n' # Line 4 'tur adipisci\n' # Line 5 'ng elit.\n').__pt_formatted_text__(), 5, # expected_height ), ( 'One line with ANSI escapes and included breaks', 12, # screen_width False, # truncate_long_lines 'Lorem\n ipsum \x1b[34m\x1b[1mdolor sit amet\x1b[0m, consectetur adipiscing elit.', # message ANSI( # Line 1 'Lorem\n' # Line 2 ' ipsum \x1b[34m\x1b[1mdolor\n' # Line 3 ' sit amet\x1b[0m, c\n' # Line 4 'onsectetur a\n' # Line 5 'dipiscing el\n' # Line 6 'it.\n' ).__pt_formatted_text__(), 6, # expected_height ), ( 'One line with ANSI escapes and included breaks; truncate lines enabled', 12, # screen_width True, # truncate_long_lines 'Lorem\n ipsum dolor sit amet, consectetur adipiscing \nelit.\n', # message ANSI( # Line 1 'Lorem\n' # Line 2 ' ipsum dolor\n' # Line 3 'elit.\n' ).__pt_formatted_text__(), 3, # expected_height ), ( 'wrapping enabled with a line break just after screen_width', 10, # screen_width False, # truncate_long_lines '01234567890\nTest Log\n', # message ANSI( '0123456789\n' '0\n' 'Test Log\n' ).__pt_formatted_text__(), 3, # expected_height ), ( 'log message with a line break at screen_width', 10, # screen_width True, # truncate_long_lines '0123456789\nTest Log\n', # message ANSI( '0123456789\n' 'Test Log\n' ).__pt_formatted_text__(), 2, # expected_height ), ]) # yapf: disable # pylint: enable=line-too-long def test_insert_linebreaks( self, _name, screen_width, truncate_long_lines, raw_text, expected_fragments, expected_height, ) -> None: """Test inserting linebreaks to wrap lines.""" formatted_text = ANSI(raw_text).__pt_formatted_text__() fragments, line_height = insert_linebreaks( formatted_text, max_line_width=screen_width, truncate_long_lines=truncate_long_lines) self.assertEqual(fragments, expected_fragments) self.assertEqual(line_height, expected_height) @parameterized.expand([ ( 'flattened split', ANSI( 'Lorem\n' ' ipsum dolor\n' 'elit.\n' ).__pt_formatted_text__(), [ ANSI('Lorem').__pt_formatted_text__(), ANSI(' ipsum dolor').__pt_formatted_text__(), ANSI('elit.').__pt_formatted_text__(), ], # expected_lines ), ( 'split fragments from insert_linebreaks', insert_linebreaks( ANSI( 'Lorem\n ipsum dolor sit amet, consectetur adipiscing elit.' ).__pt_formatted_text__(), max_line_width=15, # [0] for the fragments, [1] is line_height truncate_long_lines=False)[0], [ ANSI('Lorem').__pt_formatted_text__(), ANSI(' ipsum dolor si').__pt_formatted_text__(), ANSI('t amet, consect').__pt_formatted_text__(), ANSI('etur adipiscing').__pt_formatted_text__(), ANSI(' elit.').__pt_formatted_text__(), ], ), ( 'empty lines', # Each line should have at least one StyleAndTextTuple but without # an ending line break. [ ('', '\n'), ('', '\n'), ], [ [('', '')], [('', '')], ], ) ]) # yapf: disable def test_split_lines( self, _name, input_fragments, expected_lines, ) -> None: """Test splitting flattened StyleAndTextTuples into a list of lines.""" result_lines = split_lines(input_fragments) self.assertEqual(result_lines, expected_lines) if __name__ == '__main__': unittest.main()