Mercurial > piecrust2
comparison tests/conftest.py @ 347:76c838453dbe
tests: Support for YAML-based baking tests. Convert old code-based ones.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Wed, 15 Apr 2015 16:39:35 -0700 |
parents | 1cd67680c38c |
children | 1f22d4b10fef |
comparison
equal
deleted
inserted
replaced
346:89cc71928f6a | 347:76c838453dbe |
---|---|
1 import sys | 1 import sys |
2 import pprint | |
3 import os.path | |
2 import logging | 4 import logging |
5 import pytest | |
6 import yaml | |
7 from piecrust.configuration import merge_dicts | |
8 from .mockutil import mock_fs, mock_fs_scope | |
3 | 9 |
4 | 10 |
5 def pytest_runtest_setup(item): | 11 def pytest_runtest_setup(item): |
6 pass | 12 pass |
7 | 13 |
8 | 14 |
9 def pytest_addoption(parser): | 15 def pytest_addoption(parser): |
10 parser.addoption('--log-debug', action='store_true', | 16 parser.addoption( |
17 '--log-debug', | |
18 action='store_true', | |
11 help="Sets the PieCrust logger to output debug info to stdout.") | 19 help="Sets the PieCrust logger to output debug info to stdout.") |
12 | 20 |
13 | 21 |
14 def pytest_configure(config): | 22 def pytest_configure(config): |
15 if config.getoption('--log-debug'): | 23 if config.getoption('--log-debug'): |
16 hdl = logging.StreamHandler(stream=sys.stdout) | 24 hdl = logging.StreamHandler(stream=sys.stdout) |
17 logging.getLogger('piecrust').addHandler(hdl) | 25 logging.getLogger('piecrust').addHandler(hdl) |
18 logging.getLogger('piecrust').setLevel(logging.DEBUG) | 26 logging.getLogger('piecrust').setLevel(logging.DEBUG) |
19 | 27 |
28 | |
29 def pytest_collect_file(parent, path): | |
30 if path.ext == ".bake" and path.basename.startswith("test"): | |
31 return BakeTestFile(path, parent) | |
32 | |
33 | |
34 class BakeTestFile(pytest.File): | |
35 def collect(self): | |
36 spec = yaml.load_all(self.fspath.open()) | |
37 for i, item in enumerate(spec): | |
38 name = '%s_%d' % (self.fspath.basename, i) | |
39 if 'test_name' in item: | |
40 name += '_%s' % item['test_name'] | |
41 yield BakeTestItem(name, self, item) | |
42 | |
43 | |
44 class BakeTestItem(pytest.Item): | |
45 def __init__(self, name, parent, spec): | |
46 super(BakeTestItem, self).__init__(name, parent) | |
47 self.spec = spec | |
48 | |
49 def runtest(self): | |
50 fs = mock_fs() | |
51 | |
52 # Website config. | |
53 config = { | |
54 'site': { | |
55 'default_format': 'none', | |
56 'default_page_layout': 'none', | |
57 'default_post_layout': 'none'} | |
58 } | |
59 test_config = self.spec.get('config') | |
60 if test_config is not None: | |
61 merge_dicts(config, test_config) | |
62 fs.withConfig(config) | |
63 | |
64 # Input file-system. | |
65 input_files = self.spec.get('in') | |
66 if input_files is not None: | |
67 _add_mock_files(fs, '/kitchen', input_files) | |
68 | |
69 # Output file-system. | |
70 expected_output_files = self.spec.get('out') | |
71 expected_partial_files = self.spec.get('outfiles') | |
72 | |
73 # Bake! | |
74 from piecrust.baking.baker import Baker | |
75 with mock_fs_scope(fs): | |
76 out_dir = fs.path('kitchen/_counter') | |
77 app = fs.getApp() | |
78 baker = Baker(app, out_dir) | |
79 baker.bake() | |
80 | |
81 if expected_output_files: | |
82 actual = fs.getStructure('kitchen/_counter') | |
83 error = _compare_dicts(actual, expected_output_files) | |
84 if error: | |
85 raise ExpectedBakeOutputError(error) | |
86 | |
87 if expected_partial_files: | |
88 for key, content in expected_partial_files.items(): | |
89 try: | |
90 actual = fs.getStructure('kitchen/_counter/' + | |
91 key.lstrip('/')) | |
92 except Exception: | |
93 raise ExpectedBakeOutputError([ | |
94 "Missing expected output file: %s" % key]) | |
95 if not isinstance(actual, str): | |
96 raise ExpectedBakeOutputError([ | |
97 "Expected output file is a directory: %s" % key]) | |
98 if actual != content: | |
99 raise ExpectedBakeOutputError([ | |
100 "Unexpected output file contents:", | |
101 "%s: %s" % (key, content), | |
102 "%s: %s" % (key, actual)]) | |
103 | |
104 def reportinfo(self): | |
105 return self.fspath, 0, "bake: %s" % self.name | |
106 | |
107 def repr_failure(self, excinfo): | |
108 if isinstance(excinfo.value, ExpectedBakeOutputError): | |
109 return ('\n'.join(excinfo.value.args[0])) | |
110 return super(BakeTestItem, self).repr_failure(excinfo) | |
111 | |
112 | |
113 class ExpectedBakeOutputError(Exception): | |
114 pass | |
115 | |
116 | |
117 def _add_mock_files(fs, parent_path, spec): | |
118 for name, subspec in spec.items(): | |
119 path = os.path.join(parent_path, name) | |
120 if isinstance(subspec, str): | |
121 fs.withFile(path, subspec) | |
122 elif isinstance(subspec, dict): | |
123 _add_mock_files(fs, path, subspec) | |
124 | |
125 | |
126 def _compare_dicts(left, right, basepath=''): | |
127 key_diff = set(left.keys()) ^ set(right.keys()) | |
128 if key_diff: | |
129 extra_left = set(left.keys()) - set(right.keys()) | |
130 if extra_left: | |
131 return (["Left contains more items: "] + | |
132 ['- %s/%s' % (basepath, k) for k in extra_left]) | |
133 extra_right = set(right.keys()) - set(left.keys()) | |
134 if extra_right: | |
135 return (["Right contains more items: "] + | |
136 ['- %s/%s' % (basepath, k) for k in extra_right]) | |
137 return ["Unknown difference"] | |
138 | |
139 for key in left.keys(): | |
140 lv = left[key] | |
141 rv = right[key] | |
142 childpath = basepath + '/' + key | |
143 if type(lv) != type(rv): | |
144 return (["Different items: ", | |
145 "%s/%s: %s" % (basepath, key, pprint.pformat(lv)), | |
146 "%s/%s: %s" % (basepath, key, pprint.pformat(rv))]) | |
147 | |
148 if isinstance(lv, dict): | |
149 r = _compare_dicts(lv, rv, childpath) | |
150 if r: | |
151 return r | |
152 elif isinstance(lv, list): | |
153 r = _compare_lists(lv, rv, childpath) | |
154 if r: | |
155 return r | |
156 elif lv != rv: | |
157 return (["Different items: ", | |
158 "%s/%s: %s" % (basepath, key, pprint.pformat(lv)), | |
159 "%s/%s: %s" % (basepath, key, pprint.pformat(rv))]) | |
160 return None | |
161 | |
162 | |
163 def _compare_lists(left, right): | |
164 for i in range(min(len(left), len(right))): | |
165 l = left[i] | |
166 r = right[i] | |
167 if type(l) != type(r): | |
168 return ['Different items at index %d:' % i, | |
169 pprint.pformat(l), | |
170 pprint.pformat(r)] | |
171 if isinstance(l, dict): | |
172 r = _compare_dicts(l, r) | |
173 if r: | |
174 return r | |
175 elif isinstance(l, list): | |
176 r = _compare_lists(l, r) | |
177 if r: | |
178 return r | |
179 elif l != r: | |
180 return ['Different items at index %d:' % i, | |
181 pprint.pformat(l), | |
182 pprint.pformat(r)] | |
183 return None | |
184 |