1"Test browser, coverage 90%." 2 3from idlelib import browser 4from test.support import requires 5import unittest 6from unittest import mock 7from idlelib.idle_test.mock_idle import Func 8from idlelib.util import py_extensions 9 10from collections import deque 11import os.path 12import pyclbr 13from tkinter import Tk 14 15from idlelib.tree import TreeNode 16 17 18class ModuleBrowserTest(unittest.TestCase): 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. 72mb = pyclbr 73module, fname = 'test', 'test.py' 74C0 = mb.Class(module, 'C0', ['base'], fname, 1, end_lineno=9) 75F1 = mb._nest_function(C0, 'F1', 3, 5) 76C1 = mb._nest_class(C0, 'C1', 6, 9, ['']) 77C2 = mb._nest_class(C1, 'C2', 7, 9) 78F3 = mb._nest_function(C2, 'F3', 9, 9) 79f0 = mb.Function(module, 'f0', fname, 11, end_lineno=15) 80f1 = mb._nest_function(f0, 'f1', 12, 14) 81f2 = mb._nest_function(f1, 'f2', 13, 13) 82c1 = mb._nest_class(f0, 'c1', 15, 15) 83mock_pyclbr_tree = {'C0': C0, 'f0': f0} 84 85# Adjust C0.name, C1.name so tests do not depend on order. 86browser.transform_children(mock_pyclbr_tree, 'test') # C0(base) 87browser.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 93class TransformChildrenTest(unittest.TestCase): 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 124class ModuleBrowserTreeItemTest(unittest.TestCase): 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 176class ChildBrowserTreeItemTest(unittest.TestCase): 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 230class NestedChildrenTest(unittest.TestCase): 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 256if __name__ == '__main__': 257 unittest.main(verbosity=2) 258