Skip to content

bpo-30780: Add IDLE configdialog tests #3592

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jan 27, 2020
Merged
3 changes: 3 additions & 0 deletions Lib/idlelib/NEWS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
18 changes: 9 additions & 9 deletions Lib/idlelib/configdialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -205,7 +207,6 @@ def help(self):

Attributes accessed:
note

Methods:
view_text: Method from textview module.
"""
Expand Down Expand Up @@ -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=(
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
158 changes: 136 additions & 22 deletions Lib/idlelib/idle_test/test_configdialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -30,13 +30,15 @@
keyspage = changes['keys']
extpage = changes['extensions']


def setUpModule():
global root, dialog
idleConf.userCfg = testcfg
root = Tk()
# root.withdraw() # Comment out, see issue 30870
dialog = configdialog.ConfigDialog(root, 'Test', _utest=True)


def tearDownModule():
global root, dialog
idleConf.userCfg = usercfg
Expand All @@ -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):
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -666,16 +744,21 @@ 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'}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the corresponding additions for keys cover the failure of if not item_list and the else clause. I suspect it could be done in fewer lines, but I hope to someday factor out the common code, simpfiy the UI, and redo the tests anyway. So leave the additions alone.

# 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.
yesno.result = False
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)
Expand All @@ -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

Expand Down Expand Up @@ -1059,16 +1155,21 @@ 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.
yesno.result = False
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)
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add remaining configdialog tests for buttons and highlights and keys tabs.