blob: 1391fe9909a85592aa61b81ea9032257651964df [file] [log] [blame]
# Copyright 2022 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
"""Tests for pw_ide.editors"""
from collections import OrderedDict
from enum import Enum
import unittest
from pw_ide.editors import (
dict_deep_merge,
dict_swap_type,
EditorSettingsFile,
EditorSettingsManager,
JsonFileFormat,
Json5FileFormat,
YamlFileFormat,
_StructuredFileFormat,
)
from test_cases import PwIdeTestCase
class TestDictDeepMerge(unittest.TestCase):
"""Tests dict_deep_merge"""
# pylint: disable=unnecessary-lambda
def test_invariants_with_dict_success(self):
dict_a = {'hello': 'world'}
dict_b = {'foo': 'bar'}
expected = {
'hello': 'world',
'foo': 'bar',
}
result = dict_deep_merge(dict_b, dict_a, lambda: dict())
self.assertEqual(result, expected)
def test_invariants_with_dict_implicit_ctor_success(self):
dict_a = {'hello': 'world'}
dict_b = {'foo': 'bar'}
expected = {
'hello': 'world',
'foo': 'bar',
}
result = dict_deep_merge(dict_b, dict_a)
self.assertEqual(result, expected)
def test_invariants_with_dict_fails_wrong_ctor_type(self):
dict_a = {'hello': 'world'}
dict_b = {'foo': 'bar'}
with self.assertRaises(TypeError):
dict_deep_merge(dict_b, dict_a, lambda: OrderedDict())
def test_invariants_with_ordered_dict_success(self):
dict_a = OrderedDict({'hello': 'world'})
dict_b = OrderedDict({'foo': 'bar'})
expected = OrderedDict(
{
'hello': 'world',
'foo': 'bar',
}
)
result = dict_deep_merge(dict_b, dict_a, lambda: OrderedDict())
self.assertEqual(result, expected)
def test_invariants_with_ordered_dict_implicit_ctor_success(self):
dict_a = OrderedDict({'hello': 'world'})
dict_b = OrderedDict({'foo': 'bar'})
expected = OrderedDict(
{
'hello': 'world',
'foo': 'bar',
}
)
result = dict_deep_merge(dict_b, dict_a)
self.assertEqual(result, expected)
def test_invariants_with_ordered_dict_fails_wrong_ctor_type(self):
dict_a = OrderedDict({'hello': 'world'})
dict_b = OrderedDict({'foo': 'bar'})
with self.assertRaises(TypeError):
dict_deep_merge(dict_b, dict_a, lambda: dict())
# pylint: enable=unnecessary-lambda
def test_merge_basic(self):
dict_a = {'hello': 'world'}
dict_b = {'hello': 'bar'}
expected = {'hello': 'bar'}
result = dict_deep_merge(dict_b, dict_a)
self.assertEqual(result, expected)
def test_merge_nested_dict(self):
dict_a = {'hello': {'a': 'foo'}}
dict_b = {'hello': {'b': 'bar'}}
expected = {'hello': {'a': 'foo', 'b': 'bar'}}
result = dict_deep_merge(dict_b, dict_a)
self.assertEqual(result, expected)
def test_merge_list(self):
dict_a = {'hello': ['world']}
dict_b = {'hello': ['bar']}
expected = {'hello': ['world', 'bar']}
result = dict_deep_merge(dict_b, dict_a)
self.assertEqual(result, expected)
def test_merge_list_no_duplicates(self):
dict_a = {'hello': ['world']}
dict_b = {'hello': ['world']}
expected = {'hello': ['world']}
result = dict_deep_merge(dict_b, dict_a)
self.assertEqual(result, expected)
def test_merge_nested_dict_with_lists(self):
dict_a = {'hello': {'a': 'foo', 'c': ['lorem']}}
dict_b = {'hello': {'b': 'bar', 'c': ['ipsum']}}
expected = {'hello': {'a': 'foo', 'b': 'bar', 'c': ['lorem', 'ipsum']}}
result = dict_deep_merge(dict_b, dict_a)
self.assertEqual(result, expected)
def test_merge_object_fails(self):
class Strawman:
pass
dict_a = {'hello': 'world'}
dict_b = {'foo': Strawman()}
with self.assertRaises(TypeError):
dict_deep_merge(dict_b, dict_a)
def test_merge_copies_string(self):
test_str = 'bar'
dict_a = {'hello': {'a': 'foo'}}
dict_b = {'hello': {'b': test_str}}
result = dict_deep_merge(dict_b, dict_a)
test_str = 'something else'
self.assertEqual(result['hello']['b'], 'bar')
class TestDictSwapType(unittest.TestCase):
"""Tests dict_swap_type"""
def test_ordereddict_to_dict(self):
"""Test converting an OrderedDict to a plain dict"""
ordered_dict = OrderedDict(
{
'hello': 'world',
'foo': 'bar',
'nested': OrderedDict(
{
'lorem': 'ipsum',
'dolor': 'sit amet',
}
),
}
)
plain_dict = dict_swap_type(ordered_dict, dict)
expected_plain_dict = {
'hello': 'world',
'foo': 'bar',
'nested': {
'lorem': 'ipsum',
'dolor': 'sit amet',
},
}
# The returned dict has the content and type we expect
self.assertDictEqual(plain_dict, expected_plain_dict)
self.assertIsInstance(plain_dict, dict)
self.assertIsInstance(plain_dict['nested'], dict)
# The original OrderedDict is unchanged
self.assertIsInstance(ordered_dict, OrderedDict)
self.assertIsInstance(ordered_dict['nested'], OrderedDict)
class EditorSettingsTestType(Enum):
SETTINGS = 'settings'
class TestCasesGenericOnFileFormat:
"""Container for tests generic on FileFormat.
This misdirection is needed to prevent the base test class cases from being
run as actual tests.
"""
class EditorSettingsFileTestCase(PwIdeTestCase):
"""Test case for EditorSettingsFile with a provided FileFormat"""
def setUp(self):
if not hasattr(self, 'file_format'):
self.file_format = _StructuredFileFormat()
return super().setUp()
def test_open_new_file_and_write(self):
name = 'settings'
settings_file = EditorSettingsFile(
self.temp_dir_path, name, self.file_format
)
with settings_file.build() as settings:
settings['hello'] = 'world'
with open(
self.temp_dir_path / f'{name}.{self.file_format.ext}'
) as file:
settings_dict = self.file_format.load(file)
self.assertEqual(settings_dict['hello'], 'world')
def test_open_new_file_and_get(self):
name = 'settings'
settings_file = EditorSettingsFile(
self.temp_dir_path, name, self.file_format
)
with settings_file.build() as settings:
settings['hello'] = 'world'
settings_dict = settings_file.get()
self.assertEqual(settings_dict['hello'], 'world')
class EditorSettingsManagerTestCase(PwIdeTestCase):
"""Test case for EditorSettingsManager with a provided FileFormat"""
def setUp(self):
if not hasattr(self, 'file_format'):
self.file_format = _StructuredFileFormat()
return super().setUp()
def test_settings_merge(self):
"""Test that settings merge as expected in isolation."""
default_settings = OrderedDict(
{
'foo': 'bar',
'baz': 'qux',
'lorem': OrderedDict(
{
'ipsum': 'dolor',
}
),
}
)
types_with_defaults = {
EditorSettingsTestType.SETTINGS: lambda _: default_settings
}
ide_settings = self.make_ide_settings()
manager = EditorSettingsManager(
ide_settings,
self.temp_dir_path,
self.file_format,
types_with_defaults,
)
project_settings = OrderedDict(
{
'alpha': 'beta',
'baz': 'xuq',
'foo': 'rab',
}
)
with manager.project(
EditorSettingsTestType.SETTINGS
).build() as settings:
dict_deep_merge(project_settings, settings)
user_settings = OrderedDict(
{
'baz': 'xqu',
'lorem': OrderedDict(
{
'ipsum': 'sit amet',
'consectetur': 'adipiscing',
}
),
}
)
with manager.user(
EditorSettingsTestType.SETTINGS
).build() as settings:
dict_deep_merge(user_settings, settings)
expected = {
'alpha': 'beta',
'foo': 'rab',
'baz': 'xqu',
'lorem': {
'ipsum': 'sit amet',
'consectetur': 'adipiscing',
},
}
with manager.active(
EditorSettingsTestType.SETTINGS
).build() as active_settings:
manager.default(EditorSettingsTestType.SETTINGS).sync_to(
active_settings
)
manager.project(EditorSettingsTestType.SETTINGS).sync_to(
active_settings
)
manager.user(EditorSettingsTestType.SETTINGS).sync_to(
active_settings
)
self.assertCountEqual(
manager.active(EditorSettingsTestType.SETTINGS).get(), expected
)
class TestEditorSettingsFileJsonFormat(
TestCasesGenericOnFileFormat.EditorSettingsFileTestCase
):
"""Test EditorSettingsFile with JsonFormat"""
def setUp(self):
self.file_format = JsonFileFormat()
return super().setUp()
class TestEditorSettingsManagerJsonFormat(
TestCasesGenericOnFileFormat.EditorSettingsManagerTestCase
):
"""Test EditorSettingsManager with JsonFormat"""
def setUp(self):
self.file_format = JsonFileFormat()
return super().setUp()
class TestEditorSettingsFileJson5Format(
TestCasesGenericOnFileFormat.EditorSettingsFileTestCase
):
"""Test EditorSettingsFile with Json5Format"""
def setUp(self):
self.file_format = Json5FileFormat()
return super().setUp()
class TestEditorSettingsManagerJson5Format(
TestCasesGenericOnFileFormat.EditorSettingsManagerTestCase
):
"""Test EditorSettingsManager with Json5Format"""
def setUp(self):
self.file_format = Json5FileFormat()
return super().setUp()
class TestEditorSettingsFileYamlFormat(
TestCasesGenericOnFileFormat.EditorSettingsFileTestCase
):
"""Test EditorSettingsFile with YamlFormat"""
def setUp(self):
self.file_format = YamlFileFormat()
return super().setUp()
class TestEditorSettingsManagerYamlFormat(
TestCasesGenericOnFileFormat.EditorSettingsManagerTestCase
):
"""Test EditorSettingsManager with YamlFormat"""
def setUp(self):
self.file_format = YamlFileFormat()
return super().setUp()
if __name__ == '__main__':
unittest.main()