comparison tests/conftest.py @ 1126:be550e1f6423

tests: Improve failure reporting, improve CLI tests. CLI tests can now optionally run in a non-prepared temp directory.
author Ludovic Chabant <ludovic@chabant.com>
date Wed, 28 Feb 2018 20:16:04 -0800
parents 2aa6174453c8
children 952f3c24a99d
comparison
equal deleted inserted replaced
1125:8c3506e5ea3f 1126:be550e1f6423
104 return bool(self.config.getoption('--pc-mock-debug')) 104 return bool(self.config.getoption('--pc-mock-debug'))
105 105
106 def _prepareMockFs(self): 106 def _prepareMockFs(self):
107 fs = mock_fs() 107 fs = mock_fs()
108 108
109 if self.spec.get('no_kitchen', False):
110 fs.withDir('/')
111 return fs
112
109 # Suppress any formatting or layout so we can compare 113 # Suppress any formatting or layout so we can compare
110 # much simpler strings. 114 # much simpler strings.
111 config = { 115 config = {
112 'site': { 116 'site': {
113 'default_format': 'none', 117 'default_format': 'none',
213 argv = self.spec['args'] 217 argv = self.spec['args']
214 if isinstance(argv, str): 218 if isinstance(argv, str):
215 argv = argv.split(' ') 219 argv = argv.split(' ')
216 if self.is_theme_site: 220 if self.is_theme_site:
217 argv.insert(0, '--theme') 221 argv.insert(0, '--theme')
218 argv = ['--root', fs.path('/kitchen')] + argv 222 if not self.spec.get('no_kitchen', False):
219 223 argv = ['--root', fs.path('/kitchen')] + argv
220 expected_code = self.spec.get('code', 0)
221 expected_out = self.spec.get('out', None)
222 224
223 with mock_fs_scope(fs, keep=self.mock_debug): 225 with mock_fs_scope(fs, keep=self.mock_debug):
226 cwd = os.getcwd()
224 memstream = io.StringIO() 227 memstream = io.StringIO()
225 hdl = logging.StreamHandler(stream=memstream) 228 hdl = logging.StreamHandler(stream=memstream)
226 logging.getLogger().addHandler(hdl) 229 logging.getLogger().addHandler(hdl)
227 try: 230 try:
228 from piecrust.main import _pre_parse_chef_args, _run_chef 231 from piecrust.main import _pre_parse_chef_args, _run_chef
232 os.chdir(fs.path('/'))
229 pre_args = _pre_parse_chef_args(argv) 233 pre_args = _pre_parse_chef_args(argv)
230 exit_code = _run_chef(pre_args, argv) 234 exit_code = _run_chef(pre_args, argv)
231 finally: 235 finally:
232 logging.getLogger().removeHandler(hdl) 236 logging.getLogger().removeHandler(hdl)
233 237 os.chdir(cwd)
234 assert expected_code == exit_code 238
235 239 expected_code = self.spec.get('code', 0)
240 if expected_code != exit_code:
241 raise UnexpectedChefExitCodeError("Got '%d', expected '%d'." %
242 (exit_code, expected_code))
243
244 expected_out = self.spec.get('out', None)
236 if expected_out is not None: 245 if expected_out is not None:
237 actual_out = memstream.getvalue() 246 actual_out = memstream.getvalue()
238 if not self.spec.get('no_strip'): 247 if not self.spec.get('no_strip'):
239 actual_out = actual_out.rstrip(' \n') 248 actual_out = actual_out.rstrip(' \n')
240 expected_out = expected_out.rstrip(' \n') 249 expected_out = expected_out.rstrip(' \n')
241 if self.spec.get('replace_out_path_sep'): 250 if self.spec.get('replace_out_path_sep'):
242 expected_out = expected_out.replace('/', os.sep) 251 expected_out = expected_out.replace('/', os.sep)
243 assert expected_out == actual_out 252 if expected_out != actual_out:
253 raise UnexpectedChefOutputError(expected_out, actual_out)
254
255 expected_files = self.spec.get('files', None)
256 if expected_files is not None:
257 for path in expected_files:
258 path = '/' + path.lstrip('/')
259 if not os.path.exists(fs.path(path)):
260 raise MissingChefOutputFileError(fs, path)
244 261
245 def reportinfo(self): 262 def reportinfo(self):
246 return self.fspath, 0, "bake: %s" % self.name 263 return self.fspath, 0, "bake: %s" % self.name
247 264
248 def repr_failure(self, excinfo): 265 def repr_failure(self, excinfo):
266 if isinstance(excinfo.value, UnexpectedChefExitCodeError):
267 return str(excinfo.value)
249 if isinstance(excinfo.value, UnexpectedChefOutputError): 268 if isinstance(excinfo.value, UnexpectedChefOutputError):
250 return ('\n'.join( 269 return ('\n'.join(
251 ['Unexpected command output. Left is expected output, ' 270 ['Unexpected command output. Expected:',
252 'right is actual output'] + 271 excinfo.value.args[0],
253 excinfo.value.args[0])) 272 "Got:",
273 excinfo.value.args[1]]))
274 if isinstance(excinfo.value, MissingChefOutputFileError):
275 lines = print_fs_tree(excinfo.value.args[0].path(''))
276 return ('\n'.join(
277 ["Missing file: %s" % excinfo.value.args[1],
278 "Got output directory:"] +
279 lines))
254 return super(ChefTestItem, self).repr_failure(excinfo) 280 return super(ChefTestItem, self).repr_failure(excinfo)
255 281
256 282
283 class UnexpectedChefExitCodeError(Exception):
284 pass
285
286
257 class UnexpectedChefOutputError(Exception): 287 class UnexpectedChefOutputError(Exception):
288 pass
289
290
291 class MissingChefOutputFileError(Exception):
258 pass 292 pass
259 293
260 294
261 class ChefTestFile(YamlTestFileBase): 295 class ChefTestFile(YamlTestFileBase):
262 __item_class__ = ChefTestItem 296 __item_class__ = ChefTestItem
289 for r in records.records: 323 for r in records.records:
290 for e in r.getEntries(): 324 for e in r.getEntries():
291 errors += e.getAllErrors() 325 errors += e.getAllErrors()
292 raise BakeError(errors) 326 raise BakeError(errors)
293 327
294 check_expected_outputs(self.spec, fs, ExpectedBakeOutputError) 328 check_expected_outputs(self.spec, fs, UnexpectedBakeOutputError)
295 329
296 def reportinfo(self): 330 def reportinfo(self):
297 return self.fspath, 0, "bake: %s" % self.name 331 return self.fspath, 0, "bake: %s" % self.name
298 332
299 def repr_failure(self, excinfo): 333 def repr_failure(self, excinfo):
300 if isinstance(excinfo.value, ExpectedBakeOutputError): 334 if isinstance(excinfo.value, UnexpectedBakeOutputError):
301 return ('\n'.join( 335 return ('\n'.join(
302 ['Unexpected bake output. Left is expected output, ' 336 ['Unexpected bake output. Left is expected output, '
303 'right is actual output'] + 337 'right is actual output'] +
304 excinfo.value.args[0])) 338 excinfo.value.args[0]))
305 elif isinstance(excinfo.value, BakeError): 339 elif isinstance(excinfo.value, BakeError):
313 347
314 class BakeError(Exception): 348 class BakeError(Exception):
315 pass 349 pass
316 350
317 351
318 class ExpectedBakeOutputError(Exception): 352 class UnexpectedBakeOutputError(Exception):
319 pass 353 pass
320 354
321 355
322 class BakeTestFile(YamlTestFileBase): 356 class BakeTestFile(YamlTestFileBase):
323 __item_class__ = BakeTestItem 357 __item_class__ = BakeTestItem