1 "Test browser, coverage 90%."
2
3 from idlelib import browser
4 from test.support import requires
5 import unittest
6 from unittest import mock
7 from idlelib.idle_test.mock_idle import Func
8 from idlelib.util import py_extensions
9
10 from collections import deque
11 import os.path
12 import pyclbr
13 from tkinter import Tk
14
15 from idlelib.tree import TreeNode
16
17
18 class ESC[4;38;5;81mModuleBrowserTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
19
20 @classmethod
21 def setUpClass(cls):
22 requires('gui')
23 cls.root = Tk()
24 cls.root.withdraw()
25 cls.mb = browser.ModuleBrowser(cls.root, __file__, _utest=True)
26
27 @classmethod
28 def tearDownClass(cls):
29 cls.mb.close()
30 cls.root.update_idletasks()
31 cls.root.destroy()
32 del cls.root, cls.mb
33
34 def test_init(self):
35 mb = self.mb
36 eq = self.assertEqual
37 eq(mb.path, __file__)
38 eq(pyclbr._modules, {})
39 self.assertIsInstance(mb.node, TreeNode)
40 self.assertIsNotNone(browser.file_open)
41
42 def test_settitle(self):
43 mb = self.mb
44 self.assertIn(os.path.basename(__file__), mb.top.title())
45 self.assertEqual(mb.top.iconname(), 'Module Browser')
46
47 def test_rootnode(self):
48 mb = self.mb
49 rn = mb.rootnode()
50 self.assertIsInstance(rn, browser.ModuleBrowserTreeItem)
51
52 def test_close(self):
53 mb = self.mb
54 mb.top.destroy = Func()
55 mb.node.destroy = Func()
56 mb.close()
57 self.assertTrue(mb.top.destroy.called)
58 self.assertTrue(mb.node.destroy.called)
59 del mb.top.destroy, mb.node.destroy
60
61 def test_is_browseable_extension(self):
62 path = "/path/to/file"
63 for ext in py_extensions:
64 with self.subTest(ext=ext):
65 filename = f'{path}{ext}'
66 actual = browser.is_browseable_extension(filename)
67 expected = ext not in browser.browseable_extension_blocklist
68 self.assertEqual(actual, expected)
69
70
71 # Nested tree same as in test_pyclbr.py except for supers on C0. C1.
72 mb = pyclbr
73 module, fname = 'test', 'test.py'
74 C0 = mb.Class(module, 'C0', ['base'], fname, 1, end_lineno=9)
75 F1 = mb._nest_function(C0, 'F1', 3, 5)
76 C1 = mb._nest_class(C0, 'C1', 6, 9, [''])
77 C2 = mb._nest_class(C1, 'C2', 7, 9)
78 F3 = mb._nest_function(C2, 'F3', 9, 9)
79 f0 = mb.Function(module, 'f0', fname, 11, end_lineno=15)
80 f1 = mb._nest_function(f0, 'f1', 12, 14)
81 f2 = mb._nest_function(f1, 'f2', 13, 13)
82 c1 = mb._nest_class(f0, 'c1', 15, 15)
83 mock_pyclbr_tree = {'C0': C0, 'f0': f0}
84
85 # Adjust C0.name, C1.name so tests do not depend on order.
86 browser.transform_children(mock_pyclbr_tree, 'test') # C0(base)
87 browser.transform_children(C0.children) # C1()
88
89 # The class below checks that the calls above are correct
90 # and that duplicate calls have no effect.
91
92
93 class ESC[4;38;5;81mTransformChildrenTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
94
95 def test_transform_module_children(self):
96 eq = self.assertEqual
97 transform = browser.transform_children
98 # Parameter matches tree module.
99 tcl = list(transform(mock_pyclbr_tree, 'test'))
100 eq(tcl, [C0, f0])
101 eq(tcl[0].name, 'C0(base)')
102 eq(tcl[1].name, 'f0')
103 # Check that second call does not change suffix.
104 tcl = list(transform(mock_pyclbr_tree, 'test'))
105 eq(tcl[0].name, 'C0(base)')
106 # Nothing to traverse if parameter name isn't same as tree module.
107 tcl = list(transform(mock_pyclbr_tree, 'different name'))
108 eq(tcl, [])
109
110 def test_transform_node_children(self):
111 eq = self.assertEqual
112 transform = browser.transform_children
113 # Class with two children, one name altered.
114 tcl = list(transform(C0.children))
115 eq(tcl, [F1, C1])
116 eq(tcl[0].name, 'F1')
117 eq(tcl[1].name, 'C1()')
118 tcl = list(transform(C0.children))
119 eq(tcl[1].name, 'C1()')
120 # Function with two children.
121 eq(list(transform(f0.children)), [f1, c1])
122
123
124 class ESC[4;38;5;81mModuleBrowserTreeItemTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
125
126 @classmethod
127 def setUpClass(cls):
128 cls.mbt = browser.ModuleBrowserTreeItem(fname)
129
130 def test_init(self):
131 self.assertEqual(self.mbt.file, fname)
132
133 def test_gettext(self):
134 self.assertEqual(self.mbt.GetText(), fname)
135
136 def test_geticonname(self):
137 self.assertEqual(self.mbt.GetIconName(), 'python')
138
139 def test_isexpandable(self):
140 self.assertTrue(self.mbt.IsExpandable())
141
142 def test_listchildren(self):
143 save_rex = browser.pyclbr.readmodule_ex
144 save_tc = browser.transform_children
145 browser.pyclbr.readmodule_ex = Func(result=mock_pyclbr_tree)
146 browser.transform_children = Func(result=[f0, C0])
147 try:
148 self.assertEqual(self.mbt.listchildren(), [f0, C0])
149 finally:
150 browser.pyclbr.readmodule_ex = save_rex
151 browser.transform_children = save_tc
152
153 def test_getsublist(self):
154 mbt = self.mbt
155 mbt.listchildren = Func(result=[f0, C0])
156 sub0, sub1 = mbt.GetSubList()
157 del mbt.listchildren
158 self.assertIsInstance(sub0, browser.ChildBrowserTreeItem)
159 self.assertIsInstance(sub1, browser.ChildBrowserTreeItem)
160 self.assertEqual(sub0.name, 'f0')
161 self.assertEqual(sub1.name, 'C0(base)')
162
163 @mock.patch('idlelib.browser.file_open')
164 def test_ondoubleclick(self, fopen):
165 mbt = self.mbt
166
167 with mock.patch('os.path.exists', return_value=False):
168 mbt.OnDoubleClick()
169 fopen.assert_not_called()
170
171 with mock.patch('os.path.exists', return_value=True):
172 mbt.OnDoubleClick()
173 fopen.assert_called_once_with(fname)
174
175
176 class ESC[4;38;5;81mChildBrowserTreeItemTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
177
178 @classmethod
179 def setUpClass(cls):
180 CBT = browser.ChildBrowserTreeItem
181 cls.cbt_f1 = CBT(f1)
182 cls.cbt_C1 = CBT(C1)
183 cls.cbt_F1 = CBT(F1)
184
185 @classmethod
186 def tearDownClass(cls):
187 del cls.cbt_C1, cls.cbt_f1, cls.cbt_F1
188
189 def test_init(self):
190 eq = self.assertEqual
191 eq(self.cbt_C1.name, 'C1()')
192 self.assertFalse(self.cbt_C1.isfunction)
193 eq(self.cbt_f1.name, 'f1')
194 self.assertTrue(self.cbt_f1.isfunction)
195
196 def test_gettext(self):
197 self.assertEqual(self.cbt_C1.GetText(), 'class C1()')
198 self.assertEqual(self.cbt_f1.GetText(), 'def f1(...)')
199
200 def test_geticonname(self):
201 self.assertEqual(self.cbt_C1.GetIconName(), 'folder')
202 self.assertEqual(self.cbt_f1.GetIconName(), 'python')
203
204 def test_isexpandable(self):
205 self.assertTrue(self.cbt_C1.IsExpandable())
206 self.assertTrue(self.cbt_f1.IsExpandable())
207 self.assertFalse(self.cbt_F1.IsExpandable())
208
209 def test_getsublist(self):
210 eq = self.assertEqual
211 CBT = browser.ChildBrowserTreeItem
212
213 f1sublist = self.cbt_f1.GetSubList()
214 self.assertIsInstance(f1sublist[0], CBT)
215 eq(len(f1sublist), 1)
216 eq(f1sublist[0].name, 'f2')
217
218 eq(self.cbt_F1.GetSubList(), [])
219
220 @mock.patch('idlelib.browser.file_open')
221 def test_ondoubleclick(self, fopen):
222 goto = fopen.return_value.gotoline = mock.Mock()
223 self.cbt_F1.OnDoubleClick()
224 fopen.assert_called()
225 goto.assert_called()
226 goto.assert_called_with(self.cbt_F1.obj.lineno)
227 # Failure test would have to raise OSError or AttributeError.
228
229
230 class ESC[4;38;5;81mNestedChildrenTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
231 "Test that all the nodes in a nested tree are added to the BrowserTree."
232
233 def test_nested(self):
234 queue = deque()
235 actual_names = []
236 # The tree items are processed in breadth first order.
237 # Verify that processing each sublist hits every node and
238 # in the right order.
239 expected_names = ['f0', 'C0(base)',
240 'f1', 'c1', 'F1', 'C1()',
241 'f2', 'C2',
242 'F3']
243 CBT = browser.ChildBrowserTreeItem
244 queue.extend((CBT(f0), CBT(C0)))
245 while queue:
246 cb = queue.popleft()
247 sublist = cb.GetSubList()
248 queue.extend(sublist)
249 self.assertIn(cb.name, cb.GetText())
250 self.assertIn(cb.GetIconName(), ('python', 'folder'))
251 self.assertIs(cb.IsExpandable(), sublist != [])
252 actual_names.append(cb.name)
253 self.assertEqual(actual_names, expected_names)
254
255
256 if __name__ == '__main__':
257 unittest.main(verbosity=2)