# configobj_test.py
# doctests for ConfigObj
# A config file reader/writer that supports nested sections in config files.
# Copyright (C) 2005-2014:
# (name) : (email)
# Michael Foord: fuzzyman AT voidspace DOT org DOT uk
# Nicola Larosa: nico AT tekNico DOT net
# Rob Dennis: rdennis AT gmail DOT com
# Eli Courtwright: eli AT courtwright DOT org

# This software is licensed under the terms of the BSD license.
# http://opensource.org/licenses/BSD-3-Clause

# ConfigObj 5 - main repository for documentation and issue tracking:
# https://github.com/DiffSK/configobj

import sys
# StringIO is used to simulate config files during doctests
if sys.version_info >= (3,):
    # Python 3.x case (io does exist in 2.7, but better to use the 2.x case):
    #http://bugs.python.org/issue8025
    from io import StringIO
else:
    # Python 2.x case, explicitly NOT using cStringIO due to unicode edge cases
    from StringIO import StringIO

import os
import sys
INTP_VER = sys.version_info[:2]
if INTP_VER < (2, 2):
    raise RuntimeError("Python v.2.2 or later needed")

from codecs import BOM_UTF8

from configobj import *
from validate import Validator, VdtValueTooSmallError


def _test_validate():
    """
    >>> val = Validator()

    >>> a = ['foo = fish']
    >>> b = ['foo = integer(default=3)']
    >>> c = ConfigObj(a, configspec=b)
    >>> c
    ConfigObj({'foo': 'fish'})
    >>> from validate import Validator
    >>> v = Validator()
    >>> c.validate(v)
    0
    >>> c.default_values
    {'foo': 3}
    >>> c.restore_default('foo')
    3

    Now testing with repeated sections : BIG TEST
    
    >>> repeated_1 = '''
    ... [dogs]
    ...     [[__many__]] # spec for a dog
    ...         fleas = boolean(default=True)
    ...         tail = option(long, short, default=long)
    ...         name = string(default=rover)
    ...         [[[__many__]]]  # spec for a puppy
    ...             name = string(default="son of rover")
    ...             age = float(default=0.0)
    ... [cats]
    ...     [[__many__]] # spec for a cat
    ...         fleas = boolean(default=True)
    ...         tail = option(long, short, default=short)
    ...         name = string(default=pussy)
    ...         [[[__many__]]] # spec for a kitten
    ...             name = string(default="son of pussy")
    ...             age = float(default=0.0)
    ...         '''.split('\\n')
    >>> repeated_2 = '''
    ... [dogs]
    ... 
    ...     # blank dogs with puppies
    ...     # should be filled in by the configspec
    ...     [[dog1]]
    ...         [[[puppy1]]]
    ...         [[[puppy2]]]
    ...         [[[puppy3]]]
    ...     [[dog2]]
    ...         [[[puppy1]]]
    ...         [[[puppy2]]]
    ...         [[[puppy3]]]
    ...     [[dog3]]
    ...         [[[puppy1]]]
    ...         [[[puppy2]]]
    ...         [[[puppy3]]]
    ... [cats]
    ... 
    ...     # blank cats with kittens
    ...     # should be filled in by the configspec
    ...     [[cat1]]
    ...         [[[kitten1]]]
    ...         [[[kitten2]]]
    ...         [[[kitten3]]]
    ...     [[cat2]]
    ...         [[[kitten1]]]
    ...         [[[kitten2]]]
    ...         [[[kitten3]]]
    ...     [[cat3]]
    ...         [[[kitten1]]]
    ...         [[[kitten2]]]
    ...         [[[kitten3]]]
    ... '''.split('\\n')
    >>> repeated_3 = '''
    ... [dogs]
    ... 
    ...     [[dog1]]
    ...     [[dog2]]
    ...     [[dog3]]
    ... [cats]
    ... 
    ...     [[cat1]]
    ...     [[cat2]]
    ...     [[cat3]]
    ... '''.split('\\n')
    >>> repeated_4 = '''
    ... [__many__]
    ... 
    ...     name = string(default=Michael)
    ...     age = float(default=0.0)
    ...     sex = option(m, f, default=m)
    ... '''.split('\\n')
    >>> repeated_5 = '''
    ... [cats]
    ... [[__many__]]
    ...     fleas = boolean(default=True)
    ...     tail = option(long, short, default=short)
    ...     name = string(default=pussy)
    ...     [[[description]]]
    ...         height = float(default=3.3)
    ...         weight = float(default=6)
    ...         [[[[coat]]]]
    ...             fur = option(black, grey, brown, "tortoise shell", default=black)
    ...             condition = integer(0,10, default=5)
    ... '''.split('\\n')
    >>> val= Validator()
    >>> repeater = ConfigObj(repeated_2, configspec=repeated_1)
    >>> repeater.validate(val)
    1
    >>> repeater == {
    ...     'dogs': {
    ...         'dog1': {
    ...             'fleas': True,
    ...             'tail': 'long',
    ...             'name': 'rover',
    ...             'puppy1': {'name': 'son of rover', 'age': 0.0},
    ...             'puppy2': {'name': 'son of rover', 'age': 0.0},
    ...             'puppy3': {'name': 'son of rover', 'age': 0.0},
    ...         },
    ...         'dog2': {
    ...             'fleas': True,
    ...             'tail': 'long',
    ...             'name': 'rover',
    ...             'puppy1': {'name': 'son of rover', 'age': 0.0},
    ...             'puppy2': {'name': 'son of rover', 'age': 0.0},
    ...             'puppy3': {'name': 'son of rover', 'age': 0.0},
    ...         },
    ...         'dog3': {
    ...             'fleas': True,
    ...             'tail': 'long',
    ...             'name': 'rover',
    ...             'puppy1': {'name': 'son of rover', 'age': 0.0},
    ...             'puppy2': {'name': 'son of rover', 'age': 0.0},
    ...             'puppy3': {'name': 'son of rover', 'age': 0.0},
    ...         },
    ...     },
    ...     'cats': {
    ...         'cat1': {
    ...             'fleas': True,
    ...             'tail': 'short',
    ...             'name': 'pussy',
    ...             'kitten1': {'name': 'son of pussy', 'age': 0.0},
    ...             'kitten2': {'name': 'son of pussy', 'age': 0.0},
    ...             'kitten3': {'name': 'son of pussy', 'age': 0.0},
    ...         },
    ...         'cat2': {
    ...             'fleas': True,
    ...             'tail': 'short',
    ...             'name': 'pussy',
    ...             'kitten1': {'name': 'son of pussy', 'age': 0.0},
    ...             'kitten2': {'name': 'son of pussy', 'age': 0.0},
    ...             'kitten3': {'name': 'son of pussy', 'age': 0.0},
    ...         },
    ...         'cat3': {
    ...             'fleas': True,
    ...             'tail': 'short',
    ...             'name': 'pussy',
    ...             'kitten1': {'name': 'son of pussy', 'age': 0.0},
    ...             'kitten2': {'name': 'son of pussy', 'age': 0.0},
    ...             'kitten3': {'name': 'son of pussy', 'age': 0.0},
    ...         },
    ...     },
    ... }
    1
    >>> repeater = ConfigObj(repeated_3, configspec=repeated_1)
    >>> repeater.validate(val)
    1
    >>> repeater == {
    ...     'cats': {
    ...         'cat1': {'fleas': True, 'tail': 'short', 'name': 'pussy'},
    ...         'cat2': {'fleas': True, 'tail': 'short', 'name': 'pussy'},
    ...         'cat3': {'fleas': True, 'tail': 'short', 'name': 'pussy'},
    ...     },
    ...     'dogs': {
    ...         'dog1': {'fleas': True, 'tail': 'long', 'name': 'rover'},
    ...         'dog2': {'fleas': True, 'tail': 'long', 'name': 'rover'},
    ...         'dog3': {'fleas': True, 'tail': 'long', 'name': 'rover'},
    ...     },
    ... }
    1
    >>> repeater = ConfigObj(configspec=repeated_4)
    >>> repeater['Michael'] = {}
    >>> repeater.validate(val)
    1
    >>> repeater == {
    ...     'Michael': {'age': 0.0, 'name': 'Michael', 'sex': 'm'},
    ... }
    1
    >>> repeater = ConfigObj(repeated_3, configspec=repeated_5)
    >>> repeater == {
    ...     'dogs': {'dog1': {}, 'dog2': {}, 'dog3': {}},
    ...     'cats': {'cat1': {}, 'cat2': {}, 'cat3': {}},
    ... }
    1
    >>> repeater.validate(val)
    1
    >>> repeater == {
    ...     'dogs': {'dog1': {}, 'dog2': {}, 'dog3': {}},
    ...     'cats': {
    ...         'cat1': {
    ...             'fleas': True,
    ...             'tail': 'short',
    ...             'name': 'pussy',
    ...             'description': {
    ...                 'weight': 6.0,
    ...                 'height': 3.2999999999999998,
    ...                 'coat': {'fur': 'black', 'condition': 5},
    ...             },
    ...         },
    ...         'cat2': {
    ...             'fleas': True,
    ...             'tail': 'short',
    ...             'name': 'pussy',
    ...             'description': {
    ...                 'weight': 6.0,
    ...                 'height': 3.2999999999999998,
    ...                 'coat': {'fur': 'black', 'condition': 5},
    ...             },
    ...         },
    ...         'cat3': {
    ...             'fleas': True,
    ...             'tail': 'short',
    ...             'name': 'pussy',
    ...             'description': {
    ...                 'weight': 6.0,
    ...                 'height': 3.2999999999999998,
    ...                 'coat': {'fur': 'black', 'condition': 5},
    ...             },
    ...         },
    ...     },
    ... }
    1
    
    Test that interpolation is preserved for validated string values.
    Also check that interpolation works in configspecs.
    >>> t = ConfigObj(configspec=['test = string'])
    >>> t['DEFAULT'] = {}
    >>> t['DEFAULT']['def_test'] = 'a'
    >>> t['test'] = '%(def_test)s'
    >>> t['test']
    'a'
    >>> v = Validator()
    >>> t.validate(v)
    1
    >>> t.interpolation = False
    >>> t
    ConfigObj({'test': '%(def_test)s', 'DEFAULT': {'def_test': 'a'}})
    >>> specs = [
    ...    'interpolated string  = string(default="fuzzy-%(man)s")',
    ...    '[DEFAULT]',
    ...    'man = wuzzy',
    ...    ]
    >>> c = ConfigObj(configspec=specs)
    >>> c.validate(v)
    1
    >>> c['interpolated string']
    'fuzzy-wuzzy'

    Test SimpleVal
    >>> val = SimpleVal()
    >>> config = '''
    ... test1=40
    ... test2=hello
    ... test3=3
    ... test4=5.0
    ... [section]
    ... test1=40
    ... test2=hello
    ... test3=3
    ... test4=5.0
    ...     [[sub section]]
    ...     test1=40
    ...     test2=hello
    ...     test3=3
    ...     test4=5.0
    ... '''.split('\\n')
    >>> configspec = '''
    ... test1=''
    ... test2=''
    ... test3=''
    ... test4=''
    ... [section]
    ... test1=''
    ... test2=''
    ... test3=''
    ... test4=''
    ...     [[sub section]]
    ...     test1=''
    ...     test2=''
    ...     test3=''
    ...     test4=''
    ... '''.split('\\n')
    >>> o = ConfigObj(config, configspec=configspec)
    >>> o.validate(val)
    1
    >>> o = ConfigObj(configspec=configspec)
    >>> o.validate(val)
    0
    
    Test Flatten Errors
    >>> vtor = Validator()
    >>> my_ini = '''
    ...     option1 = True
    ...     [section1]
    ...     option1 = True
    ...     [section2]
    ...     another_option = Probably
    ...     [section3]
    ...     another_option = True
    ...     [[section3b]]
    ...     value = 3
    ...     value2 = a
    ...     value3 = 11
    ...     '''
    >>> my_cfg = '''
    ...     option1 = boolean()
    ...     option2 = boolean()
    ...     option3 = boolean(default=Bad_value)
    ...     [section1]
    ...     option1 = boolean()
    ...     option2 = boolean()
    ...     option3 = boolean(default=Bad_value)
    ...     [section2]
    ...     another_option = boolean()
    ...     [section3]
    ...     another_option = boolean()
    ...     [[section3b]]
    ...     value = integer
    ...     value2 = integer
    ...     value3 = integer(0, 10)
    ...         [[[section3b-sub]]]
    ...         value = string
    ...     [section4]
    ...     another_option = boolean()
    ...     '''
    >>> cs = my_cfg.split('\\n')
    >>> ini = my_ini.split('\\n')
    >>> cfg = ConfigObj(ini, configspec=cs)
    >>> res = cfg.validate(vtor, preserve_errors=True)
    >>> errors = []
    >>> for entry in flatten_errors(cfg, res):
    ...     section_list, key, error = entry
    ...     section_list.insert(0, '[root]')
    ...     if key is not None:
    ...         section_list.append(key)
    ...     section_string = ', '.join(section_list)
    ...     errors.append('%s%s%s' % (section_string, ' = ', error or 'missing'))
    >>> errors.sort()
    >>> for entry in errors:
    ...     print(entry)
    [root], option2 = missing
    [root], option3 = the value "Bad_value" is of the wrong type.
    [root], section1, option2 = missing
    [root], section1, option3 = the value "Bad_value" is of the wrong type.
    [root], section2, another_option = the value "Probably" is of the wrong type.
    [root], section3, section3b, section3b-sub = missing
    [root], section3, section3b, value2 = the value "a" is of the wrong type.
    [root], section3, section3b, value3 = the value "11" is too big.
    [root], section4 = missing
    """


def _test_errors():
    """
    Test the error messages and objects, in normal mode and unrepr mode.
    >>> bad_syntax = '''
    ... key = "value"
    ... key2 = "value
    ... '''.splitlines()
    >>> c = ConfigObj(bad_syntax)
    Traceback (most recent call last):
    ParseError: Parse error in value at line 3.
    >>> c = ConfigObj(bad_syntax, raise_errors=True)
    Traceback (most recent call last):
    ParseError: Parse error in value at line 3.
    >>> c = ConfigObj(bad_syntax, raise_errors=True, unrepr=True)
    Traceback (most recent call last):
    UnreprError: Parse error in value at line 3.
    >>> try:
    ...     c = ConfigObj(bad_syntax)
    ... except Exception as exc:
    ...     e = exc
    >>> assert(isinstance(e, ConfigObjError))
    >>> print(e)
    Parse error in value at line 3.
    >>> len(e.errors) == 1
    1
    >>> try:
    ...     c = ConfigObj(bad_syntax, unrepr=True)
    ... except Exception as exc:
    ...     e = exc
    >>> assert(isinstance(e, ConfigObjError))
    >>> print(e)
    Parse error from unrepr-ing value at line 3.
    >>> len(e.errors) == 1
    1
    >>> the_error = e.errors[0]
    >>> assert(isinstance(the_error, UnreprError))
    
    >>> multiple_bad_syntax = '''
    ... key = "value"
    ... key2 = "value
    ... key3 = "value2
    ... '''.splitlines()
    >>> try:
    ...     c = ConfigObj(multiple_bad_syntax)
    ... except ConfigObjError as e:
    ...     str(e)
    'Parsing failed with several errors.\\nFirst error at line 3.'
    >>> c = ConfigObj(multiple_bad_syntax, raise_errors=True)
    Traceback (most recent call last):
    ParseError: Parse error in value at line 3.
    >>> c = ConfigObj(multiple_bad_syntax, raise_errors=True, unrepr=True)
    Traceback (most recent call last):
    UnreprError: Parse error in value at line 3.
    >>> try:
    ...     c = ConfigObj(multiple_bad_syntax)
    ... except Exception as exc:
    ...     e = exc
    >>> assert(isinstance(e, ConfigObjError))
    >>> print(e)
    Parsing failed with several errors.
    First error at line 3.
    >>> len(e.errors) == 2
    1
    >>> try:
    ...     c = ConfigObj(multiple_bad_syntax, unrepr=True)
    ... except Exception as exc:
    ...     e = exc
    >>> assert(isinstance(e, ConfigObjError))
    >>> print(e)
    Parsing failed with several errors.
    First error at line 3.
    >>> len(e.errors) == 2
    1
    >>> the_error = e.errors[1]
    >>> assert(isinstance(the_error, UnreprError))
    
    >>> unknown_name = '''
    ... key = "value"
    ... key2 = value
    ... '''.splitlines()
    >>> c = ConfigObj(unknown_name)
    >>> c = ConfigObj(unknown_name, unrepr=True)
    Traceback (most recent call last):
    UnreprError: Unknown name or type in value at line 3.
    >>> c = ConfigObj(unknown_name, raise_errors=True, unrepr=True)
    Traceback (most recent call last):
    UnreprError: Unknown name or type in value at line 3.
    """


def _test_validate_with_copy_and_many():
    """
    >>> spec = '''
    ... [section]
    ... [[__many__]]
    ... value = string(default='nothing')
    ... '''
    >>> config = '''
    ... [section]
    ... [[something]]
    ... '''
    >>> c = ConfigObj(StringIO(config), configspec=StringIO(spec))
    >>> v = Validator()
    >>> r = c.validate(v, copy=True)
    >>> c['section']['something']['value'] == 'nothing'
    True
    """
    
def _test_configspec_with_hash():
    """
    >>> spec = ['stuff = string(default="#ff00dd")']
    >>> c = ConfigObj(spec, _inspec=True)
    >>> c['stuff']
    'string(default="#ff00dd")'
    >>> c = ConfigObj(configspec=spec)
    >>> v = Validator()
    >>> c.validate(v)
    1
    >>> c['stuff']
    '#ff00dd'
    
    
    >>> spec = ['stuff = string(default="fish") # wooble']
    >>> c = ConfigObj(spec, _inspec=True)
    >>> c['stuff']
    'string(default="fish") # wooble'
    """

def _test_many_check():
    """
    >>> spec = ['__many__ = integer()']
    >>> config = ['a = 6', 'b = 7']
    >>> c = ConfigObj(config, configspec=spec)
    >>> v = Validator()
    >>> c.validate(v)
    1
    >>> isinstance(c['a'], int)
    True
    >>> isinstance(c['b'], int)
    True
    
    
    >>> spec = ['[name]', '__many__ = integer()']
    >>> config = ['[name]', 'a = 6', 'b = 7']
    >>> c = ConfigObj(config, configspec=spec)
    >>> v = Validator()
    >>> c.validate(v)
    1
    >>> isinstance(c['name']['a'], int)
    True
    >>> isinstance(c['name']['b'], int)
    True
    
    
    >>> spec = ['[__many__]', '__many__ = integer()']
    >>> config = ['[name]', 'hello = 7', '[thing]', 'fish = 0']
    >>> c = ConfigObj(config, configspec=spec)
    >>> v = Validator()
    >>> c.validate(v)
    1
    >>> isinstance(c['name']['hello'], int)
    True
    >>> isinstance(c['thing']['fish'], int)
    True
    
    
    >>> spec = '''
    ... ___many___ = integer
    ... [__many__]
    ... ___many___ = boolean
    ... [[__many__]]
    ... __many__ = float
    ... '''.splitlines()
    >>> config = '''
    ... fish = 8
    ... buggle = 4
    ... [hi]
    ... one = true
    ... two = false
    ... [[bye]]
    ... odd = 3
    ... whoops = 9.0
    ... [bye]
    ... one = true
    ... two = true
    ... [[lots]]
    ... odd = 3
    ... whoops = 9.0
    ... '''.splitlines()
    >>> c = ConfigObj(config, configspec=spec)
    >>> v = Validator()
    >>> c.validate(v)
    1
    >>> isinstance(c['fish'], int)
    True
    >>> isinstance(c['buggle'], int)
    True
    >>> c['hi']['one']
    1
    >>> c['hi']['two']
    0
    >>> isinstance(c['hi']['bye']['odd'], float)
    True
    >>> isinstance(c['hi']['bye']['whoops'], float)
    True
    >>> c['bye']['one']
    1
    >>> c['bye']['two']
    1
    >>> isinstance(c['bye']['lots']['odd'], float)
    True
    >>> isinstance(c['bye']['lots']['whoops'], float)
    True
    
    
    >>> spec = ['___many___ = integer()']
    >>> config = ['a = 6', 'b = 7']
    >>> c = ConfigObj(config, configspec=spec)
    >>> v = Validator()
    >>> c.validate(v)
    1
    >>> isinstance(c['a'], int)
    True
    >>> isinstance(c['b'], int)
    True

    
    >>> spec = '''
    ... [__many__]
    ... [[__many__]]
    ... __many__ = float
    ... '''.splitlines()
    >>> config = '''
    ... [hi]
    ... [[bye]]
    ... odd = 3
    ... whoops = 9.0
    ... [bye]
    ... [[lots]]
    ... odd = 3
    ... whoops = 9.0
    ... '''.splitlines()
    >>> c = ConfigObj(config, configspec=spec)
    >>> v = Validator()
    >>> c.validate(v)
    1
    >>> isinstance(c['hi']['bye']['odd'], float)
    True
    >>> isinstance(c['hi']['bye']['whoops'], float)
    True
    >>> isinstance(c['bye']['lots']['odd'], float)
    True
    >>> isinstance(c['bye']['lots']['whoops'], float)
    True
    
    >>> s = ['[dog]', '[[cow]]', 'something = boolean', '[[__many__]]', 
    ...      'fish = integer']
    >>> c = ['[dog]', '[[cow]]', 'something = true', '[[ob]]', 
    ...      'fish = 3', '[[bo]]', 'fish = 6']
    >>> ini = ConfigObj(c, configspec=s)
    >>> v = Validator()
    >>> ini.validate(v)
    1
    >>> ini['dog']['cow']['something']
    1
    >>> ini['dog']['ob']['fish']
    3
    >>> ini['dog']['bo']['fish']
    6
    
    
    >>> s = ['[cow]', 'something = boolean', '[__many__]', 
    ...      'fish = integer']
    >>> c = ['[cow]', 'something = true', '[ob]', 
    ...      'fish = 3', '[bo]', 'fish = 6']
    >>> ini = ConfigObj(c, configspec=s)
    >>> v = Validator()
    >>> ini.validate(v)
    1
    >>> ini['cow']['something']
    1
    >>> ini['ob']['fish']
    3
    >>> ini['bo']['fish']
    6
    """

    
def _unexpected_validation_errors():
    """
    Although the input is nonsensical we should not crash but correctly 
    report the failure to validate
    
    # section specified, got scalar
    >>> from validate import ValidateError 
    >>> s = ['[cow]', 'something = boolean']
    >>> c = ['cow = true']
    >>> ini = ConfigObj(c, configspec=s)
    >>> v = Validator()
    >>> ini.validate(v)
    0

    >>> ini = ConfigObj(c, configspec=s)
    >>> res = ini.validate(v, preserve_errors=True)
    >>> check = flatten_errors(ini, res)
    >>> for entry in check:
    ...     isinstance(entry[2], ValidateError)
    ...     print(str(entry[2]))
    True
    Section 'cow' was provided as a single value
    

    # scalar specified, got section
    >>> s = ['something = boolean']
    >>> c = ['[something]', 'cow = true']
    >>> ini = ConfigObj(c, configspec=s)
    >>> v = Validator()
    >>> ini.validate(v)
    0
    
    >>> ini = ConfigObj(c, configspec=s)
    >>> res = ini.validate(v, preserve_errors=True)
    >>> check = flatten_errors(ini, res)
    >>> for entry in check:
    ...     isinstance(entry[2], ValidateError)
    ...     print(str(entry[2]))
    True
    Value 'something' was provided as a section
    
    # unexpected section
    >>> s = []
    >>> c = ['[cow]', 'dog = true']
    >>> ini = ConfigObj(c, configspec=s)
    >>> v = Validator()
    >>> ini.validate(v)
    1
    
    
    >>> s = ['[cow]', 'dog = boolean']
    >>> c = ['[cow]', 'dog = true']
    >>> ini = ConfigObj(c, configspec=s)
    >>> v = Validator()
    >>> ini.validate(v, preserve_errors=True)
    1
    """
    
def _test_pickle():
    """
    >>> import pickle
    >>> s = ['[cow]', 'dog = boolean']
    >>> c = ['[cow]', 'dog = true']
    >>> ini = ConfigObj(c, configspec=s)
    >>> v = Validator()
    >>> string = pickle.dumps(ini)
    >>> new = pickle.loads(string)
    >>> new.validate(v)
    1
    """

def _test_as_list():
    """
    >>> a = ConfigObj()
    >>> a['a'] = 1
    >>> a.as_list('a')
    [1]
    >>> a['a'] = (1,)
    >>> a.as_list('a')
    [1]
    >>> a['a'] = [1]
    >>> a.as_list('a')
    [1]
    """

def _test_list_interpolation():
    """
    >>> c = ConfigObj()
    >>> c['x'] = 'foo'
    >>> c['list'] = ['%(x)s', 3]
    >>> c['list']
    ['foo', 3]
    """

def _test_extra_values():
    """
    >>> spec = ['[section]']
    >>> infile = ['bar = 3', '[something]', 'foo = fish', '[section]', 'foo=boo']
    >>> c = ConfigObj(infile, configspec=spec)
    >>> c.extra_values
    []
    >>> c.extra_values = ['bar', 'gosh', 'what']
    >>> c.validate(Validator())
    1
    >>> c.extra_values
    ['bar', 'something']
    >>> c['section'].extra_values
    ['foo']
    >>> c['something'].extra_values
    []
    """

def _test_reset_and_clear_more():
    """
    >>> c = ConfigObj()
    >>> c.extra_values = ['foo']
    >>> c.defaults = ['bar']
    >>> c.default_values = {'bar': 'baz'}
    >>> c.clear()
    >>> c.defaults
    []
    >>> c.extra_values
    []
    >>> c.default_values
    {'bar': 'baz'}
    >>> c.extra_values = ['foo']
    >>> c.defaults = ['bar']
    >>> c.reset()
    >>> c.defaults
    []
    >>> c.extra_values
    []
    >>> c.default_values
    {}
    """

def _test_invalid_lists():
    """
    >>> v = ['string = val, val2, , val3']
    >>> c = ConfigObj(v)
    Traceback (most recent call last):
    ParseError: Parse error in value at line 1.
    >>> v = ['string = val, val2,, val3']
    >>> c = ConfigObj(v)
    Traceback (most recent call last):
    ParseError: Parse error in value at line 1.
    >>> v = ['string = val, val2,,']
    >>> c = ConfigObj(v)
    Traceback (most recent call last):
    ParseError: Parse error in value at line 1.
    >>> v = ['string = val, ,']
    >>> c = ConfigObj(v)
    Traceback (most recent call last):
    ParseError: Parse error in value at line 1.
    >>> v = ['string = val, ,  ']
    >>> c = ConfigObj(v)
    Traceback (most recent call last):
    ParseError: Parse error in value at line 1.
    >>> v = ['string = ,,']
    >>> c = ConfigObj(v)
    Traceback (most recent call last):
    ParseError: Parse error in value at line 1.
    >>> v = ['string = ,, ']
    >>> c = ConfigObj(v)
    Traceback (most recent call last):
    ParseError: Parse error in value at line 1.
    >>> v = ['string = ,foo']
    >>> c = ConfigObj(v)
    Traceback (most recent call last):
    ParseError: Parse error in value at line 1.
    >>> v = ['string = foo, ']
    >>> c = ConfigObj(v)
    >>> c['string']
    ['foo']
    >>> v = ['string = foo, "']
    >>> c = ConfigObj(v)
    Traceback (most recent call last):
    ParseError: Parse error in value at line 1.
    """

def _test_validation_with_preserve_errors():
    """
    >>> v = Validator()
    >>> spec = ['[section]', 'foo = integer']
    >>> c = ConfigObj(configspec=spec)
    >>> c.validate(v, preserve_errors=True)
    {'section': False}
    >>> c = ConfigObj(['[section]'], configspec=spec)
    >>> c.validate(v)
    False
    >>> c.validate(v, preserve_errors=True)
    {'section': {'foo': False}}
    """


# test _created on Section

# TODO: Test BOM handling
# TODO: Test error code for badly built multiline values
# TODO: Test handling of StringIO
# TODO: Test interpolation with writing


if __name__ == '__main__':
    # run the code tests in doctest format
    #
    testconfig1 = """\
    key1= val    # comment 1
    key2= val    # comment 2
    # comment 3
    [lev1a]     # comment 4
    key1= val    # comment 5
    key2= val    # comment 6
    # comment 7
    [lev1b]    # comment 8
    key1= val    # comment 9
    key2= val    # comment 10
    # comment 11
        [[lev2ba]]    # comment 12
        key1= val    # comment 13
        # comment 14
        [[lev2bb]]    # comment 15
        key1= val    # comment 16
    # comment 17
    [lev1c]    # comment 18
    # comment 19
        [[lev2c]]    # comment 20
        # comment 21
            [[[lev3c]]]    # comment 22
            key1 = val    # comment 23"""
    #
    testconfig2 = b"""\
                        key1 = 'val1'
                        key2 =   "val2"
                        key3 = val3
                        ["section 1"] # comment
                        keys11 = val1
                        keys12 = val2
                        keys13 = val3
                        [section 2]
                        keys21 = val1
                        keys22 = val2
                        keys23 = val3
                        
                            [['section 2 sub 1']]
                            fish = 3
    """
    #
    testconfig6 = b'''
    name1 = """ a single line value """ # comment
    name2 = \''' another single line value \''' # comment
    name3 = """ a single line value """
    name4 = \''' another single line value \'''
        [ "multi section" ]
        name1 = """
        Well, this is a
        multiline value
        """
        name2 = \'''
        Well, this is a
        multiline value
        \'''
        name3 = """
        Well, this is a
        multiline value
        """     # a comment
        name4 = \'''
        Well, this is a
        multiline value
        \'''  # I guess this is a comment too
    '''
    #
    # these cannot be put among the doctests, because the doctest module
    # does a string.expandtabs() on all of them, sigh
    # oneTabCfg = ['[sect]', '\t[[sect]]', '\t\tfoo = bar']
    # twoTabsCfg = ['[sect]', '\t\t[[sect]]', '\t\t\t\tfoo = bar']
    # tabsAndSpacesCfg = [b'[sect]', b'\t \t [[sect]]', b'\t \t \t \t foo = bar']
    #
    import doctest
    m = sys.modules.get('__main__')
    globs = m.__dict__.copy()
    a = ConfigObj(testconfig1.split('\n'), raise_errors=True)
    b = ConfigObj(testconfig2.split(b'\n'), raise_errors=True)
    i = ConfigObj(testconfig6.split(b'\n'), raise_errors=True)
    globs.update({'INTP_VER': INTP_VER, 'a': a, 'b': b, 'i': i})
    pre_failures, pre_tests = doctest.testmod(
        m, globs=globs,
        optionflags=doctest.IGNORE_EXCEPTION_DETAIL | doctest.ELLIPSIS)

    import configobj
    post_failures, post_tests = doctest.testmod(
        configobj, globs=globs,
        optionflags=doctest.IGNORE_EXCEPTION_DETAIL | doctest.ELLIPSIS)
    assert not (pre_failures or post_failures), (
        '{} failures out of {} tests'.format(post_failures + pre_failures,
                                             post_tests + pre_tests))


# Man alive I prefer unittest ;-)
