From 7341542f2c4010753e9ae9c618ec2c38d8cada20 Mon Sep 17 00:00:00 2001 From: Ian Wienand Date: Tue, 23 May 2017 11:21:02 +1000 Subject: [PATCH] Adding unit testing for configuration Add a range of unit-testing for configuration parsing, graph generation and mount-point generation. Unfortunately there's some global variable hacks, and some stubs, but it's a start. Change-Id: I9e4f950c2c2ea656fc0c1a14594059fb4c62fa35 --- .../block_device/tests/config/bad_plugin.yaml | 3 + .../block_device/tests/config/deep_graph.yaml | 29 ++++ .../block_device/tests/config/deep_tree.yaml | 18 ++ .../config/multiple_partitions_graph.yaml | 67 ++++++++ .../config/multiple_partitions_tree.yaml | 37 ++++ .../tests/config/simple_graph.yaml | 8 + .../tests/config/simple_tree.yaml | 5 + .../block_device/tests/test_config.py | 159 ++++++++++++++++++ .../block_device/tests/test_mount_order.py | 52 ++++++ 9 files changed, 378 insertions(+) create mode 100644 diskimage_builder/block_device/tests/config/bad_plugin.yaml create mode 100644 diskimage_builder/block_device/tests/config/deep_graph.yaml create mode 100644 diskimage_builder/block_device/tests/config/deep_tree.yaml create mode 100644 diskimage_builder/block_device/tests/config/multiple_partitions_graph.yaml create mode 100644 diskimage_builder/block_device/tests/config/multiple_partitions_tree.yaml create mode 100644 diskimage_builder/block_device/tests/config/simple_graph.yaml create mode 100644 diskimage_builder/block_device/tests/config/simple_tree.yaml create mode 100644 diskimage_builder/block_device/tests/test_config.py create mode 100644 diskimage_builder/block_device/tests/test_mount_order.py diff --git a/diskimage_builder/block_device/tests/config/bad_plugin.yaml b/diskimage_builder/block_device/tests/config/bad_plugin.yaml new file mode 100644 index 000000000..174655ffd --- /dev/null +++ b/diskimage_builder/block_device/tests/config/bad_plugin.yaml @@ -0,0 +1,3 @@ +- this_is_not_a_plugin_name: + foo: bar + baz: moo \ No newline at end of file diff --git a/diskimage_builder/block_device/tests/config/deep_graph.yaml b/diskimage_builder/block_device/tests/config/deep_graph.yaml new file mode 100644 index 000000000..993705a1e --- /dev/null +++ b/diskimage_builder/block_device/tests/config/deep_graph.yaml @@ -0,0 +1,29 @@ +- local_loop: + base: image0 + name: image0 + +- partitioning: + base: image0 + name: mbr + label: mbr + partitions: + - flags: [boot, primary] + name: root + base: image0 + size: 100% + +- mount: + base: mkfs_root + name: mount_mkfs_root + mount_point: / + +- fstab: + base: mount_mkfs_root + name: fstab_mount_mkfs_root + fsck-passno: 1 + options: defaults + +- mkfs: + base: root + name: mkfs_root + type: ext4 \ No newline at end of file diff --git a/diskimage_builder/block_device/tests/config/deep_tree.yaml b/diskimage_builder/block_device/tests/config/deep_tree.yaml new file mode 100644 index 000000000..b08a74a91 --- /dev/null +++ b/diskimage_builder/block_device/tests/config/deep_tree.yaml @@ -0,0 +1,18 @@ +- local_loop: + name: image0 + +- partitioning: + name: mbr + base: image0 + label: mbr + partitions: + - name: root + flags: [ boot, primary ] + size: 100% + mkfs: + type: ext4 + mount: + mount_point: / + fstab: + options: "defaults" + fsck-passno: 1 diff --git a/diskimage_builder/block_device/tests/config/multiple_partitions_graph.yaml b/diskimage_builder/block_device/tests/config/multiple_partitions_graph.yaml new file mode 100644 index 000000000..42c62282c --- /dev/null +++ b/diskimage_builder/block_device/tests/config/multiple_partitions_graph.yaml @@ -0,0 +1,67 @@ +- local_loop: + base: image0 + name: image0 + +- partitioning: + base: image0 + name: mbr + label: mbr + partitions: + - name: root + base: image0 + flags: [ boot, primary ] + size: 55% + - name: var + base: image0 + size: 40% + - name: var_log + base: image0 + size: 5% + +- mkfs: + base: root + name: mkfs_root + type: xfs + +- mount: + base: mkfs_root + name: mount_mkfs_root + mount_point: / + +- fstab: + base: mount_mkfs_root + name: fstab_mount_mkfs_root + fsck-passno: 1 + options: defaults + +- mkfs: + base: var + name: mkfs_var + type: xfs + +- mount: + base: mkfs_var + name: mount_mkfs_var + mount_point: /var + +- fstab: + base: mount_mkfs_var + name: fstab_mount_mkfs_var + fsck-passno: 1 + options: defaults + +- mkfs: + base: var_log + name: mkfs_var_log + type: xfs + +- mount: + base: mkfs_var_log + name: mount_mkfs_var_log + mount_point: /var/log + +- fstab: + base: mount_mkfs_var_log + name: fstab_mount_mkfs_var_log + fsck-passno: 1 + options: defaults diff --git a/diskimage_builder/block_device/tests/config/multiple_partitions_tree.yaml b/diskimage_builder/block_device/tests/config/multiple_partitions_tree.yaml new file mode 100644 index 000000000..4c931f658 --- /dev/null +++ b/diskimage_builder/block_device/tests/config/multiple_partitions_tree.yaml @@ -0,0 +1,37 @@ +- local_loop: + name: image0 + +- partitioning: + base: image0 + name: mbr + label: mbr + partitions: + - name: root + flags: [ boot, primary ] + size: 55% + mkfs: + type: xfs + mount: + mount_point: / + fstab: + options: "defaults" + fsck-passno: 1 + - name: var + size: 40% + mkfs: + type: xfs + mount: + mount_point: /var + fstab: + options: "defaults" + fsck-passno: 1 + - name: var_log + size: 5% + mkfs: + type: xfs + mount: + mount_point: /var/log + fstab: + options: "defaults" + fsck-passno: 1 + diff --git a/diskimage_builder/block_device/tests/config/simple_graph.yaml b/diskimage_builder/block_device/tests/config/simple_graph.yaml new file mode 100644 index 000000000..e1eda1467 --- /dev/null +++ b/diskimage_builder/block_device/tests/config/simple_graph.yaml @@ -0,0 +1,8 @@ +- mkfs: + name: root_fs + base: root_part + +- mount: + name: mount_root_fs + base: root_fs + mount_point: / \ No newline at end of file diff --git a/diskimage_builder/block_device/tests/config/simple_tree.yaml b/diskimage_builder/block_device/tests/config/simple_tree.yaml new file mode 100644 index 000000000..d44f319b1 --- /dev/null +++ b/diskimage_builder/block_device/tests/config/simple_tree.yaml @@ -0,0 +1,5 @@ +- mkfs: + name: root_fs + base: root_part + mount: + mount_point: / \ No newline at end of file diff --git a/diskimage_builder/block_device/tests/test_config.py b/diskimage_builder/block_device/tests/test_config.py new file mode 100644 index 000000000..ab0ae8bfc --- /dev/null +++ b/diskimage_builder/block_device/tests/test_config.py @@ -0,0 +1,159 @@ +# 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 +# +# http://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. + +import fixtures +import logging +import os +import testtools +import yaml + +from diskimage_builder.block_device.blockdevice \ + import BlockDevice + + +logger = logging.getLogger(__name__) + + +class TestConfig(testtools.TestCase): + """Helper for setting up and reading a config""" + def setUp(self): + super(TestConfig, self).setUp() + + fs = '%(asctime)s %(levelname)s [%(name)s] %(message)s' + self.log_fixture = self.useFixture( + fixtures.FakeLogger(level=logging.DEBUG, format=fs)) + + # reset all globals for each test. + # XXX: remove globals :/ + import diskimage_builder.block_device.level2.mkfs + diskimage_builder.block_device.level2.mkfs.file_system_labels = set() + import diskimage_builder.block_device.level3.mount + diskimage_builder.block_device.level3.mount.mount_points = {} + diskimage_builder.block_device.level3.mount.sorted_mount_points = None + + def load_config_file(self, f): + path = os.path.join(os.path.dirname(__file__), + 'config', f) + with open(path, 'r') as config: + return yaml.safe_load(config) + + +class TestGraphGeneration(TestConfig): + """Extra helper class for testing graph generation""" + def setUp(self): + super(TestGraphGeneration, self).setUp() + + self.fake_default_config = { + 'build-dir': '/fake', + 'image-size': '1000', + 'image-dir': '/fake', + 'mount-base': '/fake', + } + + self.bd = BlockDevice(self.fake_default_config) + + +# NOTE: inherits from TestGraphGeneration for simplicity to get +# BlockDevice.plugin_manager object for _config_tree_to_diagraph. +# Config parsing can be moved separately (and not require a +# BlockDevice object) in a later change. +class TestConfigParsing(TestGraphGeneration): + """Test parsing config file into a graph""" + + def test_config_bad_plugin(self): + # Currently, configuration parsing does not notice a missing + # plugin. This is left as a stub + return + # config = self.load_config_file('bad_plugin.yaml') + # self.assertRaises(BlockDeviceSetupException, + # self.bd._config_tree_to_digraph, + # config, self.bd.plugin_manager) + + # a graph should remain the same + def test_graph(self): + graph = self.load_config_file('simple_graph.yaml') + parsed_graph = self.bd._config_tree_to_digraph(graph, + self.bd.plugin_manager) + self.assertEqual(parsed_graph, graph) + + # equivalence of simple tree to graph + def test_simple_tree(self): + tree = self.load_config_file('simple_tree.yaml') + graph = self.load_config_file('simple_graph.yaml') + parsed_graph = self.bd.\ + _config_tree_to_digraph(tree, + self.bd.plugin_manager) + self.assertItemsEqual(parsed_graph, graph) + + # equivalence of a deeper tree to graph + def test_deep_tree(self): + tree = self.load_config_file('deep_tree.yaml') + graph = self.load_config_file('deep_graph.yaml') + parsed_graph = self.bd.\ + _config_tree_to_digraph(tree, self.bd.plugin_manager) + self.assertItemsEqual(parsed_graph, graph) + + # equivalence of a complicated multi-partition tree to graph + def test_multipart_tree(self): + tree = self.load_config_file('multiple_partitions_tree.yaml') + graph = self.load_config_file('multiple_partitions_graph.yaml') + parsed_graph = self.bd._config_tree_to_digraph(tree, + self.bd.plugin_manager) + logger.debug(parsed_graph) + self.assertItemsEqual(parsed_graph, graph) + + +class TestCreateGraph(TestGraphGeneration): + + # Test digraph generation from deep_graph config file + def test_deep_graph_generator(self): + config = self.load_config_file('deep_graph.yaml') + + graph, call_order = self.bd.create_graph(config, + self.fake_default_config) + + call_order_list = [n.name for n in call_order] + + # manually created from deep_graph.yaml + # Note unlike below, the sort here is stable because the graph + # doesn't have multiple paths with only one partition + call_order_names = ['image0', 'root', 'mkfs_root', + 'mount_mkfs_root', + 'fstab_mount_mkfs_root'] + + self.assertListEqual(call_order_list, call_order_names) + + # Test multiple parition digraph generation + def test_multiple_partitions_graph_generator(self): + config = self.load_config_file('multiple_partitions_graph.yaml') + + graph, call_order = self.bd.create_graph(config, + self.fake_default_config) + call_order_list = [n.name for n in call_order] + + # The sort creating call_order_list is unstable. + + # We want to ensure we see the "partitions" object in + # root->var->var_log order + root_pos = call_order_list.index('root') + var_pos = call_order_list.index('var') + var_log_pos = call_order_list.index('var_log') + self.assertGreater(var_pos, root_pos) + self.assertGreater(var_log_pos, var_pos) + + # Ensure mkfs happens after partition + mkfs_root_pos = call_order_list.index('mkfs_root') + self.assertLess(root_pos, mkfs_root_pos) + mkfs_var_pos = call_order_list.index('mkfs_var') + self.assertLess(var_pos, mkfs_var_pos) + mkfs_var_log_pos = call_order_list.index('mkfs_var_log') + self.assertLess(var_log_pos, mkfs_var_log_pos) diff --git a/diskimage_builder/block_device/tests/test_mount_order.py b/diskimage_builder/block_device/tests/test_mount_order.py new file mode 100644 index 000000000..e589cf2f9 --- /dev/null +++ b/diskimage_builder/block_device/tests/test_mount_order.py @@ -0,0 +1,52 @@ +# 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 +# +# http://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. + +import logging +import mock + +import diskimage_builder.block_device.tests.test_config as tc + +from diskimage_builder.block_device.level3.mount import MountPoint + +logger = logging.getLogger(__name__) + + +class TestMountOrder(tc.TestGraphGeneration): + + @mock.patch('diskimage_builder.block_device.level3.mount.exec_sudo') + def test_mount_order(self, mock_exec_sudo): + + config = self.load_config_file('multiple_partitions_graph.yaml') + + graph, call_order = self.bd.create_graph(config, + self.fake_default_config) + + result = {} + result['filesys'] = {} + result['filesys']['mkfs_root'] = {} + result['filesys']['mkfs_root']['device'] = 'fake' + result['filesys']['mkfs_var'] = {} + result['filesys']['mkfs_var']['device'] = 'fake' + result['filesys']['mkfs_var_log'] = {} + result['filesys']['mkfs_var_log']['device'] = 'fake' + + rollback = [] + + for node in call_order: + if isinstance(node, MountPoint): + # XXX: do we even need to create? We could test the + # sudo arguments from the mock in the below asserts + # too + node.create(result, rollback) + + # ensure that partitions are mounted in order root->var->var/log + self.assertListEqual(result['mount_order'], ['/', '/var', '/var/log'])