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
This commit is contained in:
Ian Wienand 2017-05-23 11:21:02 +10:00
parent 78c0766bec
commit 7341542f2c
9 changed files with 378 additions and 0 deletions

View File

@ -0,0 +1,3 @@
- this_is_not_a_plugin_name:
foo: bar
baz: moo

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,8 @@
- mkfs:
name: root_fs
base: root_part
- mount:
name: mount_root_fs
base: root_fs
mount_point: /

View File

@ -0,0 +1,5 @@
- mkfs:
name: root_fs
base: root_part
mount:
mount_point: /

View File

@ -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)

View File

@ -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'])