[Python-checkins] bpo-30780: Add IDLE configdialog tests (#3592)

Terry Jan Reedy webhook-mailer at python.org
Mon Jan 27 17:16:03 EST 2020


https://github.com/python/cpython/commit/dd023ad1619b6f1ab313986e8953eea32c18f50c
commit: dd023ad1619b6f1ab313986e8953eea32c18f50c
branch: master
author: Cheryl Sabella <cheryl.sabella at gmail.com>
committer: Terry Jan Reedy <tjreedy at udel.edu>
date: 2020-01-27T17:15:56-05:00
summary:

bpo-30780: Add IDLE configdialog tests (#3592)

Expose dialog buttons to test code and complete their test coverage.
Complete test coverage for highlights and keys tabs.

Co-authored-by: Terry Jan Reedy <tjreedy at udel.edu>

files:
A Misc/NEWS.d/next/IDLE/2020-01-27-16-44-29.bpo-30780.nR80qu.rst
M Lib/idlelib/NEWS.txt
M Lib/idlelib/configdialog.py
M Lib/idlelib/idle_test/test_configdialog.py

diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt
index eda7c2788764d..2b543985b3783 100644
--- a/Lib/idlelib/NEWS.txt
+++ b/Lib/idlelib/NEWS.txt
@@ -3,6 +3,9 @@ Released on 2020-10-05?
 ======================================
 
 
+bpo-30780: Add remaining configdialog tests for buttons and
+highlights and keys tabs.
+
 bpo-39388: Settings dialog Cancel button cancels pending changes.
 
 bpo-39050: Settings dialog Help button again displays help text.
diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py
index 2f95c9ccaa0c5..22359735874d1 100644
--- a/Lib/idlelib/configdialog.py
+++ b/Lib/idlelib/configdialog.py
@@ -149,17 +149,19 @@ def create_action_buttons(self):
         else:
             padding_args = {'padding': (6, 3)}
         outer = Frame(self, padding=2)
-        buttons = Frame(outer, padding=2)
+        buttons_frame = Frame(outer, padding=2)
+        self.buttons = {}
         for txt, cmd in (
             ('Ok', self.ok),
             ('Apply', self.apply),
             ('Cancel', self.cancel),
             ('Help', self.help)):
-            Button(buttons, text=txt, command=cmd, takefocus=FALSE,
-                   **padding_args).pack(side=LEFT, padx=5)
+            self.buttons[txt] = Button(buttons_frame, text=txt, command=cmd,
+                       takefocus=FALSE, **padding_args)
+            self.buttons[txt].pack(side=LEFT, padx=5)
         # Add space above buttons.
         Frame(outer, height=2, borderwidth=0).pack(side=TOP)
-        buttons.pack(side=BOTTOM)
+        buttons_frame.pack(side=BOTTOM)
         return outer
 
     def ok(self):
@@ -205,7 +207,6 @@ def help(self):
 
         Attributes accessed:
             note
-
         Methods:
             view_text: Method from textview module.
         """
@@ -852,6 +853,7 @@ def create_page_highlight(self):
         text.configure(
                 font=('courier', 12, ''), cursor='hand2', width=1, height=1,
                 takefocus=FALSE, highlightthickness=0, wrap=NONE)
+        # Prevent perhaps invisible selection of word or slice.
         text.bind('<Double-Button-1>', lambda e: 'break')
         text.bind('<B1-Motion>', lambda e: 'break')
         string_tags=(
@@ -1284,8 +1286,7 @@ def save_new(self, theme_name, theme):
         theme_name - string, the name of the new theme
         theme - dictionary containing the new theme
         """
-        if not idleConf.userCfg['highlight'].has_section(theme_name):
-            idleConf.userCfg['highlight'].add_section(theme_name)
+        idleConf.userCfg['highlight'].AddSection(theme_name)
         for element in theme:
             value = theme[element]
             idleConf.userCfg['highlight'].SetOption(theme_name, element, value)
@@ -1730,8 +1731,7 @@ def save_new_key_set(keyset_name, keyset):
         keyset_name - string, the name of the new key set
         keyset - dictionary containing the new keybindings
         """
-        if not idleConf.userCfg['keys'].has_section(keyset_name):
-            idleConf.userCfg['keys'].add_section(keyset_name)
+        idleConf.userCfg['keys'].AddSection(keyset_name)
         for event in keyset:
             value = keyset[event]
             idleConf.userCfg['keys'].SetOption(keyset_name, event, value)
diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py
index 817a35217bf3c..1fea6d41df811 100644
--- a/Lib/idlelib/idle_test/test_configdialog.py
+++ b/Lib/idlelib/idle_test/test_configdialog.py
@@ -8,7 +8,7 @@
 import unittest
 from unittest import mock
 from idlelib.idle_test.mock_idle import Func
-from tkinter import Tk, StringVar, IntVar, BooleanVar, DISABLED, NORMAL
+from tkinter import (Tk, StringVar, IntVar, BooleanVar, DISABLED, NORMAL)
 from idlelib import config
 from idlelib.configdialog import idleConf, changes, tracers
 
@@ -30,6 +30,7 @@
 keyspage = changes['keys']
 extpage = changes['extensions']
 
+
 def setUpModule():
     global root, dialog
     idleConf.userCfg = testcfg
@@ -37,6 +38,7 @@ def setUpModule():
     # root.withdraw()    # Comment out, see issue 30870
     dialog = configdialog.ConfigDialog(root, 'Test', _utest=True)
 
+
 def tearDownModule():
     global root, dialog
     idleConf.userCfg = usercfg
@@ -48,22 +50,56 @@ def tearDownModule():
     root = dialog = None
 
 
-class DialogTest(unittest.TestCase):
+class ConfigDialogTest(unittest.TestCase):
+
+    def test_deactivate_current_config(self):
+        pass
+
+    def activate_config_changes(self):
+        pass
+
 
-    @mock.patch(__name__+'.dialog.destroy', new_callable=Func)
-    def test_cancel(self, destroy):
+class ButtonTest(unittest.TestCase):
+
+    def test_click_ok(self):
+        d = dialog
+        apply = d.apply = mock.Mock()
+        destroy = d.destroy = mock.Mock()
+        d.buttons['Ok'].invoke()
+        apply.assert_called_once()
+        destroy.assert_called_once()
+        del d.destroy, d.apply
+
+    def test_click_apply(self):
+        d = dialog
+        deactivate = d.deactivate_current_config = mock.Mock()
+        save_ext = d.save_all_changed_extensions = mock.Mock()
+        activate = d.activate_config_changes = mock.Mock()
+        d.buttons['Apply'].invoke()
+        deactivate.assert_called_once()
+        save_ext.assert_called_once()
+        activate.assert_called_once()
+        del d.save_all_changed_extensions
+        del d.activate_config_changes, d.deactivate_current_config
+
+    def test_click_cancel(self):
+        d = dialog
+        d.destroy = Func()
         changes['main']['something'] = 1
-        dialog.cancel()
+        d.buttons['Cancel'].invoke()
         self.assertEqual(changes['main'], {})
-        self.assertEqual(destroy.called, 1)
+        self.assertEqual(d.destroy.called, 1)
+        del d.destroy
 
-    @mock.patch('idlelib.configdialog.view_text', new_callable=Func)
-    def test_help(self, view):
+    def test_click_help(self):
         dialog.note.select(dialog.keyspage)
-        dialog.help()
-        s = view.kwds['contents']
-        self.assertTrue(s.startswith('When you click') and
-                        s.endswith('a different name.\n'))
+        with mock.patch.object(configdialog, 'view_text',
+                               new_callable=Func) as view:
+            dialog.buttons['Help'].invoke()
+            title, contents = view.kwds['title'], view.kwds['contents']
+        self.assertEqual(title, 'Help for IDLE preferences')
+        self.assertTrue(contents.startswith('When you click') and
+                        contents.endswith('a different name.\n'))
 
 
 class FontPageTest(unittest.TestCase):
@@ -438,6 +474,48 @@ def click_it(start):
                 eq(d.highlight_target.get(), elem[tag])
                 eq(d.set_highlight_target.called, count)
 
+    def test_highlight_sample_double_click(self):
+        # Test double click on highlight_sample.
+        eq = self.assertEqual
+        d = self.page
+
+        hs = d.highlight_sample
+        hs.focus_force()
+        hs.see(1.0)
+        hs.update_idletasks()
+
+        # Test binding from configdialog.
+        hs.event_generate('<Enter>', x=0, y=0)
+        hs.event_generate('<Motion>', x=0, y=0)
+        # Double click is a sequence of two clicks in a row.
+        for _ in range(2):
+            hs.event_generate('<ButtonPress-1>', x=0, y=0)
+            hs.event_generate('<ButtonRelease-1>', x=0, y=0)
+
+        eq(hs.tag_ranges('sel'), ())
+
+    def test_highlight_sample_b1_motion(self):
+        # Test button motion on highlight_sample.
+        eq = self.assertEqual
+        d = self.page
+
+        hs = d.highlight_sample
+        hs.focus_force()
+        hs.see(1.0)
+        hs.update_idletasks()
+
+        x, y, dx, dy, offset = hs.dlineinfo('1.0')
+
+        # Test binding from configdialog.
+        hs.event_generate('<Leave>')
+        hs.event_generate('<Enter>')
+        hs.event_generate('<Motion>', x=x, y=y)
+        hs.event_generate('<ButtonPress-1>', x=x, y=y)
+        hs.event_generate('<B1-Motion>', x=dx, y=dy)
+        hs.event_generate('<ButtonRelease-1>', x=dx, y=dy)
+
+        eq(hs.tag_ranges('sel'), ())
+
     def test_set_theme_type(self):
         eq = self.assertEqual
         d = self.page
@@ -666,8 +744,13 @@ def test_delete_custom(self):
         idleConf.userCfg['highlight'].SetOption(theme_name, 'name', 'value')
         highpage[theme_name] = {'option': 'True'}
 
+        theme_name2 = 'other theme'
+        idleConf.userCfg['highlight'].SetOption(theme_name2, 'name', 'value')
+        highpage[theme_name2] = {'option': 'False'}
+
         # Force custom theme.
-        d.theme_source.set(False)
+        d.custom_theme_on.state(('!disabled',))
+        d.custom_theme_on.invoke()
         d.custom_name.set(theme_name)
 
         # Cancel deletion.
@@ -675,7 +758,7 @@ def test_delete_custom(self):
         d.button_delete_custom.invoke()
         eq(yesno.called, 1)
         eq(highpage[theme_name], {'option': 'True'})
-        eq(idleConf.GetSectionList('user', 'highlight'), ['spam theme'])
+        eq(idleConf.GetSectionList('user', 'highlight'), [theme_name, theme_name2])
         eq(dialog.deactivate_current_config.called, 0)
         eq(dialog.activate_config_changes.called, 0)
         eq(d.set_theme_type.called, 0)
@@ -685,13 +768,26 @@ def test_delete_custom(self):
         d.button_delete_custom.invoke()
         eq(yesno.called, 2)
         self.assertNotIn(theme_name, highpage)
-        eq(idleConf.GetSectionList('user', 'highlight'), [])
-        eq(d.custom_theme_on.state(), ('disabled',))
-        eq(d.custom_name.get(), '- no custom themes -')
+        eq(idleConf.GetSectionList('user', 'highlight'), [theme_name2])
+        eq(d.custom_theme_on.state(), ())
+        eq(d.custom_name.get(), theme_name2)
         eq(dialog.deactivate_current_config.called, 1)
         eq(dialog.activate_config_changes.called, 1)
         eq(d.set_theme_type.called, 1)
 
+        # Confirm deletion of second theme - empties list.
+        d.custom_name.set(theme_name2)
+        yesno.result = True
+        d.button_delete_custom.invoke()
+        eq(yesno.called, 3)
+        self.assertNotIn(theme_name, highpage)
+        eq(idleConf.GetSectionList('user', 'highlight'), [])
+        eq(d.custom_theme_on.state(), ('disabled',))
+        eq(d.custom_name.get(), '- no custom themes -')
+        eq(dialog.deactivate_current_config.called, 2)
+        eq(dialog.activate_config_changes.called, 2)
+        eq(d.set_theme_type.called, 2)
+
         del dialog.activate_config_changes, dialog.deactivate_current_config
         del d.askyesno
 
@@ -1059,8 +1155,13 @@ def test_delete_custom_keys(self):
         idleConf.userCfg['keys'].SetOption(keyset_name, 'name', 'value')
         keyspage[keyset_name] = {'option': 'True'}
 
+        keyset_name2 = 'other key set'
+        idleConf.userCfg['keys'].SetOption(keyset_name2, 'name', 'value')
+        keyspage[keyset_name2] = {'option': 'False'}
+
         # Force custom keyset.
-        d.keyset_source.set(False)
+        d.custom_keyset_on.state(('!disabled',))
+        d.custom_keyset_on.invoke()
         d.custom_name.set(keyset_name)
 
         # Cancel deletion.
@@ -1068,7 +1169,7 @@ def test_delete_custom_keys(self):
         d.button_delete_custom_keys.invoke()
         eq(yesno.called, 1)
         eq(keyspage[keyset_name], {'option': 'True'})
-        eq(idleConf.GetSectionList('user', 'keys'), ['spam key set'])
+        eq(idleConf.GetSectionList('user', 'keys'), [keyset_name, keyset_name2])
         eq(dialog.deactivate_current_config.called, 0)
         eq(dialog.activate_config_changes.called, 0)
         eq(d.set_keys_type.called, 0)
@@ -1078,13 +1179,26 @@ def test_delete_custom_keys(self):
         d.button_delete_custom_keys.invoke()
         eq(yesno.called, 2)
         self.assertNotIn(keyset_name, keyspage)
-        eq(idleConf.GetSectionList('user', 'keys'), [])
-        eq(d.custom_keyset_on.state(), ('disabled',))
-        eq(d.custom_name.get(), '- no custom keys -')
+        eq(idleConf.GetSectionList('user', 'keys'), [keyset_name2])
+        eq(d.custom_keyset_on.state(), ())
+        eq(d.custom_name.get(), keyset_name2)
         eq(dialog.deactivate_current_config.called, 1)
         eq(dialog.activate_config_changes.called, 1)
         eq(d.set_keys_type.called, 1)
 
+        # Confirm deletion of second keyset - empties list.
+        d.custom_name.set(keyset_name2)
+        yesno.result = True
+        d.button_delete_custom_keys.invoke()
+        eq(yesno.called, 3)
+        self.assertNotIn(keyset_name, keyspage)
+        eq(idleConf.GetSectionList('user', 'keys'), [])
+        eq(d.custom_keyset_on.state(), ('disabled',))
+        eq(d.custom_name.get(), '- no custom keys -')
+        eq(dialog.deactivate_current_config.called, 2)
+        eq(dialog.activate_config_changes.called, 2)
+        eq(d.set_keys_type.called, 2)
+
         del dialog.activate_config_changes, dialog.deactivate_current_config
         del d.askyesno
 
diff --git a/Misc/NEWS.d/next/IDLE/2020-01-27-16-44-29.bpo-30780.nR80qu.rst b/Misc/NEWS.d/next/IDLE/2020-01-27-16-44-29.bpo-30780.nR80qu.rst
new file mode 100644
index 0000000000000..2f65a00a5af3b
--- /dev/null
+++ b/Misc/NEWS.d/next/IDLE/2020-01-27-16-44-29.bpo-30780.nR80qu.rst
@@ -0,0 +1 @@
+Add remaining configdialog tests for buttons and highlights and keys tabs.



More information about the Python-checkins mailing list