Skip to content

bpo-30899: IDLE: Add idle config parser unittest #2662

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 10 commits into from
Jul 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 31 additions & 26 deletions Lib/idlelib/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,31 +81,6 @@ class IdleUserConfParser(IdleConfParser):
IdleConfigParser specialised for user configuration handling.
"""

def AddSection(self, section):
"If section doesn't exist, add it."
if not self.has_section(section):
self.add_section(section)

def RemoveEmptySections(self):
"Remove any sections that have no options."
for section in self.sections():
if not self.GetOptionList(section):
self.remove_section(section)

def IsEmpty(self):
"Return True if no sections after removing empty sections."
self.RemoveEmptySections()
return not self.sections()

def RemoveOption(self, section, option):
"""Return True if option is removed from section, else False.

False if either section does not exist or did not have option.
"""
if self.has_section(section):
return self.remove_option(section, option)
return False

def SetOption(self, section, option, value):
"""Return True if option is added or changed to value, else False.

Expand All @@ -123,6 +98,31 @@ def SetOption(self, section, option, value):
self.set(section, option, value)
return True

def RemoveOption(self, section, option):
"""Return True if option is removed from section, else False.

False if either section does not exist or did not have option.
"""
if self.has_section(section):
return self.remove_option(section, option)
return False

def AddSection(self, section):
"If section doesn't exist, add it."
if not self.has_section(section):
self.add_section(section)

def RemoveEmptySections(self):
"Remove any sections that have no options."
for section in self.sections():
if not self.GetOptionList(section):
self.remove_section(section)

def IsEmpty(self):
"Return True if no sections after removing empty sections."
self.RemoveEmptySections()
return not self.sections()

def RemoveFile(self):
"Remove user config file self.file from disk if it exists."
if os.path.exists(self.file):
Expand Down Expand Up @@ -828,9 +828,12 @@ def save_option(config_type, section, item, value):
def save_all(self):
"""Save configuration changes to the user config file.

Then clear self in preparation for additional changes.
Clear self in preparation for additional changes.
Return changed for testing.
"""
idleConf.userCfg['main'].Save()

changed = False
for config_type in self:
cfg_type_changed = False
page = self[config_type]
Expand All @@ -843,12 +846,14 @@ def save_all(self):
cfg_type_changed = True
if cfg_type_changed:
idleConf.userCfg[config_type].Save()
changed = True
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.
return changed

def delete_section(self, config_type, section):
"""Delete a section from self, userCfg, and file.
Expand Down
169 changes: 165 additions & 4 deletions Lib/idlelib/idle_test/test_config.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
'''Test idlelib.config.

Much is tested by opening config dialog live or in test_configdialog.
Coverage: 27%
Coverage: 46% (100% for IdleConfParser, IdleUserConfParser*, ConfigChanges).
* Except is OSError clause in Save method.
Much of IdleConf is exercised by ConfigDialog and test_configdialog,
but it should be tested here.
'''
from test.support import captured_stderr
import os
import tempfile
from test.support import captured_stderr, findfile
import unittest
from idlelib import config

Expand All @@ -26,6 +30,161 @@ def tearDownModule():
idleConf.userCfg = usercfg


class IdleConfParserTest(unittest.TestCase):
"""Test that IdleConfParser works"""

config = """
[one]
one = false
two = true
three = 10

[two]
one = a string
two = true
three = false
"""
Copy link
Member

Choose a reason for hiding this comment

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

Slightly redundant, but OK as is.


def test_get(self):
parser = config.IdleConfParser('')
parser.read_string(self.config)
eq = self.assertEqual

# Test with type argument.
self.assertIs(parser.Get('one', 'one', type='bool'), False)
self.assertIs(parser.Get('one', 'two', type='bool'), True)
eq(parser.Get('one', 'three', type='int'), 10)
eq(parser.Get('two', 'one'), 'a string')
self.assertIs(parser.Get('two', 'two', type='bool'), True)
self.assertIs(parser.Get('two', 'three', type='bool'), False)

# Test without type should fallback to string.
eq(parser.Get('two', 'two'), 'true')
eq(parser.Get('two', 'three'), 'false')

# If option not exist, should return None, or default.
self.assertIsNone(parser.Get('not', 'exist'))
eq(parser.Get('not', 'exist', default='DEFAULT'), 'DEFAULT')

def test_get_option_list(self):
parser = config.IdleConfParser('')
parser.read_string(self.config)
get_list = parser.GetOptionList
self.assertCountEqual(get_list('one'), ['one', 'two', 'three'])
self.assertCountEqual(get_list('two'), ['one', 'two', 'three'])
self.assertEqual(get_list('not exist'), [])

def test_load_nothing(self):
parser = config.IdleConfParser('')
parser.Load()
self.assertEqual(parser.sections(), [])

def test_load_file(self):
# Borrow test/cfgparser.1 from test_configparser.
config_path = findfile('cfgparser.1')
parser = config.IdleConfParser(config_path)
parser.Load()

self.assertEqual(parser.Get('Foo Bar', 'foo'), 'newbar')
self.assertEqual(parser.GetOptionList('Foo Bar'), ['foo'])


class IdleUserConfParserTest(unittest.TestCase):
"""Test that IdleUserConfParser works"""

def new_parser(self, path=''):
return config.IdleUserConfParser(path)

def test_set_option(self):
parser = self.new_parser()
parser.add_section('Foo')
# Setting new option in existing section should return True.
self.assertTrue(parser.SetOption('Foo', 'bar', 'true'))
# Setting existing option with same value should return False.
self.assertFalse(parser.SetOption('Foo', 'bar', 'true'))
# Setting exiting option with new value should return True.
self.assertTrue(parser.SetOption('Foo', 'bar', 'false'))
self.assertEqual(parser.Get('Foo', 'bar'), 'false')

# Setting option in new section should create section and return True.
self.assertTrue(parser.SetOption('Bar', 'bar', 'true'))
self.assertCountEqual(parser.sections(), ['Bar', 'Foo'])
self.assertEqual(parser.Get('Bar', 'bar'), 'true')

def test_remove_option(self):
parser = self.new_parser()
parser.AddSection('Foo')
parser.SetOption('Foo', 'bar', 'true')

self.assertTrue(parser.RemoveOption('Foo', 'bar'))
self.assertFalse(parser.RemoveOption('Foo', 'bar'))
self.assertFalse(parser.RemoveOption('Not', 'Exist'))

def test_add_section(self):
parser = self.new_parser()
self.assertEqual(parser.sections(), [])

# Should not add duplicate section.
# Configparser raises DuplicateError, IdleParser not.
parser.AddSection('Foo')
parser.AddSection('Foo')
parser.AddSection('Bar')
self.assertCountEqual(parser.sections(), ['Bar', 'Foo'])

def test_remove_empty_sections(self):
parser = self.new_parser()

parser.AddSection('Foo')
parser.AddSection('Bar')
parser.SetOption('Idle', 'name', 'val')
self.assertCountEqual(parser.sections(), ['Bar', 'Foo', 'Idle'])
parser.RemoveEmptySections()
self.assertEqual(parser.sections(), ['Idle'])

def test_is_empty(self):
parser = self.new_parser()

parser.AddSection('Foo')
parser.AddSection('Bar')
self.assertTrue(parser.IsEmpty())
self.assertEqual(parser.sections(), [])

parser.SetOption('Foo', 'bar', 'false')
parser.AddSection('Bar')
self.assertFalse(parser.IsEmpty())
self.assertCountEqual(parser.sections(), ['Foo'])

def test_remove_file(self):
with tempfile.TemporaryDirectory() as tdir:
path = os.path.join(tdir, 'test.cfg')
parser = self.new_parser(path)
parser.RemoveFile() # Should not raise exception.

parser.AddSection('Foo')
parser.SetOption('Foo', 'bar', 'true')
parser.Save()
self.assertTrue(os.path.exists(path))
parser.RemoveFile()
self.assertFalse(os.path.exists(path))

def test_save(self):
with tempfile.TemporaryDirectory() as tdir:
path = os.path.join(tdir, 'test.cfg')
parser = self.new_parser(path)
parser.AddSection('Foo')
parser.SetOption('Foo', 'bar', 'true')

# Should save to path when config is not empty.
self.assertFalse(os.path.exists(path))
parser.Save()
self.assertTrue(os.path.exists(path))

# Should remove the file from disk when config is empty.
parser.remove_section('Foo')
parser.Save()
self.assertFalse(os.path.exists(path))


class CurrentColorKeysTest(unittest.TestCase):
""" Test colorkeys function with user config [Theme] and [Keys] patterns.

Expand Down Expand Up @@ -179,10 +338,12 @@ def test_save_option(self): # Static function does not touch changes.

def test_save_added(self):
changes = self.load()
changes.save_all()
self.assertTrue(changes.save_all())
self.assertEqual(usermain['Msec']['mitem'], 'mval')
self.assertEqual(userhigh['Hsec']['hitem'], 'hval')
self.assertEqual(userkeys['Ksec']['kitem'], 'kval')
changes.add_option('main', 'Msec', 'mitem', 'mval')
self.assertFalse(changes.save_all())
usermain.remove_section('Msec')
userhigh.remove_section('Hsec')
userkeys.remove_section('Ksec')
Expand Down
1 change: 1 addition & 0 deletions Lib/test/cfgparser.1
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# Also used by idlelib.test_idle.test_config.
[Foo Bar]
foo=newbar
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
IDLE: Add tests for ConfigParser subclasses in config. Patch by Louie Lu.