| 1 | #!/usr/bin/env python2
 | 
| 2 | """display_test.py: Tests for display.py."""
 | 
| 3 | from __future__ import print_function
 | 
| 4 | 
 | 
| 5 | import cStringIO
 | 
| 6 | import sys
 | 
| 7 | import unittest
 | 
| 8 | 
 | 
| 9 | from core import comp_ui  # module under test
 | 
| 10 | from core import pyos
 | 
| 11 | from core import util
 | 
| 12 | 
 | 
| 13 | import line_input
 | 
| 14 | 
 | 
| 15 | 
 | 
| 16 | class VisualTest(unittest.TestCase):
 | 
| 17 | 
 | 
| 18 |     def testPrintPacked(self):
 | 
| 19 |         matches = ['foo', 'bar', 'spam', 'eggs', 'python', 'perl']
 | 
| 20 |         longest_match_len = max(len(m) for m in matches)
 | 
| 21 |         max_lines = 10
 | 
| 22 | 
 | 
| 23 |         for width in (1, 10, 20, 30, 40, 50):
 | 
| 24 |             f = cStringIO.StringIO()
 | 
| 25 |             n = comp_ui._PrintPacked(matches, longest_match_len, width,
 | 
| 26 |                                      max_lines, f)
 | 
| 27 | 
 | 
| 28 |             out = f.getvalue()
 | 
| 29 |             lines = out.splitlines()
 | 
| 30 |             max_len = max(len(line) for line in lines)
 | 
| 31 | 
 | 
| 32 |             print('WIDTH = %d' % width)
 | 
| 33 |             print('reported lines = %d' % n)
 | 
| 34 |             print('measured lines = %d' % len(lines))
 | 
| 35 |             print('max_len = %d' % max_len)
 | 
| 36 | 
 | 
| 37 |             # Make sure it fits in the width!
 | 
| 38 | 
 | 
| 39 |             # NOTE: This is too conservative because of non-printable characters
 | 
| 40 |             # like \n and ANSI escapes
 | 
| 41 |             if width != 1:
 | 
| 42 |                 self.assertLess(max_len, width)
 | 
| 43 | 
 | 
| 44 |             print('')
 | 
| 45 | 
 | 
| 46 |     def testLongStringAndSkinnyTerminal(self):
 | 
| 47 |         matches = ['foo' * 10, 'bar' * 10, 'spams' * 10, 'zzz']
 | 
| 48 |         longest_match_len = max(len(m) for m in matches)
 | 
| 49 | 
 | 
| 50 |         for width in (1, 10, 20, 30, 40, 50):
 | 
| 51 |             f = cStringIO.StringIO()
 | 
| 52 |             n = comp_ui._PrintPacked(matches, longest_match_len, width, 10, f)
 | 
| 53 | 
 | 
| 54 |             out = f.getvalue()
 | 
| 55 |             lines = out.splitlines()
 | 
| 56 |             max_len = max(len(line) for line in lines)
 | 
| 57 | 
 | 
| 58 |             print('WIDTH = %d' % width)
 | 
| 59 |             print('reported lines = %d' % n)
 | 
| 60 |             print('measured lines = %d' % len(lines))
 | 
| 61 |             print('max_len = %d' % max_len)
 | 
| 62 |             print('')
 | 
| 63 | 
 | 
| 64 |     def testTooMany(self):
 | 
| 65 |         matches = ['--flag%d' % i for i in xrange(100)]
 | 
| 66 |         longest_match_len = max(len(m) for m in matches)
 | 
| 67 | 
 | 
| 68 |         for width in (1, 10, 20, 30, 40, 50, 60):
 | 
| 69 |             f = cStringIO.StringIO()
 | 
| 70 |             n = comp_ui._PrintPacked(matches, longest_match_len, width, 10, f)
 | 
| 71 | 
 | 
| 72 |             out = f.getvalue()
 | 
| 73 |             lines = out.splitlines()
 | 
| 74 |             max_len = max(len(line) for line in lines)
 | 
| 75 | 
 | 
| 76 |             print('WIDTH = %d' % width)
 | 
| 77 |             print('reported lines = %d' % n)
 | 
| 78 |             # Count newlines since last one doesn't have a newline
 | 
| 79 |             print('measured lines = %d' % out.count('\n'))
 | 
| 80 |             print('max_len = %d' % max_len)
 | 
| 81 |             print('')
 | 
| 82 | 
 | 
| 83 |             #print(out)
 | 
| 84 | 
 | 
| 85 |     def testPrintLong(self):
 | 
| 86 |         matches = ['--all', '--almost-all', '--verbose']
 | 
| 87 |         longest_match_len = max(len(m) for m in matches)
 | 
| 88 |         descriptions = {
 | 
| 89 |             '--all': 'show all ' * 10,  # truncate
 | 
| 90 |             '--almost-all': 'foo',
 | 
| 91 |             '--verbose': 'bar'
 | 
| 92 |         }
 | 
| 93 | 
 | 
| 94 |         max_lines = 10
 | 
| 95 |         for width in (1, 10, 20, 30, 40, 50, 60):
 | 
| 96 |             f = cStringIO.StringIO()
 | 
| 97 |             n = comp_ui._PrintLong(matches, longest_match_len, width,
 | 
| 98 |                                    max_lines, descriptions, f)
 | 
| 99 | 
 | 
| 100 |             out = f.getvalue()
 | 
| 101 |             lines = out.splitlines()
 | 
| 102 |             max_len = max(len(line) for line in lines)
 | 
| 103 | 
 | 
| 104 |             print('WIDTH = %d' % width)
 | 
| 105 |             print('reported lines = %d' % n)
 | 
| 106 |             # Count newlines since last one doesn't have a newline
 | 
| 107 |             print('measured lines = %d' % out.count('\n'))
 | 
| 108 |             print('max_len = %d' % max_len)
 | 
| 109 |             print('')
 | 
| 110 | 
 | 
| 111 |             #print(out)
 | 
| 112 | 
 | 
| 113 | 
 | 
| 114 | class UiTest(unittest.TestCase):
 | 
| 115 | 
 | 
| 116 |     def testDisplays(self):
 | 
| 117 |         comp_ui_state = comp_ui.State()
 | 
| 118 |         prompt_state = comp_ui.PromptState()
 | 
| 119 |         debug_f = util.DebugFile(sys.stdout)
 | 
| 120 |         signal_safe = pyos.InitSignalSafe()
 | 
| 121 | 
 | 
| 122 |         # terminal width
 | 
| 123 |         d1 = comp_ui.NiceDisplay(80, comp_ui_state, prompt_state, debug_f,
 | 
| 124 |                                  line_input, signal_safe)
 | 
| 125 |         d2 = comp_ui.MinimalDisplay(comp_ui_state, prompt_state, debug_f)
 | 
| 126 | 
 | 
| 127 |         prompt_state.SetLastPrompt('$ ')
 | 
| 128 | 
 | 
| 129 |         for disp in [d1, d2]:
 | 
| 130 |             # This one is important
 | 
| 131 |             disp.EraseLines()
 | 
| 132 |             disp.Reset()
 | 
| 133 | 
 | 
| 134 |             # These are related but we can just set them separately.
 | 
| 135 |             comp_ui_state.line_until_tab = 'echo '  # for returning to the prompt
 | 
| 136 |             comp_ui_state.display_pos = 5  # Strip this off every candidate
 | 
| 137 | 
 | 
| 138 |             disp.PrintRequired('hello')
 | 
| 139 |             disp.PrintOptional('hello')
 | 
| 140 | 
 | 
| 141 |             matches = ['echo one', 'echo two']
 | 
| 142 |             disp.PrintCandidates(None, matches, None)
 | 
| 143 | 
 | 
| 144 |             # This needs to be aware of the terminal width.
 | 
| 145 |             # It's a bit odd since it's called as a side effect of the PromptEvaluator.
 | 
| 146 |             # That class knows about styles and so forth.
 | 
| 147 | 
 | 
| 148 |             disp.ShowPromptOnRight('RIGHT')
 | 
| 149 | 
 | 
| 150 | 
 | 
| 151 | class PromptTest(unittest.TestCase):
 | 
| 152 | 
 | 
| 153 |     def testNoEscapes(self):
 | 
| 154 |         for prompt in ["> ", "osh>", "[[]][[]][][]]][["]:
 | 
| 155 |             self.assertEqual(comp_ui._PromptLen(prompt), len(prompt))
 | 
| 156 | 
 | 
| 157 |     def testValidEscapes(self):
 | 
| 158 |         self.assertEqual(
 | 
| 159 |             comp_ui._PromptLen("\x01\033[01;34m\x02user\x01\033[00m\x02 >"),
 | 
| 160 |             len("user >"))
 | 
| 161 |         self.assertEqual(comp_ui._PromptLen("\x01\x02\x01\x02\x01\x02"), 0)
 | 
| 162 |         self.assertEqual(
 | 
| 163 |             comp_ui._PromptLen("\x01\x02 hi \x01hi\x02 \x01\x02 hello"),
 | 
| 164 |             len(" hi   hello"))
 | 
| 165 | 
 | 
| 166 |     def testNewline(self):
 | 
| 167 |         self.assertEqual(comp_ui._PromptLen("\n"), 0)
 | 
| 168 |         self.assertEqual(comp_ui._PromptLen("abc\ndef"), 3)
 | 
| 169 |         self.assertEqual(comp_ui._PromptLen(""), 0)
 | 
| 170 | 
 | 
| 171 |     def testControlCharacters(self):
 | 
| 172 |         self.assertEqual(comp_ui._PromptLen("\xef"), 1)
 | 
| 173 |         self.assertEqual(comp_ui._PromptLen("\x03\x05"), 2)
 | 
| 174 | 
 | 
| 175 | 
 | 
| 176 | if __name__ == '__main__':
 | 
| 177 |     unittest.main()
 |