1#! /usr/bin/env python
2
3import glob
4import os
5import struct
6import sys
7import unittest
8
9_glob_test_py = 'test_*.py'
10
11if os.linesep == '\n':
12    def _print(text):
13        print(text)
14else:
15    def _print(text):
16        print(text.replace('\n', os.linesep))
17
18def get_tests(dir='.', clean=False):
19    '''Get a list of test module names in the given directory.
20    '''
21    res, dir_ = [], ''
22     # walk recursively through all subdirectories
23    if os.path.isdir(dir):
24        if dir != '.':
25           dir_ = dir.rstrip(os.sep) + os.sep
26        for sub in os.listdir(dir):
27            if sub[0] != '.':  # os.curdir
28               sub = dir_ + sub
29               if os.path.isdir(sub):
30                   res.extend(get_tests(sub))
31        glob_py = dir_ + _glob_test_py
32    elif os.path.isfile(dir):
33        glob_py = dir
34        sub = os.path.split(dir)[0]
35        if sub:  # prefix
36            dir_ = sub + os.sep
37    else:
38        return res
39     # append all tests as module names
40    for test in glob.glob(glob_py):
41        test = dir_ + os.path.basename(test)
42        if clean:  # remove existing bytecodes
43            for co in ('c', 'o'):
44                try:
45                    os.remove(test + co)
46                except OSError:
47                    pass
48         # convert to module name and remove leading ./ or .\
49        test = test[:-3].replace(os.sep, '.')
50        test = test.lstrip('.')
51        res.append(test)
52    res.sort(key=lambda s: -s.count('muppy'))
53    return res  # sorted(res)
54
55def suite(dirs=['.'], clean=False, pre=True, verbose=2):
56    '''Create a suite with all tests from the given directories.
57
58    This will also include tests from subdirectories.
59    '''
60    all = True
61    res = unittest.TestSuite()
62    for dir in dirs:
63        for test in get_tests(dir, clean):
64            # Import tests relative to 'test' directory
65            test = test[test.find('.')+1:]
66            try:
67                mod = test.rfind('.') + 1
68                if mod > 0:  # from test import mod
69                   mod = test[mod:]
70                   mod = __import__(test, globals(), locals(), [mod])
71                else:  # import test
72                   mod = __import__(test)
73                res.addTest(unittest.defaultTestLoader.loadTestsFromModule(mod))
74            except ImportError:
75                if sys.hexversion < 0x02050000:
76                   _print('Warning: ignoring %r - incompatible with this Python version' % test)
77                else:
78                    raise
79            except (SyntaxError, NameError):
80                if pre:
81                   _print('Warning: ignoring %r due to an error while importing' % test)
82                else:
83                   _print('Error: %r missing or not found' % test)
84                if verbose > 2:
85                    raise  # show the error
86                all = False
87    return res, all
88
89
90if __name__ == '__main__':
91
92     # default values for options
93    clean   = False  # -c[lean]  -- remove all .pyo and .pyc files
94    pre     = True   # -pre[-install]  -- try before installation
95   #pre     = False  # -post[-install]  -- test after installation
96    verbose = 2      # -v[erbose] <level>  -- verbosity level
97
98     # get options and test case directories (files?)
99    dirs = sys.argv[1:]
100    while dirs:
101        t = dirs[0]
102        n = len(t)
103        if '-clean'.startswith(t) and n > 1:
104            clean = True
105        elif '-post-install'.startswith(t) and n > 4:
106            pre = False
107        elif '-pre-install'.startswith(t) and n > 3:
108            pre = True
109        elif '-verbose'.startswith(t) and n > 1:
110            verbose = int(dirs.pop(1))
111        else:
112            break
113        dirs = dirs[1:]
114    else:
115        dirs = ['.']
116
117    # Insert parent directory such that the code modules can be
118    # imported, but only for pre-install testing. Import test directory
119    # for pre and post-install testing.
120    t = os.path.split(sys.path[0])
121    if t and pre:
122        sys.path.insert(1, t[0])
123    sys.path.append(os.path.join(t[0], 'test'))
124
125     # print some details
126    if verbose > 1:
127        t = '%d-bit [' % (struct.calcsize('P') << 3)
128        _print('Python %s\n' % sys.version.replace('[', t))
129        if verbose > 4:
130            t = '\n          '  # indentation
131            _print('Sys.path: %s\n' % t.join(sys.path))
132        if verbose > 3:
133            _print('Test dirs: %r\n' % dirs)
134
135     # build and run single test suite
136    tst, all = suite(dirs, clean=clean, pre=pre, verbose=verbose)
137    if pre or all:
138        res = unittest.TextTestRunner(verbosity=verbose).run(tst)
139        if res.wasSuccessful():
140            sys.exit(0)
141    sys.exit(1)
142