1"Test outwin, coverage 76%."
2
3from idlelib import outwin
4import unittest
5from test.support import requires
6from tkinter import Tk, Text
7from idlelib.idle_test.mock_tk import Mbox_func
8from idlelib.idle_test.mock_idle import Func
9from unittest import mock
10
11
12class OutputWindowTest(unittest.TestCase):
13
14    @classmethod
15    def setUpClass(cls):
16        requires('gui')
17        root = cls.root = Tk()
18        root.withdraw()
19        w = cls.window = outwin.OutputWindow(None, None, None, root)
20        cls.text = w.text = Text(root)
21
22    @classmethod
23    def tearDownClass(cls):
24        cls.window.close()
25        del cls.text, cls.window
26        cls.root.destroy()
27        del cls.root
28
29    def setUp(self):
30        self.text.delete('1.0', 'end')
31
32    def test_ispythonsource(self):
33        # OutputWindow overrides ispythonsource to always return False.
34        w = self.window
35        self.assertFalse(w.ispythonsource('test.txt'))
36        self.assertFalse(w.ispythonsource(__file__))
37
38    def test_window_title(self):
39        self.assertEqual(self.window.top.title(), 'Output')
40
41    def test_maybesave(self):
42        w = self.window
43        eq = self.assertEqual
44        w.get_saved = Func()
45
46        w.get_saved.result = False
47        eq(w.maybesave(), 'no')
48        eq(w.get_saved.called, 1)
49
50        w.get_saved.result = True
51        eq(w.maybesave(), 'yes')
52        eq(w.get_saved.called, 2)
53        del w.get_saved
54
55    def test_write(self):
56        eq = self.assertEqual
57        delete = self.text.delete
58        get = self.text.get
59        write = self.window.write
60
61        # No new line - insert stays on same line.
62        delete('1.0', 'end')
63        test_text = 'test text'
64        eq(write(test_text), len(test_text))
65        eq(get('1.0', '1.end'), 'test text')
66        eq(get('insert linestart', 'insert lineend'), 'test text')
67
68        # New line - insert moves to next line.
69        delete('1.0', 'end')
70        test_text = 'test text\n'
71        eq(write(test_text), len(test_text))
72        eq(get('1.0', '1.end'), 'test text')
73        eq(get('insert linestart', 'insert lineend'), '')
74
75        # Text after new line is tagged for second line of Text widget.
76        delete('1.0', 'end')
77        test_text = 'test text\nLine 2'
78        eq(write(test_text), len(test_text))
79        eq(get('1.0', '1.end'), 'test text')
80        eq(get('2.0', '2.end'), 'Line 2')
81        eq(get('insert linestart', 'insert lineend'), 'Line 2')
82
83        # Test tags.
84        delete('1.0', 'end')
85        test_text = 'test text\n'
86        test_text2 = 'Line 2\n'
87        eq(write(test_text, tags='mytag'), len(test_text))
88        eq(write(test_text2, tags='secondtag'), len(test_text2))
89        eq(get('mytag.first', 'mytag.last'), test_text)
90        eq(get('secondtag.first', 'secondtag.last'), test_text2)
91        eq(get('1.0', '1.end'), test_text.rstrip('\n'))
92        eq(get('2.0', '2.end'), test_text2.rstrip('\n'))
93
94    def test_writelines(self):
95        eq = self.assertEqual
96        get = self.text.get
97        writelines = self.window.writelines
98
99        writelines(('Line 1\n', 'Line 2\n', 'Line 3\n'))
100        eq(get('1.0', '1.end'), 'Line 1')
101        eq(get('2.0', '2.end'), 'Line 2')
102        eq(get('3.0', '3.end'), 'Line 3')
103        eq(get('insert linestart', 'insert lineend'), '')
104
105    def test_goto_file_line(self):
106        eq = self.assertEqual
107        w = self.window
108        text = self.text
109
110        w.flist = mock.Mock()
111        gfl = w.flist.gotofileline = Func()
112        showerror = w.showerror = Mbox_func()
113
114        # No file/line number.
115        w.write('Not a file line')
116        self.assertIsNone(w.goto_file_line())
117        eq(gfl.called, 0)
118        eq(showerror.title, 'No special line')
119
120        # Current file/line number.
121        w.write(f'{str(__file__)}: 42: spam\n')
122        w.write(f'{str(__file__)}: 21: spam')
123        self.assertIsNone(w.goto_file_line())
124        eq(gfl.args, (str(__file__), 21))
125
126        # Previous line has file/line number.
127        text.delete('1.0', 'end')
128        w.write(f'{str(__file__)}: 42: spam\n')
129        w.write('Not a file line')
130        self.assertIsNone(w.goto_file_line())
131        eq(gfl.args, (str(__file__), 42))
132
133        del w.flist.gotofileline, w.showerror
134
135
136class ModuleFunctionTest(unittest.TestCase):
137
138    @classmethod
139    def setUp(cls):
140        outwin.file_line_progs = None
141
142    def test_compile_progs(self):
143        outwin.compile_progs()
144        for pat, regex in zip(outwin.file_line_pats, outwin.file_line_progs):
145            self.assertEqual(regex.pattern, pat)
146
147    @mock.patch('builtins.open')
148    def test_file_line_helper(self, mock_open):
149        flh = outwin.file_line_helper
150        test_lines = (
151            (r'foo file "testfile1", line 42, bar', ('testfile1', 42)),
152            (r'foo testfile2(21) bar', ('testfile2', 21)),
153            (r'  testfile3  : 42: foo bar\n', ('  testfile3  ', 42)),
154            (r'foo testfile4.py :1: ', ('foo testfile4.py ', 1)),
155            ('testfile5: \u19D4\u19D2: ', ('testfile5', 42)),
156            (r'testfile6: 42', None),       # only one `:`
157            (r'testfile7 42 text', None)    # no separators
158            )
159        for line, expected_output in test_lines:
160            self.assertEqual(flh(line), expected_output)
161            if expected_output:
162                mock_open.assert_called_with(expected_output[0], 'r')
163
164
165if __name__ == '__main__':
166    unittest.main(verbosity=2)
167