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@udel.edu>
This commit is contained in:
Cheryl Sabella 2020-01-27 17:15:56 -05:00 committed by Terry Jan Reedy
parent 2528a6c3d0
commit dd023ad161
4 changed files with 149 additions and 31 deletions

View file

@ -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):
@mock.patch(__name__+'.dialog.destroy', new_callable=Func)
def test_cancel(self, destroy):
def test_deactivate_current_config(self):
pass
def activate_config_changes(self):
pass
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