xref: /petsc/src/binding/petsc4py/test/runtests.py (revision 4e278199b78715991f5c71ebbd945c1489263e6c)
1import sys, os
2import optparse
3import unittest
4
5def getoptionparser():
6    parser = optparse.OptionParser()
7
8    parser.add_option("-q", "--quiet",
9                      action="store_const", const=0, dest="verbose", default=1,
10                      help="do not print status messages to stdout")
11    parser.add_option("-v", "--verbose",
12                      action="store_const", const=2, dest="verbose", default=1,
13                      help="print status messages to stdout")
14    parser.add_option("-i", "--include", type="string",
15                      action="append",  dest="include", default=[],
16                      help="include tests matching PATTERN", metavar="PATTERN")
17    parser.add_option("-e", "--exclude", type="string",
18                      action="append", dest="exclude", default=[],
19                      help="exclude tests matching PATTERN", metavar="PATTERN")
20    parser.add_option("-f", "--failfast",
21                      action="store_true", dest="failfast", default=False,
22                      help="Stop on first failure")
23    parser.add_option("--no-builddir",
24                      action="store_false", dest="builddir", default=True,
25                      help="disable testing from build directory")
26    parser.add_option("--path", type="string",
27                      action="append", dest="path", default=[],
28                      help="prepend PATH to sys.path", metavar="PATH")
29    parser.add_option("--refleaks", type="int",
30                      action="store", dest="repeats", default=3,
31                      help="run tests REPEAT times in a loop to catch leaks",
32                      metavar="REPEAT")
33    parser.add_option("--arch", type="string",
34                      action="store", dest="arch", default=None,
35                      help="use PETSC_ARCH",
36                      metavar="PETSC_ARCH")
37    parser.add_option("-s","--summary",
38                      action="store_true", dest="summary", default=0,
39                      help="print PETSc log summary")
40    return parser
41
42def getbuilddir():
43    from distutils.util import get_platform
44    s = os.path.join("build", "lib.%s-%.3s" % (get_platform(), sys.version))
45    if hasattr(sys, 'gettotalrefcount'): s += '-pydebug'
46    return s
47
48def setup_python(options):
49    rootdir = os.path.dirname(os.path.dirname(__file__))
50    builddir = os.path.join(rootdir, getbuilddir())
51    if options.builddir and os.path.exists(builddir):
52        sys.path.insert(0, builddir)
53    if options.path:
54        path = options.path[:]
55        path.reverse()
56        for p in path:
57            sys.path.insert(0, p)
58
59def setup_unittest(options):
60    from unittest import TestSuite
61    try:
62        from unittest.runner import _WritelnDecorator
63    except ImportError:
64        from unittest import _WritelnDecorator
65    #
66    writeln_orig = _WritelnDecorator.writeln
67    def writeln(self, message=''):
68        try: self.stream.flush()
69        except: pass
70        writeln_orig(self, message)
71        try: self.stream.flush()
72        except: pass
73    _WritelnDecorator.writeln = writeln
74
75def import_package(options, pkgname):
76    args = [
77        sys.argv[0],
78        '-malloc',
79        '-malloc_debug',
80        '-malloc_dump',
81    ]
82    if options.summary:
83        args.append('-log_view')
84    package = __import__(pkgname)
85    package.init(args, arch=options.arch)
86    return package
87
88def getprocessorinfo():
89    try:
90        name = os.uname()[1]
91    except:
92        import platform
93        name = platform.uname()[1]
94    from petsc4py.PETSc import COMM_WORLD
95    rank = COMM_WORLD.getRank()
96    return (rank, name)
97
98def getlibraryinfo():
99    from petsc4py import PETSc
100    (major, minor, micro) = PETSc.Sys.getVersion()
101    r = PETSc.Sys.getVersionInfo()['release']
102    if r: release = 'release'
103    else: release = 'development'
104    arch = PETSc.__arch__
105    return ("PETSc %d.%d.%d %s (conf: '%s')"
106            % (major, minor, micro, release, arch) )
107
108def getpythoninfo():
109    x, y = sys.version_info[:2]
110    return ("Python %d.%d (%s)" % (x, y, sys.executable))
111
112def getpackageinfo(pkg):
113    return ("%s %s (%s)" % (pkg.__name__,
114                            pkg.__version__,
115                            pkg.__path__[0]))
116
117def writeln(message='', endl='\n'):
118    from petsc4py.PETSc import Sys
119    Sys.syncPrint(message, endl=endl, flush=True)
120
121def print_banner(options, package):
122    r, n = getprocessorinfo()
123    fmt = "[%d@%s] %s"
124    if options.verbose:
125        writeln(fmt % (r, n, getpythoninfo()))
126        writeln(fmt % (r, n, getlibraryinfo()))
127        writeln(fmt % (r, n, getpackageinfo(package)))
128
129def load_tests(options, args):
130    from glob import glob
131    import re
132    testsuitedir = os.path.dirname(__file__)
133    sys.path.insert(0, testsuitedir)
134    pattern = 'test_*.py'
135    wildcard = os.path.join(testsuitedir, pattern)
136    testfiles = glob(wildcard)
137    testfiles.sort()
138    testsuite = unittest.TestSuite()
139    testloader = unittest.TestLoader()
140    include = exclude = None
141    if options.include:
142        include = re.compile('|'.join(options.include)).search
143    if options.exclude:
144        exclude = re.compile('|'.join(options.exclude)).search
145    for testfile in testfiles:
146        filename = os.path.basename(testfile)
147        testname = os.path.splitext(filename)[0]
148        if ((exclude and exclude(testname)) or
149            (include and not include(testname))):
150            continue
151        module = __import__(testname)
152        for arg in args:
153            try:
154                cases = testloader.loadTestsFromNames((arg,), module)
155                testsuite.addTests(cases)
156            except AttributeError:
157                pass
158        if not args:
159            cases = testloader.loadTestsFromModule(module)
160            testsuite.addTests(cases)
161    return testsuite
162
163def run_tests(options, testsuite, runner=None):
164    if runner is None:
165        runner = unittest.TextTestRunner(verbosity=options.verbose)
166        runner.failfast = options.failfast
167    result = runner.run(testsuite)
168    return result.wasSuccessful()
169
170def test_refleaks(options, args):
171    from sys import gettotalrefcount
172    from gc import collect
173    from copy import deepcopy
174    testsuite = load_tests(options, args)
175    class EmptyIO(object):
176        def write(self, *args):
177            pass
178    runner = unittest.TextTestRunner(stream=EmptyIO(), verbosity=0)
179    rank, name = getprocessorinfo()
180    r1 = r2 = 0
181    repeats = options.repeats
182    while repeats:
183        collect()
184        r1 = gettotalrefcount()
185        run_tests(options, deepcopy(testsuite), runner)
186        collect()
187        r2 = gettotalrefcount()
188        leaks = r2-r1
189        if leaks and repeats < options.repeats:
190            writeln('[%d@%s] refleaks:  (%d - %d) --> %d'
191                    % (rank, name, r2, r1, leaks))
192        repeats -= 1
193
194def abort(code=1):
195    os.abort()
196
197def shutdown(success):
198    pass
199
200def main(args=None):
201    pkgname = 'petsc4py'
202    parser = getoptionparser()
203    (options, args) = parser.parse_args(args)
204    setup_python(options)
205    setup_unittest(options)
206    package = import_package(options, pkgname)
207    print_banner(options, package)
208    testsuite = load_tests(options, args)
209    success = run_tests(options, testsuite)
210    if not success and options.failfast: abort()
211    if success and hasattr(sys, 'gettotalrefcount'):
212        test_refleaks(options, args)
213    shutdown(success)
214    return not success
215
216if __name__ == '__main__':
217    import sys
218    sys.dont_write_bytecode = True
219    sys.exit(main())
220