summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary Oberbrunner <garyo@oberbrunner.com>2014-03-30 10:53:39 -0400
committerGary Oberbrunner <garyo@oberbrunner.com>2014-03-30 10:53:39 -0400
commit90124735f5cef03585ac15112858b0cd02e917ba (patch)
tree711940a9fd90bb13ba470dc05e29ab81ff3c2126
parentac46c2a336ec3c8d85bbd7eab0a213035a9023a6 (diff)
parentad1db56962e5f484a81d0ca3014afddadc711136 (diff)
downloadscons-90124735f5cef03585ac15112858b0cd02e917ba.tar.gz
Merged in techtonik/scons (pull request #124)
New configurable test runner for unittests
-rwxr-xr-xruntest.py79
-rw-r--r--src/engine/SCons/ActionTests.py5
-rw-r--r--src/engine/SCons/BuilderTests.py5
-rw-r--r--src/engine/SCons/CacheDirTests.py4
-rw-r--r--src/engine/SCons/DefaultsTests.py4
-rw-r--r--src/engine/SCons/EnvironmentTests.py7
-rw-r--r--src/engine/SCons/ErrorsTests.py6
-rw-r--r--src/engine/SCons/ExecutorTests.py5
-rw-r--r--src/engine/SCons/JobTests.py10
-rw-r--r--src/engine/SCons/MemoizeTests.py5
-rw-r--r--src/engine/SCons/Node/AliasTests.py5
-rw-r--r--src/engine/SCons/Node/FSTests.py7
-rw-r--r--src/engine/SCons/Node/NodeTests.py5
-rw-r--r--src/engine/SCons/Node/PythonTests.py5
-rw-r--r--src/engine/SCons/PathListTests.py5
-rw-r--r--src/engine/SCons/Platform/PlatformTests.py5
-rw-r--r--src/engine/SCons/SConfTests.py6
-rw-r--r--src/engine/SCons/SConsignTests.py7
-rw-r--r--src/engine/SCons/Scanner/CTests.py9
-rw-r--r--src/engine/SCons/Scanner/DirTests.py7
-rw-r--r--src/engine/SCons/Scanner/FortranTests.py6
-rw-r--r--src/engine/SCons/Scanner/IDLTests.py12
-rw-r--r--src/engine/SCons/Scanner/LaTeXTests.py7
-rw-r--r--src/engine/SCons/Scanner/ProgTests.py7
-rw-r--r--src/engine/SCons/Scanner/RCTests.py12
-rw-r--r--src/engine/SCons/Scanner/ScannerTests.py7
-rw-r--r--src/engine/SCons/Script/MainTests.py6
-rw-r--r--src/engine/SCons/SubstTests.py5
-rw-r--r--src/engine/SCons/TaskmasterTests.py5
-rw-r--r--src/engine/SCons/Tool/JavaCommonTests.py6
-rw-r--r--src/engine/SCons/Tool/PharLapCommonTests.py5
-rw-r--r--src/engine/SCons/Tool/ToolTests.py5
-rw-r--r--src/engine/SCons/Tool/javacTests.py5
-rw-r--r--src/engine/SCons/Tool/msvsTests.py6
-rw-r--r--src/engine/SCons/Tool/wixTests.py5
-rw-r--r--src/engine/SCons/UtilTests.py4
-rw-r--r--src/engine/SCons/Variables/BoolVariableTests.py5
-rw-r--r--src/engine/SCons/Variables/EnumVariableTests.py5
-rw-r--r--src/engine/SCons/Variables/ListVariableTests.py5
-rw-r--r--src/engine/SCons/Variables/PackageVariableTests.py5
-rw-r--r--src/engine/SCons/Variables/PathVariableTests.py5
-rw-r--r--src/engine/SCons/Variables/VariablesTests.py5
-rw-r--r--src/engine/SCons/WarningsTests.py6
-rw-r--r--src/engine/SCons/cppTests.py5
-rw-r--r--test/runtest/testargv.py75
-rw-r--r--testing/framework/TestUnit/__init__.py5
-rw-r--r--testing/framework/TestUnit/cli.py35
-rw-r--r--testing/framework/TestUnit/taprunner.py120
48 files changed, 428 insertions, 142 deletions
diff --git a/runtest.py b/runtest.py
index eee88dc5..b7cbdc69 100755
--- a/runtest.py
+++ b/runtest.py
@@ -157,6 +157,7 @@ Options:
--passed Summarize which tests passed.
-q --quiet Don't print the test being executed.
--quit-on-failure Quit on any test failure
+ --runner CLASS Alternative test runner class for unit tests
-s --short-progress Short progress, prints only the command line
and a percentage value, based on the total and
current number of tests.
@@ -199,6 +200,8 @@ parser.add_option('-a', '--all', action='store_true',
help="Run all tests.")
parser.add_option('-o', '--output',
help="Save the output from a test run to the log file.")
+parser.add_option('--runner', metavar='class',
+ help="Test runner class for unit tests.")
parser.add_option('--xml',
help="Save results to file in SCons XML format.")
(options, args) = parser.parse_args()
@@ -280,13 +283,6 @@ for o, a in opts:
elif o in ['-x', '--exec']:
scons = a
-if not args and not options.all and not testlistfile:
- sys.stderr.write(usagestr + """
-runtest.py: No tests were specified.
- Tests can be specified on the command line, read from file
- with -f option, or discovered with -a to run all tests.
-""")
- sys.exit(1)
# --- setup stdout/stderr ---
@@ -619,14 +615,17 @@ old_pythonpath = os.environ.get('PYTHONPATH')
# FIXME: the following is necessary to pull in half of the testing
# harness from $srcdir/etc. Those modules should be transfered
-# to QMTest/ once we completely cut over to using that as
-# the harness, in which case this manipulation of PYTHONPATH
+# to testing/, in which case this manipulation of PYTHONPATH
# should be able to go away.
pythonpaths = [ pythonpath_dir ]
# Add path of the QMTest folder to PYTHONPATH
+# [ ] move used parts from QMTest to testing/framework/
scriptpath = os.path.dirname(os.path.realpath(__file__))
pythonpaths.append(os.path.join(scriptpath, 'QMTest'))
+# Add path for testing framework to PYTHONPATH
+pythonpaths.append(os.path.join(scriptpath, 'testing', 'framework'))
+
os.environ['PYTHONPATH'] = os.pathsep.join(pythonpaths)
@@ -643,6 +642,9 @@ if python3incompatibilities:
tests = []
+unittests = []
+endtests = []
+
def find_Tests_py(directory):
""" Look for unit tests """
result = []
@@ -674,30 +676,20 @@ def find_py(directory):
result.append(os.path.join(dirpath, fname))
return sorted(result)
-if args:
- for a in args:
- for path in glob.glob(a):
- if os.path.isdir(path):
- if path[:3] == 'src':
- for p in find_Tests_py(path):
- tests.append(p)
- elif path[:4] == 'test':
- for p in find_py(path):
- tests.append(p)
- else:
- tests.append(path)
-elif testlistfile:
+if testlistfile:
tests = open(testlistfile, 'r').readlines()
tests = [x for x in tests if x[0] != '#']
tests = [x[:-1] for x in tests]
tests = [x.strip() for x in tests]
-elif options.all:
- # Find all of the SCons functional tests in the local directory
- # tree. This is anything under the 'src' subdirectory that ends
- # with 'Tests.py', or any Python script (*.py) under the 'test'
- # subdirectory.
+else:
+ testpaths = []
+
+ # Each test path specifies a test file, or a directory to search for
+ # SCons tests. SCons code layout assumes that any file under the 'src'
+ # subdirectory that ends with 'Tests.py' is a unit test, and Python
+ # script (*.py) under the 'test' subdirectory an end-to-end test.
#
# Note that there are some tests under 'src' that *begin* with
# 'test_', but they're packaging and installation tests, not
@@ -705,13 +697,36 @@ elif options.all:
# still be executed by hand, though, and are routinely executed
# by the Aegis packaging build to make sure that we're building
# things correctly.)
- tests.extend(find_Tests_py('src'))
- tests.extend(find_py('test'))
+
+ if options.all:
+ testpaths = ['src', 'test']
+ elif args:
+ testpaths = args
+
+ for tp in testpaths:
+ for path in glob.glob(tp):
+ if os.path.isdir(path):
+ if path.startswith('src'):
+ for p in find_Tests_py(path):
+ unittests.append(p)
+ elif path.startswith('test'):
+ for p in find_py(path):
+ endtests.append(p)
+ else:
+ if path.endswith("Tests.py"):
+ unittests.append(path)
+ else:
+ endtests.append(path)
+
+ tests.extend(unittests)
+ tests.extend(endtests)
tests.sort()
if not tests:
- sys.stderr.write("""\
+ sys.stderr.write(usagestr + """
runtest.py: No tests were found.
+ Tests can be specified on the command line, read from file
+ with -f option, or discovered with -a to run all tests.
""")
sys.exit(1)
@@ -725,7 +740,6 @@ if list_only:
sys.stdout.write(t.path + "\n")
sys.exit(0)
-#
if not python:
if os.name == 'java':
python = os.path.join(sys.prefix, 'jython')
@@ -760,6 +774,9 @@ def run_test(t, io_lock, async=True):
if debug:
command_args.append(debug)
command_args.append(t.path)
+ if options.runner and t.path in unittests:
+ # For example --runner TestUnit.TAPTestRunner
+ command_args.append('--runner ' + options.runner)
t.command_args = [python] + command_args
t.command_str = " ".join([escape(python)] + command_args)
if printcommand:
diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
index 3e900d7b..809e5ceb 100644
--- a/src/engine/SCons/ActionTests.py
+++ b/src/engine/SCons/ActionTests.py
@@ -49,6 +49,7 @@ import SCons.Environment
import SCons.Errors
import TestCmd
+import TestUnit
# Initial setup of the common environment for all tests,
# a temporary working directory containing a
@@ -2108,8 +2109,8 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index 766b8fe1..70a7a3fe 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -40,6 +40,7 @@ import sys
import unittest
import TestCmd
+import TestUnit
import SCons.Action
import SCons.Builder
@@ -1632,8 +1633,8 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/CacheDirTests.py b/src/engine/SCons/CacheDirTests.py
index 21b435a8..7ac97efa 100644
--- a/src/engine/SCons/CacheDirTests.py
+++ b/src/engine/SCons/CacheDirTests.py
@@ -29,6 +29,7 @@ import sys
import unittest
from TestCmd import TestCmd
+import TestUnit
import SCons.CacheDir
@@ -287,8 +288,7 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/DefaultsTests.py b/src/engine/SCons/DefaultsTests.py
index fd10c129..8b9fb4ec 100644
--- a/src/engine/SCons/DefaultsTests.py
+++ b/src/engine/SCons/DefaultsTests.py
@@ -32,6 +32,7 @@ import unittest
from collections import UserDict
import TestCmd
+import TestUnit
import SCons.Errors
@@ -82,8 +83,7 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index 7fa8af4e..52353428 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -29,10 +29,12 @@ import copy
import io
import os
import sys
-import TestCmd
import unittest
from collections import UserDict as UD, UserList as UL
+import TestCmd
+import TestUnit
+
from SCons.Environment import *
import SCons.Warnings
@@ -3999,8 +4001,7 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/ErrorsTests.py b/src/engine/SCons/ErrorsTests.py
index 9c8b925f..d57faa50 100644
--- a/src/engine/SCons/ErrorsTests.py
+++ b/src/engine/SCons/ErrorsTests.py
@@ -25,6 +25,9 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import sys
import unittest
+
+import TestUnit
+
import SCons.Errors
@@ -99,8 +102,7 @@ class ErrorsTestCase(unittest.TestCase):
if __name__ == "__main__":
suite = unittest.makeSuite(ErrorsTestCase, 'test_')
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/ExecutorTests.py b/src/engine/SCons/ExecutorTests.py
index 62689840..9df0b2d1 100644
--- a/src/engine/SCons/ExecutorTests.py
+++ b/src/engine/SCons/ExecutorTests.py
@@ -26,6 +26,8 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import sys
import unittest
+import TestUnit
+
import SCons.Executor
@@ -455,8 +457,7 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/JobTests.py b/src/engine/SCons/JobTests.py
index e92bdbec..dd2c7aad 100644
--- a/src/engine/SCons/JobTests.py
+++ b/src/engine/SCons/JobTests.py
@@ -25,10 +25,14 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import unittest
import random
import math
-import SCons.Job
import sys
import time
+import TestUnit
+
+import SCons.Job
+
+
# a large number
num_sines = 10000
@@ -521,8 +525,8 @@ def suite():
return suite
if __name__ == "__main__":
- runner = unittest.TextTestRunner()
- result = runner.run(suite())
+ runner = TestUnit.cli.get_runner()
+ result = runner().run(suite())
if (len(result.failures) == 0
and len(result.errors) == 1
and isinstance(result.errors[0][0], SerialTestCase)
diff --git a/src/engine/SCons/MemoizeTests.py b/src/engine/SCons/MemoizeTests.py
index 9876c27f..3606d576 100644
--- a/src/engine/SCons/MemoizeTests.py
+++ b/src/engine/SCons/MemoizeTests.py
@@ -26,6 +26,8 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import sys
import unittest
+import TestUnit
+
import SCons.Memoize
@@ -180,8 +182,7 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Node/AliasTests.py b/src/engine/SCons/Node/AliasTests.py
index b0efea77..2d11bdf9 100644
--- a/src/engine/SCons/Node/AliasTests.py
+++ b/src/engine/SCons/Node/AliasTests.py
@@ -26,6 +26,8 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import sys
import unittest
+import TestUnit
+
import SCons.Errors
import SCons.Node.Alias
@@ -120,8 +122,7 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index a60b8a4b..c4dc2ced 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -31,10 +31,12 @@ import os.path
import sys
import time
import unittest
-from TestCmd import TestCmd
import shutil
import stat
+from TestCmd import TestCmd
+import TestUnit
+
import SCons.Errors
import SCons.Node.FS
import SCons.Util
@@ -3755,8 +3757,7 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index 076ca65a..da502b0f 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -30,6 +30,8 @@ import re
import sys
import unittest
+import TestUnit
+
import SCons.Errors
import SCons.Node
import SCons.Util
@@ -1311,8 +1313,7 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Node/PythonTests.py b/src/engine/SCons/Node/PythonTests.py
index 8b08c520..fcdfe77a 100644
--- a/src/engine/SCons/Node/PythonTests.py
+++ b/src/engine/SCons/Node/PythonTests.py
@@ -26,6 +26,8 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import sys
import unittest
+import TestUnit
+
import SCons.Errors
import SCons.Node.Python
@@ -120,8 +122,7 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/PathListTests.py b/src/engine/SCons/PathListTests.py
index e83fc50d..b5989bb3 100644
--- a/src/engine/SCons/PathListTests.py
+++ b/src/engine/SCons/PathListTests.py
@@ -26,6 +26,8 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import sys
import unittest
+import TestUnit
+
import SCons.PathList
@@ -196,8 +198,7 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Platform/PlatformTests.py b/src/engine/SCons/Platform/PlatformTests.py
index 6841272d..515382a3 100644
--- a/src/engine/SCons/Platform/PlatformTests.py
+++ b/src/engine/SCons/Platform/PlatformTests.py
@@ -29,6 +29,8 @@ import collections
import sys
import unittest
+import TestUnit
+
import SCons.Errors
import SCons.Platform
@@ -118,8 +120,7 @@ class PlatformTestCase(unittest.TestCase):
if __name__ == "__main__":
suite = unittest.makeSuite(PlatformTestCase, 'test_')
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/SConfTests.py b/src/engine/SCons/SConfTests.py
index 1cfb05b7..1c4b4010 100644
--- a/src/engine/SCons/SConfTests.py
+++ b/src/engine/SCons/SConfTests.py
@@ -33,6 +33,8 @@ from types import *
import unittest
import TestCmd
+import TestUnit
+
sys.stdout = io.StringIO()
@@ -752,9 +754,7 @@ int main() {
if __name__ == "__main__":
suite = unittest.makeSuite(SConfTestCase, 'test_')
- res = unittest.TextTestRunner().run(suite)
- if not res.wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/SConsignTests.py b/src/engine/SCons/SConsignTests.py
index ec063c2c..f71e53e2 100644
--- a/src/engine/SCons/SConsignTests.py
+++ b/src/engine/SCons/SConsignTests.py
@@ -25,9 +25,11 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os
import sys
-import TestCmd
import unittest
+import TestCmd
+import TestUnit
+
import SCons.dblite
import SCons.SConsign
@@ -387,8 +389,7 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Scanner/CTests.py b/src/engine/SCons/Scanner/CTests.py
index 5523f104..6418754c 100644
--- a/src/engine/SCons/Scanner/CTests.py
+++ b/src/engine/SCons/Scanner/CTests.py
@@ -28,9 +28,11 @@ import SCons.compat
import collections
import os
import sys
-import TestCmd
import unittest
+import TestCmd
+import TestUnit
+
import SCons.Node.FS
import SCons.Warnings
@@ -457,10 +459,7 @@ def suite():
return suite
if __name__ == "__main__":
- runner = unittest.TextTestRunner()
- result = runner.run(suite())
- if not result.wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite())
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Scanner/DirTests.py b/src/engine/SCons/Scanner/DirTests.py
index 968d5d32..97019215 100644
--- a/src/engine/SCons/Scanner/DirTests.py
+++ b/src/engine/SCons/Scanner/DirTests.py
@@ -28,6 +28,8 @@ import sys
import unittest
import TestCmd
+import TestUnit
+
import SCons.Node.FS
import SCons.Scanner.Dir
@@ -126,10 +128,7 @@ def suite():
return suite
if __name__ == "__main__":
- runner = unittest.TextTestRunner()
- result = runner.run(suite())
- if not result.wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite())
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Scanner/FortranTests.py b/src/engine/SCons/Scanner/FortranTests.py
index 3388ffcf..252da648 100644
--- a/src/engine/SCons/Scanner/FortranTests.py
+++ b/src/engine/SCons/Scanner/FortranTests.py
@@ -33,6 +33,7 @@ import SCons.Node.FS
import SCons.Warnings
import TestCmd
+import TestUnit
original = os.getcwd()
@@ -531,10 +532,7 @@ def suite():
return suite
if __name__ == "__main__":
- runner = unittest.TextTestRunner()
- result = runner.run(suite())
- if not result.wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite())
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Scanner/IDLTests.py b/src/engine/SCons/Scanner/IDLTests.py
index c6967226..675c70cd 100644
--- a/src/engine/SCons/Scanner/IDLTests.py
+++ b/src/engine/SCons/Scanner/IDLTests.py
@@ -23,12 +23,15 @@
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-import TestCmd
-import SCons.Scanner.IDL
import unittest
import sys
import os
import os.path
+
+import TestCmd
+import TestUnit
+
+import SCons.Scanner.IDL
import SCons.Node.FS
import SCons.Warnings
@@ -441,10 +444,7 @@ def suite():
return suite
if __name__ == "__main__":
- runner = unittest.TextTestRunner()
- result = runner.run(suite())
- if not result.wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite())
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Scanner/LaTeXTests.py b/src/engine/SCons/Scanner/LaTeXTests.py
index 8cea63fa..49553cfd 100644
--- a/src/engine/SCons/Scanner/LaTeXTests.py
+++ b/src/engine/SCons/Scanner/LaTeXTests.py
@@ -31,6 +31,8 @@ import sys
import unittest
import TestCmd
+import TestUnit
+
import SCons.Node.FS
import SCons.Scanner.LaTeX
@@ -150,10 +152,7 @@ def suite():
return suite
if __name__ == "__main__":
- runner = unittest.TextTestRunner()
- result = runner.run(suite())
- if not result.wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite())
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Scanner/ProgTests.py b/src/engine/SCons/Scanner/ProgTests.py
index 411e0358..144addb1 100644
--- a/src/engine/SCons/Scanner/ProgTests.py
+++ b/src/engine/SCons/Scanner/ProgTests.py
@@ -28,6 +28,8 @@ import sys
import unittest
import TestCmd
+import TestUnit
+
import SCons.Node.FS
import SCons.Scanner.Prog
@@ -249,10 +251,7 @@ def suite():
return suite
if __name__ == "__main__":
- runner = unittest.TextTestRunner()
- result = runner.run(suite())
- if not result.wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite())
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Scanner/RCTests.py b/src/engine/SCons/Scanner/RCTests.py
index 88028850..2864026f 100644
--- a/src/engine/SCons/Scanner/RCTests.py
+++ b/src/engine/SCons/Scanner/RCTests.py
@@ -23,12 +23,15 @@
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-import TestCmd
-import SCons.Scanner.RC
import unittest
import sys
import collections
import os
+
+import TestCmd
+import TestUnit
+
+import SCons.Scanner.RC
import SCons.Node.FS
import SCons.Warnings
@@ -164,10 +167,7 @@ def suite():
return suite
if __name__ == "__main__":
- runner = unittest.TextTestRunner()
- result = runner.run(suite())
- if not result.wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite())
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Scanner/ScannerTests.py b/src/engine/SCons/Scanner/ScannerTests.py
index ee269222..24965346 100644
--- a/src/engine/SCons/Scanner/ScannerTests.py
+++ b/src/engine/SCons/Scanner/ScannerTests.py
@@ -28,6 +28,8 @@ import collections
import sys
import unittest
+import TestUnit
+
import SCons.Scanner
class DummyFS(object):
@@ -593,10 +595,7 @@ def suite():
return suite
if __name__ == "__main__":
- runner = unittest.TextTestRunner()
- result = runner.run(suite())
- if not result.wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite())
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Script/MainTests.py b/src/engine/SCons/Script/MainTests.py
index c44c4268..fd6aaf41 100644
--- a/src/engine/SCons/Script/MainTests.py
+++ b/src/engine/SCons/Script/MainTests.py
@@ -24,6 +24,9 @@
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import unittest
+
+import TestUnit
+
import SCons.Errors
import SCons.Script.Main
@@ -43,8 +46,7 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/SubstTests.py b/src/engine/SCons/SubstTests.py
index ee9f3db5..6f2eb3fe 100644
--- a/src/engine/SCons/SubstTests.py
+++ b/src/engine/SCons/SubstTests.py
@@ -31,6 +31,8 @@ import unittest
from collections import UserDict
+import TestUnit
+
import SCons.Errors
from SCons.Subst import *
@@ -1233,8 +1235,7 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py
index e8751583..1d819924 100644
--- a/src/engine/SCons/TaskmasterTests.py
+++ b/src/engine/SCons/TaskmasterTests.py
@@ -30,6 +30,8 @@ import copy
import sys
import unittest
+import TestUnit
+
import SCons.Taskmaster
import SCons.Errors
@@ -1223,8 +1225,7 @@ Taskmaster: No candidate anymore.
if __name__ == "__main__":
suite = unittest.makeSuite(TaskmasterTestCase, 'test_')
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Tool/JavaCommonTests.py b/src/engine/SCons/Tool/JavaCommonTests.py
index 76944c15..902030d8 100644
--- a/src/engine/SCons/Tool/JavaCommonTests.py
+++ b/src/engine/SCons/Tool/JavaCommonTests.py
@@ -27,6 +27,9 @@ import os.path
import sys
import unittest
+import TestUnit
+
+import SCons.Scanner.IDL
import SCons.Tool.JavaCommon
@@ -570,8 +573,7 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Tool/PharLapCommonTests.py b/src/engine/SCons/Tool/PharLapCommonTests.py
index d1e7f602..e67d4268 100644
--- a/src/engine/SCons/Tool/PharLapCommonTests.py
+++ b/src/engine/SCons/Tool/PharLapCommonTests.py
@@ -28,6 +28,8 @@ import os.path
import os
import sys
+import TestUnit
+
import SCons.Errors
from SCons.Tool.PharLapCommon import *
@@ -58,8 +60,7 @@ class PharLapCommonTestCase(unittest.TestCase):
if __name__ == "__main__":
suite = unittest.makeSuite(PharLapCommonTestCase, 'test_')
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Tool/ToolTests.py b/src/engine/SCons/Tool/ToolTests.py
index 9595680a..3e6da5b6 100644
--- a/src/engine/SCons/Tool/ToolTests.py
+++ b/src/engine/SCons/Tool/ToolTests.py
@@ -26,6 +26,8 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import sys
import unittest
+import TestUnit
+
import SCons.Errors
import SCons.Tool
@@ -76,8 +78,7 @@ class ToolTestCase(unittest.TestCase):
if __name__ == "__main__":
suite = unittest.makeSuite(ToolTestCase, 'test_')
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Tool/javacTests.py b/src/engine/SCons/Tool/javacTests.py
index 4631c8a1..bf75d8a3 100644
--- a/src/engine/SCons/Tool/javacTests.py
+++ b/src/engine/SCons/Tool/javacTests.py
@@ -24,6 +24,8 @@
import os
import unittest
+import TestUnit
+
import SCons.Tool.javac
class DummyNode(object):
@@ -99,4 +101,5 @@ class pathoptTestCase(unittest.TestCase):
'')
if __name__ == "__main__":
- unittest.main()
+ suite = unittest.makeSuite(pathoptTestCase, 'test_')
+ TestUnit.run(suite)
diff --git a/src/engine/SCons/Tool/msvsTests.py b/src/engine/SCons/Tool/msvsTests.py
index 3a9cac7c..2f4f302a 100644
--- a/src/engine/SCons/Tool/msvsTests.py
+++ b/src/engine/SCons/Tool/msvsTests.py
@@ -25,10 +25,12 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os
import sys
-import TestCmd
import unittest
import copy
+import TestCmd
+import TestUnit
+
from SCons.Tool.msvs import *
from SCons.Tool.MSCommon.vs import SupportedVSList
import SCons.Util
@@ -759,7 +761,7 @@ if __name__ == "__main__":
del os.environ[k]
suite = unittest.makeSuite(test_class, 'test_')
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ if not TestUnit.cli.get_runner()().run(suite).wasSuccessful():
exit_val = 1
finally:
os.env = back_osenv
diff --git a/src/engine/SCons/Tool/wixTests.py b/src/engine/SCons/Tool/wixTests.py
index c815dd05..c683e98d 100644
--- a/src/engine/SCons/Tool/wixTests.py
+++ b/src/engine/SCons/Tool/wixTests.py
@@ -33,6 +33,8 @@ from SCons.Tool.wix import *
from SCons.Environment import Environment
import TestCmd
+import TestUnit
+
# create fake candle and light, so the tool's exists() method will succeed
test = TestCmd.TestCmd(workdir = '')
@@ -52,8 +54,7 @@ class WixTestCase(unittest.TestCase):
if __name__ == "__main__":
suite = unittest.makeSuite(WixTestCase, 'test_')
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py
index a1e67567..b0c15c5d 100644
--- a/src/engine/SCons/UtilTests.py
+++ b/src/engine/SCons/UtilTests.py
@@ -32,6 +32,7 @@ import unittest
from collections import UserDict, UserList, UserString
import TestCmd
+import TestUnit
import SCons.Errors
@@ -789,8 +790,7 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Variables/BoolVariableTests.py b/src/engine/SCons/Variables/BoolVariableTests.py
index 8ffb0795..7110f6f8 100644
--- a/src/engine/SCons/Variables/BoolVariableTests.py
+++ b/src/engine/SCons/Variables/BoolVariableTests.py
@@ -26,6 +26,8 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import sys
import unittest
+import TestUnit
+
import SCons.Errors
import SCons.Variables
@@ -117,8 +119,7 @@ class BoolVariableTestCase(unittest.TestCase):
if __name__ == "__main__":
suite = unittest.makeSuite(BoolVariableTestCase, 'test_')
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Variables/EnumVariableTests.py b/src/engine/SCons/Variables/EnumVariableTests.py
index f4b600d7..931dfe2b 100644
--- a/src/engine/SCons/Variables/EnumVariableTests.py
+++ b/src/engine/SCons/Variables/EnumVariableTests.py
@@ -26,6 +26,8 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import sys
import unittest
+import TestUnit
+
import SCons.Errors
import SCons.Variables
@@ -194,8 +196,7 @@ class EnumVariableTestCase(unittest.TestCase):
if __name__ == "__main__":
suite = unittest.makeSuite(EnumVariableTestCase, 'test_')
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Variables/ListVariableTests.py b/src/engine/SCons/Variables/ListVariableTests.py
index 4959f691..adfd353f 100644
--- a/src/engine/SCons/Variables/ListVariableTests.py
+++ b/src/engine/SCons/Variables/ListVariableTests.py
@@ -27,6 +27,8 @@ import copy
import sys
import unittest
+import TestUnit
+
import SCons.Errors
import SCons.Variables
@@ -124,8 +126,7 @@ class ListVariableTestCase(unittest.TestCase):
if __name__ == "__main__":
suite = unittest.makeSuite(ListVariableTestCase, 'test_')
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Variables/PackageVariableTests.py b/src/engine/SCons/Variables/PackageVariableTests.py
index 2a933484..3aa411d3 100644
--- a/src/engine/SCons/Variables/PackageVariableTests.py
+++ b/src/engine/SCons/Variables/PackageVariableTests.py
@@ -30,6 +30,8 @@ import SCons.Errors
import SCons.Variables
import TestCmd
+import TestUnit
+
class PackageVariableTestCase(unittest.TestCase):
def test_PackageVariable(self):
@@ -114,8 +116,7 @@ class PackageVariableTestCase(unittest.TestCase):
if __name__ == "__main__":
suite = unittest.makeSuite(PackageVariableTestCase, 'test_')
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Variables/PathVariableTests.py b/src/engine/SCons/Variables/PathVariableTests.py
index 084154bd..42667591 100644
--- a/src/engine/SCons/Variables/PathVariableTests.py
+++ b/src/engine/SCons/Variables/PathVariableTests.py
@@ -31,6 +31,8 @@ import SCons.Errors
import SCons.Variables
import TestCmd
+import TestUnit
+
class PathVariableTestCase(unittest.TestCase):
def test_PathVariable(self):
@@ -227,8 +229,7 @@ class PathVariableTestCase(unittest.TestCase):
if __name__ == "__main__":
suite = unittest.makeSuite(PathVariableTestCase, 'test_')
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/Variables/VariablesTests.py b/src/engine/SCons/Variables/VariablesTests.py
index ad46bd6d..d2110c12 100644
--- a/src/engine/SCons/Variables/VariablesTests.py
+++ b/src/engine/SCons/Variables/VariablesTests.py
@@ -25,7 +25,9 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import sys
import unittest
+
import TestSCons
+import TestUnit
import SCons.Variables
import SCons.Subst
@@ -655,8 +657,7 @@ if __name__ == "__main__":
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/WarningsTests.py b/src/engine/SCons/WarningsTests.py
index 583b12a2..7b8d00da 100644
--- a/src/engine/SCons/WarningsTests.py
+++ b/src/engine/SCons/WarningsTests.py
@@ -25,6 +25,9 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import sys
import unittest
+
+import TestUnit
+
import SCons.Warnings
class TestOutput(object):
@@ -125,8 +128,7 @@ class WarningsTestCase(unittest.TestCase):
if __name__ == "__main__":
suite = unittest.makeSuite(WarningsTestCase, 'test_')
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/src/engine/SCons/cppTests.py b/src/engine/SCons/cppTests.py
index 2f2025be..5a06beed 100644
--- a/src/engine/SCons/cppTests.py
+++ b/src/engine/SCons/cppTests.py
@@ -27,6 +27,8 @@ import atexit
import sys
import unittest
+import TestUnit
+
import cpp
@@ -705,8 +707,7 @@ if __name__ == '__main__':
pass
names.sort()
suite.addTests(list(map(tclass, names)))
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ TestUnit.run(suite)
# Local Variables:
# tab-width:4
diff --git a/test/runtest/testargv.py b/test/runtest/testargv.py
new file mode 100644
index 00000000..62faf51b
--- /dev/null
+++ b/test/runtest/testargv.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# 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.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Test subdir args for runtest.py, for example:
+
+ python runtest.py test/subdir
+
+"""
+
+import os
+
+import TestRuntest
+
+test = TestRuntest.TestRuntest()
+test.subdir('test', ['test', 'subdir'])
+
+files = {}
+files['pythonstring'] = TestRuntest.pythonstring
+
+files['one'] = os.path.join('test/subdir', 'test_one.py')
+files['two'] = os.path.join('test/subdir', 'two.py')
+files['three'] = os.path.join('test', 'test_three.py')
+
+test.write_passing_test(files['one'])
+test.write_passing_test(files['two'])
+test.write_passing_test(files['three'])
+
+expect_stdout = """\
+%(pythonstring)s -tt %(one)s
+PASSING TEST STDOUT
+%(pythonstring)s -tt %(two)s
+PASSING TEST STDOUT
+""" % files
+
+expect_stderr = """\
+PASSING TEST STDERR
+PASSING TEST STDERR
+"""
+
+test.run(arguments = '--no-progress test/subdir',
+ status = 0,
+ stdout = expect_stdout,
+ stderr = expect_stderr)
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/testing/framework/TestUnit/__init__.py b/testing/framework/TestUnit/__init__.py
new file mode 100644
index 00000000..51cf9720
--- /dev/null
+++ b/testing/framework/TestUnit/__init__.py
@@ -0,0 +1,5 @@
+
+__all__ = ['TAPTestRunner', 'TAPTestResult', 'run']
+
+from .taprunner import TAPTestRunner, TAPTestResult
+from .cli import run
diff --git a/testing/framework/TestUnit/cli.py b/testing/framework/TestUnit/cli.py
new file mode 100644
index 00000000..6aec7354
--- /dev/null
+++ b/testing/framework/TestUnit/cli.py
@@ -0,0 +1,35 @@
+"""
+Choose test runner class from --runner command line option
+and execute test cases.
+"""
+
+import unittest
+import optparse
+import sys
+
+
+def get_runner():
+ parser = optparse.OptionParser()
+ parser.add_option('--runner', default='unittest.TextTestRunner',
+ help='name of test runner class to use')
+ opts, args = parser.parse_args()
+
+ fromsplit = opts.runner.rsplit('.', 1)
+ if len(fromsplit) < 2:
+ raise ValueError('Can\'t use module as a runner')
+ else:
+ runnermod = __import__(fromsplit[0])
+ return getattr(runnermod, fromsplit[1])
+
+
+def run(suite=None):
+ runner = get_runner()
+ if suite:
+ if not runner().run(suite).wasSuccessful():
+ sys.exit(1)
+ else:
+ unittest.main(argv=sys.argv[:1], testRunner=runner)
+
+
+if __name__ == '__main__':
+ run()
diff --git a/testing/framework/TestUnit/taprunner.py b/testing/framework/TestUnit/taprunner.py
new file mode 100644
index 00000000..01e0e813
--- /dev/null
+++ b/testing/framework/TestUnit/taprunner.py
@@ -0,0 +1,120 @@
+"""
+Format unittest results in Test Anything Protocol (TAP).
+http://testanything.org/tap-version-13-specification.html
+
+Public domain work by:
+ anatoly techtonik <techtonik@gmail.com>
+
+"""
+
+from unittest import suite
+from unittest.runner import TextTestRunner, TextTestResult
+
+__version__ = "0.1"
+
+class TAPTestResult(TextTestResult):
+
+ def _process(self, test, msg, failtype = None, directive = None):
+ """ increase the counter, format and output TAP info """
+ # counterhack: increase test counter
+ test.suite.tap_counter += 1
+ msg = "%s %d" % (msg, test.suite.tap_counter)
+ if "not" not in msg:
+ msg += " " # justify
+ self.stream.write("%s - " % msg)
+ if failtype:
+ self.stream.write("%s - " % failtype)
+ self.stream.write("%s" % test.__class__.__name__)
+ self.stream.write(".%s" % test._testMethodName)
+ if directive:
+ self.stream.write(directive)
+ self.stream.write("\n")
+ # [ ] write test __doc__ (if exists) in comment
+ self.stream.flush()
+
+ def addSuccess(self, test):
+ super(TextTestResult, self).addSuccess(test)
+ self._process(test, "ok")
+
+ def addFailure(self, test, err):
+ super(TextTestResult, self).addFailure(test, err)
+ self._process(test, "not ok", "FAIL")
+ # [ ] add structured data about assertion
+
+ def addError(self, test, err):
+ super(TextTestResult, self).addError(test, err)
+ self._process(test, "not ok", "ERROR")
+ # [ ] add structured data about exception
+
+ def addSkip(self, test, reason):
+ super(TextTestResult, self).addSkip(test, reason)
+ self._process(test, "ok", directive=(" # SKIP %s" % reason))
+
+ def addExpectedFailure(self, test, err):
+ super(TextTestResult, self).addExpectedFailure(test, err)
+ self._process(test, "not ok", directive=(" # TODO"))
+
+ def addUnexpectedSuccess(self, test):
+ super(TextTestResult, self).addUnexpectedSuccess(test)
+ self._process(test, "not ok", "FAIL (unexpected success)")
+
+ """
+ def printErrors(self):
+ def printErrorList(self, flavour, errors):
+ """
+
+
+class TAPTestRunner(TextTestRunner):
+ resultclass = TAPTestResult
+
+ def run(self, test):
+ self.stream.write("TAP version 13\n")
+ # [ ] add commented block with test suite __doc__
+ # [ ] check call with a single test
+ # if isinstance(test, suite.TestSuite):
+ self.stream.write("1..%s\n" % len(list(test)))
+
+ # counterhack: inject test counter into test suite
+ test.tap_counter = 0
+ # counterhack: inject reference to suite into each test case
+ for case in test:
+ case.suite = test
+
+ return super(TAPTestRunner, self).run(test)
+
+
+if __name__ == "__main__":
+ import sys
+ import unittest
+
+ class Test(unittest.TestCase):
+ def test_ok(self):
+ pass
+ def test_fail(self):
+ self.assertTrue(False)
+ def test_error(self):
+ bad_symbol
+ @unittest.skip("skipin'")
+ def test_skip(self):
+ pass
+ @unittest.expectedFailure
+ def test_not_ready(self):
+ self.fail()
+ @unittest.expectedFailure
+ def test_invalid_fail_mark(self):
+ pass
+ def test_another_ok(self):
+ pass
+
+
+ suite = unittest.TestSuite([
+ Test('test_ok'),
+ Test('test_fail'),
+ Test('test_error'),
+ Test('test_skip'),
+ Test('test_not_ready'),
+ Test('test_invalid_fail_mark'),
+ Test('test_another_ok')
+ ])
+ if not TAPTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)