From 112d4bfb148011d91a021e11c978f76e6d35abaa Mon Sep 17 00:00:00 2001 From: Renato Alves Date: Sun, 9 Feb 2014 20:10:24 -0500 Subject: [PATCH] Improvement TW-1255 - #TW-1255 New testing framework (thanks to Renato Alves). Signed-off-by: Paul Beckingham --- ChangeLog | 1 + test/simpletap/__init__.py | 134 +++++++++++++++++++++++++++++++++++++ test/taprunner/__init__.py | 98 --------------------------- test/version.py | 12 +++- 4 files changed, 145 insertions(+), 100 deletions(-) create mode 100644 test/simpletap/__init__.py delete mode 100644 test/taprunner/__init__.py diff --git a/ChangeLog b/ChangeLog index 9d2219c81..70e425b6c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,7 @@ Features + #1501 info report streamlining - partially implemented. + #TW-255 'Mask' instead of 'iMask' shown in info report (thanks to Benjamin Weber) + + #TW-1255 New testing framework (thanks to Renato Alves). + Removed deprecated 'echo.command' setting, in favor of the 'header' and 'affected' verbosity tokens. + Removed deprecated 'edit.verbose' setting, in favor of the 'edit' verbosity diff --git a/test/simpletap/__init__.py b/test/simpletap/__init__.py new file mode 100644 index 000000000..26ff3b108 --- /dev/null +++ b/test/simpletap/__init__.py @@ -0,0 +1,134 @@ +################################################################################ +## taskwarrior - a command line task list manager. +## +## Copyright 2006-2014, Paul Beckingham, Federico Hernandez. +## +## Permission is hereby granted, free of charge, to any person obtaining a copy +## of this software and associated documentation files (the "Software"), to deal +## in the Software without restriction, including without limitation the rights +## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +## copies of the Software, and to permit persons to whom the Software is +## furnished to do so, subject to the following conditions: +## +## The above copyright notice and this permission notice shall be included +## in all copies or substantial portions of the Software. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +## SOFTWARE. +## +## http://www.opensource.org/licenses/mit-license.php +## +################################################################################ + +# Original version by Renato Alves + +import sys +import time +import unittest +import warnings + + +class TAPTestResult(unittest.result.TestResult): + def __init__(self, stream, descriptions, verbosity): + super(TAPTestResult, self).__init__(stream, descriptions, verbosity) + self.stream = stream + self.descriptions = descriptions + self.verbosity = verbosity + + def getDescription(self, test): + doc_first_line = test.shortDescription() + if self.descriptions and doc_first_line: + return doc_first_line + else: + return str(test) + + def startTestRun(self, total="unk"): + self.stream.writeln("1..{0}".format(total)) + + def report(self, test, status=None, err=None): + desc = self.getDescription(test) + try: + exception, msg, _ = err + except (TypeError, ValueError): + exception = "" + msg = err + else: + exception = exception.__name__ + msg = str(msg) + + if status: + if status == "SKIP": + self.stream.writeln("skip {0} - {1}".format(self.testsRun, + desc)) + else: + self.stream.writeln("not ok {0} - {1}".format(self.testsRun, + desc)) + self.stream.writeln("# {0}: {1}".format(status, exception)) + padding = " " * (len(status) + 3) + for line in msg.splitlines(): + self.stream.writeln("#{0}{1}".format(padding, line)) + else: + self.stream.writeln("ok {0} - {1}".format(self.testsRun, desc)) + + self.stream.flush() + + def addSuccess(self, test): + super(TAPTestResult, self).addSuccess(test) + self.report(test) + + def addError(self, test, err): + super(TAPTestResult, self).addError(test, err) + self.report(test, "ERROR", err) + + def addFailure(self, test, err): + super(TAPTestResult, self).addFailure(test, err) + self.report(test, "FAIL", err) + + def addSkip(self, test, reason): + super(TAPTestResult, self).addSkip(test, reason) + self.report(test, "SKIP", reason) + + +class TAPTestRunner(unittest.runner.TextTestRunner): + """A test runner that displays results using the Test Anything Protocol + syntax. + + Inherits from TextTestRunner the default runner. + """ + resultclass = TAPTestResult + + def run(self, test): + result = self._makeResult() + unittest.signals.registerResult(result) + result.failfast = self.failfast + result.buffer = self.buffer + + with warnings.catch_warnings(): + if getattr(self, "warnings", None): + # if self.warnings is set, use it to filter all the warnings + warnings.simplefilter(self.warnings) + # if the filter is 'default' or 'always', special-case the + # warnings from the deprecated unittest methods to show them + # no more than once per module, because they can be fairly + # noisy. The -Wd and -Wa flags can be used to bypass this + # only when self.warnings is None. + if self.warnings in ['default', 'always']: + warnings.filterwarnings('module', + category=DeprecationWarning, + message='Please use assert\w+ instead.') + startTestRun = getattr(result, 'startTestRun', None) + if startTestRun is not None: + startTestRun(test.countTestCases()) + try: + test(result) + finally: + stopTestRun = getattr(result, 'stopTestRun', None) + if stopTestRun is not None: + stopTestRun() + + return result diff --git a/test/taprunner/__init__.py b/test/taprunner/__init__.py deleted file mode 100644 index 9dc97e1fc..000000000 --- a/test/taprunner/__init__.py +++ /dev/null @@ -1,98 +0,0 @@ -# -# Code from https://github.com/vit1251/unittest-tap-reporting -# No explicit license -# -# With modifications by Renato Alves -# - -import sys -import time -import unittest - - -class TAPTestResult(unittest.result.TestResult): - version = 13 - - def __init__(self, stream=None, descriptions=None, plan=None): - super(TAPTestResult, self).__init__() - #self.stream = sys.stdout - self.stream = stream - self._current = 0 - self.descriptions = descriptions - #self.stream.write("TAP version %d\n" % (self.version, )) - self.stream.write("%d..%d\n" % (1, plan, )) - - def getDescription(self, test): - doc_first_line = test.shortDescription() - if self.descriptions and doc_first_line: - return doc_first_line - #return ' - '.join((str(test), doc_first_line)) - else: - return str(test) - - def addSuccess(self, test): - #super(TAPTestResult, self).addSuccess(test) - #print test - self.stream.write("ok %d - %s\n" % (self._current+1, self.getDescription(test))) - self.stream.flush() - self._current += 1 - - def addError(self, test, err): - (exctype, value, tb) = err - self.stream.write("not ok %d - %s\n# ERROR: %s\n" % (self._current+1, self.getDescription(test), value)) - self.stream.flush() - self._current += 1 - - def addFailure(self, test, err): - (exctype, value, tb) = err - self.stream.write("not ok %d - %s\n# FAIL: %s\n" % (self._current+1, self.getDescription(test), value)) - self.stream.flush() - self._current += 1 - - def addSkip(self, test, reason): - self.stream.write("not ok %d - %s # SKIP: %s\n" % (self._current+1, self.getDescription(test), reason)) - self.stream.flush() - self._current += 1 - - -class TAPTestRunner(object): - """A test runner ia abstract class that displays results in user defined form. - - It prints out the names of tests as they are run, errors as they - occur, and a summary of the results at the end of the test run. - """ - resultclass = TAPTestResult - - def __init__(self, stream=sys.stderr, descriptions=True, plan=None, failfast=False, buffer=False, resultclass=None): - self.stream = stream - self.descriptions = descriptions - self.plan = plan - self.failfast = failfast - self.buffer = buffer - if resultclass is not None: - self.resultclass = resultclass - - def _makeResult(self, test): - if self.plan is None: - self.plan = test.countTestCases() - return self.resultclass(self.stream, self.descriptions, self.plan) - - def run(self, test): - "Run the given test case or test suite." - result = self._makeResult(test=test) - #registerResult(result) - result.failfast = self.failfast - result.buffer = self.buffer - startTime = time.time() - startTestRun = getattr(result, 'startTestRun', None) - if startTestRun is not None: - startTestRun() - try: - test(result) - finally: - stopTestRun = getattr(result, 'stopTestRun', None) - if stopTestRun is not None: - stopTestRun() - stopTime = time.time() - timeTaken = stopTime - startTime - return result diff --git a/test/version.py b/test/version.py index fbade4c6d..df898c52d 100755 --- a/test/version.py +++ b/test/version.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python2.7 # -*- coding: utf-8 -*- import sys @@ -43,6 +43,14 @@ class TestVersion(unittest.TestCase): expected = "Copyright \(C\) \d{4} - %d" % (datetime.now().year - 1,) self.assertRegexpMatches(out.decode("utf8"), expected) + def testFailOther(self): + """Nothing to do with Copyright""" + self.assertEqual("I like to code", "I like\nto code\n") + + @unittest.skipIf(1 != 0, "This machine has sane logic") + def testSkipped(self): + """Test all logic of the world""" + def tearDown(self): """Executed after each test in the class""" @@ -53,7 +61,7 @@ class TestVersion(unittest.TestCase): if __name__ == "__main__": - from taprunner import TAPTestRunner + from simpletap import TAPTestRunner unittest.main(testRunner=TAPTestRunner()) # vim: ai sts=4 et sw=4