michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: from unittest import TextTestRunner as _TestRunner, TestResult as _TestResult michael@0: import unittest michael@0: import inspect michael@0: from StringIO import StringIO michael@0: import os michael@0: michael@0: '''Helper to make python unit tests report the way that the Mozilla michael@0: unit test infrastructure expects tests to report. michael@0: michael@0: Usage: michael@0: michael@0: import unittest michael@0: import mozunit michael@0: michael@0: if __name__ == '__main__': michael@0: mozunit.main() michael@0: ''' michael@0: michael@0: class _MozTestResult(_TestResult): michael@0: def __init__(self, stream, descriptions): michael@0: _TestResult.__init__(self) michael@0: self.stream = stream michael@0: self.descriptions = descriptions michael@0: michael@0: def getDescription(self, test): michael@0: if self.descriptions: michael@0: return test.shortDescription() or str(test) michael@0: else: michael@0: return str(test) michael@0: michael@0: def addSuccess(self, test): michael@0: _TestResult.addSuccess(self, test) michael@0: filename = inspect.getfile(test.__class__) michael@0: testname = test._testMethodName michael@0: self.stream.writeln("TEST-PASS | {0} | {1}".format(filename, testname)) michael@0: michael@0: def addError(self, test, err): michael@0: _TestResult.addError(self, test, err) michael@0: self.printFail(test, err) michael@0: michael@0: def addFailure(self, test, err): michael@0: _TestResult.addFailure(self, test, err) michael@0: self.printFail(test,err) michael@0: michael@0: def printFail(self, test, err): michael@0: exctype, value, tb = err michael@0: # Skip test runner traceback levels michael@0: while tb and self._is_relevant_tb_level(tb): michael@0: tb = tb.tb_next michael@0: if not tb: michael@0: self.stream.writeln("TEST-UNEXPECTED-FAIL | NO TRACEBACK |") michael@0: _f, _ln, _t = inspect.getframeinfo(tb)[:3] michael@0: self.stream.writeln("TEST-UNEXPECTED-FAIL | {0} | line {1}, {2}: {3}" michael@0: .format(_f, _ln, _t, value.message)) michael@0: michael@0: def printErrorList(self): michael@0: for test, err in self.errors: michael@0: self.stream.writeln("ERROR: {0}".format(self.getDescription(test))) michael@0: self.stream.writeln("{0}".format(err)) michael@0: michael@0: michael@0: class MozTestRunner(_TestRunner): michael@0: def _makeResult(self): michael@0: return _MozTestResult(self.stream, self.descriptions) michael@0: def run(self, test): michael@0: result = self._makeResult() michael@0: test(result) michael@0: result.printErrorList() michael@0: return result michael@0: michael@0: class MockedFile(StringIO): michael@0: def __init__(self, context, filename, content = ''): michael@0: self.context = context michael@0: self.name = filename michael@0: StringIO.__init__(self, content) michael@0: michael@0: def close(self): michael@0: self.context.files[self.name] = self.getvalue() michael@0: StringIO.close(self) michael@0: michael@0: def __enter__(self): michael@0: return self michael@0: michael@0: def __exit__(self, type, value, traceback): michael@0: self.close() michael@0: michael@0: class MockedOpen(object): michael@0: ''' michael@0: Context manager diverting the open builtin such that opening files michael@0: can open "virtual" file instances given when creating a MockedOpen. michael@0: michael@0: with MockedOpen({'foo': 'foo', 'bar': 'bar'}): michael@0: f = open('foo', 'r') michael@0: michael@0: will thus open the virtual file instance for the file 'foo' to f. michael@0: michael@0: MockedOpen also masks writes, so that creating or replacing files michael@0: doesn't touch the file system, while subsequently opening the file michael@0: will return the recorded content. michael@0: michael@0: with MockedOpen(): michael@0: f = open('foo', 'w') michael@0: f.write('foo') michael@0: self.assertRaises(Exception,f.open('foo', 'r')) michael@0: ''' michael@0: def __init__(self, files = {}): michael@0: self.files = {} michael@0: for name, content in files.iteritems(): michael@0: self.files[os.path.abspath(name)] = content michael@0: michael@0: def __call__(self, name, mode = 'r'): michael@0: absname = os.path.abspath(name) michael@0: if 'w' in mode: michael@0: file = MockedFile(self, absname) michael@0: elif absname in self.files: michael@0: file = MockedFile(self, absname, self.files[absname]) michael@0: elif 'a' in mode: michael@0: file = MockedFile(self, absname, self.open(name, 'r').read()) michael@0: else: michael@0: file = self.open(name, mode) michael@0: if 'a' in mode: michael@0: file.seek(0, os.SEEK_END) michael@0: return file michael@0: michael@0: def __enter__(self): michael@0: import __builtin__ michael@0: self.open = __builtin__.open michael@0: self._orig_path_exists = os.path.exists michael@0: __builtin__.open = self michael@0: os.path.exists = self._wrapped_exists michael@0: michael@0: def __exit__(self, type, value, traceback): michael@0: import __builtin__ michael@0: __builtin__.open = self.open michael@0: os.path.exists = self._orig_path_exists michael@0: michael@0: def _wrapped_exists(self, p): michael@0: if p in self.files: michael@0: return True michael@0: michael@0: abspath = os.path.abspath(p) michael@0: if abspath in self.files: michael@0: return True michael@0: michael@0: return self._orig_path_exists(p) michael@0: michael@0: def main(*args): michael@0: unittest.main(testRunner=MozTestRunner(),*args)