1#  _________________________________________________________________________
2#
3#  PyUtilib: A Python utility library.
4#  Copyright (c) 2008 Sandia Corporation.
5#  This software is distributed under the BSD License.
6#  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
7#  the U.S. Government retains certain rights in this software.
8#  _________________________________________________________________________
9
10import sys
11try:
12    import nose
13    import nose.plugins
14except ImportError:
15    print("This testing utility requires the 'nose' Python testing tool")
16    sys.exit(1)
17try:
18    import nosexunit
19    import nosexunit.plugin
20    nose_xunit_installed = True
21except ImportError:
22    nose_xunit_installed = False
23try:
24    import coverage
25    import nose.plugins.cover
26    coverage_installed = True
27except ImportError:
28    coverage_installed = False
29
30import re
31import logging
32import os
33import os.path
34import glob
35import copy
36from optparse import OptionParser
37
38# Ignore exceptions that occur during a logging statement
39logging.raiseExceptions = 0
40
41
42class UnwantedPackagePlugin(nose.plugins.Plugin):
43    enabled = True
44    name = "unwanted-package"
45
46    def __init__(self, unwanted):
47        self.unwanted = unwanted
48
49    def configure(self, options, conf):
50        pass
51
52    def wantDirectory(self, dirname):
53        want = None
54        if os.path.basename(dirname) in self.unwanted:
55            want = False
56        return want
57
58
59def generate_options(package, packages, subpackages, filter, options, testdir,
60                     xunitdir, dirname):
61    if not type(packages) is list:
62        tmp = [packages]
63    else:
64        tmp = packages
65    newargs = []
66    if options.xunit:
67        newargs = ['--with-xunit', '--xunit-file', xunitdir + os.sep + 'TEST-' +
68                   package + '.xml']
69        #if coverage_installed:
70    #newargs = newargs + ['--source-folder', '.']
71    if coverage_installed:
72        newargs = newargs + ['--with-coverage', '--cover-erase',
73                             '--cover-inclusive', '--cover-package', package]
74        if options.filtering or options.filtering_all:
75            for item in filter:
76                newargs.append('--cover-filter=' + item)
77            if len(tmp) > 0:
78                for item in subpackages:
79                    if not item in tmp:
80                        newargs.append('--cover-filter=' + package + '.' + item)
81    if options.verbose:
82        newargs = newargs + ['--verbosity=3']
83    newargs = newargs + ['--where', dirname]
84    return newargs
85
86
87def main(argv, package, subpackages, filter, testdir, dirname="test"):
88
89    parser = OptionParser()
90    parser.add_option(
91        "--no-xunit",
92        help="Disable generation of XUnit XML summary",
93        action="store_false",
94        dest="xunit",
95        default=True)
96    parser.add_option(
97        "-v",
98        "--verbose",
99        help="Provide verbose output",
100        action="store_true",
101        dest="verbose",
102        default=False)
103    parser.add_option(
104        "-f",
105        "--filter",
106        help="Enable filtering of coverage tests with a customized nose installation",
107        action="store_true",
108        dest="filtering",
109        default=False)
110    parser.add_option(
111        "-F",
112        "--filter-all",
113        help="Apply nose to all packages, filtering each coverage test",
114        action="store_true",
115        dest="filtering_all",
116        default=False)
117    parser.usage = "runtests [options] <package> [...]"
118    parser.description = "A utility to run Pyomo tests.  A particular feature of this tool is the ability to filter coverage information, using a customized installation of the nose utility."
119    parser.epilog = """The customized nose utility that supports filtering can be obtained from Bill Hart (wehart@sandia.gov)."""
120    #
121    # Process argv list
122    #
123    (options, args) = parser.parse_args(args=argv)
124    #
125    # Preprocess arguments, to eliminate trailing '/' or '\\' characters, which a user
126    # might add using tab completion.
127    #
128    for i in range(0, len(args)):
129        if args[i][-1] == os.sep:
130            args[i] = args[i][:-1]
131    #
132    # Verify that the arguments are valid subpackages
133    #
134    unwanted = copy.copy(subpackages)
135    for arg in args[1:]:
136        if not arg in subpackages:
137            print("ERROR: no subpackage '" + arg)
138            sys.exit(1)
139        unwanted.remove(arg)
140    if unwanted == subpackages:
141        unwanted = []
142    #
143    # Remove coverage files from previous 'runtests' executions
144    #
145    coverage_file = testdir + os.sep + dirname + os.sep + ".coverage"
146    if os.path.exists(coverage_file):
147        os.remove(coverage_file)
148    os.environ["COVERAGE_FILE"] = coverage_file
149    #for file in glob.glob("*/.coverage"):
150    #os.remove(file)
151    #for file in glob.glob("*/*/.coverage"):
152    #os.remove(file)
153    #
154    # Remove XML files from previous 'runtests' executions
155    #
156    xunitdir = testdir + os.sep + dirname + os.sep + "xunit"
157    if os.path.exists(xunitdir):
158        for file in glob.glob(xunitdir + os.sep + "TEST*.xml"):
159            os.remove(file)
160    else:
161        os.mkdir(xunitdir)
162
163    plugins = [UnwantedPackagePlugin(unwanted)]
164    #if nose_xunit_installed and options.xunit:
165    #plugins.append(nosexunit.plugin.NoseXUnit())
166    #if coverage_installed:
167    #plugins.append(nose.plugins.cover.Coverage())
168    #plugins.append(nose.plugins.xunit.Xunit())
169    if not options.filtering_all:
170        newargs = ['runtests'] + generate_options(
171            package, args[1:], subpackages, filter, options, testdir, xunitdir,
172            dirname) + args[1:]
173        os.chdir(testdir)
174        if options.verbose:
175            print("Nose Arguments:", newargs)
176            print("Test Dir:", testdir)
177            if len(args) > 1:
178                print("Test Packages:",)
179                for arg in args[1:]:
180                    print(arg,)
181                print("")
182            if len(unwanted) > 1:
183                print("Ignored Packages:", unwanted)
184        sys.argv = newargs
185        nose.run(argv=newargs, addplugins=plugins)
186    else:
187        if len(args[1:]) == 0:
188            arglist = subpackages
189        else:
190            arglist = args[1:]
191        for arg in arglist:
192            newargs = ['runtests'] + generate_options(arg, options) + [arg]
193            print("Package Coverage Tests: " + arg)
194            tmp = sys.stdout
195            os.chdir(testdir)
196            if options.verbose:
197                print("Nose Arguments:", newargs)
198                print("Test Dir:", testdir)
199                if len(args) > 1:
200                    print("Test Packages:",)
201                    for arg in args[1:]:
202                        print(arg,)
203                    print("")
204                if len(unwanted) > 1:
205                    print("Ignored Packages:", unwanted)
206            nose.run(argv=newargs, addplugins=plugins)
207            sys.stdout = tmp
208    #
209    # Rename xUnit files and insert the package information
210    #
211    if os.path.exists(xunitdir):
212        p = re.compile('^.*TEST-(.+)\.xml')
213        for file in glob.glob(xunitdir + os.sep + "TEST-*.xml"):
214            oldname = p.match(file).group(1)
215            if oldname == "test":
216                suffix = ""
217            else:
218                suffix = "." + oldname
219
220            FILE = open(file, "r")
221            text0 = "".join(FILE.readlines())
222            FILE.close()
223            ptmp = re.compile("testsuite name=\"nosetests\"")
224            text1 = ptmp.sub("testsuite name=\"" + package + "\"", text0)
225            #ptmp = re.compile("classname=\""+oldname)
226            #text2 = ptmp.sub("classname=\""+package+suffix, text1)
227            #FILE = open(xunitdir+os.sep+"TEST-"+package+suffix+".xml","w")
228            FILE = open(file, "w")
229            FILE.write(text1)
230            FILE.close()
231            #os.remove(file)
232