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))
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)
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)
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)
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)
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"
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)
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
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
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
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)
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))
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))
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)
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))
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))
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"
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))
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)
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)
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)
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))
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))
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)
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
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))
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))
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
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)
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)