summaryrefslogtreecommitdiffstats
path: root/Lib/idlelib/idle_test
diff options
context:
space:
mode:
authorTerry Jan Reedy <tjreedy@udel.edu>2017-09-22 21:28:01 (GMT)
committerGitHub <noreply@github.com>2017-09-22 21:28:01 (GMT)
commitfa1cae5832cbcfafedc4b1879c2abc85452f4edd (patch)
tree73d41f20b90099ff832ba87e5695a944b2a9c497 /Lib/idlelib/idle_test
parent6de35849cb7a18bfaad828eb57a2e6caa7978690 (diff)
downloadcpython-fa1cae5832cbcfafedc4b1879c2abc85452f4edd.zip
cpython-fa1cae5832cbcfafedc4b1879c2abc85452f4edd.tar.gz
cpython-fa1cae5832cbcfafedc4b1879c2abc85452f4edd.tar.bz2
[3.6] bpo-1612262: IDLE: Class Browser shows nested functions, classes (GH-2573) (#3702)
Original patches for code and tests by Guilherme Polo and Cheryl Sabella, respectively. (cherry picked from commit 058de11360ea6816a6e978c7be0bcbea99a3f7da)
Diffstat (limited to 'Lib/idlelib/idle_test')
-rw-r--r--Lib/idlelib/idle_test/test_browser.py242
1 files changed, 242 insertions, 0 deletions
diff --git a/Lib/idlelib/idle_test/test_browser.py b/Lib/idlelib/idle_test/test_browser.py
new file mode 100644
index 0000000..875b0b9
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_browser.py
@@ -0,0 +1,242 @@
+""" Test idlelib.browser.
+
+Coverage: 88%
+(Higher, because should exclude 3 lines that .coveragerc won't exclude.)
+"""
+
+import os.path
+import unittest
+from idlelib import _pyclbr as pyclbr
+
+from idlelib import browser, filelist
+from idlelib.tree import TreeNode
+from test.support import requires
+from unittest import mock
+from tkinter import Tk
+from idlelib.idle_test.mock_idle import Func
+from collections import deque
+
+
+class ClassBrowserTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+ cls.root.withdraw()
+ cls.flist = filelist.FileList(cls.root)
+ cls.file = __file__
+ cls.path = os.path.dirname(cls.file)
+ cls.module = os.path.basename(cls.file).rstrip('.py')
+ cls.cb = browser.ClassBrowser(cls.flist, cls.module, [cls.path], _utest=True)
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cb.close()
+ cls.root.destroy()
+ del cls.root, cls.flist, cls.cb
+
+ def test_init(self):
+ cb = self.cb
+ eq = self.assertEqual
+ eq(cb.name, self.module)
+ eq(cb.file, self.file)
+ eq(cb.flist, self.flist)
+ eq(pyclbr._modules, {})
+ self.assertIsInstance(cb.node, TreeNode)
+
+ def test_settitle(self):
+ cb = self.cb
+ self.assertIn(self.module, cb.top.title())
+ self.assertEqual(cb.top.iconname(), 'Class Browser')
+
+ def test_rootnode(self):
+ cb = self.cb
+ rn = cb.rootnode()
+ self.assertIsInstance(rn, browser.ModuleBrowserTreeItem)
+
+ def test_close(self):
+ cb = self.cb
+ cb.top.destroy = Func()
+ cb.node.destroy = Func()
+ cb.close()
+ self.assertTrue(cb.top.destroy.called)
+ self.assertTrue(cb.node.destroy.called)
+ del cb.top.destroy, cb.node.destroy
+
+
+# Same nested tree creation as in test_pyclbr.py except for super on C0.
+mb = pyclbr
+module, fname = 'test', 'test.py'
+f0 = mb.Function(module, 'f0', fname, 1)
+f1 = mb._nest_function(f0, 'f1', 2)
+f2 = mb._nest_function(f1, 'f2', 3)
+c1 = mb._nest_class(f0, 'c1', 5)
+C0 = mb.Class(module, 'C0', ['base'], fname, 6)
+F1 = mb._nest_function(C0, 'F1', 8)
+C1 = mb._nest_class(C0, 'C1', 11, [''])
+C2 = mb._nest_class(C1, 'C2', 12)
+F3 = mb._nest_function(C2, 'F3', 14)
+mock_pyclbr_tree = {'f0': f0, 'C0': C0}
+
+# transform_children(mock_pyclbr_tree, 'test') mutates C0.name.
+
+class TransformChildrenTest(unittest.TestCase):
+
+ def test_transform_children(self):
+ eq = self.assertEqual
+ # Parameter matches tree module.
+ tcl = list(browser.transform_children(mock_pyclbr_tree, 'test'))
+ eq(tcl[0], f0)
+ eq(tcl[1], C0)
+ eq(tcl[1].name, 'C0(base)')
+ # Check that second call does not add second '(base)' suffix.
+ tcl = list(browser.transform_children(mock_pyclbr_tree, 'test'))
+ eq(tcl[1].name, 'C0(base)')
+ # Nothing to traverse if parameter name isn't same as tree module.
+ tn = browser.transform_children(mock_pyclbr_tree, 'different name')
+ self.assertEqual(list(tn), [])
+ # No name parameter.
+ tn = browser.transform_children({'f1': f1, 'c1': c1})
+ self.assertEqual(list(tn), [f1, c1])
+
+
+class ModuleBrowserTreeItemTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.mbt = browser.ModuleBrowserTreeItem(fname)
+
+ def test_init(self):
+ self.assertEqual(self.mbt.file, fname)
+
+ def test_gettext(self):
+ self.assertEqual(self.mbt.GetText(), fname)
+
+ def test_geticonname(self):
+ self.assertEqual(self.mbt.GetIconName(), 'python')
+
+ def test_isexpandable(self):
+ self.assertTrue(self.mbt.IsExpandable())
+
+ def test_listchildren(self):
+ save_rex = browser.pyclbr.readmodule_ex
+ save_tc = browser.transform_children
+ browser.pyclbr.readmodule_ex = Func(result=mock_pyclbr_tree)
+ browser.transform_children = Func(result=[f0, C0])
+ try:
+ self.assertEqual(self.mbt.listchildren(), [f0, C0])
+ finally:
+ browser.pyclbr.readmodule_ex = save_rex
+ browser.transform_children = save_tc
+
+ def test_getsublist(self):
+ mbt = self.mbt
+ mbt.listchildren = Func(result=[f0, C0])
+ sub0, sub1 = mbt.GetSubList()
+ del mbt.listchildren
+ self.assertIsInstance(sub0, browser.ChildBrowserTreeItem)
+ self.assertIsInstance(sub1, browser.ChildBrowserTreeItem)
+ self.assertEqual(sub0.name, 'f0')
+ self.assertEqual(sub1.name, 'C0')
+
+
+ def test_ondoubleclick(self):
+ mbt = self.mbt
+ fopen = browser.file_open = mock.Mock()
+
+ with mock.patch('os.path.exists', return_value=False):
+ mbt.OnDoubleClick()
+ fopen.assert_not_called()
+
+ with mock.patch('os.path.exists', return_value=True):
+ mbt.OnDoubleClick()
+ fopen.assert_called()
+ fopen.called_with(fname)
+
+ del browser.file_open
+
+
+class ChildBrowserTreeItemTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ CBT = browser.ChildBrowserTreeItem
+ cls.cbt_f1 = CBT(f1)
+ cls.cbt_C1 = CBT(C1)
+ cls.cbt_F1 = CBT(F1)
+
+ @classmethod
+ def tearDownClass(cls):
+ del cls.cbt_C1, cls.cbt_f1, cls.cbt_F1
+
+ def test_init(self):
+ eq = self.assertEqual
+ eq(self.cbt_C1.name, 'C1')
+ self.assertFalse(self.cbt_C1.isfunction)
+ eq(self.cbt_f1.name, 'f1')
+ self.assertTrue(self.cbt_f1.isfunction)
+
+ def test_gettext(self):
+ self.assertEqual(self.cbt_C1.GetText(), 'class C1')
+ self.assertEqual(self.cbt_f1.GetText(), 'def f1(...)')
+
+ def test_geticonname(self):
+ self.assertEqual(self.cbt_C1.GetIconName(), 'folder')
+ self.assertEqual(self.cbt_f1.GetIconName(), 'python')
+
+ def test_isexpandable(self):
+ self.assertTrue(self.cbt_C1.IsExpandable())
+ self.assertTrue(self.cbt_f1.IsExpandable())
+ self.assertFalse(self.cbt_F1.IsExpandable())
+
+ def test_getsublist(self):
+ eq = self.assertEqual
+ CBT = browser.ChildBrowserTreeItem
+
+ f1sublist = self.cbt_f1.GetSubList()
+ self.assertIsInstance(f1sublist[0], CBT)
+ eq(len(f1sublist), 1)
+ eq(f1sublist[0].name, 'f2')
+
+ eq(self.cbt_F1.GetSubList(), [])
+
+ def test_ondoubleclick(self):
+ fopen = browser.file_open = mock.Mock()
+ goto = fopen.return_value.gotoline = mock.Mock()
+ self.cbt_F1.OnDoubleClick()
+ fopen.assert_called()
+ goto.assert_called()
+ goto.assert_called_with(self.cbt_F1.obj.lineno)
+ del browser.file_open
+ # Failure test would have to raise OSError or AttributeError.
+
+
+class NestedChildrenTest(unittest.TestCase):
+ "Test that all the nodes in a nested tree are added to the BrowserTree."
+
+ def test_nested(self):
+ queue = deque()
+ actual_names = []
+ # The tree items are processed in breadth first order.
+ # Verify that processing each sublist hits every node and
+ # in the right order.
+ expected_names = ['f0', 'C0', # This is run before transform test.
+ 'f1', 'c1', 'F1', 'C1()',
+ 'f2', 'C2',
+ 'F3']
+ CBT = browser.ChildBrowserTreeItem
+ queue.extend((CBT(f0), CBT(C0)))
+ while queue:
+ cb = queue.popleft()
+ sublist = cb.GetSubList()
+ queue.extend(sublist)
+ self.assertIn(cb.name, cb.GetText())
+ self.assertIn(cb.GetIconName(), ('python', 'folder'))
+ self.assertIs(cb.IsExpandable(), sublist != [])
+ actual_names.append(cb.name)
+ self.assertEqual(actual_names, expected_names)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)