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