bpo-30779: IDLE -- Factor ConfigChanges class from configdialog, put in config; test. (#2612)

* In config, put dump test code in a function; run it and unittest in 'if __name__ == '__main__'.
* Add class config.ConfigChanges based on changes_class_v4.py on bpo issue.
* Add class test_config.ChangesTest, partly based on configdialog_tests_v1.py on bpo issue.
* Revise configdialog to use ConfigChanges, mostly as specified in tracker msg297804.
* Revise test_configdialog to match configdialog changes.  All tests pass in both files.
* Remove configdialog functions unused or moved to ConfigChanges.
Cheryl Sabella contributed parts of the patch.
This commit is contained in:
terryjreedy 2017-07-07 16:00:57 -04:00 committed by GitHub
parent 1881befb90
commit 349abd9e37
4 changed files with 277 additions and 171 deletions

View File

@ -44,7 +44,7 @@ class IdleConfParser(ConfigParser):
"""
cfgFile - string, fully specified configuration file name
"""
self.file = cfgFile
self.file = cfgFile # This is currently '' when testing.
ConfigParser.__init__(self, defaults=cfgDefaults, strict=False)
def Get(self, section, option, type=None, default=None, raw=False):
@ -73,7 +73,8 @@ class IdleConfParser(ConfigParser):
def Load(self):
"Load the configuration file from disk."
self.read(self.file)
if self.file:
self.read(self.file)
class IdleUserConfParser(IdleConfParser):
"""
@ -130,21 +131,22 @@ class IdleUserConfParser(IdleConfParser):
def Save(self):
"""Update user configuration file.
Remove empty sections. If resulting config isn't empty, write the file
to disk. If config is empty, remove the file from disk if it exists.
If self not empty after removing empty sections, write the file
to disk. Otherwise, remove the file from disk if it exists.
"""
if not self.IsEmpty():
fname = self.file
try:
cfgFile = open(fname, 'w')
except OSError:
os.unlink(fname)
cfgFile = open(fname, 'w')
with cfgFile:
self.write(cfgFile)
else:
self.RemoveFile()
fname = self.file
if fname:
if not self.IsEmpty():
try:
cfgFile = open(fname, 'w')
except OSError:
os.unlink(fname)
cfgFile = open(fname, 'w')
with cfgFile:
self.write(cfgFile)
else:
self.RemoveFile()
class IdleConf:
"""Hold config parsers for all idle config files in singleton instance.
@ -158,7 +160,7 @@ class IdleConf:
(user home dir)/.idlerc/config-{config-type}.cfg
"""
def __init__(self):
self.config_types = ('main', 'extensions', 'highlight', 'keys')
self.config_types = ('main', 'highlight', 'keys', 'extensions')
self.defaultCfg = {}
self.userCfg = {}
self.cfg = {} # TODO use to select userCfg vs defaultCfg
@ -766,7 +768,6 @@ class IdleConf:
idleConf = IdleConf()
_warned = set()
def _warn(msg, *key):
key = (msg,) + key
@ -778,9 +779,100 @@ def _warn(msg, *key):
_warned.add(key)
class ConfigChanges(dict):
"""Manage a user's proposed configuration option changes.
Names used across multiple methods:
page -- one of the 4 top-level dicts representing a
.idlerc/config-x.cfg file.
config_type -- name of a page.
section -- a section within a page/file.
option -- name of an option within a section.
value -- value for the option.
Methods
add_option: Add option and value to changes.
save_option: Save option and value to config parser.
save_all: Save all the changes to the config parser and file.
delete_section: Delete section if it exists.
clear: Clear all changes by clearing each page.
"""
def __init__(self):
"Create a page for each configuration file"
self.pages = [] # List of unhashable dicts.
for config_type in idleConf.config_types:
self[config_type] = {}
self.pages.append(self[config_type])
def add_option(self, config_type, section, item, value):
"Add item/value pair for config_type and section."
page = self[config_type]
value = str(value) # Make sure we use a string.
if section not in page:
page[section] = {}
page[section][item] = value
@staticmethod
def save_option(config_type, section, item, value):
"""Return True if the configuration value was added or changed.
Helper for save_all.
"""
if idleConf.defaultCfg[config_type].has_option(section, item):
if idleConf.defaultCfg[config_type].Get(section, item) == value:
# The setting equals a default setting, remove it from user cfg.
return idleConf.userCfg[config_type].RemoveOption(section, item)
# If we got here, set the option.
return idleConf.userCfg[config_type].SetOption(section, item, value)
def save_all(self):
"""Save configuration changes to the user config file.
Then clear self in preparation for additional changes.
"""
idleConf.userCfg['main'].Save()
for config_type in self:
cfg_type_changed = False
page = self[config_type]
for section in page:
if section == 'HelpFiles': # Remove it for replacement.
idleConf.userCfg['main'].remove_section('HelpFiles')
cfg_type_changed = True
for item, value in page[section].items():
if self.save_option(config_type, section, item, value):
cfg_type_changed = True
if cfg_type_changed:
idleConf.userCfg[config_type].Save()
for config_type in ['keys', 'highlight']:
# Save these even if unchanged!
idleConf.userCfg[config_type].Save()
self.clear()
# ConfigDialog caller must add the following call
# self.save_all_changed_extensions() # Uses a different mechanism.
def delete_section(self, config_type, section):
"""Delete a section from self, userCfg, and file.
Used to delete custom themes and keysets.
"""
if section in self[config_type]:
del self[config_type][section]
configpage = idleConf.userCfg[config_type]
configpage.remove_section(section)
configpage.Save()
def clear(self):
"""Clear all 4 pages.
Called in save_all after saving to idleConf.
XXX Mark window *title* when there are changes; unmark here.
"""
for page in self.pages:
page.clear()
# TODO Revise test output, write expanded unittest
#
if __name__ == '__main__':
def _dump(): # htest # (not really, but ignore in coverage)
from zlib import crc32
line, crc = 0, 0
@ -790,10 +882,10 @@ if __name__ == '__main__':
line += 1
crc = crc32(txt.encode(encoding='utf-8'), crc)
print(txt)
#print('***', line, crc, '***') # uncomment for diagnosis
#print('***', line, crc, '***') # Uncomment for diagnosis.
def dumpCfg(cfg):
print('\n', cfg, '\n') # has variable '0xnnnnnnnn' addresses
print('\n', cfg, '\n') # Cfg has variable '0xnnnnnnnn' address.
for key in sorted(cfg.keys()):
sections = cfg[key].sections()
sprint(key)
@ -808,3 +900,9 @@ if __name__ == '__main__':
dumpCfg(idleConf.defaultCfg)
dumpCfg(idleConf.userCfg)
print('\nlines = ', line, ', crc = ', crc, sep='')
if __name__ == '__main__':
import unittest
unittest.main('idlelib.idle_test.test_config',
verbosity=2, exit=False)
#_dump()

View File

@ -20,7 +20,7 @@ import tkinter.colorchooser as tkColorChooser
import tkinter.font as tkFont
import tkinter.messagebox as tkMessageBox
from idlelib.config import idleConf
from idlelib.config import idleConf, ConfigChanges
from idlelib.config_key import GetKeysDialog
from idlelib.dynoption import DynOptionMenu
from idlelib import macosx
@ -28,6 +28,8 @@ from idlelib.query import SectionName, HelpSource
from idlelib.tabbedpages import TabbedPageSet
from idlelib.textview import view_text
changes = ConfigChanges()
class ConfigDialog(Toplevel):
"""Config dialog for IDLE.
@ -71,7 +73,6 @@ class ConfigDialog(Toplevel):
'Shell Stdout Text': ('stdout', '12'),
'Shell Stderr Text': ('stderr', '13'),
}
self.reset_changed_items() # Initialize changed_items dict.
self.create_widgets()
self.resizable(height=FALSE, width=FALSE)
self.transient(parent)
@ -559,16 +560,16 @@ class ConfigDialog(Toplevel):
overriding the default font, we need to write out everything.
"""
value = self.font_name.get()
self.add_changed_item('main', 'EditorWindow', 'font', value)
changes.add_option('main', 'EditorWindow', 'font', value)
value = self.font_size.get()
self.add_changed_item('main', 'EditorWindow', 'font-size', value)
changes.add_option('main', 'EditorWindow', 'font-size', value)
value = self.font_bold.get()
self.add_changed_item('main', 'EditorWindow', 'font-bold', value)
changes.add_option('main', 'EditorWindow', 'font-bold', value)
def var_changed_space_num(self, *params):
"Store change to indentation size."
value = self.space_num.get()
self.add_changed_item('main', 'Indent', 'num-spaces', value)
changes.add_option('main', 'Indent', 'num-spaces', value)
def var_changed_colour(self, *params):
"Process change to color choice."
@ -584,13 +585,13 @@ class ConfigDialog(Toplevel):
value = self.builtin_theme.get()
if value not in old_themes:
if idleConf.GetOption('main', 'Theme', 'name') not in old_themes:
self.add_changed_item('main', 'Theme', 'name', old_themes[0])
self.add_changed_item('main', 'Theme', 'name2', value)
changes.add_option('main', 'Theme', 'name', old_themes[0])
changes.add_option('main', 'Theme', 'name2', value)
self.new_custom_theme.config(text='New theme, see Help',
fg='#500000')
else:
self.add_changed_item('main', 'Theme', 'name', value)
self.add_changed_item('main', 'Theme', 'name2', '')
changes.add_option('main', 'Theme', 'name', value)
changes.add_option('main', 'Theme', 'name2', '')
self.new_custom_theme.config(text='', fg='black')
self.paint_theme_sample()
@ -602,7 +603,7 @@ class ConfigDialog(Toplevel):
"""
value = self.custom_theme.get()
if value != '- no custom themes -':
self.add_changed_item('main', 'Theme', 'name', value)
changes.add_option('main', 'Theme', 'name', value)
self.paint_theme_sample()
def var_changed_is_builtin_theme(self, *params):
@ -612,7 +613,7 @@ class ConfigDialog(Toplevel):
selected theme type.
"""
value = self.is_builtin_theme.get()
self.add_changed_item('main', 'Theme', 'default', value)
changes.add_option('main', 'Theme', 'default', value)
if value:
self.var_changed_builtin_theme()
else:
@ -628,11 +629,11 @@ class ConfigDialog(Toplevel):
key_set = self.custom_keys.get()
event = self.list_bindings.get(ANCHOR).split()[0]
if idleConf.IsCoreBinding(event):
self.add_changed_item('keys', key_set, event, value)
changes.add_option('keys', key_set, event, value)
else: # Event is an extension binding.
ext_name = idleConf.GetExtnNameForEvent(event)
ext_keybind_section = ext_name + '_cfgBindings'
self.add_changed_item('extensions', ext_keybind_section, event, value)
changes.add_option('extensions', ext_keybind_section, event, value)
def var_changed_builtin_keys(self, *params):
"Process selection of builtin key set."
@ -645,13 +646,13 @@ class ConfigDialog(Toplevel):
value = self.builtin_keys.get()
if value not in old_keys:
if idleConf.GetOption('main', 'Keys', 'name') not in old_keys:
self.add_changed_item('main', 'Keys', 'name', old_keys[0])
self.add_changed_item('main', 'Keys', 'name2', value)
changes.add_option('main', 'Keys', 'name', old_keys[0])
changes.add_option('main', 'Keys', 'name2', value)
self.new_custom_keys.config(text='New key set, see Help',
fg='#500000')
else:
self.add_changed_item('main', 'Keys', 'name', value)
self.add_changed_item('main', 'Keys', 'name2', '')
changes.add_option('main', 'Keys', 'name', value)
changes.add_option('main', 'Keys', 'name2', '')
self.new_custom_keys.config(text='', fg='black')
self.load_keys_list(value)
@ -659,13 +660,13 @@ class ConfigDialog(Toplevel):
"Process selection of custom key set."
value = self.custom_keys.get()
if value != '- no custom keys -':
self.add_changed_item('main', 'Keys', 'name', value)
changes.add_option('main', 'Keys', 'name', value)
self.load_keys_list(value)
def var_changed_are_keys_builtin(self, *params):
"Process toggle between builtin key set and custom key set."
value = self.are_keys_builtin.get()
self.add_changed_item('main', 'Keys', 'default', value)
changes.add_option('main', 'Keys', 'default', value)
if value:
self.var_changed_builtin_keys()
else:
@ -674,59 +675,27 @@ class ConfigDialog(Toplevel):
def var_changed_win_width(self, *params):
"Store change to window width."
value = self.win_width.get()
self.add_changed_item('main', 'EditorWindow', 'width', value)
changes.add_option('main', 'EditorWindow', 'width', value)
def var_changed_win_height(self, *params):
"Store change to window height."
value = self.win_height.get()
self.add_changed_item('main', 'EditorWindow', 'height', value)
changes.add_option('main', 'EditorWindow', 'height', value)
def var_changed_startup_edit(self, *params):
"Store change to toggle for starting IDLE in the editor or shell."
value = self.startup_edit.get()
self.add_changed_item('main', 'General', 'editor-on-startup', value)
changes.add_option('main', 'General', 'editor-on-startup', value)
def var_changed_autosave(self, *params):
"Store change to autosave."
value = self.autosave.get()
self.add_changed_item('main', 'General', 'autosave', value)
changes.add_option('main', 'General', 'autosave', value)
def var_changed_encoding(self, *params):
"Store change to encoding."
value = self.encoding.get()
self.add_changed_item('main', 'EditorWindow', 'encoding', value)
def reset_changed_items(self):
"""Reset dictionary containing the items changed on each tab.
When any config item is changed in this dialog, an entry
should be made in the relevant section (config type) of this
dictionary. The key should be the config file section name and the
value a dictionary, whose key:value pairs are item=value pairs for
that config file section.
"""
self.changed_items = {'main':{}, 'highlight':{}, 'keys':{},
'extensions':{}}
def add_changed_item(self, typ, section, item, value):
"Add item/value pair to changed items dictionary for typ and section."
value = str(value) # Make sure we use a string.
if section not in self.changed_items[typ]:
self.changed_items[typ][section] = {}
self.changed_items[typ][section][item] = value
def GetDefaultItems(self):
"Return dictionary of default configuration settings."
d_items={'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}}
for config_type in d_items:
sections = idleConf.GetSectionList('default', config_type)
for section in sections:
d_items[config_type][section] = {}
options = idleConf.defaultCfg[config_type].GetOptionList(section)
for option in options:
d_items[config_type][section][option] = (
idleConf.defaultCfg[config_type].Get(section, option))
return d_items
changes.add_option('main', 'EditorWindow', 'encoding', value)
def set_theme_type(self):
"Set available screen options based on builtin or custom theme."
@ -769,8 +738,8 @@ class ConfigDialog(Toplevel):
else:
current_key_set_name = self.custom_keys.get()
current_bindings = idleConf.GetCurrentKeySet()
if current_key_set_name in self.changed_items['keys']: # unsaved changes
key_set_changes = self.changed_items['keys'][current_key_set_name]
if current_key_set_name in changes['keys']: # unsaved changes
key_set_changes = changes['keys'][current_key_set_name]
for event in key_set_changes:
current_bindings[event] = key_set_changes[event].split()
current_key_sequences = list(current_bindings.values())
@ -832,8 +801,8 @@ class ConfigDialog(Toplevel):
binding = ' '.join(prev_keys[event])
new_keys[event_name] = binding
# Handle any unsaved changes to prev key set.
if prev_key_set_name in self.changed_items['keys']:
key_set_changes = self.changed_items['keys'][prev_key_set_name]
if prev_key_set_name in changes['keys']:
key_set_changes = changes['keys'][prev_key_set_name]
for event in key_set_changes:
new_keys[event] = key_set_changes[event]
# Save the new key set.
@ -863,10 +832,10 @@ class ConfigDialog(Toplevel):
for bind_name in bind_names:
key = ' '.join(keyset[bind_name])
bind_name = bind_name[2:-2] # Trim off the angle brackets.
if keyset_name in self.changed_items['keys']:
if keyset_name in changes['keys']:
# Handle any unsaved changes to this key set.
if bind_name in self.changed_items['keys'][keyset_name]:
key = self.changed_items['keys'][keyset_name][bind_name]
if bind_name in changes['keys'][keyset_name]:
key = changes['keys'][keyset_name][bind_name]
self.list_bindings.insert(END, bind_name+' - '+key)
if reselect:
self.list_bindings.see(list_index)
@ -886,12 +855,8 @@ class ConfigDialog(Toplevel):
'Delete Key Set', delmsg % keyset_name, parent=self):
return
self.deactivate_current_config()
# Remove key set from config.
idleConf.userCfg['keys'].remove_section(keyset_name)
if keyset_name in self.changed_items['keys']:
del(self.changed_items['keys'][keyset_name])
# Write changes.
idleConf.userCfg['keys'].Save()
# Remove key set from changes, config, and file.
changes.remove(keyset_name)
# Reload user key set list.
item_list = idleConf.GetSectionList('user', 'keys')
item_list.sort()
@ -906,7 +871,8 @@ class ConfigDialog(Toplevel):
self.builtin_keys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')
or idleConf.default_keys())
# User can't back out of these changes, they must be applied now.
self.save_all_changed_configs()
changes.save_all()
self.save_all_changed_extensions()
self.activate_config_changes()
self.set_keys_type()
@ -923,12 +889,8 @@ class ConfigDialog(Toplevel):
'Delete Theme', delmsg % theme_name, parent=self):
return
self.deactivate_current_config()
# Remove theme from config.
idleConf.userCfg['highlight'].remove_section(theme_name)
if theme_name in self.changed_items['highlight']:
del(self.changed_items['highlight'][theme_name])
# Write changes.
idleConf.userCfg['highlight'].Save()
# Remove theme from changes, config, and file.
changes.delete_section('highlight')
# Reload user theme list.
item_list = idleConf.GetSectionList('user', 'highlight')
item_list.sort()
@ -941,7 +903,8 @@ class ConfigDialog(Toplevel):
self.is_builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
self.builtin_theme.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
# User can't back out of these changes, they must be applied now.
self.save_all_changed_configs()
changes.save_all()
self.save_all_changed_extensions()
self.activate_config_changes()
self.set_theme_type()
@ -979,7 +942,7 @@ class ConfigDialog(Toplevel):
self.text_highlight_sample.tag_config(sample_element, **{plane:new_colour})
theme = self.custom_theme.get()
theme_element = sample_element + '-' + plane
self.add_changed_item('highlight', theme, theme_element, new_colour)
changes.add_option('highlight', theme, theme_element, new_colour)
def get_new_theme_name(self, message):
"Return name of new theme from query popup."
@ -1010,8 +973,8 @@ class ConfigDialog(Toplevel):
theme_name = self.custom_theme.get()
new_theme = idleConf.GetThemeDict(theme_type, theme_name)
# Apply any of the old theme's unsaved changes to the new theme.
if theme_name in self.changed_items['highlight']:
theme_changes = self.changed_items['highlight'][theme_name]
if theme_name in changes['highlight']:
theme_changes = changes['highlight'][theme_name]
for element in theme_changes:
new_theme[element] = theme_changes[element]
# Save the new theme.
@ -1078,8 +1041,8 @@ class ConfigDialog(Toplevel):
colours['background'] = idleConf.GetHighlight(
theme, 'normal', fgBg='bg')
# Handle any unsaved changes to this theme.
if theme in self.changed_items['highlight']:
theme_dict = self.changed_items['highlight'][theme]
if theme in changes['highlight']:
theme_dict = changes['highlight'][theme]
if element + '-foreground' in theme_dict:
colours['foreground'] = theme_dict[element + '-foreground']
if element + '-background' in theme_dict:
@ -1150,10 +1113,10 @@ class ConfigDialog(Toplevel):
self.set_helplist_button_states()
def update_user_help_changed_items(self):
"Clear and rebuild the HelpFiles section in self.changed_items"
self.changed_items['main']['HelpFiles'] = {}
"Clear and rebuild the HelpFiles section in changes"
changes['main']['HelpFiles'] = {}
for num in range(1, len(self.user_helplist) + 1):
self.add_changed_item(
changes.add_option(
'main', 'HelpFiles', str(num),
';'.join(self.user_helplist[num-1][:2]))
@ -1317,37 +1280,6 @@ class ConfigDialog(Toplevel):
value = theme[element]
idleConf.userCfg['highlight'].SetOption(theme_name, element, value)
def set_user_value(self, config_type, section, item, value):
"Return True if the configuration value was added or changed."
if idleConf.defaultCfg[config_type].has_option(section, item):
if idleConf.defaultCfg[config_type].Get(section, item) == value:
# The setting equals a default setting, remove it from user cfg.
return idleConf.userCfg[config_type].RemoveOption(section, item)
# If we got here, set the option.
return idleConf.userCfg[config_type].SetOption(section, item, value)
def save_all_changed_configs(self):
"Save all configuration changes to the user config file."
idleConf.userCfg['main'].Save()
for config_type in self.changed_items:
cfg_type_changed = False
for section in self.changed_items[config_type]:
if section == 'HelpFiles':
# This section gets completely replaced.
idleConf.userCfg['main'].remove_section('HelpFiles')
cfg_type_changed = True
for item in self.changed_items[config_type][section]:
value = self.changed_items[config_type][section][item]
if self.set_user_value(config_type, section, item, value):
cfg_type_changed = True
if cfg_type_changed:
idleConf.userCfg[config_type].Save()
for config_type in ['keys', 'highlight']:
# Save these even if unchanged!
idleConf.userCfg[config_type].Save()
self.reset_changed_items() # Clear the changed items dict.
self.save_all_changed_extensions() # Uses a different mechanism.
def deactivate_current_config(self):
"Remove current key bindings."
# Before a config is saved, some cleanup of current
@ -1378,7 +1310,8 @@ class ConfigDialog(Toplevel):
def apply(self):
"Apply config changes and leave dialog open."
self.deactivate_current_config()
self.save_all_changed_configs()
changes.save_all()
self.save_all_changed_extensions()
self.activate_config_changes()
def help(self):

View File

@ -9,12 +9,13 @@ from idlelib import config
# Tests should not depend on fortuitous user configurations.
# They must not affect actual user .cfg files.
# Replace user parsers with empty parsers that cannot be saved.
# Replace user parsers with empty parsers that cannot be saved
# due to getting '' as the filename when created.
idleConf = config.idleConf
usercfg = idleConf.userCfg
testcfg = {}
usermain = testcfg['main'] = config.IdleUserConfParser('') # filename
usermain = testcfg['main'] = config.IdleUserConfParser('')
userhigh = testcfg['highlight'] = config.IdleUserConfParser('')
userkeys = testcfg['keys'] = config.IdleUserConfParser('')
@ -136,6 +137,87 @@ class CurrentColorKeysTest(unittest.TestCase):
userkeys.remove_section('Custom Keys')
class ChangesTest(unittest.TestCase):
empty = {'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}}
def load(self): # Test_add_option verifies that this works.
changes = self.changes
changes.add_option('main', 'Msec', 'mitem', 'mval')
changes.add_option('highlight', 'Hsec', 'hitem', 'hval')
changes.add_option('keys', 'Ksec', 'kitem', 'kval')
return changes
loaded = {'main': {'Msec': {'mitem': 'mval'}},
'highlight': {'Hsec': {'hitem': 'hval'}},
'keys': {'Ksec': {'kitem':'kval'}},
'extensions': {}}
def setUp(self):
self.changes = config.ConfigChanges()
def test_init(self):
self.assertEqual(self.changes, self.empty)
def test_add_option(self):
changes = self.load()
self.assertEqual(changes, self.loaded)
changes.add_option('main', 'Msec', 'mitem', 'mval')
self.assertEqual(changes, self.loaded)
def test_save_option(self): # Static function does not touch changes.
save_option = self.changes.save_option
self.assertTrue(save_option('main', 'Indent', 'what', '0'))
self.assertFalse(save_option('main', 'Indent', 'what', '0'))
self.assertEqual(usermain['Indent']['what'], '0')
self.assertTrue(save_option('main', 'Indent', 'use-spaces', '0'))
self.assertEqual(usermain['Indent']['use-spaces'], '0')
self.assertTrue(save_option('main', 'Indent', 'use-spaces', '1'))
self.assertFalse(usermain.has_option('Indent', 'use-spaces'))
usermain.remove_section('Indent')
def test_save_added(self):
changes = self.load()
changes.save_all()
self.assertEqual(usermain['Msec']['mitem'], 'mval')
self.assertEqual(userhigh['Hsec']['hitem'], 'hval')
self.assertEqual(userkeys['Ksec']['kitem'], 'kval')
usermain.remove_section('Msec')
userhigh.remove_section('Hsec')
userkeys.remove_section('Ksec')
def test_save_help(self):
changes = self.changes
changes.save_option('main', 'HelpFiles', 'IDLE', 'idledoc')
changes.add_option('main', 'HelpFiles', 'ELDI', 'codeldi')
changes.save_all()
self.assertFalse(usermain.has_option('HelpFiles', 'IDLE'))
self.assertTrue(usermain.has_option('HelpFiles', 'ELDI'))
def test_save_default(self): # Cover 2nd and 3rd false branches.
changes = self.changes
changes.add_option('main', 'Indent', 'use-spaces', '1')
# save_option returns False; cfg_type_changed remains False.
# TODO: test that save_all calls usercfg Saves.
def test_delete_section(self):
changes = self.load()
changes.delete_section('main', 'fake') # Test no exception.
self.assertEqual(changes, self.loaded) # Test nothing deleted.
for cfgtype, section in (('main', 'Msec'), ('keys', 'Ksec')):
changes.delete_section(cfgtype, section)
with self.assertRaises(KeyError):
changes[cfgtype][section] # Test section gone.
# TODO Test change to userkeys and maybe save call.
def test_clear(self):
changes = self.load()
changes.clear()
self.assertEqual(changes, self.empty)
class WarningTest(unittest.TestCase):
def test_warn(self):

View File

@ -3,7 +3,7 @@
Half the class creates dialog, half works with user customizations.
Coverage: 46% just by creating dialog, 56% with current tests.
"""
from idlelib.configdialog import ConfigDialog, idleConf # test import
from idlelib.configdialog import ConfigDialog, idleConf, changes
from test.support import requires
requires('gui')
from tkinter import Tk
@ -21,17 +21,13 @@ testcfg = {
'extensions': config.IdleUserConfParser(''),
}
# ConfigDialog.changed_items is a 3-level hierarchical dictionary of
# pending changes that mirrors the multilevel user config dict.
# For testing, record args in a list for comparison with expected.
changes = []
root = None
configure = None
mainpage = changes['main']
highpage = changes['highlight']
keyspage = changes['keys']
class TestDialog(ConfigDialog):
def add_changed_item(self, *args):
changes.append(args)
class TestDialog(ConfigDialog): pass # Delete?
def setUpModule():
@ -63,31 +59,28 @@ class FontTabTest(unittest.TestCase):
default_size = str(default_font[1])
default_bold = default_font[2] == 'bold'
configure.font_name.set('Test Font')
expected = [
('main', 'EditorWindow', 'font', 'Test Font'),
('main', 'EditorWindow', 'font-size', default_size),
('main', 'EditorWindow', 'font-bold', default_bold)]
self.assertEqual(changes, expected)
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)
expected = [
('main', 'EditorWindow', 'font', 'Test Font'),
('main', 'EditorWindow', 'font-size', '20'),
('main', 'EditorWindow', 'font-bold', default_bold)]
self.assertEqual(changes, expected)
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)
expected = [
('main', 'EditorWindow', 'font', 'Test Font'),
('main', 'EditorWindow', 'font-size', '20'),
('main', 'EditorWindow', 'font-bold', not default_bold)]
self.assertEqual(changes, expected)
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_tabspace(self):
configure.space_num.set(6)
self.assertEqual(changes, [('main', 'Indent', 'num-spaces', 6)])
self.assertEqual(mainpage, {'Indent': {'num-spaces': '6'}})
class HighlightTest(unittest.TestCase):
@ -111,19 +104,19 @@ class GeneralTest(unittest.TestCase):
def test_startup(self):
configure.radio_startup_edit.invoke()
self.assertEqual(changes,
[('main', 'General', 'editor-on-startup', 1)])
self.assertEqual(mainpage,
{'General': {'editor-on-startup': '1'}})
def test_autosave(self):
configure.radio_save_auto.invoke()
self.assertEqual(changes, [('main', 'General', 'autosave', 1)])
self.assertEqual(mainpage, {'General': {'autosave': '1'}})
def test_editor_size(self):
configure.entry_win_height.insert(0, '1')
self.assertEqual(changes, [('main', 'EditorWindow', 'height', '140')])
self.assertEqual(mainpage, {'EditorWindow': {'height': '140'}})
changes.clear()
configure.entry_win_width.insert(0, '1')
self.assertEqual(changes, [('main', 'EditorWindow', 'width', '180')])
self.assertEqual(mainpage, {'EditorWindow': {'width': '180'}})
#def test_help_sources(self): pass # TODO