xref: /petsc/src/binding/petsc4py/test/runtests.py (revision f8b9f887efa5534f423807e37a1fe2e87d779bac)
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 = os.path.join(rootdir, getbuilddir())
178    if options.builddir and os.path.exists(builddir):
179        sys.path.insert(0, builddir)
180    if options.path:
181        path = options.path[:]
182        path.reverse()
183        for p in path:
184            sys.path.insert(0, p)
185
186
187def setup_unittest(options):
188    try:
189        from unittest.runner import _WritelnDecorator
190    except ImportError:
191        from unittest import _WritelnDecorator
192    #
193    writeln_orig = _WritelnDecorator.writeln
194
195    def writeln(self, message=''):
196        try:
197            self.stream.flush()
198        except Exception:
199            pass
200        writeln_orig(self, message)
201        try:
202            self.stream.flush()
203        except Exception:
204            pass
205
206    _WritelnDecorator.writeln = writeln
207
208
209def import_package(options, pkgname):
210    args = [sys.argv[0]]
211    if options.memdebug:
212        args.append('-malloc_debug')
213        args.append('-malloc_dump')
214    if options.summary:
215        args.append('-log_view')
216    package = __import__(pkgname)
217    package.init(args, arch=options.arch)
218
219
220def print_banner(options):
221    r, n = getprocessorinfo()
222    prefix = '[%d@%s]' % (r, n)
223
224    def writeln(message='', endl='\n'):
225        if message is None:
226            return
227        from petsc4py.PETSc import Sys
228
229        message = f'{prefix} {message}'
230        Sys.syncPrint(message, endl=endl, flush=True)
231
232    if options.verbose:
233        writeln(getpythoninfo())
234        writeln(getpackageinfo('numpy'))
235        for entry in components:
236            writeln(getlibraryinfo(entry))
237            writeln(getpackageinfo('%s4py' % entry.lower()))
238
239
240def load_tests(options, args):
241    from glob import glob
242    import re
243
244    testsuitedir = os.path.dirname(__file__)
245    sys.path.insert(0, testsuitedir)
246    pattern = 'test_*.py'
247    wildcard = os.path.join(testsuitedir, pattern)
248    testfiles = glob(wildcard)
249    testfiles.sort()
250    testsuite = unittest.TestSuite()
251    testloader = unittest.TestLoader()
252    if options.patterns:
253        testloader.testNamePatterns = [ # novermin
254            ('*%s*' % p) if ('*' not in p) else p for p in options.patterns
255        ]
256    include = exclude = None
257    if options.include:
258        include = re.compile('|'.join(options.include)).search
259    if options.exclude:
260        exclude = re.compile('|'.join(options.exclude)).search
261    for testfile in testfiles:
262        filename = os.path.basename(testfile)
263        testname = os.path.splitext(filename)[0]
264        if (exclude and exclude(testname)) or (include and not include(testname)):
265            continue
266        module = __import__(testname)
267        for arg in args:
268            try:
269                cases = testloader.loadTestsFromNames((arg,), module)
270                testsuite.addTests(cases)
271            except AttributeError:
272                pass
273        if not args:
274            cases = testloader.loadTestsFromModule(module)
275            testsuite.addTests(cases)
276    return testsuite
277
278
279def run_tests(options, testsuite, runner=None):
280    if runner is None:
281        runner = unittest.TextTestRunner(verbosity=options.verbose)
282        runner.failfast = options.failfast
283    result = runner.run(testsuite)
284    return result.wasSuccessful()
285
286
287def abort(code=1):
288    os.abort()
289
290
291def shutdown(success):
292    pass
293
294
295def main(args=None):
296    pkgname = '%s4py' % components[-1].lower()
297    parser = getoptionparser()
298    (options, args) = parser.parse_args(args)
299    setup_python(options)
300    setup_unittest(options)
301    import_package(options, pkgname)
302    print_banner(options)
303    testsuite = load_tests(options, args)
304    success = run_tests(options, testsuite)
305    if not success and options.failfast:
306        abort()
307    shutdown(success)
308    return not success
309
310
311if __name__ == '__main__':
312    import sys
313
314    sys.dont_write_bytecode = True
315    sys.exit(main())
316