Issue #28572: Add 10% to coverage of IDLE's test_configdialog.

Update and augment description of the configuration system.
This commit is contained in:
Terry Jan Reedy 2016-11-07 17:15:01 -05:00
parent f5df73025d
commit f46b7823e6
4 changed files with 179 additions and 73 deletions

View File

@ -4,44 +4,50 @@
# When IDLE starts, it will look in
# the following two sets of files, in order:
#
# default configuration
# ---------------------
# config-main.def the default general config file
# config-extensions.def the default extension config file
# config-highlight.def the default highlighting config file
# config-keys.def the default keybinding config file
# default configuration files in idlelib
# --------------------------------------
# config-main.def default general config file
# config-extensions.def default extension config file
# config-highlight.def default highlighting config file
# config-keys.def default keybinding config file
#
# user configuration
# -------------------
# ~/.idlerc/config-main.cfg the user general config file
# ~/.idlerc/config-extensions.cfg the user extension config file
# ~/.idlerc/config-highlight.cfg the user highlighting config file
# ~/.idlerc/config-keys.cfg the user keybinding config file
# user configuration files in ~/.idlerc
# -------------------------------------
# config-main.cfg user general config file
# config-extensions.cfg user extension config file
# config-highlight.cfg user highlighting config file
# config-keys.cfg user keybinding config file
#
# On Windows2000 and Windows XP the .idlerc directory is at
# Documents and Settings\<username>\.idlerc
#
# On Windows98 it is at c:\.idlerc
# On Windows, the default location of the home directory ('~' above)
# depends on the version. For Windows 10, it is C:\Users\<username>.
#
# Any options the user saves through the config dialog will be saved to
# the relevant user config file. Reverting any general setting to the
# default causes that entry to be wiped from the user file and re-read
# from the default file. User highlighting themes or keybinding sets are
# retained unless specifically deleted within the config dialog. Choosing
# one of the default themes or keysets just applies the relevant settings
# from the default file.
# the relevant user config file. Reverting any general or extension
# setting to the default causes that entry to be wiped from the user
# file and re-read from the default file. This rule applies to each
# item, except that the three editor font items are saved as a group.
#
# User highlighting themes and keybinding sets must have (section) names
# distinct from the default names. All items are added and saved as a
# group. They are retained unless specifically deleted within the config
# dialog. Choosing one of the default themes or keysets just applies the
# relevant settings from the default file.
#
# Additional help sources are listed in the [HelpFiles] section below
# and should be viewable by a web browser (or the Windows Help viewer in
# the case of .chm files). These sources will be listed on the Help
# menu. The pattern, and two examples, are
#
# Additional help sources are listed in the [HelpFiles] section and must be
# viewable by a web browser (or the Windows Help viewer in the case of .chm
# files). These sources will be listed on the Help menu. The pattern is
# <sequence_number = menu item;/path/to/help/source>
# You can't use a semi-colon in a menu item or path. The path will be platform
# specific because of path separators, drive specs etc.
# 1 = IDLE;C:/Programs/Python36/Lib/idlelib/help.html
# 2 = Pillow;https://pillow.readthedocs.io/en/latest/
#
# It is best to use the Configuration GUI to set up additional help sources!
# Example:
#1 = My Extra Help Source;/usr/share/doc/foo/index.html
#2 = Another Help Source;/path/to/another.pdf
# You can't use a semi-colon in a menu item or path. The path will be
# platform specific because of path separators, drive specs etc.
#
# The default files should not be edited except to add new sections to
# config-extensions.def for added extensions . The user files should be
# modified through the Settings dialog.
[General]
editor-on-startup= 0

View File

@ -1,13 +1,20 @@
"""Provides access to stored IDLE configuration information.
"""idlelib.config -- Manage IDLE configuration information.
Refer to the comments at the beginning of config-main.def for a description of
the available configuration files and the design implemented to update user
configuration information. In particular, user configuration choices which
duplicate the defaults will be removed from the user's configuration files,
and if a file becomes empty, it will be deleted.
The comments at the beginning of config-main.def describe the
configuration files and the design implemented to update user
configuration information. In particular, user configuration choices
which duplicate the defaults will be removed from the user's
configuration files, and if a user file becomes empty, it will be
deleted.
The contents of the user files may be altered using the Options/Configure IDLE
menu to access the configuration GUI (configdialog.py), or manually.
The configuration database maps options to values. Comceptually, the
database keys are tuples (config-type, section, item). As implemented,
there are separate dicts for default and user values. Each has
config-type keys 'main', 'extensions', 'highlight', and 'keys'. The
value for each key is a ConfigParser instance that maps section and item
to values. For 'main' and 'extenstons', user values override
default values. For 'highlight' and 'keys', user sections augment the
default sections (and must, therefore, have distinct names).
Throughout this module there is an emphasis on returning useable defaults
when a problem occurs in returning a requested configuration value back to

View File

@ -392,28 +392,28 @@ class ConfigDialog(Toplevel):
text=' Additional Help Sources ')
#frameRun
labelRunChoiceTitle = Label(frameRun, text='At Startup')
radioStartupEdit = Radiobutton(
self.radioStartupEdit = Radiobutton(
frameRun, variable=self.startupEdit, value=1,
command=self.SetKeysType, text="Open Edit Window")
radioStartupShell = Radiobutton(
text="Open Edit Window")
self.radioStartupShell = Radiobutton(
frameRun, variable=self.startupEdit, value=0,
command=self.SetKeysType, text='Open Shell Window')
text='Open Shell Window')
#frameSave
labelRunSaveTitle = Label(frameSave, text='At Start of Run (F5) ')
radioSaveAsk = Radiobutton(
self.radioSaveAsk = Radiobutton(
frameSave, variable=self.autoSave, value=0,
command=self.SetKeysType, text="Prompt to Save")
radioSaveAuto = Radiobutton(
text="Prompt to Save")
self.radioSaveAuto = Radiobutton(
frameSave, variable=self.autoSave, value=1,
command=self.SetKeysType, text='No Prompt')
text='No Prompt')
#frameWinSize
labelWinSizeTitle = Label(
frameWinSize, text='Initial Window Size (in characters)')
labelWinWidthTitle = Label(frameWinSize, text='Width')
entryWinWidth = Entry(
self.entryWinWidth = Entry(
frameWinSize, textvariable=self.winWidth, width=3)
labelWinHeightTitle = Label(frameWinSize, text='Height')
entryWinHeight = Entry(
self.entryWinHeight = Entry(
frameWinSize, textvariable=self.winHeight, width=3)
#frameHelp
frameHelpList = Frame(frameHelp)
@ -443,17 +443,17 @@ class ConfigDialog(Toplevel):
frameHelp.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
#frameRun
labelRunChoiceTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5)
radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5)
self.radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5)
self.radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5)
#frameSave
labelRunSaveTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5)
radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5)
self.radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5)
self.radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5)
#frameWinSize
labelWinSizeTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5)
self.entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5)
labelWinHeightTitle.pack(side=RIGHT, anchor=E, pady=5)
entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5)
self.entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5)
labelWinWidthTitle.pack(side=RIGHT, anchor=E, pady=5)
#frameHelp
frameHelpListButtons.pack(side=RIGHT, padx=5, pady=5, fill=Y)

View File

@ -1,30 +1,123 @@
'''Test idlelib.configdialog.
"""Test idlelib.configdialog.
Coverage: 46% just by creating dialog.
The other half is code for working with user customizations.
'''
from idlelib.configdialog import ConfigDialog # always test import
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 test.support import requires
requires('gui')
from tkinter import Tk
import unittest
import idlelib.config as config
class ConfigDialogTest(unittest.TestCase):
# Tests should not depend on fortuitous user configurations.
# They must not affect actual user .cfg files.
# Use solution from test_config: empty parsers with no filename.
usercfg = idleConf.userCfg
testcfg = {
'main': config.IdleUserConfParser(''),
'highlight': config.IdleUserConfParser(''),
'keys': config.IdleUserConfParser(''),
'extensions': config.IdleUserConfParser(''),
}
@classmethod
def setUpClass(cls):
cls.root = Tk()
cls.root.withdraw()
# ConfigDialog.changedItems 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 = []
class TestDialog(ConfigDialog):
def AddChangedItem(self, *args):
changes.append(args)
@classmethod
def tearDownClass(cls):
cls.root.update_idletasks()
cls.root.destroy()
del cls.root
def setUpModule():
global root, configure
idleConf.userCfg = testcfg
root = Tk()
root.withdraw()
configure = TestDialog(root, 'Test', _utest=True)
def test_configdialog(self):
d = ConfigDialog(self.root, 'Test', _utest=True)
d.remove_var_callbacks()
def tearDownModule():
global root, configure
idleConf.userCfg = testcfg
configure.remove_var_callbacks()
del configure
root.update_idletasks()
root.destroy()
del root
class FontTabTest(unittest.TestCase):
def setUp(self):
changes.clear()
def test_font(self):
configure.fontName.set('Test Font')
expected = [
('main', 'EditorWindow', 'font', 'Test Font'),
('main', 'EditorWindow', 'font-size', '10'),
('main', 'EditorWindow', 'font-bold', False)]
self.assertEqual(changes, expected)
changes.clear()
configure.fontSize.set(12)
expected = [
('main', 'EditorWindow', 'font', 'Test Font'),
('main', 'EditorWindow', 'font-size', '12'),
('main', 'EditorWindow', 'font-bold', False)]
self.assertEqual(changes, expected)
changes.clear()
configure.fontBold.set(True)
expected = [
('main', 'EditorWindow', 'font', 'Test Font'),
('main', 'EditorWindow', 'font-size', '12'),
('main', 'EditorWindow', 'font-bold', True)]
self.assertEqual(changes, expected)
#def test_sample(self): pass # TODO
def test_tabspace(self):
configure.spaceNum.set(6)
self.assertEqual(changes, [('main', 'Indent', 'num-spaces', 6)])
class HighlightTest(unittest.TestCase):
def setUp(self):
changes.clear()
#def test_colorchoose(self): pass # TODO
class KeysTest(unittest.TestCase):
def setUp(self):
changes.clear()
class GeneralTest(unittest.TestCase):
def setUp(self):
changes.clear()
def test_startup(self):
configure.radioStartupEdit.invoke()
self.assertEqual(changes,
[('main', 'General', 'editor-on-startup', 1)])
def test_autosave(self):
configure.radioSaveAuto.invoke()
self.assertEqual(changes, [('main', 'General', 'autosave', 1)])
def test_editor_size(self):
configure.entryWinHeight.insert(0, '1')
self.assertEqual(changes, [('main', 'EditorWindow', 'height', '140')])
changes.clear()
configure.entryWinWidth.insert(0, '1')
self.assertEqual(changes, [('main', 'EditorWindow', 'width', '180')])
#def test_help_sources(self): pass # TODO
if __name__ == '__main__':