Skip to content

Instantly share code, notes, and snippets.

@fuzzylogiq
Last active July 7, 2016 13:16
Show Gist options
  • Select an option

  • Save fuzzylogiq/b01d1f120c2db4b803575daf66f34bb8 to your computer and use it in GitHub Desktop.

Select an option

Save fuzzylogiq/b01d1f120c2db4b803575daf66f34bb8 to your computer and use it in GitHub Desktop.
#!/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