"""mergedeep test module""" import inspect import unittest from collections import Counter from copy import deepcopy from mergedeep import merge, Strategy class test_mergedeep(unittest.TestCase): """mergedeep function tests.""" ############################################################################################################################## # REPLACE ############################################################################################################################## def test_should_merge_3_dicts_into_new_dict_using_replace_strategy_and_only_mutate_target(self,): expected = { "a": {"b": {"c": 5, "_c": 15}, "B": {"C": 10}}, "d": 3, "e": {1: 2, "a": {"f": 2}}, "f": [4, 5, 6], "g": (100, 200), "h": Counter({"a": 5, "b": 1, "c": 1}), "i": 2, "j": Counter({"z": 2}), "z": Counter({"a": 2}), } a = { "a": {"b": {"c": 5}}, "d": 1, "e": {2: 3}, "f": [1, 2, 3], "g": (2, 4, 6), "h": Counter({"a": 1, "b": 1}), "j": 1, } a_copy = deepcopy(a) b = { "a": {"B": {"C": 10}}, "d": 2, "e": 2, "f": [4, 5, 6], "g": (100, 200), "h": Counter({"a": 5, "c": 1}), "i": Counter({"a": 1}), "z": Counter({"a": 2}), } b_copy = deepcopy(b) c = { "a": {"b": {"_c": 15}}, "d": 3, "e": {1: 2, "a": {"f": 2}}, "i": 2, "j": Counter({"z": 2}), "z": Counter({"a": 2}), } c_copy = deepcopy(c) actual = merge({}, a, b, c, strategy=Strategy.REPLACE) self.assertEqual(actual, expected) self.assertEqual(a, a_copy) self.assertEqual(b, b_copy) self.assertEqual(c, c_copy) def test_should_merge_2_dicts_into_existing_dict_using_replace_strategy_and_only_mutate_target(self,): expected = { "a": {"b": {"c": 5, "_c": 15}, "B": {"C": 10}}, "d": 3, "e": {1: 2, "a": {"f": 2}}, "f": [4, 5, 6], "g": (100, 200), "h": Counter({"a": 1, "b": 1, "c": 1}), "i": 2, "j": Counter({"z": 2}), } a = { "a": {"b": {"c": 5}}, "d": 1, "e": {2: 3}, "f": [1, 2, 3], "g": (2, 4, 6), "h": Counter({"a": 1, "b": 1}), "j": 1, } a_copy = deepcopy(a) b = { "a": {"B": {"C": 10}}, "d": 2, "e": 2, "f": [4, 5, 6], "g": (100, 200), "h": Counter({"a": 1, "c": 1}), "i": Counter({"a": 1}), } b_copy = deepcopy(b) c = {"a": {"b": {"_c": 15}}, "d": 3, "e": {1: 2, "a": {"f": 2}}, "i": 2, "j": Counter({"z": 2})} c_copy = deepcopy(c) actual = merge(a, b, c, strategy=Strategy.REPLACE) self.assertEqual(actual, expected) self.assertEqual(actual, a) self.assertNotEqual(a, a_copy) self.assertEqual(b, b_copy) self.assertEqual(c, c_copy) def test_should_have_default_strategy_of_replace(self): func_spec = inspect.getfullargspec(merge) default_strategy = Strategy.REPLACE self.assertEqual(func_spec.kwonlydefaults.get("strategy"), default_strategy) # mock_merge.method.assert_called_with(target, source, strategy=Strategy.REPLACE) ############################################################################################################################## # ADDITIVE ############################################################################################################################## def test_should_merge_3_dicts_into_new_dict_using_additive_strategy_on_lists_and_only_mutate_target(self,): expected = { "a": {"b": {"c": 5, "_c": 15}, "B": {"C": 10}}, "d": 3, "e": {1: 2, "a": {"f": 2}}, "f": [1, 2, 3, 4, 5, 6], } a = {"a": {"b": {"c": 5}}, "d": 1, "e": {2: 3}, "f": [1, 2, 3]} a_copy = deepcopy(a) b = {"a": {"B": {"C": 10}}, "d": 2, "e": 2, "f": [4, 5, 6]} b_copy = deepcopy(b) c = {"a": {"b": {"_c": 15}}, "d": 3, "e": {1: 2, "a": {"f": 2}}} c_copy = deepcopy(c) actual = merge({}, a, b, c, strategy=Strategy.ADDITIVE) self.assertEqual(actual, expected) self.assertEqual(a, a_copy) self.assertEqual(b, b_copy) self.assertEqual(c, c_copy) def test_should_merge_3_dicts_into_new_dict_using_additive_strategy_on_sets_and_only_mutate_target(self,): expected = { "a": {"b": {"c": 5, "_c": 15}, "B": {"C": 10}}, "d": 3, "e": {1: 2, "a": {"f": 2}}, "f": {1, 2, 3, 4, 5, 6}, } a = {"a": {"b": {"c": 5}}, "d": 1, "e": {2: 3}, "f": {1, 2, 3}} a_copy = deepcopy(a) b = {"a": {"B": {"C": 10}}, "d": 2, "e": 2, "f": {4, 5, 6}} b_copy = deepcopy(b) c = {"a": {"b": {"_c": 15}}, "d": 3, "e": {1: 2, "a": {"f": 2}}} c_copy = deepcopy(c) actual = merge({}, a, b, c, strategy=Strategy.ADDITIVE) self.assertEqual(actual, expected) self.assertEqual(a, a_copy) self.assertEqual(b, b_copy) self.assertEqual(c, c_copy) def test_should_merge_3_dicts_into_new_dict_using_additive_strategy_on_tuples_and_only_mutate_target(self,): expected = { "a": {"b": {"c": 5, "_c": 15}, "B": {"C": 10}}, "d": 3, "e": {1: 2, "a": {"f": 2}}, "f": (1, 2, 3, 4, 5, 6), } a = {"a": {"b": {"c": 5}}, "d": 1, "e": {2: 3}, "f": (1, 2, 3)} a_copy = deepcopy(a) b = {"a": {"B": {"C": 10}}, "d": 2, "e": 2, "f": (4, 5, 6)} b_copy = deepcopy(b) c = {"a": {"b": {"_c": 15}}, "d": 3, "e": {1: 2, "a": {"f": 2}}} c_copy = deepcopy(c) actual = merge({}, a, b, c, strategy=Strategy.ADDITIVE) self.assertEqual(actual, expected) self.assertEqual(a, a_copy) self.assertEqual(b, b_copy) self.assertEqual(c, c_copy) def test_should_merge_3_dicts_into_new_dict_using_additive_strategy_on_counters_and_only_mutate_target(self,): expected = { "a": {"b": {"c": 5, "_c": 15}, "B": {"C": 10}}, "d": 3, "e": {1: 2, "a": {"f": 2}}, "f": Counter({"a": 2, "c": 1, "b": 1}), "i": 2, "j": Counter({"z": 2}), "z": Counter({"a": 4}), } a = { "a": {"b": {"c": 5}}, "d": 1, "e": {2: 3}, "f": Counter({"a": 1, "c": 1}), "i": Counter({"f": 9}), "j": Counter({"a": 1, "z": 4}), } a_copy = deepcopy(a) b = { "a": {"B": {"C": 10}}, "d": 2, "e": 2, "f": Counter({"a": 1, "b": 1}), "j": [1, 2, 3], "z": Counter({"a": 2}), } b_copy = deepcopy(b) c = { "a": {"b": {"_c": 15}}, "d": 3, "e": {1: 2, "a": {"f": 2}}, "i": 2, "j": Counter({"z": 2}), "z": Counter({"a": 2}), } c_copy = deepcopy(c) actual = merge({}, a, b, c, strategy=Strategy.ADDITIVE) self.assertEqual(actual, expected) self.assertEqual(a, a_copy) self.assertEqual(b, b_copy) self.assertEqual(c, c_copy) def test_should_not_copy_references(self): before = 1 after = 99 o1 = {"key1": before} o2 = {"key2": before} expected = {"list": deepcopy([o1, o2]), "tuple": deepcopy((o1, o2))} a = {"list": [o1], "tuple": (o1,)} b = {"list": [o2], "tuple": (o2,)} actual = merge({}, a, b, strategy=Strategy.ADDITIVE) o1["key1"] = after o2["key2"] = after self.assertEqual(actual, expected) # Copied dicts should `not` mutate self.assertEqual(actual["list"][0]["key1"], before) self.assertEqual(actual["list"][1]["key2"], before) self.assertEqual(actual["tuple"][0]["key1"], before) self.assertEqual(actual["tuple"][1]["key2"], before) # Non-copied dicts should mutate self.assertEqual(a["list"][0]["key1"], after) self.assertEqual(b["list"][0]["key2"], after) self.assertEqual(a["tuple"][0]["key1"], after) self.assertEqual(b["tuple"][0]["key2"], after) ############################################################################################################################## # TYPESAFE # TYPESAFE_REPLACE ############################################################################################################################## def test_should_raise_TypeError_using_typesafe_strategy_if_types_differ(self): a = {"a": {"b": {"c": 5}}, "d": 1, "e": {2: 3}, "f": [1, 2, 3]} b = {"a": {"B": {"C": 10}}, "d": 2, "e": 2, "f": [4, 5, 6]} c = {"a": {"b": {"_c": 15}}, "d": 3, "e": {1: 2, "a": {"f": 2}}} with self.assertRaises(TypeError): merge({}, a, b, c, strategy=Strategy.TYPESAFE) def test_should_raise_TypeError_using_typesafe_replace_strategy_if_types_differ(self,): a = {"a": {"b": {"c": 5}}, "d": 1, "e": {2: 3}, "f": [1, 2, 3]} b = {"a": {"B": {"C": 10}}, "d": 2, "e": 2, "f": [4, 5, 6]} c = {"a": {"b": {"_c": 15}}, "d": 3, "e": {1: 2, "a": {"f": 2}}} with self.assertRaises(TypeError): merge({}, a, b, c, strategy=Strategy.TYPESAFE_REPLACE) def test_should_merge_3_dicts_into_new_dict_using_typesafe_strategy_and_only_mutate_target_if_types_are_compatible( self, ): expected = { "a": {"b": {"c": 5, "_c": 15}, "B": {"C": 10}}, "d": 3, "f": [4, 5, 6], "g": {2, 3, 4}, "h": (1, 3), "z": Counter({"a": 1, "b": 1, "c": 1}), } a = {"a": {"b": {"c": 5}}, "d": 1, "f": [1, 2, 3], "g": {1, 2, 3}, "z": Counter({"a": 1, "b": 1})} a_copy = deepcopy(a) b = {"a": {"B": {"C": 10}}, "d": 2, "f": [4, 5, 6], "g": {2, 3, 4}, "h": (1,)} b_copy = deepcopy(b) c = {"a": {"b": {"_c": 15}}, "d": 3, "h": (1, 3), "z": Counter({"a": 1, "c": 1})} c_copy = deepcopy(c) actual = merge({}, a, b, c, strategy=Strategy.TYPESAFE) self.assertEqual(actual, expected) self.assertEqual(a, a_copy) self.assertEqual(b, b_copy) self.assertEqual(c, c_copy) def test_should_merge_3_dicts_into_new_dict_using_typesafe_replace_strategy_and_only_mutate_target_if_types_are_compatible( self, ): expected = { "a": {"b": {"c": 5, "_c": 15}, "B": {"C": 10}}, "d": 3, "f": [4, 5, 6], "g": {2, 3, 4}, "h": (1, 3), "z": Counter({"a": 1, "b": 1, "c": 1}), } a = {"a": {"b": {"c": 5}}, "d": 1, "f": [1, 2, 3], "g": {1, 2, 3}, "z": Counter({"a": 1, "b": 1})} a_copy = deepcopy(a) b = {"a": {"B": {"C": 10}}, "d": 2, "f": [4, 5, 6], "g": {2, 3, 4}, "h": (1,)} b_copy = deepcopy(b) c = {"a": {"b": {"_c": 15}}, "d": 3, "h": (1, 3), "z": Counter({"a": 1, "c": 1})} c_copy = deepcopy(c) actual = merge({}, a, b, c, strategy=Strategy.TYPESAFE_REPLACE) self.assertEqual(actual, expected) self.assertEqual(a, a_copy) self.assertEqual(b, b_copy) self.assertEqual(c, c_copy) ############################################################################################################################## # TYPESAFE_ADDITIVE ############################################################################################################################## def test_should_raise_TypeError_using_typesafe_additive_strategy_if_types_differ(self,): a = {"a": {"b": {"c": 5}}, "d": 1, "e": {2: 3}, "f": [1, 2, 3]} b = {"a": {"B": {"C": 10}}, "d": 2, "e": 2, "f": [4, 5, 6]} c = {"a": {"b": {"_c": 15}}, "d": 3, "e": {1: 2, "a": {"f": 2}}} with self.assertRaises(TypeError): merge({}, a, b, c, strategy=Strategy.TYPESAFE_ADDITIVE) def test_should_merge_3_dicts_into_new_dict_using_typesafe_additive_strategy_and_only_mutate_target_if_types_are_compatible( self, ): expected = { "a": {"b": {"c": 5, "_c": 15}, "B": {"C": 10}}, "d": 3, "f": [1, 2, 3, 4, 5, 6], "g": {1, 2, 3, 4}, "h": (1, 1, 3), "z": Counter({"a": 2, "b": 1, "c": 1}), } a = {"a": {"b": {"c": 5}}, "d": 1, "f": [1, 2, 3], "g": {1, 2, 3}, "z": Counter({"a": 1, "b": 1})} a_copy = deepcopy(a) b = {"a": {"B": {"C": 10}}, "d": 2, "f": [4, 5, 6], "g": {2, 3, 4}, "h": (1,)} b_copy = deepcopy(b) c = {"a": {"b": {"_c": 15}}, "d": 3, "h": (1, 3), "z": Counter({"a": 1, "c": 1})} c_copy = deepcopy(c) actual = merge({}, a, b, c, strategy=Strategy.TYPESAFE_ADDITIVE) self.assertEqual(actual, expected) self.assertEqual(a, a_copy) self.assertEqual(b, b_copy) self.assertEqual(c, c_copy) if __name__ == "__main__": unittest.main()