]> granicus.if.org Git - python/commitdiff
bpo-30870: IDLE: Add configdialog fontlist selection unittest (#2666)
authorLouie Lu <git@louie.lu>
Fri, 14 Jul 2017 00:35:48 +0000 (08:35 +0800)
committerterryjreedy <tjreedy@udel.edu>
Fri, 14 Jul 2017 00:35:48 +0000 (20:35 -0400)
Initial patch by Louie Lu.

Lib/idlelib/configdialog.py
Lib/idlelib/idle_test/mock_idle.py
Lib/idlelib/idle_test/test_configdialog.py

index a2cfaab0d0acbe9b5610d461d23924cddc2c73da..0d68b8081e380599e920cef89030cdf293ab85a5 100644 (file)
@@ -47,7 +47,8 @@ class ConfigDialog(Toplevel):
         self.parent = parent
         if _htest:
             parent.instance_dict = {}
-        self.withdraw()
+        if not _utest:
+            self.withdraw()
 
         self.configure(borderwidth=5)
         self.title(title or 'IDLE Preferences')
@@ -76,7 +77,6 @@ class ConfigDialog(Toplevel):
         self.create_widgets()
         self.resizable(height=FALSE, width=FALSE)
         self.transient(parent)
-        self.grab_set()
         self.protocol("WM_DELETE_WINDOW", self.cancel)
         self.fontlist.focus_set()
         # XXX Decide whether to keep or delete these key bindings.
@@ -88,6 +88,7 @@ class ConfigDialog(Toplevel):
         self.attach_var_callbacks()  # Avoid callbacks during load_configs.
 
         if not _utest:
+            self.grab_set()
             self.wm_deiconify()
             self.wait_window()
 
index c7b49ef02b885107c4704b07d54c72835d1ee422..8f3147b1c5ce3d9ba07344e1449559f1c7d3eb63 100644 (file)
@@ -6,24 +6,25 @@ Attributes and methods will be added as needed for tests.
 from idlelib.idle_test.mock_tk import Text
 
 class Func:
-    '''Mock function captures args and returns result set by test.
+    '''Record call, capture args, return/raise result set by test.
 
-    Attributes:
-    self.called - records call even if no args, kwds passed.
-    self.result - set by init, returned by call.
-    self.args - captures positional arguments.
-    self.kwds - captures keyword arguments.
+    When mock function is called, set or use attributes:
+    self.called - increment call number even if no args, kwds passed.
+    self.args - capture positional arguments.
+    self.kwds - capture keyword arguments.
+    self.result - return or raise value set in __init__.
 
-    Most common use will probably be to mock methods.
+    Most common use will probably be to mock instance methods.
+    Given class instance, can set and delete as instance attribute.
     Mock_tk.Var and Mbox_func are special variants of this.
     '''
     def __init__(self, result=None):
-        self.called = False
+        self.called = 0
         self.result = result
         self.args = None
         self.kwds = None
     def __call__(self, *args, **kwds):
-        self.called = True
+        self.called += 1
         self.args = args
         self.kwds = kwds
         if isinstance(self.result, BaseException):
index 26b045d353550b2b169051dbb58a51e3069ed0aa..4f1f9af180abb18c5d03b4f6b8a8390ca3abc4b3 100644 (file)
@@ -1,7 +1,7 @@
 """Test idlelib.configdialog.
 
 Half the class creates dialog, half works with user customizations.
-Coverage: 46% just by creating dialog, 56% with current tests.
+Coverage: 46% just by creating dialog, 60% with current tests.
 """
 from idlelib.configdialog import ConfigDialog, idleConf, changes
 from test.support import requires
@@ -9,6 +9,7 @@ requires('gui')
 from tkinter import Tk
 import unittest
 import idlelib.config as config
+from idlelib.idle_test.mock_idle import Func
 
 # Tests should not depend on fortuitous user configurations.
 # They must not affect actual user .cfg files.
@@ -22,27 +23,29 @@ testcfg = {
 }
 
 root = None
-configure = None
+dialog = None
 mainpage = changes['main']
 highpage = changes['highlight']
 keyspage = changes['keys']
 
-class TestDialog(ConfigDialog): pass  # Delete?
+
+class TestDialog(ConfigDialog):
+    pass  # Delete?
 
 
 def setUpModule():
-    global root, configure
+    global root, dialog
     idleConf.userCfg = testcfg
     root = Tk()
-    root.withdraw()
-    configure = TestDialog(root, 'Test', _utest=True)
+    # root.withdraw()    # Comment out, see issue 30870
+    dialog = TestDialog(root, 'Test', _utest=True)
 
 
 def tearDownModule():
-    global root, configure
+    global root, dialog
     idleConf.userCfg = usercfg
-    configure.remove_var_callbacks()
-    del configure
+    dialog.remove_var_callbacks()
+    del dialog
     root.update_idletasks()
     root.destroy()
     del root
@@ -58,31 +61,105 @@ class FontTabTest(unittest.TestCase):
         default_font = idleConf.GetFont(root, 'main', 'EditorWindow')
         default_size = str(default_font[1])
         default_bold = default_font[2] == 'bold'
-        configure.font_name.set('Test Font')
+        dialog.font_name.set('Test Font')
         expected = {'EditorWindow': {'font': 'Test Font',
                                      'font-size': default_size,
                                      'font-bold': str(default_bold)}}
         self.assertEqual(mainpage, expected)
         changes.clear()
-        configure.font_size.set(20)
+        dialog.font_size.set(20)
         expected = {'EditorWindow': {'font': 'Test Font',
                                      'font-size': '20',
                                      'font-bold': str(default_bold)}}
         self.assertEqual(mainpage, expected)
         changes.clear()
-        configure.font_bold.set(not default_bold)
+        dialog.font_bold.set(not default_bold)
         expected = {'EditorWindow': {'font': 'Test Font',
                                      'font-size': '20',
                                      'font-bold': str(not default_bold)}}
         self.assertEqual(mainpage, expected)
 
-    #def test_sample(self): pass  # TODO
+    def test_set_sample(self):
+        # Set_font_sample also sets highlight_sample.
+        pass
 
     def test_tabspace(self):
-        configure.space_num.set(6)
+        dialog.space_num.set(6)
         self.assertEqual(mainpage, {'Indent': {'num-spaces': '6'}})
 
 
+class FontSelectTest(unittest.TestCase):
+    # These two functions test that selecting a new font in the
+    # list of fonts changes font_name and calls set_font_sample.
+    # The fontlist widget and on_fontlist_select event handler
+    # are tested here together.
+
+    @classmethod
+    def setUpClass(cls):
+        if dialog.fontlist.size() < 2:
+            cls.skipTest('need at least 2 fonts')
+        dialog.set_font_sample = Func()  # Mask instance method.
+
+    @classmethod
+    def tearDownClass(cls):
+        del dialog.set_font_sample  # Unmask instance method.
+
+    def setUp(self):
+        dialog.set_font_sample.called = 0
+        changes.clear()
+
+    def test_select_font_key(self):
+        # Up and Down keys should select a new font.
+
+        fontlist = dialog.fontlist
+        fontlist.activate(0)
+        font = dialog.fontlist.get('active')
+
+        # Test Down key.
+        fontlist.focus_force()
+        fontlist.update()
+        fontlist.event_generate('<Key-Down>')
+        fontlist.event_generate('<KeyRelease-Down>')
+
+        down_font = fontlist.get('active')
+        self.assertNotEqual(down_font, font)
+        self.assertIn(dialog.font_name.get(), down_font.lower())
+        self.assertEqual(dialog.set_font_sample.called, 1)
+
+        # Test Up key.
+        fontlist.focus_force()
+        fontlist.update()
+        fontlist.event_generate('<Key-Up>')
+        fontlist.event_generate('<KeyRelease-Up>')
+
+        up_font = fontlist.get('active')
+        self.assertEqual(up_font, font)
+        self.assertIn(dialog.font_name.get(), up_font.lower())
+        self.assertEqual(dialog.set_font_sample.called, 2)
+
+    def test_select_font_mouse(self):
+        # Click on item should select that item.
+
+        fontlist = dialog.fontlist
+        fontlist.activate(0)
+
+        # Select next item in listbox
+        fontlist.focus_force()
+        fontlist.see(1)
+        fontlist.update()
+        x, y, dx, dy = fontlist.bbox(1)
+        x += dx // 2
+        y += dy // 2
+        fontlist.event_generate('<Button-1>', x=x, y=y)
+        fontlist.event_generate('<ButtonRelease-1>', x=x, y=y)
+
+        font1 = fontlist.get(1)
+        select_font = fontlist.get('anchor')
+        self.assertEqual(select_font, font1)
+        self.assertIn(dialog.font_name.get(), font1.lower())
+        self.assertEqual(dialog.set_font_sample.called, 1)
+
+
 class HighlightTest(unittest.TestCase):
 
     def setUp(self):
@@ -103,19 +180,19 @@ class GeneralTest(unittest.TestCase):
         changes.clear()
 
     def test_startup(self):
-        configure.radio_startup_edit.invoke()
+        dialog.radio_startup_edit.invoke()
         self.assertEqual(mainpage,
                          {'General': {'editor-on-startup': '1'}})
 
     def test_autosave(self):
-        configure.radio_save_auto.invoke()
+        dialog.radio_save_auto.invoke()
         self.assertEqual(mainpage, {'General': {'autosave': '1'}})
 
     def test_editor_size(self):
-        configure.entry_win_height.insert(0, '1')
+        dialog.entry_win_height.insert(0, '1')
         self.assertEqual(mainpage, {'EditorWindow': {'height': '140'}})
         changes.clear()
-        configure.entry_win_width.insert(0, '1')
+        dialog.entry_win_width.insert(0, '1')
         self.assertEqual(mainpage, {'EditorWindow': {'width': '180'}})
 
     #def test_help_sources(self): pass  # TODO