| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2017 The LUCI Authors. All rights reserved. | 2 # Copyright 2017 The LUCI Authors. All rights reserved. |
| 3 # Use of this source code is governed under the Apache License, Version 2.0 | 3 # Use of this source code is governed under the Apache License, Version 2.0 |
| 4 # that can be found in the LICENSE file. | 4 # that can be found in the LICENSE file. |
| 5 | 5 |
| 6 import json | 6 import json |
| 7 import os | 7 import os |
| 8 import shutil | 8 import shutil |
| 9 import subprocess | 9 import subprocess |
| 10 import sys | 10 import sys |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 137 'version': 1, | 137 'version': 1, |
| 138 'valid': True, | 138 'valid': True, |
| 139 } | 139 } |
| 140 | 140 |
| 141 def diff_failure(self, test): | 141 def diff_failure(self, test): |
| 142 """Simulates a diff failure for |test|.""" | 142 """Simulates a diff failure for |test|.""" |
| 143 self._result.setdefault('test_failures', {}).setdefault( | 143 self._result.setdefault('test_failures', {}).setdefault( |
| 144 test, {'failures': []})['failures'].append({'diff_failure': {}}) | 144 test, {'failures': []})['failures'].append({'diff_failure': {}}) |
| 145 return self | 145 return self |
| 146 | 146 |
| 147 def internal_failure(self, test): |
| 148 """Simulates an internal failure for |test|.""" |
| 149 self._result.setdefault('test_failures', {}).setdefault( |
| 150 test, {'failures': []})['failures'].append({'internal_failure': {}}) |
| 151 return self |
| 152 |
| 147 def check_failure(self, test, filename, lineno, func, args=None, kwargs=None, | 153 def check_failure(self, test, filename, lineno, func, args=None, kwargs=None, |
| 148 name=None): | 154 name=None): |
| 149 """Simulates a check failure for |test|. | 155 """Simulates a check failure for |test|. |
| 150 | 156 |
| 151 Arguments: | 157 Arguments: |
| 152 test(str): name of the test | 158 test(str): name of the test |
| 153 filename(str): path where check is introduced | 159 filename(str): path where check is introduced |
| 154 lineno(int): line number where check is introduced | 160 lineno(int): line number where check is introduced |
| 155 func(str): function/callable name of the check | 161 func(str): function/callable name of the check |
| 156 args(list): arguments for |func| | 162 args(list): arguments for |func| |
| (...skipping 264 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 421 | 427 |
| 422 def test_test_recipe_syntax_error(self): | 428 def test_test_recipe_syntax_error(self): |
| 423 rw = RecipeWriter(os.path.join(self._root_dir, 'recipes'), 'foo') | 429 rw = RecipeWriter(os.path.join(self._root_dir, 'recipes'), 'foo') |
| 424 rw.RunStepsLines = ['baz'] | 430 rw.RunStepsLines = ['baz'] |
| 425 rw.add_expectation('basic') | 431 rw.add_expectation('basic') |
| 426 rw.write() | 432 rw.write() |
| 427 with self.assertRaises(subprocess.CalledProcessError) as cm: | 433 with self.assertRaises(subprocess.CalledProcessError) as cm: |
| 428 self._run_recipes('test', 'run', '--json', self.json_path) | 434 self._run_recipes('test', 'run', '--json', self.json_path) |
| 429 self.assertIn('NameError: global name \'baz\' is not defined', | 435 self.assertIn('NameError: global name \'baz\' is not defined', |
| 430 cm.exception.output) | 436 cm.exception.output) |
| 431 self.assertFalse(self.json_contents.get('valid')) | 437 self.assertEqual( |
| 438 self.json_generator |
| 439 .invalid() |
| 440 .internal_failure('foo.basic') |
| 441 .coverage_failure('recipes/foo.py', [6]) |
| 442 .unused_expectation('recipes/foo.expected') |
| 443 .unused_expectation('recipes/foo.expected/basic.json').get(), |
| 444 self.json_contents) |
| 432 | 445 |
| 433 def test_test_recipe_module_uncovered(self): | 446 def test_test_recipe_module_uncovered(self): |
| 434 mw = RecipeModuleWriter(self._root_dir, 'foo') | 447 mw = RecipeModuleWriter(self._root_dir, 'foo') |
| 435 mw.write() | 448 mw.write() |
| 436 with self.assertRaises(subprocess.CalledProcessError) as cm: | 449 with self.assertRaises(subprocess.CalledProcessError) as cm: |
| 437 self._run_recipes('test', 'run', '--json', self.json_path) | 450 self._run_recipes('test', 'run', '--json', self.json_path) |
| 438 self.assertIn('The following modules lack test coverage: foo', | 451 self.assertIn('The following modules lack test coverage: foo', |
| 439 cm.exception.output) | 452 cm.exception.output) |
| 440 self.assertEqual(self.json_generator.uncovered_module('foo').get(), | 453 self.assertEqual(self.json_generator.uncovered_module('foo').get(), |
| 441 self.json_contents) | 454 self.json_contents) |
| 442 | 455 |
| 443 def test_test_recipe_module_syntax_error(self): | 456 def test_test_recipe_module_syntax_error(self): |
| 444 mw = RecipeModuleWriter(self._root_dir, 'foo_module') | 457 mw = RecipeModuleWriter(self._root_dir, 'foo_module') |
| 445 mw.methods['foo'] = ['baz'] | 458 mw.methods['foo'] = ['baz'] |
| 446 mw.write() | 459 mw.write() |
| 447 mw.example.DEPS = ['foo_module'] | 460 mw.example.DEPS = ['foo_module'] |
| 448 mw.example.RunStepsLines = ['api.foo_module.foo()'] | 461 mw.example.RunStepsLines = ['api.foo_module.foo()'] |
| 449 mw.example.write() | 462 mw.example.write() |
| 450 with self.assertRaises(subprocess.CalledProcessError) as cm: | 463 with self.assertRaises(subprocess.CalledProcessError) as cm: |
| 451 self._run_recipes('test', 'run', '--json', self.json_path) | 464 self._run_recipes('test', 'run', '--json', self.json_path) |
| 452 self.assertIn('NameError: global name \'baz\' is not defined', | 465 self.assertIn('NameError: global name \'baz\' is not defined', |
| 453 cm.exception.output) | 466 cm.exception.output) |
| 454 self.assertIn('FATAL: Insufficient coverage', cm.exception.output) | 467 self.assertIn('FATAL: Insufficient coverage', cm.exception.output) |
| 455 self.assertFalse(self.json_contents.get('valid')) | 468 self.assertEqual( |
| 469 self.json_generator |
| 470 .invalid() |
| 471 .internal_failure('foo_module:example.basic') |
| 472 .coverage_failure('recipe_modules/foo_module/api.py', [6]) |
| 473 .coverage_failure('recipe_modules/foo_module/example.py', [6]) |
| 474 .get(), |
| 475 self.json_contents) |
| 456 | 476 |
| 457 def test_test_recipe_module_syntax_error_in_example(self): | 477 def test_test_recipe_module_syntax_error_in_example(self): |
| 458 mw = RecipeModuleWriter(self._root_dir, 'foo_module') | 478 mw = RecipeModuleWriter(self._root_dir, 'foo_module') |
| 459 mw.methods['foo'] = ['pass'] | 479 mw.methods['foo'] = ['pass'] |
| 460 mw.write() | 480 mw.write() |
| 461 mw.example.DEPS = ['foo_module'] | 481 mw.example.DEPS = ['foo_module'] |
| 462 mw.example.RunStepsLines = ['baz'] | 482 mw.example.RunStepsLines = ['baz'] |
| 463 mw.example.write() | 483 mw.example.write() |
| 464 with self.assertRaises(subprocess.CalledProcessError) as cm: | 484 with self.assertRaises(subprocess.CalledProcessError) as cm: |
| 465 self._run_recipes('test', 'run', '--json', self.json_path) | 485 self._run_recipes('test', 'run', '--json', self.json_path) |
| 466 self.assertIn('NameError: global name \'baz\' is not defined', | 486 self.assertIn('NameError: global name \'baz\' is not defined', |
| 467 cm.exception.output) | 487 cm.exception.output) |
| 468 self.assertIn('FATAL: Insufficient coverage', cm.exception.output) | 488 self.assertIn('FATAL: Insufficient coverage', cm.exception.output) |
| 469 self.assertFalse(self.json_contents.get('valid')) | 489 self.assertEqual( |
| 490 self.json_generator |
| 491 .invalid() |
| 492 .internal_failure('foo_module:example.basic') |
| 493 .coverage_failure('recipe_modules/foo_module/api.py', [6]) |
| 494 .coverage_failure('recipe_modules/foo_module/example.py', [6]) |
| 495 .get(), |
| 496 self.json_contents) |
| 470 | 497 |
| 471 def test_test_recipe_module_example_not_covered(self): | 498 def test_test_recipe_module_example_not_covered(self): |
| 472 mw = RecipeModuleWriter(self._root_dir, 'foo_module') | 499 mw = RecipeModuleWriter(self._root_dir, 'foo_module') |
| 473 mw.methods['foo'] = ['pass'] | 500 mw.methods['foo'] = ['pass'] |
| 474 mw.write() | 501 mw.write() |
| 475 mw.example.DEPS = ['foo_module'] | 502 mw.example.DEPS = ['foo_module'] |
| 476 mw.example.RunStepsLines = ['if False:', ' pass'] | 503 mw.example.RunStepsLines = ['if False:', ' pass'] |
| 477 mw.example.write() | 504 mw.example.write() |
| 478 with self.assertRaises(subprocess.CalledProcessError) as cm: | 505 with self.assertRaises(subprocess.CalledProcessError) as cm: |
| 479 self._run_recipes('test', 'run', '--json', self.json_path) | 506 self._run_recipes('test', 'run', '--json', self.json_path) |
| (...skipping 379 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 859 | 886 |
| 860 g1.diff_failure('foo_diff') | 887 g1.diff_failure('foo_diff') |
| 861 g2.diff_failure('foo_diff').diff_failure('bar_diff') | 888 g2.diff_failure('foo_diff').diff_failure('bar_diff') |
| 862 | 889 |
| 863 g1.check_failure( | 890 g1.check_failure( |
| 864 'foo_check', 'foo_file', 1, 'foo_func') | 891 'foo_check', 'foo_file', 1, 'foo_func') |
| 865 g2.check_failure( | 892 g2.check_failure( |
| 866 'foo_check', 'foo_file', 1, 'foo_func').check_failure( | 893 'foo_check', 'foo_file', 1, 'foo_func').check_failure( |
| 867 'bar_check', 'bar_file', 2, 'bar_func', ['bar_args']) | 894 'bar_check', 'bar_file', 2, 'bar_func', ['bar_args']) |
| 868 | 895 |
| 896 g1.internal_failure('foo_internal') |
| 897 g2.internal_failure('foo_internal').internal_failure('bar_internal') |
| 898 |
| 869 g1.uncovered_module('foo_module') | 899 g1.uncovered_module('foo_module') |
| 870 g2.uncovered_module('foo_module').uncovered_module('bar_module') | 900 g2.uncovered_module('foo_module').uncovered_module('bar_module') |
| 871 | 901 |
| 872 g1.unused_expectation( | 902 g1.unused_expectation( |
| 873 'foo_expectation') | 903 'foo_expectation') |
| 874 g2.unused_expectation( | 904 g2.unused_expectation( |
| 875 'foo_expectation').unused_expectation('bar_expectation') | 905 'foo_expectation').unused_expectation('bar_expectation') |
| 876 | 906 |
| 877 with self.assertRaises(subprocess.CalledProcessError) as cm: | 907 with self.assertRaises(subprocess.CalledProcessError) as cm: |
| 878 self._run_recipes( | 908 self._run_recipes( |
| 879 'test', 'diff', | 909 'test', 'diff', |
| 880 '--baseline', g1.write(), | 910 '--baseline', g1.write(), |
| 881 '--actual', g2.write(), | 911 '--actual', g2.write(), |
| 882 '--json', self.json_path) | 912 '--json', self.json_path) |
| 883 self.assertEqual( | 913 self.assertEqual( |
| 884 self.json_generator | 914 self.json_generator |
| 885 .coverage_failure('bar_coverage', [2]) | 915 .coverage_failure('bar_coverage', [2]) |
| 886 .diff_failure('bar_diff') | 916 .diff_failure('bar_diff') |
| 887 .check_failure('bar_check', 'bar_file', 2, 'bar_func', ['bar_args']) | 917 .check_failure('bar_check', 'bar_file', 2, 'bar_func', ['bar_args']) |
| 918 .internal_failure('bar_internal') |
| 888 .uncovered_module('bar_module') | 919 .uncovered_module('bar_module') |
| 889 .unused_expectation('bar_expectation') | 920 .unused_expectation('bar_expectation') |
| 890 .get(), | 921 .get(), |
| 891 self.json_contents) | 922 self.json_contents) |
| 892 | 923 |
| 893 | 924 |
| 894 if __name__ == '__main__': | 925 if __name__ == '__main__': |
| 895 sys.exit(unittest.main()) | 926 sys.exit(unittest.main()) |
| OLD | NEW |