From 37d1d679f693a398240ed02132bedd5d588c7db3 Mon Sep 17 00:00:00 2001 From: Whisperity Date: Fri, 2 Aug 2024 17:24:29 +0200 Subject: [PATCH] todo: How to fix update traversal being too eager in reporting --- codechecker_common/configuration_access.py | 7 ++ .../tests/unit/test_configuration_access.py | 101 +++++++++++++++++- 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/codechecker_common/configuration_access.py b/codechecker_common/configuration_access.py index 8ebeed7dcd..16fab183d0 100644 --- a/codechecker_common/configuration_access.py +++ b/codechecker_common/configuration_access.py @@ -960,6 +960,13 @@ def _traverse(path: PurePosixPath, opt: OptionBase, self_data_slice: dict, new_data_slice: dict) -> bool: + # FIXME: Do not add to the "updated_options" and + # "unsuccessful_updates" automatically, but pass the new elements + # up the stack created by recursing _traverse(). + # Failing an element of an OptionDirectoryList will "rollback" the + # changes of the inner elements, so it must NOT appear in + # updated_options!!! + if isinstance(opt, OptionDirectoryList): success = True # Updating lists is more convoluted than fixed trees. diff --git a/codechecker_common/tests/unit/test_configuration_access.py b/codechecker_common/tests/unit/test_configuration_access.py index 7a27744d9c..c49fe77777 100644 --- a/codechecker_common/tests/unit/test_configuration_access.py +++ b/codechecker_common/tests/unit/test_configuration_access.py @@ -17,7 +17,8 @@ class ConfigurationAccessTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.SimpleSchema = cfg.Schema() - cls.SimpleSchema.add_option("default", "/default") + cls.SimpleSchema.add_option("default", "/default", + supports_update=False) cls.SimpleSchema.add_option("writable", "/writable", read_only=False) cls.SimpleSchema.add_option("with_default_1", "/with_default_1", default=42, read_only=False) @@ -226,3 +227,101 @@ def test_early_validation(self): self.assertSetEqual( {"/Users/0/Privileges/0/Scope", "/Users/2/Privileges/0/ID"}, set(dict(fails).keys())) + + def test_update(self): + c1 = {"default": 1} + c2 = {"default": 2} + c = cfg.Configuration.from_memory(self.SimpleSchema, c1) + changes, fails = c.update_from_memory(c2) + + self.assertEqual(len(changes), 0) + self.assertEqual(len(fails), 1) + self.assertDictEqual( + {"/default": + cfg.ConfigurationUpdateFailureReason.UPDATE_UNSUPPORTED}, + {path: reason for path, _, reason in fails}) + + c1 = { + "Users": [ + { + "username": "root", + "Privileges": [ + { + "ID": 0, + "Scope": [""] + } + ] + }, + { + "username": "admin", + "Privileges": [ + { + "ID": 42, + "Scope": ["normal"] + }, + { + "ID": 43, + "Scope": ["secret", "confidential"] + } + ] + }, + { + "username": "user", + "Privileges": [ + { + "ID": 1337, + "Scope": ["nothing"] + } + ] + } + ] + } + c2 = { + "Users": [ + { + "username": "__root", + "Privileges": [ + { + "ID": 1, + "Scope": [""] + } + ] + }, + { + "username": "__admin", + "Privileges": [ + { + "ID": 42, + "Scope": ["normal"] + }, + { + "ID": 43, + "Scope": ["secret", "confidential"] + } + ] + }, + { + "username": "?", # This change is dropped... + "Privileges": [ + { + "ID": None, # ... because this is invalid. + "Scope": ["nothing"] + } + ] + } + ] + } + c = cfg.Configuration.from_memory(self.QuadraticSchema, c1) + changes, fails = c.update_from_memory(c2) + + self.assertEqual(len(changes), 2) + self.assertSetEqual( + {"/Users/0/username", "/Users/0/Privileges/0/ID", + "/Users/1/username"}, + set(dict(changes).keys())) + + self.assertEqual(len(fails), 1) + self.assertDictEqual( + {"/Users/2/Privileges/0/ID": + cfg.ConfigurationUpdateFailureReason.VERIFICATION_FAILED}, + {path: reason for path, _, reason in fails})