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

1# 

2# Copyright (c) Microsoft Corporation. 

3# Licensed under the MIT License. 

4# 

5"""Unit tests for configuration persistence service.""" 

6 

7import os 

8import sys 

9 

10import pytest 

11 

12from mlos_bench.config.schemas import ConfigSchema 

13from mlos_bench.services.config_persistence import ConfigPersistenceService 

14from mlos_bench.util import path_join 

15 

16if sys.version_info < (3, 9): 

17 from importlib_resources import files 

18else: 

19 from importlib.resources import files 

20 

21 

22# pylint: disable=redefined-outer-name 

23 

24 

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 ) 

41 

42 

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) 

51 

52 

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) 

62 

63 

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 ) 

77 

78 

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) 

85 

86 

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 

93 

94 

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 

106 

107 

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) 

118 

119 

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 

132 

133 

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)