示例#1
0
def test_invalid_default_kind(tmpdir):
    ''' Check that we raise an error if we supply an invalid datatype or
    kind (precision) in the configuration file '''

    # Test invalid datatype
    content = re.sub(r"real:", "reality:", _CONFIG_CONTENT, flags=re.MULTILINE)
    config_file = tmpdir.join("config_dyn")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()
        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(config_file=str(config_file))

        assert ("Invalid datatype found in the \'[dynamo0.3]\' section "
                in str(err.value))
        assert ("Valid datatypes are: '['real', 'integer', 'logical']'"
                in str(err.value))

    # Test invalid kind (precision)
    content = re.sub("integer: i_def,", "integer: ,", _CONFIG_CONTENT)
    config_file = tmpdir.join("config_dyn")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()
        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(config_file=str(config_file))

        assert ("Supplied kind parameters \'[\'l_def\', \'r_def\']\' in "
                "the \'[dynamo0.3]\' section" in str(err.value))
        assert ("do not define the default kind for one or more supported "
                "datatypes \'[\'real\', \'integer\', \'logical\']\'."
                in str(err.value))
示例#2
0
def test_missing_file(tmpdir):
    ''' Check that we get the expected error when the specified
    config file cannot be found '''
    with pytest.raises(ConfigurationError) as err:
        config = Config()
        config.load(config_file=os.path.join(str(tmpdir), "not_a_file.cfg"))
    assert "not_a_file.cfg does not exist" in str(err)
示例#3
0
def test_invalid_prefix(tmpdir):
    '''Tests invalid PSyData prefixes (i.e. ones that would result
    in invalid Fortran names when used).
    '''

    for prefix in ["1", "&AB", "?", "_ab", "ab'", "cd\"", "ef?"]:
        content = re.sub(r"^VALID_PSY_DATA_PREFIXES.*$",
                         "VALID_PSY_DATA_PREFIXES=" + prefix,
                         _CONFIG_CONTENT,
                         flags=re.MULTILINE)
        config_file = tmpdir.join("config.invalid_psydata")
        with config_file.open(mode="w") as new_cfg:
            new_cfg.write(content)
            new_cfg.close()
            config = Config()
            with pytest.raises(ConfigurationError) as err:
                config.load(config_file=str(config_file))
            # When there is a '"' in the invalid prefix, the "'" in the
            # error message is escaped with a '\'. So in order to test the
            # invalid 'cd"' prefix, we need to have two tests in the assert:
            assert "Invalid PsyData-prefix '{0}' in config file" \
                   .format(prefix) in str(err.value)  \
                or "Invalid PsyData-prefix \\'{0}\\' in config file" \
                   .format(prefix) in str(err.value)
            assert "The prefix must be valid for use as the start of a " \
                   "Fortran variable name." in str(err.value)
示例#4
0
def test_broken_fmt(tmpdir):
    ''' Check the error if the formatting of the configuration file is
    wrong. '''
    # Create a 'config' file without any section headers
    content = "COMPUTE_ANNEXED_DOFS = false\n"
    config_file = tmpdir.join("config")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        with pytest.raises(ConfigurationError) as err:
            config = Config()
            config.load(config_file=str(config_file))
        assert ("ConfigParser failed to read the configuration file. Is it "
                "formatted correctly? (Error was: File contains no section "
                "headers" in str(err.value))

    # Test for general parsing error (here broken key-value mapping)
    content = re.sub(r"^DEFAULTSTUBAPI = .*$",
                     "DEFAULT",
                     _CONFIG_CONTENT,
                     flags=re.MULTILINE)
    config_file = tmpdir.join("config")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        with pytest.raises(ConfigurationError) as err:
            config = Config()
            config.load(config_file=str(config_file))
        assert "Error was: Source contains parsing errors" in str(err.value)
示例#5
0
def test_dm():
    ''' Checks for getter and setter for distributed memory '''
    config = Config()
    config.load(config_file=TEST_CONFIG)
    # Check the setter method
    config.distributed_memory = False
    assert not config.distributed_memory
    with pytest.raises(ConfigurationError) as err:
        config.distributed_memory = "not-a-bool"
    assert "distributed_memory must be a boolean but got " in str(err)
示例#6
0
def test_default_api(tmpdir):
    '''If a config file has no default-api specified, but contains only
    a single (non-default) section, this section should be used as the
    default api.
    '''
    content = re.sub(r"^API.*$", "", _CONFIG_CONTENT, flags=re.MULTILINE)
    config_file = tmpdir.join("config")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()
        config = Config()
        config.load(str(config_file))
        assert config.api == "dynamo0.3"
示例#7
0
def test_default_access_mapping(tmpdir):
    '''Test that the default access mapping is correctly converted
    to AccessTypes.'''
    config_file = tmpdir.join("config")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(_CONFIG_CONTENT)
        new_cfg.close()
        config = Config()
        config.load(str(config_file))

        api_config = config.api_conf("dynamo0.3")
        for access_mode in api_config.get_access_mapping().values():
            assert isinstance(access_mode, AccessType)
示例#8
0
def test_root_name_load(tmpdir, content, result):
    '''Check that the config class returns appropriate values from a
    config file when PSYIR_ROOT_NAME is and isn't provided.

    '''
    config = Config()
    config_file = tmpdir.join("config")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

    config.load(str(config_file))
    assert config._psyir_root_name == result
    assert config.psyir_root_name == result
示例#9
0
def test_default_stubapi_missing():
    ''' Check that we raise an error if the default stub API is missing,
    in which case it defaults to the default_api'''
    content = re.sub(r"^DEFAULTSTUBAPI = .*$",
                     "",
                     _CONFIG_CONTENT,
                     flags=re.MULTILINE)
    with tempfile.NamedTemporaryFile(delete=False, mode="w") as new_cfg:
        new_name = new_cfg.name
        new_cfg.write(content)
        new_cfg.close()
        config = Config()
        config.load(config_file=new_name)

        assert config.default_stub_api == config.default_api
示例#10
0
def test_default_stubapi_missing(tmpdir):
    ''' Check that we raise an error if the default stub API is missing,
    in which case it defaults to the default_api'''
    content = re.sub(r"^DEFAULTSTUBAPI = .*$",
                     "",
                     _CONFIG_CONTENT,
                     flags=re.MULTILINE)
    config_file = tmpdir.join("config")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()
        config = Config()
        config.load(config_file=str(config_file))

        assert config.default_stub_api == config.default_api
示例#11
0
def test_wrong_api():
    ''' Check that we raise the correct errors when a user queries
    API-specific configuration options '''
    _config = Config()
    _config.load(config_file=TEST_CONFIG)
    with pytest.raises(ConfigurationError) as err:
        _ = _config.api_conf("blah")
    assert "API 'blah' is not in the list" in str(err)
    with pytest.raises(ConfigurationError) as err:
        _ = _config.api_conf("dynamo0.1")
    assert ("Configuration file did not contain a section for the "
            "'dynamo0.1' API" in str(err))
    with pytest.raises(ValueError) as err:
        _config.api = "invalid"
    assert "'invalid' is not a valid API" in str(err)
示例#12
0
def test_broken_fmt(tmpdir):
    ''' Check the error if the formatting of the configuration file is
    wrong. '''
    # Create a 'config' file without any section headers
    content = "COMPUTE_ANNEXED_DOFS = false\n"
    config_file = tmpdir.join("config")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        with pytest.raises(ConfigurationError) as err:
            config = Config()
            config.load(config_file=str(config_file))
        assert ("ConfigParser failed to read the configuration file. Is it "
                "formatted correctly? (Error was: File contains no section "
                "headers" in str(err))
示例#13
0
def test_broken_fmt():
    ''' Check the error if the formatting of the configuration file is
    wrong. '''
    # Create a 'config' file without any section headers
    content = "COMPUTE_ANNEXED_DOFS = false\n"
    with tempfile.NamedTemporaryFile(delete=False, mode="w") as new_cfg:
        new_name = new_cfg.name
        new_cfg.write(content)
        new_cfg.close()

        with pytest.raises(ConfigurationError) as err:
            config = Config()
            config.load(config_file=new_name)
        assert ("ConfigParser failed to read the configuration file. Is it "
                "formatted correctly? (Error was: File contains no section "
                "headers" in str(err))
示例#14
0
def test_entry_not_bool(tmpdir, option):
    ''' Check that we raise an error if the value of any options expecting
    a boolean value are not Boolean '''
    content = re.sub(r"^{0} = .*$".format(option),
                     "{0} = tree".format(option),
                     _CONFIG_CONTENT,
                     flags=re.MULTILINE)
    config_file = tmpdir.join("config_dyn")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()
        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(config_file=str(config_file))

        assert "error while parsing {0}".format(option) in str(err.value)
        assert "Not a boolean: tree" in str(err.value)
示例#15
0
def test_api_unimplemented():
    ''' Check that we raise the correct error if we supply a config file
        containing a section for an API for which we've not implemented
        API-specific configuration. '''
    content = re.sub(r"^\[dynamo0.3\]$",
                     "[gocean0.1]",
                     _CONFIG_CONTENT,
                     flags=re.MULTILINE)
    with tempfile.NamedTemporaryFile(delete=False, mode="w") as new_cfg:
        new_name = new_cfg.name
        new_cfg.write(content)
        new_cfg.close()
        config = Config()
        with pytest.raises(NotImplementedError) as err:
            config.load(new_name)
        assert ("file contains a gocean0.1 section but no Config sub-class "
                "has been implemented for this API" in str(err))
示例#16
0
def test_default_stubapi_invalid(tmpdir):
    ''' Check that we raise an error if the default stub API is not in
    the list of supported stub APIs '''
    content = re.sub(r"^DEFAULTSTUBAPI = .*$",
                     "DEFAULTSTUBAPI = invalid",
                     _CONFIG_CONTENT,
                     flags=re.MULTILINE)
    config_name = tmpdir.join("config")
    with config_name.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()
        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(config_file=str(config_name))

        assert ("The default stub API (invalid) is not in the list of "
                "supported stub APIs" in str(err))
示例#17
0
def test_default_api():
    '''If a config file has no default-api specified, but contains only
    a single (non-default) section, this section should be used as the
    default api.
    '''
    content = re.sub(r"^API.*$",
                     "",
                     _CONFIG_CONTENT,
                     flags=re.MULTILINE)

    with tempfile.NamedTemporaryFile(delete=False, mode="w") as new_cfg:
        new_name = new_cfg.name
        new_cfg.write(content)
        new_cfg.close()
        config = Config()
        config.load(new_name)
        assert config.api == "dynamo0.3"
示例#18
0
def test_default_stubapi_invalid():
    ''' Check that we raise an error if the default stub API is not in
    the list of supported stub APIs '''
    content = re.sub(r"^DEFAULTSTUBAPI = .*$",
                     "DEFAULTSTUBAPI = invalid",
                     _CONFIG_CONTENT,
                     flags=re.MULTILINE)
    with tempfile.NamedTemporaryFile(delete=False, mode="w") as new_cfg:
        new_name = new_cfg.name
        new_cfg.write(content)
        new_cfg.close()
        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(config_file=new_name)

        assert ("The default stub API (invalid) is not in the list of "
                "supported stub APIs" in str(err))
示例#19
0
def test_default_missing():
    ''' Check that we produce a suitable error if the [DEFAULT] section
    of the configuration file is missing '''
    content = '''\
[dynamo0.3]
COMPUTE_ANNEXED_DOFS = false
'''
    with tempfile.NamedTemporaryFile(delete=False, mode="w") as new_cfg:
        new_name = new_cfg.name
        new_cfg.write(content)
        new_cfg.close()

        with pytest.raises(ConfigurationError) as err:
            config = Config()
            config.load(config_file=new_name)

        assert "configuration error (file=" in str(err)
        assert "Configuration file has no [DEFAULT] section" in str(err)
示例#20
0
def test_default_missing(tmpdir):
    ''' Check that we produce a suitable error if the [DEFAULT] section
    of the configuration file is missing '''
    content = '''\
[dynamo0.3]
COMPUTE_ANNEXED_DOFS = false
'''
    config_file = tmpdir.join("config")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        with pytest.raises(ConfigurationError) as err:
            config = Config()
            config.load(config_file=str(config_file))

        assert "configuration error (file=" in str(err)
        assert "Configuration file has no [DEFAULT] section" in str(err)
示例#21
0
def test_access_mapping_order(tmpdir):
    ''' Test that the order of the access mappings in the config file
    does not affect the correct access type-mode conversion. '''
    content = re.sub(r"gh_write: write, gh_readwrite: readwrite",
                     "gh_readwrite: readwrite, gh_write: write",
                     _CONFIG_CONTENT)
    content = re.sub(r"gh_inc: inc, gh_sum: sum", "gh_sum: sum, gh_inc: inc",
                     content)
    config_file = tmpdir.join("config")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()
        config = Config()
        config.load(str(config_file))

        api_config = Config.get().api_conf("dynamo0.3")
        for access_mode in api_config.get_access_mapping().values():
            assert isinstance(access_mode, AccessType)
示例#22
0
def test_not_int(int_entry):
    ''' Check that we catch cases where we expect an integer in the config
    file but don't get one. '''
    content = re.sub(r"^{0} = .*$".format(int_entry),
                     "{0} = wrong".format(int_entry),
                     _CONFIG_CONTENT,
                     flags=re.MULTILINE)
    with tempfile.NamedTemporaryFile(delete=False, mode="w") as new_cfg:
        new_name = new_cfg.name
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(config_file=new_name)

        assert "configuration error (file=" in str(err)
        assert (": error while parsing {0}: invalid literal".format(int_entry)
                in str(err))
示例#23
0
def test_not_int(int_entry, tmpdir):
    ''' Check that we catch cases where we expect an integer in the config
    file but don't get one. '''
    content = re.sub(r"^{0} = .*$".format(int_entry),
                     "{0} = wrong".format(int_entry),
                     _CONFIG_CONTENT,
                     flags=re.MULTILINE)
    config_file = tmpdir.join("config")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(config_file=str(config_file))

        assert "configuration error (file=" in str(err)
        assert (": error while parsing {0}: invalid literal".format(int_entry)
                in str(err))
示例#24
0
def test_invalid_access_mapping(tmpdir):
    '''Test that providing an invalid an invalid access type (i.e. not
    'read', 'write', ...) raises an exception.
    '''
    # Test for an invalid key
    content = _CONFIG_CONTENT + "access_mapping = gh_read:invalid"
    config_file = tmpdir.join("config")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()
        config = Config()
        with pytest.raises(ConfigurationError) as cerr:
            config.load(str(config_file))
        assert "Unknown access type 'invalid' found for key 'gh_read'" \
            in str(cerr)

    # Test that all values of the mapping are access types:
    api_config = Config.get().api_conf("dynamo0.3")
    for access_mode in api_config.get_access_mapping().values():
        assert isinstance(access_mode, AccessType)
示例#25
0
def config(config_file, content):
    ''' A utility function that creates and populates a temporary
    PSyclone configuration file for testing purposes.

    :param config_file: local path to the temporary configuration file.
    :type config: :py:class:`py._path.local.LocalPath`
    :param str content: the entry for the temporary configuration file.

    :returns: a test Config instance.
    :rtype: :py:class:`psyclone.configuration.Config`

    '''
    # Create and populate a temporary config file
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()
    # Create and populate a test Config object
    config_obj = Config()
    config_obj.load(config_file=str(config_file))
    return config_obj
示例#26
0
def test_no_mandatory_option(tmpdir, option):
    ''' Check that we raise an error if we do not provide mandatory
    configuration options for LFRic (Dynamo0.3) API '''

    content = re.sub(r"^{0} = .*$".format(option),
                     "",
                     _CONFIG_CONTENT,
                     flags=re.MULTILINE)
    config_file = tmpdir.join("config_dyn")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()
        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(config_file=str(config_file))

        assert ("Missing mandatory configuration option in the "
                "\'[dynamo0.3]\' section " in str(err.value))
        assert ("Valid options are: '['access_mapping', "
                "'compute_annexed_dofs', 'default_kind', "
                "'run_time_checks']" in str(err.value))
示例#27
0
def test_api_unimplemented(tmpdir, monkeypatch):
    ''' Check that we raise the correct error if we supply a config file
        containing a section for an API for which we've not implemented
        API-specific configuration. '''
    # Since all APIs need a API-specific section, for this error we
    # need to temporarily add a new supported API, that will not
    # be in the config file:

    monkeypatch.setattr(Config, "_supported_api_list",
                        Config._supported_api_list + ["UNIMPLEMENTED"])
    content = re.sub(r"^\[dynamo0.3\]$",
                     "[UNIMPLEMENTED]",
                     _CONFIG_CONTENT,
                     flags=re.MULTILINE)
    config_file = tmpdir.join("config")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()
        config = Config()
        with pytest.raises(NotImplementedError) as err:
            config.load(str(config_file))
        assert ("file contains a UNIMPLEMENTED section but no Config "
                "sub-class has been implemented for this API" in str(err))
示例#28
0
def test_psy_data_prefix(tmpdir):
    ''' Check the handling of PSyData class prefixes.'''
    config_file = tmpdir.join("config.correct")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(_CONFIG_CONTENT)
        new_cfg.close()
        config = Config()
        config.load(config_file=str(config_file))

        assert "profile" in config.valid_psy_data_prefixes
        assert "extract" in config.valid_psy_data_prefixes
        assert len(config.valid_psy_data_prefixes) == 2

    # Now handle a config file without psy data prefixes:
    # This should not raise an exception, but define an empty list
    content = re.sub(r"VALID_PSY_DATA_PREFIXES", "NO-PSY-DATA",
                     _CONFIG_CONTENT)
    config_file = tmpdir.join("config.no_psydata")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()
        config = Config()
        config.load(str(config_file))
        assert not config.valid_psy_data_prefixes
示例#29
0
def test_invalid_nemo_config_files(tmpdir):
    ''' Test various error conditions.
    '''

    # Valid configuration file without nemo-specific settings.
    # We add several lines in the tests for various error conditions
    # pylint: disable=invalid-name
    _CONFIG_CONTENT = '''\
    [DEFAULT]
    DEFAULTAPI = nemo
    DEFAULTSTUBAPI = dynamo0.3
    DISTRIBUTED_MEMORY = true
    REPRODUCIBLE_REDUCTIONS = false
    REPROD_PAD_SIZE = 8
    [nemo]
    '''
    # Create a config files with a nemo section and a mapping-lon
    # but leave out each required key
    for (key, data) in [("var", "start: 1, stop: jpi"),
                        ("start", "var: ji, stop: jpi"),
                        ("stop", "var: ji, start: 1")]:
        content = _CONFIG_CONTENT + "mapping-lon = " + data
        Config._instance = None
        config_file = tmpdir.join("config1")
        with config_file.open(mode="w") as new_cfg:
            new_cfg.write(content)
            new_cfg.close()

            config = Config()
            with pytest.raises(ConfigurationError) as err:
                config.load(str(config_file))
            assert "does not contain key '{0}".format(key) in str(err.value)

    # Add an invalid index-order
    content = _CONFIG_CONTENT + \
        '''mapping-lon = var: i, start: 1, stop:2
    mapping-lat = var: j, start: 1, stop:2
    index-order = invalid'''
    config_file = tmpdir.join("config2")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        assert "Invalid loop type \"invalid\" found " in str(err.value)
        assert "Must be one of [\\'lon\\', \\'lat\\']"

    # Add an invalid key:
    content = _CONFIG_CONTENT + "invalid-key=value"
    config_file = tmpdir.join("config3")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        assert "Invalid key \"invalid-key\" found in the \"nemo\" section " \
               "of the configuration file \"{0}\".". format(str(config_file)) \
               in str(err.value)

    # Use a variable name more than once:
    content = _CONFIG_CONTENT + \
        '''mapping-lon = var: i, start: 1, stop:2
    mapping-lat = var: i, start: 1, stop:2
    index-order = lon, lat'''
    config_file = tmpdir.join("config4")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        assert "mapping-lat defines variable \"i\" again in the \"nemo\" "\
               "section of the file \"{0}\".".format(str(config_file)) \
               in str(err.value)
示例#30
0
def test_invalid_config_files(tmpdir):
    ''' Test various error conditions.
    '''

    # Valid configuration file without iteration spaces. We add several
    # iteration spaces to it to test for various error conditions
    # pylint: disable=invalid-name
    # pylint: disable=too-many-statements
    _CONFIG_CONTENT = '''\
    [DEFAULT]
    DEFAULTAPI = dynamo0.3
    DEFAULTSTUBAPI = dynamo0.3
    DISTRIBUTED_MEMORY = true
    REPRODUCIBLE_REDUCTIONS = false
    REPROD_PAD_SIZE = 8
    [gocean1.0]
    '''
    # Create a config files with gocean1.0 section, but an
    # invalid iteration space:
    content = _CONFIG_CONTENT + "iteration-spaces=a:b"
    config_file = tmpdir.join("config1")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        assert "An iteration space must be in the form" in str(err.value)
        assert "But got \"a:b\"" in str(err.value)

    # Try a multi-line specification to make sure all lines are tested
    content = _CONFIG_CONTENT + "iteration-spaces=a:b:c:1:2:3:4\n        d:e"
    config_file = tmpdir.join("config2")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        assert "An iteration space must be in the form" in str(err.value)
        assert "But got \"d:e\"" in str(err.value)

    # Invalid {} expression in first loop bound
    content = _CONFIG_CONTENT + "iteration-spaces=a:b:c:{X}:2:3:4"
    config_file = tmpdir.join("config3")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        assert "Only '{start}' and '{stop}' are allowed as bracketed "\
               "expression in an iteration space." in str(err.value)
        assert "But got {X}" in str(err.value)

    # Invalid {} expression in last loop bound:
    content = _CONFIG_CONTENT + "iteration-spaces=a:b:c:1:2:3:{Y}"
    config_file = tmpdir.join("config4")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        assert "Only '{start}' and '{stop}' are allowed as bracketed "\
               "expression in an iteration space." in str(err.value)
        assert "But got {Y}" in str(err.value)

    # Add an invalid key:
    content = _CONFIG_CONTENT + "invalid-key=value"
    config_file = tmpdir.join("config5")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        assert "Invalid key \"invalid-key\" found in \"{0}\".".\
            format(str(config_file)) in str(err.value)

        for i in [
                "DEFAULTAPI", "DEFAULTSTUBAPI", "DISTRIBUTED_MEMORY",
                "REPRODUCIBLE_REDUCTIONS"
        ]:
            # They keys are returned in lower case
            assert i.lower() in config.get_default_keys()

    with pytest.raises(InternalError) as err:
        GOLoop.add_bounds(1)
    # Different error message (for type) in python2 vs python3:
    assert "The parameter 'bound_info' must be a string, got '1' "\
           "(type <type 'int'>)" in str(err.value) or \
           "The parameter 'bound_info' must be a string, got '1' "\
           "(type <class 'int'>)" in str(err.value)

    # Test syntactically invalid loop boundaries
    with pytest.raises(ConfigurationError) as err:
        GOLoop.add_bounds("offset:field:space:1(:2:3:4")
    assert "Expression '1(' is not a valid do loop boundary" in str(err.value)
    with pytest.raises(ConfigurationError) as err:
        GOLoop.add_bounds("offset:field:space:1:2:3:4+")
    assert "Expression '4+' is not a valid do loop boundary" in str(err.value)

    # Test invalid field properties - too many fields
    content = _CONFIG_CONTENT + "grid-properties = a: {0}%%b:c:d:e"
    config_file = tmpdir.join("config1")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        assert "Invalid property \"a\" found with value \"{0}%b:c:d:e\"" \
               in str(err.value)

    # Test invalid field properties - not enough fields
    content = _CONFIG_CONTENT + "grid-properties = a:b"
    config_file = tmpdir.join("config1")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        assert "Invalid property \"a\" found with value \"b\"" \
               in str(err.value)

    # Test missing required values
    content = _CONFIG_CONTENT + "grid-properties = a:b:array:real"
    config_file = tmpdir.join("config1")
    with config_file.open(mode="w") as new_cfg:
        new_cfg.write(content)
        new_cfg.close()

        config = Config()
        with pytest.raises(ConfigurationError) as err:
            config.load(str(config_file))
        # The config file {0} does not contain values for "..."
        assert "does not contain values for the following, mandatory grid " \
            "property: \"go_grid_xstop\"" in str(err.value)