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