| OLD | NEW |
| 1 # Copyright 2016 The LUCI Authors. All rights reserved. | 1 # Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
| 3 # that can be found in the LICENSE file. | 3 # that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import calendar | 5 import calendar |
| 6 import collections | 6 import collections |
| 7 import contextlib | 7 import contextlib |
| 8 import datetime | 8 import datetime |
| 9 import itertools | 9 import itertools |
| 10 import json | 10 import json |
| (...skipping 388 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 399 | 399 |
| 400 # Convert when_timestamp to UNIX timestamp. | 400 # Convert when_timestamp to UNIX timestamp. |
| 401 when = change.get('when_timestamp') | 401 when = change.get('when_timestamp') |
| 402 if isinstance(when, datetime.datetime): | 402 if isinstance(when, datetime.datetime): |
| 403 when = calendar.timegm(when.utctimetuple()) | 403 when = calendar.timegm(when.utctimetuple()) |
| 404 change['when_timestamp'] = when | 404 change['when_timestamp'] = when |
| 405 | 405 |
| 406 return change | 406 return change |
| 407 | 407 |
| 408 | 408 |
| 409 class fakeEnviron(object): |
| 410 """This is a fake dictionary which is meant to emulate os.environ strictly for |
| 411 the purposes of interacting with _merge_envs. |
| 412 |
| 413 It supports: |
| 414 * Any key access is answered with <key>, allowing this to be used as |
| 415 a % format argument. |
| 416 * Deleting/setting items sets them to None/value, appropriately. |
| 417 * `in` checks always returns True |
| 418 * copy() returns self |
| 419 |
| 420 The 'formatted' result can be obtained by looking at .data. |
| 421 """ |
| 422 def __init__(self): |
| 423 self.data = {} |
| 424 |
| 425 def __getitem__(self, key): |
| 426 return '<%s>' % key |
| 427 |
| 428 def __delitem__(self, key): |
| 429 self.data[key] = None |
| 430 |
| 431 def __contains__(self, key): |
| 432 return True |
| 433 |
| 434 def __setitem__(self, key, value): |
| 435 self.data[key] = value |
| 436 |
| 437 def copy(self): |
| 438 return self |
| 439 |
| 440 |
| 409 class SimulationStepRunner(StepRunner): | 441 class SimulationStepRunner(StepRunner): |
| 410 """Pretends to run steps, instead recording what would have been run. | 442 """Pretends to run steps, instead recording what would have been run. |
| 411 | 443 |
| 412 This is the main workhorse of recipes.py simulation_test. Returns the log of | 444 This is the main workhorse of recipes.py simulation_test. Returns the log of |
| 413 steps that would have been run in steps_ran. Uses test_data to mock return | 445 steps that would have been run in steps_ran. Uses test_data to mock return |
| 414 values. | 446 values. |
| 415 """ | 447 """ |
| 416 | 448 |
| 417 def __init__(self, stream_engine, test_data, annotator): | 449 def __init__(self, stream_engine, test_data, annotator): |
| 418 self._test_data = test_data | 450 self._test_data = test_data |
| 419 self._stream_engine = stream_engine | 451 self._stream_engine = stream_engine |
| 420 self._annotator = annotator | 452 self._annotator = annotator |
| 421 self._step_history = collections.OrderedDict() | 453 self._step_history = collections.OrderedDict() |
| 422 | 454 |
| 423 @property | 455 @property |
| 424 def stream_engine(self): | 456 def stream_engine(self): |
| 425 return self._stream_engine | 457 return self._stream_engine |
| 426 | 458 |
| 427 def open_step(self, step_config): | 459 def open_step(self, step_config): |
| 428 try: | 460 try: |
| 429 test_data_fn = step_config.step_test_data or recipe_test_api.StepTestData | 461 test_data_fn = step_config.step_test_data or recipe_test_api.StepTestData |
| 430 step_test = self._test_data.pop_step_test_data(step_config.name, | 462 step_test = self._test_data.pop_step_test_data(step_config.name, |
| 431 test_data_fn) | 463 test_data_fn) |
| 432 rendered_step = render_step(step_config, step_test) | 464 rendered_step = render_step(step_config, step_test) |
| 465 step_env = _merge_envs(fakeEnviron(), (rendered_step.config.env or {})) |
| 466 rendered_step = rendered_step._replace( |
| 467 config=rendered_step.config._replace(env=step_env.data)) |
| 433 step_config = None # Make sure we use rendered step config. | 468 step_config = None # Make sure we use rendered step config. |
| 434 | 469 |
| 435 # Layer the simulation step on top of the given stream engine. | 470 # Layer the simulation step on top of the given stream engine. |
| 436 step_stream = self._stream_engine.new_step_stream(rendered_step.config) | 471 step_stream = self._stream_engine.new_step_stream(rendered_step.config) |
| 437 except: | 472 except: |
| 438 with self.stream_engine.make_step_stream('Step Preparation Exception') as
s: | 473 with self.stream_engine.make_step_stream('Step Preparation Exception') as
s: |
| 439 s.set_step_status('EXCEPTION') | 474 s.set_step_status('EXCEPTION') |
| 440 with s.new_log_stream('exception') as l: | 475 with s.new_log_stream('exception') as l: |
| 441 l.write_split(traceback.format_exc()) | 476 l.write_split(traceback.format_exc()) |
| 442 raise | 477 raise |
| (...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 748 supplied command, and only uses the |env| kwarg for modifying the environment | 783 supplied command, and only uses the |env| kwarg for modifying the environment |
| 749 of the child process. | 784 of the child process. |
| 750 """ | 785 """ |
| 751 saved_path = os.environ['PATH'] | 786 saved_path = os.environ['PATH'] |
| 752 try: | 787 try: |
| 753 if path is not None: | 788 if path is not None: |
| 754 os.environ['PATH'] = path | 789 os.environ['PATH'] = path |
| 755 yield | 790 yield |
| 756 finally: | 791 finally: |
| 757 os.environ['PATH'] = saved_path | 792 os.environ['PATH'] = saved_path |
| OLD | NEW |