view tests/test.py @ 506:dffa692751b1

Implement clear_cache() for the configuration
author Franz Glasner <fzglas.hg@dom66.de>
date Sat, 18 Dec 2021 11:06:34 +0100
parents 4f90e1eb7af8
children ad1e630ba736
line wrap: on
line source

# -*- coding: utf-8 -*-

import sys
import os
import unittest
import platform
import io

sys.path.insert(
    0,
    os.path.abspath(
        os.path.normpath(os.path.join(os.path.dirname(__file__), ".."))))

import configmix
import configmix.ini
import configmix.yaml
import configmix.json
import configmix.py
import configmix.toml
from configmix.compat import u, PY2


TESTDATADIR = os.path.join(
    os.path.abspath(os.path.dirname(__file__)),
    "data")


class T01Basic(unittest.TestCase):

    """Check with low-level internal interfaces"""

    def __check_types(self, cfg):
        self.assertEqual(u("the value"),
                         cfg.get("key1"))
        self.assertTrue(isinstance(cfg.get("key1"), type(u(''))))
        self.assertEqual(2, cfg.get("key2"))
        self.assertEqual(5.7, cfg.get("key3"))
        self.assertTrue(isinstance(cfg.get("key1"), type(u(''))))
        self.assertTrue(cfg.get("key4"))
        self.assertTrue(isinstance(cfg.get("key4"), bool))
        self.assertFalse(cfg.get("key5"))
        self.assertTrue(isinstance(cfg.get("key5"), bool))
        self.assertEqual(255, cfg.get("key6"))
        self.assertEqual(u("Umlaute: ÄÖÜäöüß"),
                         cfg.get("key7"))

    def __check_comment(self, cfg):
        # Check comments: low level comments are *not* filtered
        self.assertEqual(u("Comment 1"), cfg.get("__comment1"))
        self.assertEqual(u("Comment no 2"), cfg.get("__comment2"))

    def __check_no_comment(self, cfg):

        def _c(name):
            def _f():
                cfg[u(name)]
            return _f

        # Variables with leading underscores are *not*  imported by default
        self.assertRaises(KeyError, _c("__comment1"))
        self.assertRaises(KeyError, _c("__comment2"))

    def __check_tree(self, cfg):
        self.assertEqual(u("in the root namespace"),
                         cfg.get("key1"))
        self.assertEqual(u("in the root namespace -- too"),
                         cfg.get("key2"))
        self.assertEqual(32,
                         cfg["tree1"]["key3"])
        self.assertEqual(u("get this as `tree1.tree2.key4'"),
                         cfg["tree1"]["tree2"]["key4"])
        self.assertTrue(cfg["tree1"]["tree2"]["key5"])

    def test01_ini_types(self):
        cfg = configmix.ini.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.__check_types(cfg)
        self.__check_comment(cfg)

    def test01_toml_types(self):
        cfg = configmix.toml.load(os.path.join(TESTDATADIR, "conf1.toml"))
        self.__check_types(cfg)
        self.__check_comment(cfg)

    def test02_py_types(self):
        cfg = configmix.py.load(os.path.join(TESTDATADIR, "conf1.py"))
        self.__check_types(cfg)
        self.__check_no_comment(cfg)

    def test03_yaml_types(self):
        with io.open(os.path.join(TESTDATADIR, "conf1.yml"), "rt",
                     encoding="utf-8") as f:
            cfg = configmix.yaml.safe_load(f)
            if configmix.yaml.OrderedDict:
                self.assertTrue(isinstance(cfg, configmix.yaml.OrderedDict))
            self.__check_types(cfg)
            self.__check_comment(cfg)

    def test04_json_types(self):
        cfg = configmix.json.load(os.path.join(TESTDATADIR, "conf1.json"))
        self.assertTrue(isinstance(cfg, configmix.json.DictImpl))
        self.__check_types(cfg)
        self.__check_comment(cfg)

    def test05_py_export_all(self):
        # When __all__ is given only it's keys are exported
        cfg = configmix.py.load(os.path.join(TESTDATADIR, "conf2.py"))
        self.assertEqual(u("the next value"), cfg.get("key1"))
        self.assertTrue(isinstance(cfg.get("key1"), type(u(''))))
        self.assertTrue(cfg.get("key2") is None)

    def test06_py_hide_private(self):
        # When no __all__ is given all symbols with leading "_" are hidden
        cfg = configmix.py.load(os.path.join(TESTDATADIR, "conf3.py"))
        self.assertEqual(u("the next value "), cfg.get("key1"))
        self.assertTrue(isinstance(cfg.get("key1"), type(u(''))))
        self.assertTrue(cfg.get("_key2") is None)

    def test07_ini_tree(self):
        cfg = configmix.ini.load(os.path.join(TESTDATADIR, "conf10.ini"))
        self.__check_tree(cfg)

    def test08_py_tree(self):
        cfg = configmix.py.load(os.path.join(TESTDATADIR, "conf10.py"))
        self.__check_tree(cfg)

    def test09_yaml_tree(self):
        with io.open(os.path.join(TESTDATADIR, "conf10.yml"), "rt",
                     encoding="utf-8") as f:
            cfg = configmix.yaml.safe_load(f)
            self.__check_tree(cfg)

    def test10_json_tree(self):
        cfg = configmix.json.load(os.path.join(TESTDATADIR, "conf10.json"))
        self.__check_tree(cfg)

    def test11_toml_tree(self):
        cfg = configmix.toml.load(os.path.join(TESTDATADIR, "conf10.toml"))
        self.__check_tree(cfg)

    def test12_yaml_no_duplicate_keys(self):
        import yaml.constructor as yc
        with io.open(os.path.join(TESTDATADIR, "duplicate-keys.yml"), "rt",
                     encoding="utf-8") as f:
            cfg = configmix.yaml.safe_load(f)

        with io.open(os.path.join(TESTDATADIR, "duplicate-keys.yml"), "rt",
                     encoding="utf-8") as f:
            self.assertRaises(yc.ConstructorError,
                              configmix.yaml.safe_load,
                              f, strict=True)

    def test13_yaml_no_duplicate_keys_2(self):
        import yaml.constructor as yc
        with io.open(os.path.join(TESTDATADIR, "duplicate-keys-2.yml"), "rt",
                     encoding="utf-8") as f:
            cfg = configmix.yaml.safe_load(f)

        with io.open(os.path.join(TESTDATADIR, "duplicate-keys-2.yml"), "rt",
                     encoding="utf-8") as f:
            self.assertRaises(yc.ConstructorError,
                              configmix.yaml.safe_load,
                              f, strict=True)


class _T02MixinLoadAndMerge:

    def test01_load(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"))

        self.assertEqual(u("the_database_user"),
                         cfg.getvar_s("db.user.name"))
        self.assertEqual(u("the-database-password"),
                         cfg.getvar_s("db.user.pwd"))

        tmpdir = cfg.getvar_s("tmpdir")
        if os.name == 'nt':
            self.assertFalse(u('/') in tmpdir)
        self.assertEqual(os.path.normpath(
            os.path.abspath(os.path.join(os.getcwd(), "tmp"))),
                         tmpdir)

        self.assertEqual(u("anotherhost"),
                         cfg.getvar_s("db.locinfo.ro.hostname"))
        self.assertEqual(u("localhost"),
                         cfg.getvar_s("db.locinfo.rw.hostname"))

        self.assertEqual(5432, cfg.getvar_s("db.locinfo.ro.port"))

        url = cfg.getvar_s("db.engines.ro.url")
        self.assertEqual(
            u("postgresql+psycopg2://the_database_user:the-database-password@anotherhost:5432/my_database_catalog"),
            url)

        self.assertEqual(u("not a list any more"),
                         cfg.getvar_s("test.List"))

        self.assertEqual(list(range(0, 3)),
                         cfg.getvar_s("test.Str"))

    def test02_load_with_ini(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"),
            os.path.join(TESTDATADIR, "conf22.ini"))

        self.assertEqual(u("the_database_user_2"),
                         cfg.getvar_s("db.user.name"))
        self.assertEqual(u("the-database-password-2"),
                         cfg.getvar_s("db.user.pwd"))

        tmpdir = cfg.getvar_s("tmpdir")
        self.assertEqual(u(os.getcwd()) + u("/tmp\\2"), tmpdir)

        self.assertEqual(u("3rd-host"),
                         cfg.getvar_s("db.locinfo.ro.hostname"))
        self.assertEqual(u("localhost"),
                         cfg.getvar_s("db.locinfo.rw.hostname"))

        self.assertEqual(5432, cfg.getvar_s("db.locinfo.ro.port"))

        url = cfg.getvar_s("db.engines.ro.url")
        self.assertEqual(
            u("postgresql+psycopg2://the_database_user_2:the-database-password-2@3rd-host:5432/my_database_catalog"),
            url)

    def test02b_load_with_ini(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"),
            os.path.join(TESTDATADIR, "conf22.ini"))
        self.assertEqual(u("3rd-host3rd-host"),
                         cfg.getvar_s("db.locinfo.ro.hostname2"))

    def test02_load_with_json(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"),
            # .ini replaced with an equivalent .json
            os.path.join(TESTDATADIR, "conf23.json"))

        self.assertEqual(u("the_database_user_2"),
                         cfg.getvar_s("db.user.name"))
        self.assertEqual(u("the-database-password-2"),
                         cfg.getvar_s("db.user.pwd"))

        tmpdir = cfg.getvar_s("tmpdir")
        self.assertEqual(u(os.getcwd()) + u("/tmp\\3"), tmpdir)

        self.assertEqual(u("3rd-host"),
                         cfg.getvar_s("db.locinfo.ro.hostname"))
        self.assertEqual(u("localhost"),
                         cfg.getvar_s("db.locinfo.rw.hostname"))

        self.assertEqual(5432, cfg.getvar_s("db.locinfo.ro.port"))

        url = cfg.getvar_s("db.engines.ro.url")
        self.assertEqual(
            u("postgresql+psycopg2://the_database_user_2:the-database-password-2@3rd-host:5432/my_database_catalog"),
            url)

    def test02_load_with_toml(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"),
            # .ini replaced with an equivalent .toml
            os.path.join(TESTDATADIR, "conf24.toml"))

        self.assertEqual(u("the_database_user_2"),
                         cfg.getvar_s("db.user.name"))
        self.assertEqual(u("the-database-password-2"),
                         cfg.getvar_s("db.user.pwd"))

        tmpdir = cfg.getvar_s("tmpdir")
        self.assertEqual(u(os.getcwd()) + u("/tmp\\4"), tmpdir)

        self.assertEqual(u("3rd-host"),
                         cfg.getvar_s("db.locinfo.ro.hostname"))
        self.assertEqual(u("localhost"),
                         cfg.getvar_s("db.locinfo.rw.hostname"))

        self.assertEqual(5432, cfg.getvar_s("db.locinfo.ro.port"))

        url = cfg.getvar_s("db.engines.ro.url")
        self.assertEqual(
            u("postgresql+psycopg2://the_database_user_2:the-database-password-2@3rd-host:5432/my_database_catalog"),
            url)

    def test03_namespace(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"),
            os.path.join(TESTDATADIR, "conf22.ini"))
        self.assertEqual(u(os.getcwd()), cfg.getvar("OS:cwd"))
        self.assertEqual(u(platform.python_version()),
                         cfg.getvar_s("PY:version"))

    def test03_namespace_l(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"),
            os.path.join(TESTDATADIR, "conf22.ini"))
        self.assertEqual(u(os.getcwd()), cfg.getvarl("cwd", namespace="OS"))
        self.assertEqual(u(platform.python_version()),
                         cfg.getvarl_s("version", namespace="PY"))

    def test04_no_filter(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"),
            os.path.join(TESTDATADIR, "conf22.ini"))

        def _look():
            return cfg.getvar("OS:cwd|upper")

        self.assertRaises(KeyError, _look)

    def test04_no_filter_l(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"),
            os.path.join(TESTDATADIR, "conf22.ini"))

        def _look():
            return cfg.getvarl("cwd|upper", namespace="OS")

        self.assertRaises(KeyError, _look)

    def test05_comments(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"),
                         os.path.join(TESTDATADIR, "conf21.yml"),
                         os.path.join(TESTDATADIR, "conf22.ini"),
                         os.path.join(TESTDATADIR, "conf23.json"),
                         os.path.join(TESTDATADIR, "conf24.toml"))

        def _c(name):
            def _f():
                cfg.getvar_s(name)
            return _f

        # Variables with leading underscores are *not*  imported by default
        self.assertEqual(0o0027, int(cfg.getvar_s("process.umask"), 0))
        self.assertRaises(KeyError, _c("process.__doc1"))
        self.assertRaises(KeyError, _c("db.__comment1"))
        self.assertRaises(KeyError, _c("db.user.__doc2"))

    def test05_comments_l(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"),
                         os.path.join(TESTDATADIR, "conf21.yml"),
                         os.path.join(TESTDATADIR, "conf22.ini"),
                         os.path.join(TESTDATADIR, "conf23.json"),
                         os.path.join(TESTDATADIR, "conf24.toml"))

        def _c(*names):
            def _f():
                cfg.getvarl_s(*names)
            return _f

        # Variables with leading underscores are *not*  imported by default
        self.assertEqual(0o0027, int(cfg.getvarl_s("process", "umask"), 0))
        self.assertRaises(KeyError, _c("process", "__doc1"))
        self.assertRaises(KeyError, _c("db", "__comment1"))
        self.assertRaises(KeyError, _c("db", "user", "__doc2"))

    def test06_check_all_comments(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"),
                         os.path.join(TESTDATADIR, "conf21.yml"),
                         os.path.join(TESTDATADIR, "conf22.ini"),
                         os.path.join(TESTDATADIR, "conf23.json"),
                         os.path.join(TESTDATADIR, "conf24.toml"))

        def _check(d):
            for k, v in d.items():
                self.assertFalse(configmix._is_comment(k))
                if isinstance(v, dict):
                    _check(v)

        _check(cfg)

    def test07_deletions(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"),
                         os.path.join(TESTDATADIR, "conf21.yml"),
                         os.path.join(TESTDATADIR, "conf22.ini"),
                         os.path.join(TESTDATADIR, "conf23.json"),
                         os.path.join(TESTDATADIR, "conf24.toml"),
                         os.path.join(TESTDATADIR, "delete-in-dict.yml"))
        # automatic clean-up
        self.assertRaises(KeyError, cfg.getvar_s, "not-deleted")
        # explicit deletion
        self.assertRaises(KeyError, cfg.getvar_s, "to-be-deleted")
        self.assertRaises(KeyError, cfg.getvar_s, "db.user.name")
        self.assertEqual("the-database-password-2", cfg.getvar_s("db.user.pwd"))
        self.assertRaises(KeyError, cfg.getvar_s, "test.Str")
        self.assertEqual("not a list any more", cfg.getvar_s("test.List"))
        self.assertEqual("the last value",
                         cfg.getvar_s("to-be-deleted-but-reassigned"))

    def test07_deletions_l(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"),
                         os.path.join(TESTDATADIR, "conf21.yml"),
                         os.path.join(TESTDATADIR, "conf22.ini"),
                         os.path.join(TESTDATADIR, "conf23.json"),
                         os.path.join(TESTDATADIR, "conf24.toml"),
                         os.path.join(TESTDATADIR, "delete-in-dict.yml"))
        # automatic clean-up
        self.assertRaises(KeyError, cfg.getvarl_s, "not-deleted")
        # explicit deletion
        self.assertRaises(KeyError, cfg.getvarl_s, "to-be-deleted")
        self.assertRaises(KeyError, cfg.getvarl_s, "db" "user.name")
        self.assertEqual("the-database-password-2",
                         cfg.getvarl_s("db", "user", "pwd"))
        self.assertRaises(KeyError, cfg.getvarl_s, "test", "Str")
        self.assertEqual("not a list any more", cfg.getvarl_s("test", "List"))
        self.assertEqual("the last value",
                         cfg.getvarl_s("to-be-deleted-but-reassigned"))

    def test08_None_filter_single(self):
        cfg = self._load()
        x = cfg.expand_variable("{{non-existing|None}}")
        self.assertIsNone(x)

    def test09_None_filter_embedded(self):
        cfg = self._load()
        x = cfg.expand_variable("A{{non-existing|None}}Z")
        self.assertEqual("AZ", x)

    def test10_Empty_filtersingle(self):
        cfg = self._load()
        x = cfg.expand_variable("{{non-existing|Empty}}")
        self.assertEqual("", x)

    def test11_None_filter_pass_through(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"),
                         os.path.join(TESTDATADIR, "conf21.yml"),
                         os.path.join(TESTDATADIR, "conf22.ini"),
                         os.path.join(TESTDATADIR, "conf23.json"),
                         os.path.join(TESTDATADIR, "conf24.toml"))
        x = cfg.expand_variable("{{intl.cache.items|None}}")
        self.assertEqual(10, x)

    def test12_Empty_filter_pass_through(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"),
                         os.path.join(TESTDATADIR, "conf21.yml"),
                         os.path.join(TESTDATADIR, "conf22.ini"),
                         os.path.join(TESTDATADIR, "conf23.json"),
                         os.path.join(TESTDATADIR, "conf24.toml"))
        x = cfg.expand_variable("{{intl.cache.items|Empty}}")
        self.assertEqual(10, x)

    def test13_keyerror(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertRaises(KeyError, cfg.getvar_s, "non.existing.key")

    def test14_getvar_with_default(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual("999", cfg.getvar("non.existing.key", default="999"))

    def test15_getvar_s_with_default(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual("999", cfg.getvar_s("non.existing.key",
                                             default="999"))

    def test16_getintvar_s_with_default(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(9999, cfg.getintvar_s("non.existing.key",
                                               default=9999))
    def test17_getintvar_s_with_default(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertFalse(cfg.getboolvar_s("non.existing.key",
                                          default=u('false')))

    def test18_getfirstvar_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertRaises(
            KeyError,
            cfg.getfirstvar,
            "db.non.existing.key",
            "db.non.existing.key2")

    def test19_getfirstvar_nonexisting_default(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertIsNone(cfg.getfirstvar("db.non.existing.key",
                                          "db.non.existing.key2",
                                          "intl.non.existing",
                                          default=None))

    def test20_getfirstvar_existing(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual("test-configmix", cfg.getfirstvar("intl.domain"))
        self.assertEqual("test-configmix", cfg.getfirstvar("intl.domain",
                                                           "intl.fallback"))
        self.assertEqual("de", cfg.getfirstvar("intl.fallback",
                                               "intl.domain",
                                               default=None))

        self.assertEqual("de", cfg.getfirstvar("intl.non.existing",
                                               "intl.fallback",
                                               default=None))

    def test21_getfirstvar_s_existing(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"))
        self.assertEqual(
            os.getcwd()+"/locale",
            cfg.getfirstvar_s("intl.non.existing", "intl.localedir"))
        self.assertEqual(
            os.getcwd()+"/locale",
            cfg.getfirstvar_s("intl.localedir", "intl.non.existing"))

    def test22_getfirstvar_s_non_existing(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"))
        self.assertIsNone(
            cfg.getfirstvar_s("intl.non.existing", "intl.non.existing2",
                              default=None))
        self.assertRaises(
            KeyError,
            cfg.getfirstvar_s,
            "intl.non.existing",
            "intl.non.existing2")

    def test23_getfirstintvar_s_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertIsNone(cfg.getfirstintvar_s("db.non.existing.key",
                                               "db.non.existing.key2",
                                               "intl.non.existing",
                                               default=None))
        self.assertRaises(
            KeyError,
            cfg.getfirstintvar_s,
            "db.non.existing.key",
            "db.non.exksting.key2",
            "intl.non.existing")

    def test23_getfirstintvar_s_nonexisting_default(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(20,
                         cfg.getfirstintvar_s("db.non.existing.key",
                                              "db.non.existing.key2",
                                              "intl.non.existing",
                                              default=u("20")))
        self.assertEqual(30,
                         cfg.getfirstintvar_s("db.non.existing.key",
                                              "db.non.existing.key2",
                                              "intl.non.existing",
                                              default=30))
        self.assertRaises(
            KeyError,
            cfg.getfirstintvar_s,
            "db.non.existing.key",
            "db.non.exksting.key2",
            "intl.non.existing")

    def test24_getfirstintvar_s_existing(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(10,
                         cfg.getfirstintvar_s("db.non.existing.key",
                                              "intl.cache.items",
                                              "db.non.existing.key2",
                                              default=u("20")))

    def test25_getfirstboolvar_s_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertFalse(
            cfg.getfirstboolvar_s("db.non.existing.key",
                                  "db.non.existing.key2",
                                  "db.engines.rw.no-echo",
                                  default=u("false")))
        self.assertTrue(
            cfg.getfirstboolvar_s("db.non.existing.key",
                                  "db.non.existing.key2",
                                  "db.engines.rw.no-echo",
                                  default=True))
        self.assertRaises(
            KeyError,
            cfg.getfirstboolvar_s,
            "db.non.existing.key",
            "db.non.exksting.key2",
            "b.engines.rw.no-echo")

    def test26_getfirstboolvar_s_existing(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertFalse(
            cfg.getfirstboolvar_s("db.non.existing.key",
                                  "db.engines.rw.echo",
                                  "db.non.existing.key2",
                                  default=u("true")))

    def test27_getfirstvarl_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertRaises(
            KeyError,
            cfg.getfirstvarl,
            *[["db", "non", "existing", "key"],
              ("db", "non", "existing", "key2")])

    def test27b_getfirstvarl_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertRaises(
            KeyError,
            cfg.getfirstvarl,
            *[{"namespace": None, "path": ["db", "non", "existing", "key"]},
              {"namespace": None, "path": ["db", "non", "existing", "key2"]}])

    def test28_getfirstvarl_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertIsNone(cfg.getfirstvarl(
            *[["db", "non", "existing", "key"],
              ("db", "non", "existing", "key2")],
            default=None))

    def test28b_getfirstvarl_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertIsNone(cfg.getfirstvarl(
            *[{"namespace": None, "path": ["db", "non", "existing", "key"]},
              {"namespace": None, "path": ("db", "non", "existing", "key2")}],
            default=None))

    def test29_getfirstvarl_existing(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(
            "test-configmix",
            cfg.getfirstvarl(*(("intl", "domain"),)))
        self.assertEqual(
            "test-configmix",
            cfg.getfirstvarl(*(("intl", "domain"), ("intl", "fallback"))))
        self.assertEqual(
            "de",
            cfg.getfirstvarl(*[["intl", "fallback"],
                               ["intl", "domain"]],
                               default=None))
        self.assertEqual(
            "de",
            cfg.getfirstvarl(*[["intl", "non", "existing"],
                               ["intl", "fallback"]],
                               default=None))

    def test29b_getfirstvarl_existing(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(
            "test-configmix",
            cfg.getfirstvarl(*({"namespace": None,
                                "path": ("intl", "domain")},)))
        self.assertEqual(
            "test-configmix",
            cfg.getfirstvarl(*({"namespace": None,
                                "path": ("intl", "domain")},
                               {"namespace": None, "path": ("intl", "fallback")})))
        self.assertEqual(
            "de",
            cfg.getfirstvarl(*[{"namespace": None, "path": ["intl", "fallback"]},
                               {"namespace": None, "path": ["intl", "domain"]}],
                               default=None))
        self.assertEqual(
            "de",
            cfg.getfirstvarl(*[{"namespace": None, "path": ["intl", "non", "existing"]},
                               {"namespace": None, "path": ["intl", "fallback"]}],
                               default=None))

    def test30_getfirstvarl_s_existing(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"))
        self.assertEqual(
            os.getcwd()+"/locale",
            cfg.getfirstvarl_s(*[["intl", "non", "existing"],
                                 ["intl", "localedir"]]))
        self.assertEqual(
            os.getcwd()+"/locale",
            cfg.getfirstvarl_s(*[["intl", "localedir"], ["intl", "non", "existing"]]))

    def test30b_getfirstvarl_s_existing(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"))
        self.assertEqual(
            os.getcwd()+"/locale",
            cfg.getfirstvarl_s(*[{"namespace": None, "path": ["intl", "non", "existing"]},
                                 {"namespace": None, "path": ["intl", "localedir"]}]))
        self.assertEqual(
            os.getcwd()+"/locale",
            cfg.getfirstvarl_s(*[{"namespace": None, "path": ["intl", "localedir"]}, {"namespace": None, "path": ["intl", "non", "existing"]}]))

    def test31_getfirstvar_s_non_existing(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"))
        self.assertIsNone(
            cfg.getfirstvarl_s(
                *[["intl", "non", "existing"], ["intl", "non", "existing2"]],
                default=None))
        self.assertRaises(
            KeyError,
            cfg.getfirstvarl_s,
            ["intl" ,"non", "existing"],
            ["intl", "non", "existing2"])

    def test31b_getfirstvar_s_non_existing(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"))
        self.assertIsNone(
            cfg.getfirstvarl_s(
                *[{"namespace": None, "path": ["intl", "non", "existing"]},
                  {"namespace": None, "path": ["intl", "non", "existing2"]}],
                default=None))
        self.assertRaises(
            KeyError,
            cfg.getfirstvarl_s,
            {"namespace": None, "path": ["intl" ,"non", "existing"]},
            {"namespace": None, "path": ["intl", "non", "existing2"]})

    def test32_getfirstintvarl_s_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertIsNone(cfg.getfirstintvarl_s(
            *(("db", "non", "existing", "key"),
              ("db", "non", "existing", "key2"),
              ("intl", "non", "existing")),
            default=None))
        self.assertRaises(
            KeyError,
            cfg.getfirstintvarl_s,
            ("db", "non", "existing", "key"),
            ("db", "non", "exksting", "key2"),
            ("intl", "non", "existing"))

    def test33_getfirstintvarl_s_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(
            20,
            cfg.getfirstintvarl_s(
                *(("db", "non", "existing", ".key"),
                  ("db", "non", "existing", "key2"),
                  ("intl", "non", "existing")),
                default=u("20")))
        self.assertEqual(
            30,
            cfg.getfirstintvarl_s(
                *(("db", "non", "existing", "key"),
                  ("db", "non", "existing", "key2"),
                  ("intl", "non", "existing")),
                default=30))
        self.assertRaises(
            KeyError,
            cfg.getfirstintvarl_s,
            ("db", "non", "existing", "key"),
            ("db", "non", "exksting", "key2"),
            ("intl", "non", "existing"))

    def test34_getfirstintvarl_s_existing(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(
            10,
            cfg.getfirstintvarl_s(
                *(("db", "non", "existing", "key"),
                  ("intl", "cache", "items"),
                  ("db", "non", "existing", "key2")),
                default=u("20")))

    def test35_keysl(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(
            set([u"domain", u"localedir", u"fallback", u"cache",]),
            set(cfg.getkeysl(u"intl")))

    def test36_keys(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(
            set([u"name", u"pwd"]),
            set(cfg.getkeys(u"db.user")))

    def test37_get_root_object(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertTrue(cfg.getvarl() is cfg)

    def test38_get_root_object(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertTrue(cfg.getvar(u"") is cfg)

    def test39_get_root_keys(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertEqual(
            set([u"key1", u"key2", u"tree1"]),
            set(cfg.getkeys(u"")))

    def test39b_get_root_keys(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertEqual(
            set([u"key1", u"key2", u"tree1"]),
            set(cfg.getkeysl()))

    def test40_contains_with_string(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertTrue(u"tree1" in cfg)
        self.assertFalse(u"non-existing-tree1" in cfg)

    def test41_contains_with_path(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertTrue((u"tree1", u"tree2") in cfg)
        self.assertFalse((u"tree1", u"non-existing-tree2") in cfg)

    def test43_get_with_string(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertEqual(
            u"in the root namespace",
            cfg.get(u"key1"))
        self.assertTrue(
            cfg.get(u"key1-not", default=None) is None)

    def test43_get_with_path(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertEqual(
            0x20,
            cfg.get((u"tree1", u"key3")))
        self.assertEqual(
            0x1,
            cfg.get((u"no", u"key"), default=0x1))

    def test44_iterator(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.py"))
        s = []
        for k in cfg:
            s.append(k)
        s.sort()

        self.assertEqual([u"key1", u"key2", u"tree1"], s)

    def test45_clear_cache(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.py"))
        cfg.clear_cache()


class T02LoadAndMerge(_T02MixinLoadAndMerge, unittest.TestCase):

    def setUp(self):
        self._load = configmix.load

    def test0100_identity(self):
        cfg = configmix.ini.load(os.path.join(TESTDATADIR, "conf1.ini"))
        cfg2 = configmix.merge(cfg, None)
        self.assertEqual(id(cfg), id(cfg2))

    def test0101_identity(self):
        cfg = configmix.ini.load(os.path.join(TESTDATADIR, "conf1.ini"))
        cfg2 = configmix.merge(cfg, {})
        self.assertEqual(id(cfg), id(cfg2))


class T03SafeLoadAndMerge(_T02MixinLoadAndMerge, unittest.TestCase):

    def setUp(self):
        self._load = configmix.safe_load

    def test0100_deepcopy(self):
        cfg = configmix.ini.load(os.path.join(TESTDATADIR, "conf1.ini"))
        cfg2 = configmix.safe_merge(cfg, None)
        self.assertNotEqual(id(cfg), id(cfg2))

    def test0101_deepcopy(self):
        cfg = configmix.ini.load(os.path.join(TESTDATADIR, "conf1.ini"))
        cfg2 = configmix.safe_merge(cfg, {})
        self.assertNotEqual(id(cfg), id(cfg2))


class T04CustomExtension(unittest.TestCase):

    def setUp(self):
        self._reset()

    def tearDown(self):
        self._reset()

    def _reset(self):
        configmix.clear_assoc()
        for pat, fmode in configmix.DEFAULT_ASSOC:
            configmix.set_assoc(pat, fmode)

    def test01_additional(self):
        configmix.set_assoc("*.conf", configmix.get_default_assoc("*.yml"))
        cfg = configmix.load(
            os.path.join(TESTDATADIR, "conf1.ini"),
            os.path.join(TESTDATADIR, "conf30.conf"))
        self.assertEqual(u("Umlaute: ÄÖÜäöüß"), cfg.getvar_s("key7"))
        self.assertEqual(u("new value"), cfg.getvar_s("key-new"))

    def test02_only_style_wrong_style(self):
        configmix.clear_assoc()
        configmix.set_assoc("*.conf", configmix.get_default_assoc("*.yml"))

        def _ld():
            return configmix.load(os.path.join(TESTDATADIR, "conf1.ini"),
                                  os.path.join(TESTDATADIR, "conf30.conf"))

        self.assertRaises(ValueError, _ld)

    def test02_ignore_one_style(self):
        configmix.clear_assoc()
        configmix.set_assoc("*.conf", configmix.get_default_assoc("*.yml"))
        configmix.set_assoc("*", "-*-ignore-*-", append=True)

        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"),
                             os.path.join(TESTDATADIR, "conf30.conf"))
        self.assertEqual(u("new value"), cfg.getvar_s("key-new"))
        self.assertRaises(KeyError, cfg.getvar_s, "key1")

    def test02_ignore_all(self):
        configmix.clear_assoc()
        configmix.set_assoc("*.conf", configmix.get_default_assoc("*.yml"))
        # This inserts at index 0 effectively ignoring all configuration files!
        configmix.set_assoc("*", "-*-ignore-*-")

        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"),
                             os.path.join(TESTDATADIR, "conf30.conf"))
        self.assertRaises(KeyError, cfg.getvar_s, "key-new")
        self.assertRaises(KeyError, cfg.getvar_s, "key1")

    def test03_only_style_corrrect_style(self):
        configmix.clear_assoc()
        configmix.set_assoc("*.conf", configmix.get_default_assoc("*.yml"))
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf30.conf"))
        self.assertEqual(u("new value"), cfg.getvar_s("key-new"))

        def _g():
            return cfg.getvar_s("key7")

        self.assertRaises(KeyError, _g)

    def test04_determine_mode(self):
        configmix.clear_assoc()
        configmix.set_assoc("*.conf", configmix.try_determine_filemode)
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf30.conf"))
        self.assertEqual(u("new value"), cfg.getvar_s("key-new"))

        self.assertRaises(KeyError, cfg.getvar_s, "key7")

    def test05_try_determine_mode_none(self):
        configmix.clear_assoc()
        configmix.set_assoc("*.conf", configmix.try_determine_filemode)

        def _ld():
            return configmix.load(os.path.join(TESTDATADIR, "no-mode.conf"))

        self.assertRaises(ValueError, _ld)

    def test06_try_determine_mode_unknown(self):
        configmix.clear_assoc()
        configmix.set_assoc("*.conf", configmix.try_determine_filemode)

        def _ld():
            return configmix.load(os.path.join(TESTDATADIR, "unknown-mode.conf"))

        self.assertRaises(KeyError, _ld)


class T05SubstituteExpand(unittest.TestCase):

    def setUp(self):
        self._reset()

    def tearDown(self):
        self._reset()

    def _reset(self):
        configmix.clear_assoc()
        for pat, fmode in configmix.DEFAULT_ASSOC:
            configmix.set_assoc(pat, fmode)

    def test01_expand_int_ini(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.assertEqual(2, cfg.getvar_s("key100"))

    def test01_expand_int_ini_l(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.assertEqual(2, cfg.getvarl_s("key100"))

    def test02_expand_int_indirect_ini(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.assertEqual(2, cfg.getvar_s("key102"))

    def test02_expand_int_indirect_ini_l(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.assertEqual(2, cfg.getvarl_s("key102"))

    def test03_expand_int2str_ini(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.assertEqual("the 2 value", cfg.getvar_s("key101"))

    def test03_expand_int2str_ini_l(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.assertEqual("the 2 value", cfg.getvarl_s("key101"))

    def test04_expand_intint2str_ini(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.assertEqual("22", cfg.getvar_s("key103"))

    def test04_expand_intint2str_ini_l(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.assertEqual("22", cfg.getvarl_s("key103"))


class T06References(unittest.TestCase):

    def setUp(self):
        self._reset()
        self._cfg = configmix.load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"),
            os.path.join(TESTDATADIR, "conf22.ini"),
            os.path.join(TESTDATADIR, "conf23.json"),
            os.path.join(TESTDATADIR, "conf24.toml"),
            os.path.join(TESTDATADIR, "reference-style.yml"))

    def tearDown(self):
        self._reset()

    def _reset(self):
        configmix.clear_assoc()
        for pat, fmode in configmix.DEFAULT_ASSOC:
            configmix.set_assoc(pat, fmode)

    def test01_reference_without_expansions(self):
        self.assertTrue(isinstance(self._cfg.getvar("wsgi.profiler"), dict))
        self.assertTrue(isinstance(
            self._cfg.getvar("wsgi.profiler.params"), dict))
        self.assertEqual("werkzeug",
                         self._cfg.getvar("wsgi.profiler.params.type"))
        self.assertTrue(self._cfg.getvar("wsgi.profiler.params.params.evalex"))
        self.assertEqual(self._cfg.getvar("wsgi.debugger"),
                         self._cfg.getvar("wsgi.profiler.params"))

    def test01_reference_without_expansions_l(self):
        self.assertTrue(isinstance(self._cfg.getvarl("wsgi", "profiler"), dict))
        self.assertTrue(isinstance(
            self._cfg.getvarl("wsgi", "profiler", "params"), dict))
        self.assertEqual(
            "werkzeug",
            self._cfg.getvarl("wsgi", "profiler", "params", "type"))
        self.assertTrue(self._cfg.getvarl(
            "wsgi", "profiler", "params", "params", "evalex"))
        self.assertEqual(self._cfg.getvarl("wsgi", "debugger"),
                         self._cfg.getvarl("wsgi", "profiler", "params"))

    def test02_reference__with_expansions(self):
        self.assertTrue(isinstance(self._cfg.getvar_s("wsgi.profiler"), dict))
        self.assertTrue(isinstance(
            self._cfg.getvar_s("wsgi.profiler.params"), dict))
        self.assertTrue(
            self._cfg.getvar_s("wsgi.profiler.params.params.evalex"))
        self.assertEqual("werkzeug",
                         self._cfg.getvar_s("wsgi.profiler.params.type"))

    def test02_reference__with_expansions_l(self):
        self.assertTrue(isinstance(
            self._cfg.getvarl_s("wsgi", "profiler"), dict))
        self.assertTrue(isinstance(
            self._cfg.getvarl_s("wsgi", "profiler", "params"), dict))
        self.assertTrue(
            self._cfg.getvarl_s("wsgi", "profiler", "params", "params", "evalex"))
        self.assertEqual(
            "werkzeug",
            self._cfg.getvarl_s("wsgi", "profiler", "params", "type"))

    def test03_direct_attribute_access_to_expanded_references(self):
        self.assertEqual(
            u"werkzeug",
            self._cfg.wsgi.profiler.params.type)

    def test03b_dict_like_access_expands_references(self):
        self.assertEqual(
            u"werkzeug",
            self._cfg[(u"wsgi", u"profiler", u"params", u"type")])

    def test03c_dict_like_access_with_single_string_key(self):
        self.assertTrue(
            u"profiler" in self._cfg[u"wsgi"])

    def test04_indirect_recursive_references(self):
        self.assertEqual(
            "werkzeug",
            self._cfg.getvar_s("testref.here.params.type"))
        self.assertTrue(
            self._cfg.getvar_s("testref.here.params.params.evalex"))

    def test04_indirect_recursive_references_l(self):
        self.assertEqual(
            "werkzeug",
            self._cfg.getvarl_s("testref", "here", "params", "type"))
        self.assertTrue(
            self._cfg.getvarl_s("testref", "here", "params", "params", "evalex"))

    def test05_recursive_expansion(self):
        c = self._cfg.getvar_s("testref")
        self.assertTrue(c["here"]["params"]["params"]["evalex"])

    def test05_recursive_expansion_l(self):
        c = self._cfg.getvarl_s("testref")
        self.assertTrue(c["here"]["params"]["params"]["evalex"])

    def test06_no_recursive_expansion_in_getvar_parents(self):
        v = self._cfg.getvar("wsgi.profiler")
        self.assertEqual(
            "{{ref:#wsgi.debugger}}",
            v["params"])

    def test06_no_recursive_expansion_in_getvar_parents_l(self):
        v = self._cfg.getvarl("wsgi", "profiler")
        self.assertEqual(
            "{{ref:#wsgi.debugger}}",
            v["params"])

    def test07_explicit_reference_expansion(self):
        v = self._cfg.getvar("wsgi.profiler")
        self.assertTrue(isinstance(self._cfg.expand_if_reference(v["params"]),
                                   dict))

    def test07_explicit_reference_expansion_l(self):
        v = self._cfg.getvarl("wsgi", "profiler")
        self.assertTrue(isinstance(self._cfg.expand_if_reference(v["params"]),
                                   dict))

    def test08_explicit_indirect_expansion_through_value(self):
        v = self._cfg.getvar_s("expand-ref-value.key0")
        self.assertTrue(isinstance(v, bool))
        self.assertTrue(v)
        # but not that .getvar does not **not**
        v2 = self._cfg.getvar("expand-ref-value.key0")
        self.assertEqual("{{testref.here.params.params.evalex}}", v2)

    def test08_explicit_indirect_expansion_through_value_l(self):
        v = self._cfg.getvarl_s("expand-ref-value", "key0")
        self.assertTrue(isinstance(v, bool))
        self.assertTrue(v)
        # but not that .getvar does not **not**
        v2 = self._cfg.getvarl("expand-ref-value", "key0")
        self.assertEqual("{{testref.here.params.params.evalex}}", v2)


class T07Quoting(unittest.TestCase):

    def setUp(self):
        self._reset()
        self._cfg = configmix.load(os.path.join(TESTDATADIR, "quoting.yml"))

    def tearDown(self):
        self._reset()

    def _reset(self):
        configmix.clear_assoc()
        for pat, fmode in configmix.DEFAULT_ASSOC:
            configmix.set_assoc(pat, fmode)

    def test_getvar(self):
        self.assertEqual(
            "value",
            self._cfg.getvar("%x23%x3a%x7c%x25%x2e.%x2e.%x3a.%x25.%x7c./.%x23"))
        self.assertEqual(
            "value",
            self._cfg.getvar(
                "%u0023%u003a%u007c%u0025%u002e.%u002e.%u003a.%u0025.%u007c./.%u0023"))
        self.assertEqual(
            "value",
            self._cfg.getvar(
                "%U00000023%U0000003a%U0000007c%U00000025%U0000002e.%U0000002e.%U0000003a.%U00000025.%U0000007c./.%U00000023"))

    def test_getvar_s(self):
        self.assertEqual(
            "value",
            self._cfg.getvar_s("%x23%x3a%x7c%x25%x2e.%x2e.%x3a.%x25.%x7c./.%x23"))
        self.assertEqual(
            "value",
            self._cfg.getvar_s(
                "%u0023%u003a%u007c%u0025%u002e.%u002e.%u003a.%u0025.%u007c./.%u0023"))
        self.assertEqual(
            "value",
            self._cfg.getvar_s(
                "%U00000023%U0000003a%U0000007c%U00000025%U0000002e.%U0000002e.%U0000003a.%U00000025.%U0000007c./.%U00000023"))

    def test_getvarl(self):
        self.assertEqual(
            "value",
            self._cfg.getvarl("#:|%.", ".", ":", "%", "|", "/", "#"))

    def test_getvarl_s(self):
        self.assertEqual(
            "value",
            self._cfg.getvarl_s("#:|%.", ".", ":", "%", "|", "/", "#"))

    def test_interpolation1(self):
        self.assertEqual(
            "value",
            self._cfg.getvarl_s("events", "qc-2021.1-5G-summit", "xname"))

    def test_interpolation2(self):
        self.assertEqual(
            "value",
            self._cfg.getvar_s("events.qc-2021%x2e1-5G-summit.xname"))

    def test_reference1(self):
        self.assertTrue(
            isinstance(
                self._cfg.getvar_s("events.qc-2021%x2e1-5G-summit.xref"),
                dict))

    def test_reference2(self):
        self.assertEqual(
            "value",
            self._cfg.getvar_s("events.qc-2021%x2e1-5G-summit.xref.%x23"))

    def test_quoting_and_unquoting_are_inverse(self):
        for c in """%.:#|"'{}[]""":
            qc = configmix.quote(c)
            self.assertTrue(qc.startswith("%x") and len(qc) == 4)
            self.assertEqual(c, configmix.unquote(qc))

    def test_quoting_and_unquoting_are_identical(self):
        # other characters
        for c in """abc09/""":
            qc = configmix.quote(c)
            self.assertEqual(c, qc)
            self.assertEqual(c, configmix.unquote(qc))

    def test_namespace_quoting(self):
        v1 = self._cfg.getvar("PY:version")
        v2 = self._cfg.getvar("P%x59:version")
        v3 = self._cfg.getvar("%U00000050Y:version")
        v4 = self._cfg.getvar("%x50%u0059:version")
        v5 = self._cfg.getvarl_s("events", "qc-2021.1-5G-summit", "xver")
        v6 = self._cfg.getvar_s("events.qc-2021%x2e1-5G-summit.xver")
        self.assertEqual(v1, v2)
        self.assertEqual(v1, v3)
        self.assertEqual(v1, v4)
        self.assertEqual(v1, v5)
        self.assertEqual(v1, v6)

    def test_direct_ref_namespace_quoting(self):
        v = self._cfg.getvar_s("re%x66:#%x23%u003a%x7c%U00000025%x2e.%x2e.%x3a.%x25.%x7c./.%x23")
        self.assertEqual("value", v)


class T08Jailed(unittest.TestCase):

    def setUp(self):
        pass

    def tearDown(self):
        self._reset()

    def _reset(self):
        configmix.clear_assoc()
        for pat, fmode in configmix.DEFAULT_ASSOC:
            configmix.set_assoc(pat, fmode)

    def test_root(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(root=u"tree1")

        self.assertTrue(jcfg.getvarl(u"tree2", u"key5"))
        self.assertTrue(jcfg.getvarl_s(u"tree2", u"key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvarl(u"tree2", u"key4"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvarl_s(u"tree2", u"key4"))

        self.assertTrue(jcfg.getvar(u"tree2.key5"))
        self.assertTrue(jcfg.getvar_s(u"tree2.key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvar(u"tree2.key4"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvar_s(u"tree2.key4"))

    def test_root2(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(root=u"tree1.tree2")

        self.assertTrue(jcfg.getvarl(u"key5"))
        self.assertTrue(jcfg.getvarl_s(u"key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvarl(u"key4"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvarl_s(u"key4"))

        self.assertTrue(jcfg.getvar(u"key5"))
        self.assertTrue(jcfg.getvar_s(u"key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvar(u"key4"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvar_s(u"key4"))

    def test_root_non_existing_raises(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertRaises(KeyError, cfg.jailed, root=u"tree-non-existing")
        self.assertRaises(
            KeyError, cfg.jailed, rootpath=(u"non", u"existing", u"tree"))

    def test_rootpath(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=[u"tree1"])

        self.assertTrue(jcfg.getvarl(u"tree2", u"key5"))
        self.assertTrue(jcfg.getvarl_s(u"tree2", u"key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvarl_s(u"tree2", u"key4"))

        self.assertTrue(jcfg.getvar(u"tree2.key5"))
        self.assertTrue(jcfg.getvar_s(u"tree2.key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvar_s(u"tree2.key4"))

    def test_rootpath_non_existing_raises(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertRaises(
            KeyError, cfg.jailed, rootpath=[u"tree-non-existing"])

    def test_root_empty(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(root=u"")

        self.assertFalse(jcfg._path)
        self.assertFalse(jcfg._pathstr)
        self.assertTrue(jcfg._path is not None)
        self.assertTrue(jcfg._pathstr is not None)

        self.assertTrue(jcfg.getvarl(u"tree1", u"tree2", u"key5"))
        self.assertTrue(jcfg.getvarl_s(u"tree1", u"tree2", u"key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvarl_s(u"tree1", u"tree2", u"key4"))

        self.assertTrue(jcfg.getvar(u"tree1.tree2.key5"))
        self.assertTrue(jcfg.getvar_s(u"tree1.tree2.key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvar_s(u"tree1.tree2.key4"))

    def test_rootpath_empty(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=tuple())

        self.assertFalse(jcfg._path)
        self.assertFalse(jcfg._pathstr)
        self.assertTrue(jcfg._path is not None)
        self.assertTrue(jcfg._pathstr is not None)

        self.assertTrue(jcfg.getvarl(u"tree1", u"tree2", u"key5"))
        self.assertTrue(jcfg.getvarl_s(u"tree1", u"tree2", u"key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvarl_s(u"tree1", u"tree2", u"key4"))

        self.assertTrue(jcfg.getvar(u"tree1.tree2.key5"))
        self.assertTrue(jcfg.getvar_s(u"tree1.tree2.key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvar_s(u"tree1.tree2.key4"))

    def test_rootpath_getfirstvar_nonexisting(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1",))

        self.assertIsNone(jcfg.getfirstvarl(
            *[(u"a", u"b"),
              (u"tree2", u"no-key")],
            default=None))
        self.assertIsNone(jcfg.getfirstvarl_s(
            *[(u"a", u"b"),
              (u"tree2", u"no-key")],
            default=None))
        self.assertIsNone(jcfg.getfirstvar(
            u"a.b", u"tree2.no-key",
            default=None))
        self.assertIsNone(jcfg.getfirstvar_s(
            u"a.b", u"tree2.no-key",
            default=None))

    def test_rootpath_getfirstvar_raising(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1",))

        self.assertRaises(
            KeyError,
            jcfg.getfirstvarl,
            (u"a", u"b"),
            (u"tree2", u"no-key"))
        self.assertRaises(
            KeyError,
            jcfg.getfirstvarl_s,
            (u"a", u"b"),
            (u"tree2", u"no-key"))
        self.assertRaises(
            KeyError,
            jcfg.getfirstvar,
            u"a.b", u"tree2.no-key")
        self.assertRaises(
            KeyError,
            jcfg.getfirstvar_s,
            u"a.b", u"tree2.no-key")

    def test_rootpath_getfirstvar_existing(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1",))

        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getfirstvarl(
                *[(u"a", u"b"),
                  (u"tree2", u"key4")]))
        self.assertEqual(
            0x20,
            jcfg.getfirstvarl_s(
                *[(u"a", u"b"),
                  (u"key3", ),
                  (u"tree2", u"key4")]))
        self.assertEqual(
            0x20,
            jcfg.getfirstvar(
                u"key1", u"key3"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getfirstvar_s(
                u"a.b", u"tree2.key4", u"tree2.key5"))

    def test_root_getfirstvar_nonexisting(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(root=u"tree1")

        self.assertIsNone(jcfg.getfirstvarl(
            *[(u"a", u"b"),
              (u"tree2", u"no-key")],
            default=None))
        self.assertIsNone(jcfg.getfirstvarl_s(
            *[(u"a", u"b"),
              (u"tree2", u"no-key")],
            default=None))
        self.assertIsNone(jcfg.getfirstvarl_s(
            *[(u"a", u"b"),
              (u"tree2", u"no-key")],
            default=None))
        self.assertIsNone(jcfg.getfirstvar_s(
            u"a.b", u"tree2.no-key",
            default=None))

    def test_root_getfirstvar_raising(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(root=u"tree1")

        self.assertRaises(
            KeyError,
            jcfg.getfirstvarl,
            (u"a", u"b"),
            (u"tree2", u"no-key"))
        self.assertRaises(
            KeyError,
            jcfg.getfirstvarl_s,
            (u"a", u"b"),
            (u"tree2", u"no-key"))
        self.assertRaises(
            KeyError,
            jcfg.getfirstvar,
            u"a.b", u"tree2.no-key")
        self.assertRaises(
            KeyError,
            jcfg.getfirstvar_s,
            u"a.b", u"tree2.no-key")

    def test_base_rebind(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertFalse(cfg.is_jail)

        jcfg = cfg.jailed(rootpath=[])
        self.assertTrue(jcfg.is_jail)

        self.assertTrue(jcfg.base is cfg)

        self.assertRaises(
            KeyError,
            jcfg.getvarl,
            u"tree1", u"tree2", u"no-key")
        self.assertEqual(
            0x20,
            jcfg.getvar(u"tree1.key3"))
        self.assertEqual(
            u"in the root namespace",
            jcfg.getvar("key1"))

        cfg2 = configmix.load(os.path.join(TESTDATADIR, "conf2.py"))
        self.assertFalse(cfg2.is_jail)

        jcfg.rebind(cfg2)
        self.assertTrue(jcfg.base is cfg2)

        self.assertRaises(
            KeyError,
            jcfg.getvar,
            u"tree1.key3")
        self.assertEqual(
            u"the next value",
            jcfg.getvar("key1"))

    def test_rebind_no_nesting(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertFalse(cfg.is_jail)

        jcfg1 = cfg.jailed(rootpath=[])
        self.assertTrue(jcfg1.is_jail)
        jcfg2 = cfg.jailed(rootpath=[u"tree1"])
        self.assertTrue(jcfg2.is_jail)

        self.assertRaises(
            TypeError,
            jcfg1.rebind,
            jcfg2)

        # base is not because rebind() failed
        self.assertTrue(cfg is jcfg1.base)

    def test_getbool(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2"))

        self.assertFalse(jcfg.getboolvar_s(u"key6"))
        self.assertEqual(u"off", jcfg.getvarl_s(u"key6"))
        self.assertTrue(jcfg.getvar_s(u"key6"))

    def test_subjail_from_rootpath_empty(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=tuple())

        sjcfg = jcfg.jailed(rootpath=(u"tree1",))

        self.assertTrue(sjcfg._path)
        self.assertTrue(sjcfg._pathstr)

        self.assertEqual(0x20, sjcfg.getintvar_s(u"key3"))

        self.assertTrue(sjcfg.base is cfg)

    def test_subjail_from_root_empty(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(root=u"")

        sjcfg = jcfg.jailed(root=u"tree1")

        self.assertTrue(sjcfg._path)
        self.assertTrue(sjcfg._pathstr)

        self.assertEqual(0x20, sjcfg.getintvar_s(u"key3"))

        self.assertTrue(sjcfg.base is cfg)

    def test_getkeys(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(root=u"tree1")

        self.assertEqual(
            set([u"key4", u"key5", u"key6", u"key7", u"key8", u"key9"]),
            set(jcfg.getkeysl(u"tree2")))

        self.assertEqual(
            set([u"key4", u"key5", u"key6", u"key7", u"key8", u"key9"]),
            set(jcfg.getkeys(u"tree2")))

        self.assertEqual(
            set([u"key3", u"tree2"]),
            set(jcfg.getkeysl()))

    def test_getkeys_all_empty_paths(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=tuple())

        self.assertEqual(
            set([u"key1", u"key2", u"tree1"]),
            set(jcfg.getkeysl()))

        self.assertEqual(
            set([u"key1", u"key2", u"tree1"]),
            set(jcfg.getkeys(u"")))

    def test_repr_empty_rootpath(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=tuple())

        self.assertEqual(
            r"_JailedConfiguration(rootpath=())",
            repr(jcfg))

    def test_repr_non_empty_rootpath(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2"))

        if PY2:
            self.assertEqual(
                r"_JailedConfiguration(rootpath=(u'tree1', u'tree2'))",
                repr(jcfg))
        else:
            self.assertEqual(
                r"_JailedConfiguration(rootpath=('tree1', 'tree2'))",
                repr(jcfg))

    def test_dict_level_access_with_single_key(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2"))
        self.assertEqual(
            u"off",
            jcfg[u"key6"])
        try:
            jcfg[u"key3"]
        except KeyError:
            pass
        else:
            self.fail("KeyError expected")

    def test_dict_level_access_with_path(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg[(u"key4",)])
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg[[u"key4"]])
        try:
            jcfg[(u"key3",)]
        except KeyError:
            pass
        else:
            self.fail("KeyError expected")
        try:
            jcfg[[u"key3"]]
        except KeyError:
            pass
        else:
            self.fail("KeyError expected")

    def test_contains_with_string(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1",))

        self.assertTrue(u"key3" in jcfg)
        self.assertFalse(u"key3-not" in jcfg)

    def test_contains_with_path(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1",))

        self.assertTrue((u"key3",) in jcfg)
        self.assertFalse((u"key3-not",) in jcfg)

        self.assertTrue((u"tree2", u"key5") in jcfg)
        self.assertFalse((u"tree2", u"no-key") in jcfg)

        self.assertTrue([u"tree2", u"key5"] in jcfg)
        self.assertFalse([u"tree2", u"no-key"] in jcfg)

    def test_get_with_string(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1",))

        self.assertEqual(
            0x20,
            jcfg.get(u"key3"))
        self.assertEqual(
            0x2,
            jcfg.get(u"no-key3", default=0x2))

    def test_get_with_path(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1",))

        self.assertEqual(
            0x20,
            jcfg.get((u"key3",)))
        self.assertEqual(
            0x3,
            jcfg.get((u"no-key",), default=0x3))
        self.assertEqual(
            u"off",
            jcfg.get((u"tree2", u"key6")))
        self.assertEqual(
            u"the default",
            jcfg.get((u"no", u"key"), default=u"the default"))
        self.assertTrue(
            jcfg.get((u"no", u"key")) is None)

        self.assertEqual(
            u"off",
            jcfg.get([u"tree2", u"key6"]))
        self.assertEqual(
            u"the default",
            jcfg.get([u"no", u"key"], default=u"the default"))
        self.assertTrue(
            jcfg.get([u"no", u"key"]) is None)

    def test_attribute_access(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1",))

        self.assertEqual(0x20, jcfg.key3)
        self.assertEqual(u"off", jcfg.tree2.key6)

    def test_attribute_access_non_existing(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1",))

        try:
            jcfg.non_existing
        except AttributeError:
            pass
        else:
            self.fail("AttributeError expected")

        try:
            jcfg.tree2.non_existing
        except AttributeError:
            pass
        else:
            self.fail("AttributeError expected")

    def test_iteration_dict(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1",))
        s = []
        for k in jcfg:
            s.append(k)
        s.sort()
        self.assertEqual([u"key3", u"tree2"], s)

    def test_iteration_list(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2", u"key8"))
        s = []
        for k in jcfg:
            s.append(k)
        s.sort()
        self.assertEqual([u"in the root namespace", u"val1", u"val2"], s)

    def test_boolean_context_list_false(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2", u"key7"))
        self.assertFalse(jcfg)
        self.assertEqual(0, len(jcfg))

    def test_boolean_context_list_true(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2", u"key8"))
        self.assertTrue(jcfg)
        self.assertEqual(3, len(jcfg))

    def test_boolean_context_dict_false(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2", u"key9"))
        self.assertFalse(jcfg)
        self.assertEqual(0, len(jcfg))

    def test_boolean_context_dict_true(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2"))
        self.assertTrue(jcfg)
        self.assertEqual(6, len(jcfg))

    def test_list_by_index(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2", u"key8"))
        self.assertEqual(u"val1", jcfg[0])
        self.assertEqual(u"val2", jcfg[1])
        self.assertEqual(u"in the root namespace", jcfg[2])


if __name__ == "__main__":
    unittest.main()