Last active
July 7, 2016 13:16
-
-
Save fuzzylogiq/b01d1f120c2db4b803575daf66f34bb8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/python | |
| # encoding: utf-8 | |
| """ | |
| recipe_tester.py | |
| Module for testing autopkg recipes | |
| Copyright (C) University of Oxford 2016 | |
| Ben Goodstein <ben.goodstein at it.ox.ac.uk> | |
| This program is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation, either version 3 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| """ | |
| import plistlib | |
| import sys | |
| import time | |
| import argparse | |
| DEFAULT_RECIPE = '/Users/ouit0354/Developer/recipe_checker/Github.munki.recipe' | |
| class ResultError(Exception): | |
| pass | |
| class Tester(): | |
| def __init__(self): | |
| self._passes = [] | |
| self._fails = [] | |
| self._warns = [] | |
| def __call__(self): | |
| self._runTests() | |
| def _evaluateTest(self, result, severity): | |
| if result: | |
| return 'pass' | |
| elif severity == 'warn': | |
| return 'warn' | |
| else: | |
| return 'fail' | |
| def assertDictContains(self, aDict, keyList, notBlank=False, | |
| expectedValue=None, severity="fail"): | |
| ''' Asserts whether keyList is present in nested aDict ''' | |
| keyFound = False | |
| # Could be None | |
| if aDict: | |
| for key in keyList: | |
| if key in aDict: | |
| keyFound = True | |
| if isinstance(aDict[key], dict): | |
| aDict = aDict[key] | |
| else: | |
| keyFound = False | |
| if expectedValue and aDict[keyList[-1]] != expectedValue: | |
| keyFound = False | |
| if notBlank and aDict[keyList[-1]] == "": | |
| keyFound = False | |
| return self._evaluateTest(keyFound, severity) | |
| def assertTrue(self, expr, severity="fail"): | |
| return self._evaluateTest(expr, severity) | |
| def _runTests(self, stream=sys.stdout): | |
| header = "=" * 70 + "\n" | |
| separator = "-" * 70 + "\n" | |
| tests = [f for f in dir(self) if f.startswith('test')] | |
| startTime = time.time() | |
| for test in tests: | |
| result = getattr(self, test)() | |
| if result == 'fail': | |
| self._fails.append(test) | |
| stream.write('F') | |
| elif result == 'warn': | |
| self._warns.append(test) | |
| stream.write('W') | |
| elif result == 'pass': | |
| self._passes.append(test) | |
| stream.write('.') | |
| else: | |
| raise ResultError('Result not recognised') | |
| timeTaken = time.time() - startTime | |
| stream.write('\n') | |
| if self._fails: | |
| stream.write(header) | |
| for fail in self._fails: | |
| stream.write('FAIL: %s\n' % fail) | |
| if self._warns: | |
| stream.write(header) | |
| for warn in self._warns: | |
| stream.write('WARN: %s\n' % warn) | |
| stream.write(separator) | |
| stream.write( | |
| "Ran %d tests in %.3f seconds.\n\n" % (len(tests), timeTaken) | |
| ) | |
| if self._fails: | |
| stream.write('FAILED (failures=%d)\n' % len(self._fails)) | |
| elif self._warns: | |
| stream.write('OK (warnings=%d)\n' % len(self._warns)) | |
| else: | |
| stream.write('OK\n') | |
| class RecipeTester(Tester): | |
| def __init__(self, filePath): | |
| Tester.__init__(self) | |
| self.filePath = filePath | |
| try: | |
| self.contents = plistlib.readPlist(self.filePath) | |
| except Exception: | |
| self.contents = None | |
| def test_filename_ends_with_recipe(self): | |
| return self.assertTrue(self.filePath.endswith('.recipe')) | |
| def test_recipe_is_loaded(self): | |
| return self.assertTrue(self.contents) | |
| def test_attribution_author_email_not_blank(self): | |
| return self.assertDictContains(self.contents, | |
| ['Attribution', 'Author', 'Email'], | |
| notBlank=True) | |
| if __name__ == '__main__': | |
| parser = argparse.ArgumentParser('Run tests on a recipe file') | |
| parser.add_argument('file', nargs='?', default=DEFAULT_RECIPE) | |
| args = parser.parse_args() | |
| rfile = args.file | |
| rt = RecipeTester(rfile) | |
| rt() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment