| #!/usr/bin/env python3 |
| |
| # Copyright 2023 Google LLC |
| # SPDX-License-Identifier: Apache-2.0 |
| """ |
| Tests for check_init_priorities |
| """ |
| |
| import mock |
| import pathlib |
| import unittest |
| |
| from elftools.elf.relocation import Section |
| from elftools.elf.sections import SymbolTableSection |
| |
| import check_init_priorities |
| |
| class TestPriority(unittest.TestCase): |
| """Tests for the Priority class.""" |
| |
| def test_priority_parsing(self): |
| prio1 = check_init_priorities.Priority("POST_KERNEL", 12) |
| self.assertEqual(prio1._level_priority, (3, 12)) |
| |
| prio1 = check_init_priorities.Priority("APPLICATION", 9999) |
| self.assertEqual(prio1._level_priority, (4, 9999)) |
| |
| with self.assertRaises(ValueError): |
| check_init_priorities.Priority("i-am-not-a-priority", 0) |
| check_init_priorities.Priority("_DOESNOTEXIST0_", 0) |
| |
| def test_priority_levels(self): |
| prios = [ |
| check_init_priorities.Priority("EARLY", 0), |
| check_init_priorities.Priority("EARLY", 1), |
| check_init_priorities.Priority("PRE_KERNEL_1", 0), |
| check_init_priorities.Priority("PRE_KERNEL_1", 1), |
| check_init_priorities.Priority("PRE_KERNEL_2", 0), |
| check_init_priorities.Priority("PRE_KERNEL_2", 1), |
| check_init_priorities.Priority("POST_KERNEL", 0), |
| check_init_priorities.Priority("POST_KERNEL", 1), |
| check_init_priorities.Priority("APPLICATION", 0), |
| check_init_priorities.Priority("APPLICATION", 1), |
| check_init_priorities.Priority("SMP", 0), |
| check_init_priorities.Priority("SMP", 1), |
| ] |
| |
| self.assertListEqual(prios, sorted(prios)) |
| |
| def test_priority_strings(self): |
| prio = check_init_priorities.Priority("POST_KERNEL", 12) |
| self.assertEqual(str(prio), "POST_KERNEL+12") |
| self.assertEqual(repr(prio), "<Priority POST_KERNEL 12>") |
| |
| class testZephyrInitLevels(unittest.TestCase): |
| """Tests for the ZephyrInitLevels class.""" |
| |
| @mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None) |
| def test_load_objects(self, mock_zilinit): |
| mock_elf = mock.Mock() |
| |
| sts = mock.Mock(spec=SymbolTableSection) |
| rel = mock.Mock(spec=Section) |
| mock_elf.iter_sections.return_value = [sts, rel] |
| |
| s0 = mock.Mock() |
| s0.name = "a" |
| s0.entry.st_info.type = "STT_OBJECT" |
| s0.entry.st_size = 4 |
| s0.entry.st_value = 0xaa |
| s0.entry.st_shndx = 1 |
| |
| s1 = mock.Mock() |
| s1.name = None |
| |
| s2 = mock.Mock() |
| s2.name = "b" |
| s2.entry.st_info.type = "STT_FUNC" |
| s2.entry.st_size = 8 |
| s2.entry.st_value = 0xbb |
| s2.entry.st_shndx = 2 |
| |
| sts.iter_symbols.return_value = [s0, s1, s2] |
| |
| obj = check_init_priorities.ZephyrInitLevels("") |
| obj._elf = mock_elf |
| obj._load_objects() |
| |
| self.assertDictEqual(obj._objects, {0xaa: ("a", 4, 1), 0xbb: ("b", 8, 2)}) |
| |
| @mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None) |
| def test_load_level_addr(self, mock_zilinit): |
| mock_elf = mock.Mock() |
| |
| sts = mock.Mock(spec=SymbolTableSection) |
| rel = mock.Mock(spec=Section) |
| mock_elf.iter_sections.return_value = [sts, rel] |
| |
| s0 = mock.Mock() |
| s0.name = "__init_EARLY_start" |
| s0.entry.st_value = 0x00 |
| |
| s1 = mock.Mock() |
| s1.name = "__init_PRE_KERNEL_1_start" |
| s1.entry.st_value = 0x11 |
| |
| s2 = mock.Mock() |
| s2.name = "__init_PRE_KERNEL_2_start" |
| s2.entry.st_value = 0x22 |
| |
| s3 = mock.Mock() |
| s3.name = "__init_POST_KERNEL_start" |
| s3.entry.st_value = 0x33 |
| |
| s4 = mock.Mock() |
| s4.name = "__init_APPLICATION_start" |
| s4.entry.st_value = 0x44 |
| |
| s5 = mock.Mock() |
| s5.name = "__init_SMP_start" |
| s5.entry.st_value = 0x55 |
| |
| s6 = mock.Mock() |
| s6.name = "__init_end" |
| s6.entry.st_value = 0x66 |
| |
| sts.iter_symbols.return_value = [s0, s1, s2, s3, s4, s5, s6] |
| |
| obj = check_init_priorities.ZephyrInitLevels("") |
| obj._elf = mock_elf |
| obj._load_level_addr() |
| |
| self.assertDictEqual(obj._init_level_addr, { |
| "EARLY": 0x00, |
| "PRE_KERNEL_1": 0x11, |
| "PRE_KERNEL_2": 0x22, |
| "POST_KERNEL": 0x33, |
| "APPLICATION": 0x44, |
| "SMP": 0x55, |
| }) |
| self.assertEqual(obj._init_level_end, 0x66) |
| |
| @mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None) |
| def test_device_ord_from_name(self, mock_zilinit): |
| obj = check_init_priorities.ZephyrInitLevels("") |
| |
| self.assertEqual(obj._device_ord_from_name(None), None) |
| self.assertEqual(obj._device_ord_from_name("hey, hi!"), None) |
| self.assertEqual(obj._device_ord_from_name("__device_dts_ord_123"), 123) |
| |
| @mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None) |
| def test_object_name(self, mock_zilinit): |
| obj = check_init_priorities.ZephyrInitLevels("") |
| obj._objects = {0x123: ("name", 4)} |
| |
| self.assertEqual(obj._object_name(0), "NULL") |
| self.assertEqual(obj._object_name(73), "unknown") |
| self.assertEqual(obj._object_name(0x123), "name") |
| |
| @mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None) |
| def test_initlevel_pointer_32(self, mock_zilinit): |
| obj = check_init_priorities.ZephyrInitLevels("") |
| obj._elf = mock.Mock() |
| obj._elf.elfclass = 32 |
| mock_section = mock.Mock() |
| obj._elf.get_section.return_value = mock_section |
| mock_section.header.sh_addr = 0x100 |
| mock_section.data.return_value = (b"\x01\x00\x00\x00" |
| b"\x02\x00\x00\x00" |
| b"\x03\x00\x00\x00") |
| |
| self.assertEqual(obj._initlevel_pointer(0x100, 0, 0), 1) |
| self.assertEqual(obj._initlevel_pointer(0x100, 1, 0), 2) |
| self.assertEqual(obj._initlevel_pointer(0x104, 0, 0), 2) |
| self.assertEqual(obj._initlevel_pointer(0x104, 1, 0), 3) |
| |
| @mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None) |
| def test_initlevel_pointer_64(self, mock_zilinit): |
| obj = check_init_priorities.ZephyrInitLevels("") |
| obj._elf = mock.Mock() |
| obj._elf.elfclass = 64 |
| mock_section = mock.Mock() |
| obj._elf.get_section.return_value = mock_section |
| mock_section.header.sh_addr = 0x100 |
| mock_section.data.return_value = (b"\x01\x00\x00\x00\x00\x00\x00\x00" |
| b"\x02\x00\x00\x00\x00\x00\x00\x00" |
| b"\x03\x00\x00\x00\x00\x00\x00\x00") |
| |
| self.assertEqual(obj._initlevel_pointer(0x100, 0, 0), 1) |
| self.assertEqual(obj._initlevel_pointer(0x100, 1, 0), 2) |
| self.assertEqual(obj._initlevel_pointer(0x108, 0, 0), 2) |
| self.assertEqual(obj._initlevel_pointer(0x108, 1, 0), 3) |
| |
| @mock.patch("check_init_priorities.ZephyrInitLevels._object_name") |
| @mock.patch("check_init_priorities.ZephyrInitLevels._initlevel_pointer") |
| @mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None) |
| def test_process_initlevels(self, mock_zilinit, mock_ip, mock_on): |
| obj = check_init_priorities.ZephyrInitLevels("") |
| obj._init_level_addr = { |
| "EARLY": 0x00, |
| "PRE_KERNEL_1": 0x00, |
| "PRE_KERNEL_2": 0x00, |
| "POST_KERNEL": 0x08, |
| "APPLICATION": 0x0c, |
| "SMP": 0x0c, |
| } |
| obj._init_level_end = 0x0c |
| obj._objects = { |
| 0x00: ("a", 4, 0), |
| 0x04: ("b", 4, 0), |
| 0x08: ("c", 4, 0), |
| } |
| |
| mock_ip.side_effect = lambda *args: args |
| |
| def mock_obj_name(*args): |
| if args[0] == (0, 0, 0): |
| return "i0" |
| elif args[0] == (0, 1, 0): |
| return "__device_dts_ord_11" |
| elif args[0] == (4, 0, 0): |
| return "i1" |
| elif args[0] == (4, 1, 0): |
| return "__device_dts_ord_22" |
| return f"name_{args[0][0]}_{args[0][1]}" |
| mock_on.side_effect = mock_obj_name |
| |
| obj._process_initlevels() |
| |
| self.assertDictEqual(obj.initlevels, { |
| "EARLY": [], |
| "PRE_KERNEL_1": [], |
| "PRE_KERNEL_2": ["a: i0(__device_dts_ord_11)", "b: i1(__device_dts_ord_22)"], |
| "POST_KERNEL": ["c: name_8_0(name_8_1)"], |
| "APPLICATION": [], |
| "SMP": [], |
| }) |
| self.assertDictEqual(obj.devices, { |
| 11: (check_init_priorities.Priority("PRE_KERNEL_2", 0), "i0"), |
| 22: (check_init_priorities.Priority("PRE_KERNEL_2", 1), "i1"), |
| }) |
| |
| class testValidator(unittest.TestCase): |
| """Tests for the Validator class.""" |
| |
| @mock.patch("check_init_priorities.ZephyrInitLevels") |
| @mock.patch("pickle.load") |
| def test_initialize(self, mock_pl, mock_zil): |
| mock_log = mock.Mock() |
| mock_prio = mock.Mock() |
| mock_obj = mock.Mock() |
| mock_obj.defined_devices = {123: mock_prio} |
| mock_zil.return_value = mock_obj |
| |
| with mock.patch("builtins.open", mock.mock_open()) as mock_open: |
| validator = check_init_priorities.Validator("path", "pickle", mock_log) |
| |
| self.assertEqual(validator._obj, mock_obj) |
| mock_zil.assert_called_once_with("path") |
| mock_open.assert_called_once_with(pathlib.Path("pickle"), "rb") |
| |
| @mock.patch("check_init_priorities.Validator.__init__", return_value=None) |
| def test_check_dep_same_node(self, mock_vinit): |
| validator = check_init_priorities.Validator("", "", None) |
| validator.log = mock.Mock() |
| |
| validator._check_dep(123, 123) |
| |
| self.assertFalse(validator.log.info.called) |
| self.assertFalse(validator.log.warning.called) |
| self.assertFalse(validator.log.error.called) |
| |
| @mock.patch("check_init_priorities.Validator.__init__", return_value=None) |
| def test_check_dep_no_prio(self, mock_vinit): |
| validator = check_init_priorities.Validator("", "", None) |
| validator.log = mock.Mock() |
| validator._obj = mock.Mock() |
| |
| validator._ord2node = {1: mock.Mock(), 2: mock.Mock()} |
| validator._ord2node[1]._binding = None |
| validator._ord2node[2]._binding = None |
| |
| validator._obj.devices = {1: (10, "i1")} |
| validator._check_dep(1, 2) |
| |
| validator._obj.devices = {2: (20, "i2")} |
| validator._check_dep(1, 2) |
| |
| self.assertFalse(validator.log.info.called) |
| self.assertFalse(validator.log.warning.called) |
| self.assertFalse(validator.log.error.called) |
| |
| @mock.patch("check_init_priorities.Validator.__init__", return_value=None) |
| def test_check(self, mock_vinit): |
| validator = check_init_priorities.Validator("", "", None) |
| validator.log = mock.Mock() |
| validator._obj = mock.Mock() |
| validator.errors = 0 |
| |
| validator._ord2node = {1: mock.Mock(), 2: mock.Mock()} |
| validator._ord2node[1]._binding = None |
| validator._ord2node[1].path = "/1" |
| validator._ord2node[2]._binding = None |
| validator._ord2node[2].path = "/2" |
| |
| validator._obj.devices = {1: (10, "i1"), 2: (20, "i2")} |
| |
| validator._check_dep(2, 1) |
| validator._check_dep(1, 2) |
| |
| validator.log.info.assert_called_once_with("/2 <i2> 20 > /1 <i1> 10") |
| validator.log.error.assert_has_calls([ |
| mock.call("/1 <i1> is initialized before its dependency /2 <i2> (10 < 20)") |
| ]) |
| self.assertEqual(validator.errors, 1) |
| |
| @mock.patch("check_init_priorities.Validator.__init__", return_value=None) |
| def test_check_same_prio_assert(self, mock_vinit): |
| validator = check_init_priorities.Validator("", "", None) |
| validator.log = mock.Mock() |
| validator._obj = mock.Mock() |
| validator.errors = 0 |
| |
| validator._ord2node = {1: mock.Mock(), 2: mock.Mock()} |
| validator._ord2node[1]._binding = None |
| validator._ord2node[1].path = "/1" |
| validator._ord2node[2]._binding = None |
| validator._ord2node[2].path = "/2" |
| |
| validator._obj.devices = {1: (10, "i1"), 2: (10, "i2")} |
| |
| with self.assertRaises(ValueError): |
| validator._check_dep(1, 2) |
| |
| @mock.patch("check_init_priorities.Validator.__init__", return_value=None) |
| def test_check_swapped(self, mock_vinit): |
| validator = check_init_priorities.Validator("", "", None) |
| validator.log = mock.Mock() |
| validator._obj = mock.Mock() |
| validator.errors = 0 |
| |
| save_inverted_priorities = check_init_priorities._INVERTED_PRIORITY_COMPATIBLES |
| |
| check_init_priorities._INVERTED_PRIORITY_COMPATIBLES = set([("compat-3", "compat-1")]) |
| |
| validator._ord2node = {1: mock.Mock(), 3: mock.Mock()} |
| validator._ord2node[1]._binding.compatible = "compat-1" |
| validator._ord2node[1].path = "/1" |
| validator._ord2node[3]._binding.compatible = "compat-3" |
| validator._ord2node[3].path = "/3" |
| |
| validator._obj.devices = {1: (20, "i1"), 3: (10, "i3")} |
| |
| validator._check_dep(3, 1) |
| |
| self.assertListEqual(validator.log.info.call_args_list, [ |
| mock.call("Swapped priority: compat-3, compat-1"), |
| mock.call("/3 <i1> 20 > /1 <i3> 10"), |
| ]) |
| self.assertEqual(validator.errors, 0) |
| |
| check_init_priorities._INVERTED_PRIORITY_COMPATIBLES = save_inverted_priorities |
| |
| @mock.patch("check_init_priorities.Validator.__init__", return_value=None) |
| def test_check_ignored(self, mock_vinit): |
| validator = check_init_priorities.Validator("", "", None) |
| validator.log = mock.Mock() |
| validator._obj = mock.Mock() |
| validator.errors = 0 |
| |
| save_ignore_compatibles = check_init_priorities._IGNORE_COMPATIBLES |
| |
| check_init_priorities._IGNORE_COMPATIBLES = set(["compat-3"]) |
| |
| validator._ord2node = {1: mock.Mock(), 3: mock.Mock()} |
| validator._ord2node[1]._binding.compatible = "compat-1" |
| validator._ord2node[1].path = "/1" |
| validator._ord2node[3]._binding.compatible = "compat-3" |
| validator._ord2node[3].path = "/3" |
| |
| validator._obj.devices = {1: 20, 3: 10} |
| |
| validator._check_dep(3, 1) |
| |
| self.assertListEqual(validator.log.info.call_args_list, [ |
| mock.call("Ignoring priority: compat-3"), |
| ]) |
| self.assertEqual(validator.errors, 0) |
| |
| check_init_priorities._IGNORE_COMPATIBLES = save_ignore_compatibles |
| |
| @mock.patch("check_init_priorities.Validator._check_dep") |
| @mock.patch("check_init_priorities.Validator.__init__", return_value=None) |
| def test_check_edt(self, mock_vinit, mock_cd): |
| d0 = mock.Mock() |
| d0.dep_ordinal = 1 |
| d1 = mock.Mock() |
| d1.dep_ordinal = 2 |
| d2 = mock.Mock() |
| d2.dep_ordinal = 3 |
| |
| dev0 = mock.Mock() |
| dev0.depends_on = [d0] |
| dev1 = mock.Mock() |
| dev1.depends_on = [d1] |
| dev2 = mock.Mock() |
| dev2.depends_on = [d2] |
| |
| validator = check_init_priorities.Validator("", "", None) |
| validator._ord2node = {1: dev0, 2: dev1, 3: dev2} |
| validator._obj = mock.Mock() |
| validator._obj.devices = {1: 10, 2: 10, 3: 20} |
| |
| validator.check_edt() |
| |
| self.assertListEqual(mock_cd.call_args_list, [ |
| mock.call(1, 1), |
| mock.call(2, 2), |
| mock.call(3, 3), |
| ]) |
| |
| if __name__ == "__main__": |
| unittest.main() |