xref: /petsc/src/binding/petsc4py/test/runtests.py (revision 2ff79c18c26c94ed8cb599682f680f231dca6444)
1# Author:  Lisandro Dalcin
2# Contact: dalcinl@gmail.com
3import os
4import sys
5import optparse
6import unittest
7
8__unittest = True
9
10components = [
11    'PETSc',
12]
13
14
15def getoptionparser():
16    parser = optparse.OptionParser()
17
18    parser.add_option(
19        '-q',
20        '--quiet',
21        action='store_const',
22        const=0,
23        dest='verbose',
24        default=1,
25        help='do not print status messages to stdout',
26    )
27    parser.add_option(
28        '-v',
29        '--verbose',
30        action='store_const',
31        const=2,
32        dest='verbose',
33        default=1,
34        help='print status messages to stdout',
35    )
36    parser.add_option(
37        '-i',
38        '--include',
39        type='string',
40        action='append',
41        dest='include',
42        default=[],
43        help='include tests matching PATTERN',
44        metavar='PATTERN',
45    )
46    parser.add_option(
47        '-e',
48        '--exclude',
49        type='string',
50        action='append',
51        dest='exclude',
52        default=[],
53        help='exclude tests matching PATTERN',
54        metavar='PATTERN',
55    )
56    parser.add_option(
57        '-k',
58        '--pattern',
59        type='string',
60        action='append',
61        dest='patterns',
62        default=[],
63        help='only run tests which match the given substring',
64    )
65    parser.add_option(
66        '-f',
67        '--failfast',
68        action='store_true',
69        dest='failfast',
70        default=False,
71        help='Stop on first failure',
72    )
73    parser.add_option(
74        '--no-builddir',
75        action='store_false',
76        dest='builddir',
77        default=True,
78        help='disable testing from build directory',
79    )
80    parser.add_option(
81        '--path',
82        type='string',
83        action='append',
84        dest='path',
85        default=[],
86        help='prepend PATH to sys.path',
87        metavar='PATH',
88    )
89    parser.add_option(
90        '--arch',
91        type='string',
92        action='store',
93        dest='arch',
94        default=None,
95        help='use PETSC_ARCH',
96        metavar='PETSC_ARCH',
97    )
98    parser.add_option(
99        '-s',
100        '--summary',
101        action='store_true',
102        dest='summary',
103        default=0,
104        help='print PETSc log summary',
105    )
106    parser.add_option(
107        '--no-memdebug',
108        action='store_false',
109        dest='memdebug',
110        default=True,
111        help='Do not use PETSc memory debugging',
112    )
113    return parser
114
115
116def getbuilddir():
117    try:
118        try:
119            from setuptools.dist import Distribution
120        except ImportError:
121            from distutils.dist import Distribution
122        try:
123            from setuptools.command.build import build
124        except ImportError:
125            from distutils.command.build import build
126        cmd_obj = build(Distribution())
127        cmd_obj.finalize_options()
128        return cmd_obj.build_platlib
129    except Exception:
130        return None
131
132
133def getprocessorinfo():
134    try:
135        name = os.uname()[1]
136    except Exception:
137        import platform
138
139        name = platform.uname()[1]
140    from petsc4py.PETSc import COMM_WORLD
141
142    rank = COMM_WORLD.getRank()
143    return (rank, name)
144
145
146def getlibraryinfo(name):
147    modname = f'{name.lower()}4py.{name}'
148    module = __import__(modname, fromlist=[name])
149    (major, minor, micro), devel = module.Sys.getVersion(devel=True)
150    r = not devel
151    if r:
152        release = 'release'
153    else:
154        release = 'development'
155    arch = module.__arch__
156    return "%s %d.%d.%d %s (conf: '%s')" % (name, major, minor, micro, release, arch)
157
158
159def getpythoninfo():
160    x, y, z = sys.version_info[:3]
161    return 'Python %d.%d.%d (%s)' % (x, y, z, sys.executable)
162
163
164def getpackageinfo(pkg):
165    try:
166        pkg = __import__(pkg)
167    except ImportError:
168        return None
169    name = pkg.__name__
170    version = pkg.__version__
171    path = pkg.__path__[0]
172    return f'{name} {version} ({path})'
173
174
175def setup_python(options):
176    rootdir = os.path.dirname(os.path.dirname(__file__))
177    builddir = getbuilddir()
178    if builddir is not None:
179        builddir = os.path.join(rootdir, builddir)
180    if options.builddir and builddir is not None and os.path.exists(builddir):
181        sys.path.insert(0, builddir)
182    if options.path:
183        path = options.path[:]
184        path.reverse()
185        for p in path:
186            sys.path.insert(0, p)
187
188
189def setup_unittest(options):
190    try:
191        from unittest.runner import _WritelnDecorator
192    except ImportError:
193        from unittest import _WritelnDecorator
194    #
195    writeln_orig = _WritelnDecorator.writeln
196
197    def writeln(self, message=''):
198        try:
199            self.stream.flush()
200        except Exception:
201            pass
202        writeln_orig(self, message)
203        try:
204            self.stream.flush()
205        except Exception:
206            pass
207
208    _WritelnDecorator.writeln = writeln
209
210
211def import_package(options, pkgname):
212    args = [sys.argv[0]]
213    if options.memdebug:
214        args.append('-malloc_debug')
215        args.append('-malloc_dump')
216    if options.summary:
217        args.append('-log_view')
218    package = __import__(pkgname)
219    package.init(args, arch=options.arch)
220
221
222def print_banner(options):
223    r, n = getprocessorinfo()
224    prefix = '[%d@%s]' % (r, n)
225
226    def writeln(message='', endl='\n'):
227        if message is None:
228            return
229        from petsc4py.PETSc import Sys
230
231        message = f'{prefix} {message}'
232        Sys.syncPrint(message, endl=endl, flush=True)
233
234    if options.verbose:
235        writeln(getpythoninfo())
236        writeln(getpackageinfo('numpy'))
237        for entry in components:
238            writeln(getlibraryinfo(entry))
239            writeln(getpackageinfo('%s4py' % entry.lower()))
240
241
242def load_tests(options, args):
243    from glob import glob
244    import re
245
246    testsuitedir = os.path.dirname(__file__)
247    sys.path.insert(0, testsuitedir)
248    pattern = 'test_*.py'
249    wildcard = os.path.join(testsuitedir, pattern)
250    testfiles = glob(wildcard)
251    testfiles.sort()
252    testsuite = unittest.TestSuite()
253    testloader = unittest.TestLoader()
254    if options.patterns:
255        testloader.testNamePatterns = [ # novermin
256            ('*%s*' % p) if ('*' not in p) else p for p in options.patterns
257        ]
258    include = exclude = None
259    if options.include:
260        include = re.compile('|'.join(options.include)).search
261    if options.exclude:
262        exclude = re.compile('|'.join(options.exclude)).search
263    for testfile in testfiles:
264        filename = os.path.basename(testfile)
265        testname = os.path.splitext(filename)[0]
266        if (exclude and exclude(testname)) or (include and not include(testname)):
267            continue
268        module = __import__(testname)
269        for arg in args:
270            try:
271                cases = testloader.loadTestsFromNames((arg,), module)
272                testsuite.addTests(cases)
273            except AttributeError:
274                pass
275        if not args:
276            cases = testloader.loadTestsFromModule(module)
277            testsuite.addTests(cases)
278    return testsuite
279
280
281def run_tests(options, testsuite, runner=None):
282    if runner is None:
283        runner = unittest.TextTestRunner(verbosity=options.verbose)
284        runner.failfast = options.failfast
285    result = runner.run(testsuite)
286    return result.wasSuccessful()
287
288
289def abort(code=1):
290    os.abort()
291
292
293def shutdown(success):
294    pass
295
296
297def main(args=None):
298    pkgname = '%s4py' % components[-1].lower()
299    parser = getoptionparser()
300    (options, args) = parser.parse_args(args)
301    setup_python(options)
302    setup_unittest(options)
303    import_package(options, pkgname)
304    print_banner(options)
305    testsuite = load_tests(options, args)
306    success = run_tests(options, testsuite)
307    if not success and options.failfast:
308        abort()
309    shutdown(success)
310    return not success
311
312
313if __name__ == '__main__':
314    import sys
315
316    sys.dont_write_bytecode = True
317    sys.exit(main())
318