1# Copyright 2022 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14"""Tests for pw_ide.editors""" 15 16from collections import OrderedDict 17from enum import Enum 18import unittest 19 20from pw_ide.editors import ( 21 dict_deep_merge, 22 EditorSettingsFile, 23 EditorSettingsManager, 24 JsonFileFormat, 25) 26 27from test_cases import PwIdeTestCase 28 29 30class TestDictDeepMerge(unittest.TestCase): 31 """Tests dict_deep_merge""" 32 33 def test_invariants_with_dict_success(self): 34 # pylint: disable=unnecessary-lambda 35 dict_a = {'hello': 'world'} 36 dict_b = {'foo': 'bar'} 37 38 expected = { 39 'hello': 'world', 40 'foo': 'bar', 41 } 42 43 result = dict_deep_merge(dict_b, dict_a, lambda: dict()) 44 self.assertEqual(result, expected) 45 46 def test_invariants_with_dict_implicit_ctor_success(self): 47 # pylint: disable=unnecessary-lambda 48 dict_a = {'hello': 'world'} 49 dict_b = {'foo': 'bar'} 50 51 expected = { 52 'hello': 'world', 53 'foo': 'bar', 54 } 55 56 result = dict_deep_merge(dict_b, dict_a) 57 self.assertEqual(result, expected) 58 59 def test_invariants_with_dict_fails_wrong_ctor_type(self): 60 # pylint: disable=unnecessary-lambda 61 dict_a = {'hello': 'world'} 62 dict_b = {'foo': 'bar'} 63 64 with self.assertRaises(TypeError): 65 dict_deep_merge(dict_b, dict_a, lambda: OrderedDict()) 66 67 def test_invariants_with_ordered_dict_success(self): 68 # pylint: disable=unnecessary-lambda 69 dict_a = OrderedDict({'hello': 'world'}) 70 dict_b = OrderedDict({'foo': 'bar'}) 71 72 expected = OrderedDict( 73 { 74 'hello': 'world', 75 'foo': 'bar', 76 } 77 ) 78 79 result = dict_deep_merge(dict_b, dict_a, lambda: OrderedDict()) 80 self.assertEqual(result, expected) 81 82 def test_invariants_with_ordered_dict_implicit_ctor_success(self): 83 # pylint: disable=unnecessary-lambda 84 dict_a = OrderedDict({'hello': 'world'}) 85 dict_b = OrderedDict({'foo': 'bar'}) 86 87 expected = OrderedDict( 88 { 89 'hello': 'world', 90 'foo': 'bar', 91 } 92 ) 93 94 result = dict_deep_merge(dict_b, dict_a) 95 self.assertEqual(result, expected) 96 97 def test_invariants_with_ordered_dict_fails_wrong_ctor_type(self): 98 # pylint: disable=unnecessary-lambda 99 dict_a = OrderedDict({'hello': 'world'}) 100 dict_b = OrderedDict({'foo': 'bar'}) 101 102 with self.assertRaises(TypeError): 103 dict_deep_merge(dict_b, dict_a, lambda: dict()) 104 105 106class TestEditorSettingsFile(PwIdeTestCase): 107 """Tests EditorSettingsFile""" 108 109 def test_open_new_file_and_write(self): 110 name = 'settings' 111 json_fmt = JsonFileFormat() 112 settings_file = EditorSettingsFile(self.temp_dir_path, name, json_fmt) 113 114 with settings_file.modify() as settings: 115 settings['hello'] = 'world' 116 117 with open(self.temp_dir_path / f'{name}.{json_fmt.ext}') as file: 118 settings_dict = json_fmt.load(file) 119 120 self.assertEqual(settings_dict['hello'], 'world') 121 122 def test_open_new_file_and_get(self): 123 name = 'settings' 124 json_fmt = JsonFileFormat() 125 settings_file = EditorSettingsFile(self.temp_dir_path, name, json_fmt) 126 127 with settings_file.modify() as settings: 128 settings['hello'] = 'world' 129 130 settings_dict = settings_file.get() 131 self.assertEqual(settings_dict['hello'], 'world') 132 133 def test_open_new_file_no_backup(self): 134 name = 'settings' 135 json_fmt = JsonFileFormat() 136 settings_file = EditorSettingsFile(self.temp_dir_path, name, json_fmt) 137 138 with settings_file.modify() as settings: 139 settings['hello'] = 'world' 140 141 backup_files = [ 142 path 143 for path in self.temp_dir_path.iterdir() 144 if path.name != f'{name}.{json_fmt.ext}' 145 ] 146 147 self.assertEqual(len(backup_files), 0) 148 149 def test_open_existing_file_and_backup(self): 150 name = 'settings' 151 json_fmt = JsonFileFormat() 152 settings_file = EditorSettingsFile(self.temp_dir_path, name, json_fmt) 153 154 with settings_file.modify() as settings: 155 settings['hello'] = 'world' 156 157 with settings_file.modify() as settings: 158 settings['hello'] = 'mundo' 159 160 settings_dict = settings_file.get() 161 self.assertEqual(settings_dict['hello'], 'mundo') 162 163 backup_files = [ 164 path 165 for path in self.temp_dir_path.iterdir() 166 if path.name != f'{name}.{json_fmt.ext}' 167 ] 168 169 self.assertEqual(len(backup_files), 1) 170 171 with open(backup_files[0]) as file: 172 settings_dict = json_fmt.load(file) 173 174 self.assertEqual(settings_dict['hello'], 'world') 175 176 def test_open_existing_file_with_reinit_and_backup(self): 177 name = 'settings' 178 json_fmt = JsonFileFormat() 179 settings_file = EditorSettingsFile(self.temp_dir_path, name, json_fmt) 180 181 with settings_file.modify() as settings: 182 settings['hello'] = 'world' 183 184 with settings_file.modify(reinit=True) as settings: 185 settings['hello'] = 'mundo' 186 187 settings_dict = settings_file.get() 188 self.assertEqual(settings_dict['hello'], 'mundo') 189 190 backup_files = [ 191 path 192 for path in self.temp_dir_path.iterdir() 193 if path.name != f'{name}.{json_fmt.ext}' 194 ] 195 196 self.assertEqual(len(backup_files), 1) 197 198 with open(backup_files[0]) as file: 199 settings_dict = json_fmt.load(file) 200 201 self.assertEqual(settings_dict['hello'], 'world') 202 203 def open_existing_file_no_change_no_backup(self): 204 name = 'settings' 205 json_fmt = JsonFileFormat() 206 settings_file = EditorSettingsFile(self.temp_dir_path, name, json_fmt) 207 208 with settings_file.modify() as settings: 209 settings['hello'] = 'world' 210 211 with settings_file.modify() as settings: 212 settings['hello'] = 'world' 213 214 settings_dict = settings_file.get() 215 self.assertEqual(settings_dict['hello'], 'world') 216 217 backup_files = [ 218 path 219 for path in self.temp_dir_path.iterdir() 220 if path.name != f'{name}.{json_fmt.ext}' 221 ] 222 223 self.assertEqual(len(backup_files), 0) 224 225 with open(backup_files[0]) as file: 226 settings_dict = json_fmt.load(file) 227 228 self.assertEqual(settings_dict['hello'], 'world') 229 230 def test_write_bad_file_restore_backup(self): 231 name = 'settings' 232 json_fmt = JsonFileFormat() 233 settings_file = EditorSettingsFile(self.temp_dir_path, name, json_fmt) 234 235 with settings_file.modify() as settings: 236 settings['hello'] = 'world' 237 238 with self.assertRaises(TypeError): 239 with settings_file.modify() as settings: 240 settings['hello'] = object() 241 242 settings_dict = settings_file.get() 243 self.assertEqual(settings_dict['hello'], 'world') 244 245 backup_files = [ 246 path 247 for path in self.temp_dir_path.iterdir() 248 if path.name != f'{name}.{json_fmt.ext}' 249 ] 250 251 self.assertEqual(len(backup_files), 0) 252 253 254class EditorSettingsTestType(Enum): 255 SETTINGS = 'settings' 256 257 258class TestEditorSettingsManager(PwIdeTestCase): 259 """Tests EditorSettingsManager""" 260 261 def test_settings_merge(self): 262 """Test that settings merge as expected in isolation.""" 263 default_settings = OrderedDict( 264 { 265 'foo': 'bar', 266 'baz': 'qux', 267 'lorem': OrderedDict( 268 { 269 'ipsum': 'dolor', 270 } 271 ), 272 } 273 ) 274 275 types_with_defaults = { 276 EditorSettingsTestType.SETTINGS: lambda _: default_settings 277 } 278 279 ide_settings = self.make_ide_settings() 280 json_fmt = JsonFileFormat() 281 manager = EditorSettingsManager( 282 ide_settings, self.temp_dir_path, json_fmt, types_with_defaults 283 ) 284 285 project_settings = OrderedDict( 286 { 287 'alpha': 'beta', 288 'baz': 'xuq', 289 'foo': 'rab', 290 } 291 ) 292 293 with manager.project( 294 EditorSettingsTestType.SETTINGS 295 ).modify() as settings: 296 dict_deep_merge(project_settings, settings) 297 298 user_settings = OrderedDict( 299 { 300 'baz': 'xqu', 301 'lorem': OrderedDict( 302 { 303 'ipsum': 'sit amet', 304 'consectetur': 'adipiscing', 305 } 306 ), 307 } 308 ) 309 310 with manager.user(EditorSettingsTestType.SETTINGS).modify() as settings: 311 dict_deep_merge(user_settings, settings) 312 313 expected = { 314 'alpha': 'beta', 315 'foo': 'rab', 316 'baz': 'xqu', 317 'lorem': { 318 'ipsum': 'sit amet', 319 'consectetur': 'adipiscing', 320 }, 321 } 322 323 with manager.active( 324 EditorSettingsTestType.SETTINGS 325 ).modify() as active_settings: 326 manager.default(EditorSettingsTestType.SETTINGS).sync_to( 327 active_settings 328 ) 329 manager.project(EditorSettingsTestType.SETTINGS).sync_to( 330 active_settings 331 ) 332 manager.user(EditorSettingsTestType.SETTINGS).sync_to( 333 active_settings 334 ) 335 336 self.assertCountEqual( 337 manager.active(EditorSettingsTestType.SETTINGS).get(), expected 338 ) 339 340 341if __name__ == '__main__': 342 unittest.main() 343