Coverage for mlos_bench/mlos_bench/tests/services/config_persistence_test.py: 100%
61 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 00:44 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 00:44 +0000
1#
2# Copyright (c) Microsoft Corporation.
3# Licensed under the MIT License.
4#
5"""Unit tests for configuration persistence service."""
7import os
8import sys
10import pytest
12from mlos_bench.config.schemas import ConfigSchema
13from mlos_bench.services.config_persistence import ConfigPersistenceService
14from mlos_bench.util import path_join
16if sys.version_info < (3, 9):
17 from importlib_resources import files
18else:
19 from importlib.resources import files
22# pylint: disable=redefined-outer-name
25@pytest.fixture
26def config_persistence_service() -> ConfigPersistenceService:
27 """Test fixture for ConfigPersistenceService."""
28 return ConfigPersistenceService(
29 {
30 "config_path": [
31 "./non-existent-dir/test/foo/bar", # Non-existent config path
32 ".", # cwd
33 str(
34 files("mlos_bench.tests.config").joinpath("")
35 ), # Test configs (relative to mlos_bench/tests)
36 # Shouldn't be necessary since we automatically add this.
37 # str(files("mlos_bench.config").joinpath("")), # Stock configs
38 ]
39 }
40 )
43def test_cwd_in_explicit_search_path(config_persistence_service: ConfigPersistenceService) -> None:
44 """Check that CWD is in the search path in the correct place."""
45 # pylint: disable=protected-access
46 assert config_persistence_service._config_path is not None
47 cwd = path_join(os.getcwd(), abs_path=True)
48 assert config_persistence_service._config_path.index(cwd) == 1
49 with pytest.raises(ValueError):
50 config_persistence_service._config_path.index(cwd, 2)
53def test_cwd_in_default_search_path() -> None:
54 """Checks that the CWD is prepended to the search path if not explicitly present."""
55 # pylint: disable=protected-access
56 config_persistence_service = ConfigPersistenceService()
57 assert config_persistence_service._config_path is not None
58 cwd = path_join(os.getcwd(), abs_path=True)
59 assert config_persistence_service._config_path.index(cwd) == 0
60 with pytest.raises(ValueError):
61 config_persistence_service._config_path.index(cwd, 1)
64def test_resolve_stock_path(config_persistence_service: ConfigPersistenceService) -> None:
65 """Check if we can actually find a file somewhere in `config_path`."""
66 # pylint: disable=protected-access
67 assert config_persistence_service._config_path is not None
68 assert ConfigPersistenceService.BUILTIN_CONFIG_PATH in config_persistence_service._config_path
69 file_path = "storage/in-memory.jsonc"
70 path = config_persistence_service.resolve_path(file_path)
71 assert path.endswith(file_path)
72 assert os.path.exists(path)
73 assert os.path.samefile(
74 ConfigPersistenceService.BUILTIN_CONFIG_PATH,
75 os.path.commonpath([ConfigPersistenceService.BUILTIN_CONFIG_PATH, path]),
76 )
79def test_resolve_path(config_persistence_service: ConfigPersistenceService) -> None:
80 """Check if we can actually find a file somewhere in `config_path`."""
81 file_path = "tunable-values/tunable-values-example.jsonc"
82 path = config_persistence_service.resolve_path(file_path)
83 assert path.endswith(file_path)
84 assert os.path.exists(path)
87def test_resolve_path_fail(config_persistence_service: ConfigPersistenceService) -> None:
88 """Check if non-existent file resolves without using `config_path`."""
89 file_path = "foo/non-existent-config.json"
90 path = config_persistence_service.resolve_path(file_path)
91 assert not os.path.exists(path)
92 assert path == file_path
95def test_load_config(config_persistence_service: ConfigPersistenceService) -> None:
96 """Check if we can successfully load a config file located relative to
97 `config_path`.
98 """
99 tunables_data = config_persistence_service.load_config(
100 "tunable-values/tunable-values-example.jsonc",
101 ConfigSchema.TUNABLE_VALUES,
102 )
103 assert tunables_data is not None
104 assert isinstance(tunables_data, dict)
105 assert len(tunables_data) >= 1
108def test_load_bad_config_path(config_persistence_service: ConfigPersistenceService) -> None:
109 """Check if we can successfully load a config file located relative to
110 `config_path`.
111 """
112 with pytest.raises(FileNotFoundError) as exc_info:
113 _ = config_persistence_service.load_config(
114 "DNE/tunable-values/tunable-values-example.jsonc-DNE-}]",
115 ConfigSchema.TUNABLE_VALUES,
116 )
117 assert "No such file or directory" in str(exc_info.value)
120def test_load_config_string(config_persistence_service: ConfigPersistenceService) -> None:
121 """Check if we can load a valid json string as well."""
122 json_str = """
123 {
124 "tunable_param_1": "value_1",
125 "tunable_param_2": "value_2",
126 }
127 """
128 tunables_data = config_persistence_service.load_config(json_str, ConfigSchema.TUNABLE_VALUES)
129 assert tunables_data is not None
130 assert isinstance(tunables_data, dict)
131 assert len(tunables_data) >= 1
134def test_load_bad_config_string(config_persistence_service: ConfigPersistenceService) -> None:
135 """Check how we handle loading a bad config string."""
136 json_str = """
137 {
138 "tunable_param_1": "value_1",
139 "tunable_param_2": "value_2",
140 //} // Missing closing brace
141 """
142 with pytest.raises(ValueError) as exc_info:
143 _ = config_persistence_service.load_config(json_str, ConfigSchema.TUNABLE_VALUES)
144 assert "Failed to parse config from JSON string" in str(exc_info.value)