comparison tests/conftest.py @ 351:1f22d4b10fef

tests: Improve bake tests output, add support for partial output checks.
author Ludovic Chabant <ludovic@chabant.com>
date Fri, 17 Apr 2015 16:08:23 -0700
parents 76c838453dbe
children a9929e0b8f66
comparison
equal deleted inserted replaced
350:b8ff1780b491 351:1f22d4b10fef
78 baker = Baker(app, out_dir) 78 baker = Baker(app, out_dir)
79 baker.bake() 79 baker.bake()
80 80
81 if expected_output_files: 81 if expected_output_files:
82 actual = fs.getStructure('kitchen/_counter') 82 actual = fs.getStructure('kitchen/_counter')
83 error = _compare_dicts(actual, expected_output_files) 83 error = _compare_dicts(expected_output_files, actual)
84 if error: 84 if error:
85 raise ExpectedBakeOutputError(error) 85 raise ExpectedBakeOutputError(error)
86 86
87 if expected_partial_files: 87 if expected_partial_files:
88 for key, content in expected_partial_files.items(): 88 keys = list(sorted(expected_partial_files.keys()))
89 for key in keys:
89 try: 90 try:
90 actual = fs.getStructure('kitchen/_counter/' + 91 actual = fs.getFileEntry('kitchen/_counter/' +
91 key.lstrip('/')) 92 key.lstrip('/'))
92 except Exception: 93 except Exception as e:
93 raise ExpectedBakeOutputError([ 94 raise ExpectedBakeOutputError([
94 "Missing expected output file: %s" % key]) 95 "Can't access output file %s: %s" % (key, e)])
95 if not isinstance(actual, str): 96
96 raise ExpectedBakeOutputError([ 97 expected = expected_partial_files[key]
97 "Expected output file is a directory: %s" % key]) 98 # HACK because for some reason PyYAML adds a new line for those
98 if actual != content: 99 # and I have no idea why.
99 raise ExpectedBakeOutputError([ 100 expected = expected.rstrip('\n')
100 "Unexpected output file contents:", 101 cmpres = _compare_str(expected, actual, key)
101 "%s: %s" % (key, content), 102 if cmpres:
102 "%s: %s" % (key, actual)]) 103 raise ExpectedBakeOutputError(cmpres)
103 104
104 def reportinfo(self): 105 def reportinfo(self):
105 return self.fspath, 0, "bake: %s" % self.name 106 return self.fspath, 0, "bake: %s" % self.name
106 107
107 def repr_failure(self, excinfo): 108 def repr_failure(self, excinfo):
108 if isinstance(excinfo.value, ExpectedBakeOutputError): 109 if isinstance(excinfo.value, ExpectedBakeOutputError):
109 return ('\n'.join(excinfo.value.args[0])) 110 return ('\n'.join(
111 ['Unexpected bake output. Left is expected output, '
112 'right is actual output'] +
113 excinfo.value.args[0]))
110 return super(BakeTestItem, self).repr_failure(excinfo) 114 return super(BakeTestItem, self).repr_failure(excinfo)
111 115
112 116
113 class ExpectedBakeOutputError(Exception): 117 class ExpectedBakeOutputError(Exception):
114 pass 118 pass
119 path = os.path.join(parent_path, name) 123 path = os.path.join(parent_path, name)
120 if isinstance(subspec, str): 124 if isinstance(subspec, str):
121 fs.withFile(path, subspec) 125 fs.withFile(path, subspec)
122 elif isinstance(subspec, dict): 126 elif isinstance(subspec, dict):
123 _add_mock_files(fs, path, subspec) 127 _add_mock_files(fs, path, subspec)
128
129
130 def _compare(left, right, path):
131 if type(left) != type(right):
132 return (["Different items: ",
133 "%s: %s" % (path, pprint.pformat(left)),
134 "%s: %s" % (path, pprint.pformat(right))])
135 if isinstance(left, str):
136 return _compare_str(left, right, path)
137 elif isinstance(left, dict):
138 return _compare_dicts(left, right, path)
139 elif isinstance(left, list):
140 return _compare_lists(left, right, path)
141 elif left != right:
142 return (["Different items: ",
143 "%s: %s" % (path, pprint.pformat(left)),
144 "%s: %s" % (path, pprint.pformat(right))])
124 145
125 146
126 def _compare_dicts(left, right, basepath=''): 147 def _compare_dicts(left, right, basepath=''):
127 key_diff = set(left.keys()) ^ set(right.keys()) 148 key_diff = set(left.keys()) ^ set(right.keys())
128 if key_diff: 149 if key_diff:
138 159
139 for key in left.keys(): 160 for key in left.keys():
140 lv = left[key] 161 lv = left[key]
141 rv = right[key] 162 rv = right[key]
142 childpath = basepath + '/' + key 163 childpath = basepath + '/' + key
143 if type(lv) != type(rv): 164 cmpres = _compare(lv, rv, childpath)
144 return (["Different items: ", 165 if cmpres:
145 "%s/%s: %s" % (basepath, key, pprint.pformat(lv)), 166 return cmpres
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 167 return None
161 168
162 169
163 def _compare_lists(left, right): 170 def _compare_lists(left, right, path):
164 for i in range(min(len(left), len(right))): 171 for i in range(min(len(left), len(right))):
165 l = left[i] 172 l = left[i]
166 r = right[i] 173 r = right[i]
167 if type(l) != type(r): 174 cmpres = _compare(l, r, path)
168 return ['Different items at index %d:' % i, 175 if cmpres:
169 pprint.pformat(l), 176 return cmpres
170 pprint.pformat(r)] 177 if len(left) > len(right):
171 if isinstance(l, dict): 178 return (["Left '%s' contains more items. First extra item: " % path,
172 r = _compare_dicts(l, r) 179 left[len(right)]])
173 if r: 180 if len(right) > len(left):
174 return r 181 return (["Right '%s' contains more items. First extra item: " % path,
175 elif isinstance(l, list): 182 right[len(left)]])
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 183 return None
184 184
185
186 def _compare_str(left, right, path):
187 if left == right:
188 return None
189 for i in range(min(len(left), len(right))):
190 if left[i] != right[i]:
191 start = max(0, i - 5)
192 lend = min(len(left), i + 5)
193 rend = min(len(right), i + 5)
194 return ["Items '%s' differ at index %d:" % (path, i),
195 left[start:lend],
196 (' ' * start + '^'),
197 right[start:rend],
198 (' ' * start + '^')]
199 if len(left) > len(right):
200 return ["Left is longer.",
201 "Left '%s': " % path, left,
202 "Right '%s': " % path, right,
203 "Extra items: %r" % left[len(right):]]
204 if len(right) > len(left):
205 return ["Right is longer.",
206 "Left '%s': " % path, left,
207 "Right '%s': " % path, right,
208 "Extra items: %r" % right[len(left):]]
209