Show More
@@ -43,10 +43,6 b' from nose.plugins import Plugin' | |||
|
43 | 43 | |
|
44 | 44 | # Our own imports |
|
45 | 45 | from IPython.utils.importstring import import_item |
|
46 | from IPython.utils.path import get_ipython_package_dir | |
|
47 | from IPython.utils.warn import warn | |
|
48 | ||
|
49 | from IPython.testing import globalipapp | |
|
50 | 46 | from IPython.testing.plugin.ipdoctest import IPythonDoctest |
|
51 | 47 | from IPython.external.decorators import KnownFailure, knownfailureif |
|
52 | 48 | |
@@ -303,6 +299,8 b" sec.exclude('exporters.tests.files')" | |||
|
303 | 299 | #----------------------------------------------------------------------------- |
|
304 | 300 | |
|
305 | 301 | def check_exclusions_exist(): |
|
302 | from IPython.utils.path import get_ipython_package_dir | |
|
303 | from IPython.utils.warn import warn | |
|
306 | 304 | parent = os.path.dirname(get_ipython_package_dir()) |
|
307 | 305 | for sec in test_sections: |
|
308 | 306 | for pattern in sec.exclusions: |
@@ -416,11 +414,12 b' def run_iptest():' | |||
|
416 | 414 | # objects should, individual shells shouldn't). But for now, this |
|
417 | 415 | # workaround allows the test suite for the inprocess module to complete. |
|
418 | 416 | if section.name != 'kernel.inprocess': |
|
417 | from IPython.testing import globalipapp | |
|
419 | 418 | globalipapp.start_ipython() |
|
420 | 419 | |
|
421 | 420 | # Now nose can run |
|
422 | 421 | TestProgram(argv=argv, addplugins=plugins) |
|
423 | 422 | |
|
424 | ||
|
425 | 423 | if __name__ == '__main__': |
|
426 | 424 | run_iptest() |
|
425 |
@@ -21,6 +21,7 b' from __future__ import print_function' | |||
|
21 | 21 | import argparse |
|
22 | 22 | import multiprocessing.pool |
|
23 | 23 | import os |
|
24 | import shutil | |
|
24 | 25 | import signal |
|
25 | 26 | import sys |
|
26 | 27 | import subprocess |
@@ -39,6 +40,8 b' class IPTestController(object):' | |||
|
39 | 40 | section = None |
|
40 | 41 | #: list, command line arguments to be executed |
|
41 | 42 | cmd = None |
|
43 | #: str, Python command to execute in subprocess | |
|
44 | pycmd = None | |
|
42 | 45 | #: dict, extra environment variables to set for the subprocess |
|
43 | 46 | env = None |
|
44 | 47 | #: list, TemporaryDirectory instances to clear up when the process finishes |
@@ -50,28 +53,38 b' class IPTestController(object):' | |||
|
50 | 53 | def __init__(self, section): |
|
51 | 54 | """Create new test runner.""" |
|
52 | 55 | self.section = section |
|
53 | self.cmd = [sys.executable, '-m', 'IPython.testing.iptest', section] | |
|
56 | # pycmd is put into cmd[2] in IPTestController.launch() | |
|
57 | self.cmd = [sys.executable, '-c', None, section] | |
|
58 | self.pycmd = "from IPython.testing.iptest import run_iptest; run_iptest()" | |
|
54 | 59 | self.env = {} |
|
55 | 60 | self.dirs = [] |
|
56 | 61 | ipydir = TemporaryDirectory() |
|
57 | 62 | self.dirs.append(ipydir) |
|
58 | 63 | self.env['IPYTHONDIR'] = ipydir.name |
|
59 | workingdir = TemporaryDirectory() | |
|
64 | self.workingdir = workingdir = TemporaryDirectory() | |
|
60 | 65 | self.dirs.append(workingdir) |
|
61 | 66 | self.env['IPTEST_WORKING_DIR'] = workingdir.name |
|
67 | # This means we won't get odd effects from our own matplotlib config | |
|
68 | self.env['MPLCONFIGDIR'] = workingdir.name | |
|
62 | 69 | |
|
63 | 70 | def add_xunit(self): |
|
64 | 71 | xunit_file = os.path.abspath(self.section + '.xunit.xml') |
|
65 | 72 | self.cmd.extend(['--with-xunit', '--xunit-file', xunit_file]) |
|
66 | 73 | |
|
67 |
def add_coverage(self |
|
|
68 | self.cmd.append('--with-coverage') | |
|
69 | for include in test_sections[self.section].includes: | |
|
70 | self.cmd.extend(['--cover-package', include]) | |
|
71 | if xml: | |
|
72 | coverage_xml = os.path.abspath(self.section + ".coverage.xml") | |
|
73 | self.cmd.extend(['--cover-xml', '--cover-xml-file', coverage_xml]) | |
|
74 | def add_coverage(self): | |
|
75 | coverage_rc = ("[run]\n" | |
|
76 | "data_file = {data_file}\n" | |
|
77 | "source =\n" | |
|
78 | " {source}\n" | |
|
79 | ).format(data_file=os.path.abspath('.coverage.'+self.section), | |
|
80 | source="\n ".join(test_sections[self.section].includes)) | |
|
74 | 81 | |
|
82 | config_file = os.path.join(self.workingdir.name, '.coveragerc') | |
|
83 | with open(config_file, 'w') as f: | |
|
84 | f.write(coverage_rc) | |
|
85 | ||
|
86 | self.env['COVERAGE_PROCESS_START'] = config_file | |
|
87 | self.pycmd = "import coverage; coverage.process_startup(); " + self.pycmd | |
|
75 | 88 | |
|
76 | 89 | def launch(self): |
|
77 | 90 | # print('*** ENV:', self.env) # dbg |
@@ -80,6 +93,7 b' class IPTestController(object):' | |||
|
80 | 93 | env.update(self.env) |
|
81 | 94 | output = subprocess.PIPE if self.buffer_output else None |
|
82 | 95 | stdout = subprocess.STDOUT if self.buffer_output else None |
|
96 | self.cmd[2] = self.pycmd | |
|
83 | 97 | self.process = subprocess.Popen(self.cmd, stdout=output, |
|
84 | 98 | stderr=stdout, env=env) |
|
85 | 99 | |
@@ -131,7 +145,7 b' def test_controllers_to_run(inc_slow=False, xunit=False, coverage=False):' | |||
|
131 | 145 | if xunit: |
|
132 | 146 | controller.add_xunit() |
|
133 | 147 | if coverage: |
|
134 |
controller.add_coverage( |
|
|
148 | controller.add_coverage() | |
|
135 | 149 | res.append(controller) |
|
136 | 150 | return res |
|
137 | 151 | |
@@ -178,7 +192,7 b' def report():' | |||
|
178 | 192 | |
|
179 | 193 | return ''.join(out) |
|
180 | 194 | |
|
181 | def run_iptestall(inc_slow=False, jobs=1, xunit=False, coverage=False): | |
|
195 | def run_iptestall(inc_slow=False, jobs=1, xunit_out=False, coverage_out=False): | |
|
182 | 196 | """Run the entire IPython test suite by calling nose and trial. |
|
183 | 197 | |
|
184 | 198 | This function constructs :class:`IPTester` instances for all IPython |
@@ -200,8 +214,8 b' def run_iptestall(inc_slow=False, jobs=1, xunit=False, coverage=False):' | |||
|
200 | 214 | if jobs != 1: |
|
201 | 215 | IPTestController.buffer_output = True |
|
202 | 216 | |
|
203 | controllers = test_controllers_to_run(inc_slow=inc_slow, xunit=xunit, | |
|
204 | coverage=coverage) | |
|
217 | controllers = test_controllers_to_run(inc_slow=inc_slow, xunit=xunit_out, | |
|
218 | coverage=coverage_out) | |
|
205 | 219 | |
|
206 | 220 | # Run all test runners, tracking execution time |
|
207 | 221 | failed = [] |
@@ -257,9 +271,48 b' def run_iptestall(inc_slow=False, jobs=1, xunit=False, coverage=False):' | |||
|
257 | 271 | print('-'*40) |
|
258 | 272 | print('Runner failed:', controller.section) |
|
259 | 273 | print('You may wish to rerun this one individually, with:') |
|
260 | failed_call_args = [py3compat.cast_unicode(x) for x in controller.cmd] | |
|
261 | print(u' '.join(failed_call_args)) | |
|
274 | print(' iptest', *controller.cmd[3:]) | |
|
262 | 275 | print() |
|
276 | ||
|
277 | if coverage_out: | |
|
278 | from coverage import coverage | |
|
279 | cov = coverage(data_file='.coverage') | |
|
280 | cov.combine() | |
|
281 | cov.save() | |
|
282 | ||
|
283 | # Coverage HTML report | |
|
284 | if coverage_out == 'html': | |
|
285 | html_dir = 'ipy_htmlcov' | |
|
286 | shutil.rmtree(html_dir, ignore_errors=True) | |
|
287 | print("Writing HTML coverage report to %s/ ... " % html_dir, end="") | |
|
288 | sys.stdout.flush() | |
|
289 | ||
|
290 | # Custom HTML reporter to clean up module names. | |
|
291 | from coverage.html import HtmlReporter | |
|
292 | class CustomHtmlReporter(HtmlReporter): | |
|
293 | def find_code_units(self, morfs): | |
|
294 | super(CustomHtmlReporter, self).find_code_units(morfs) | |
|
295 | for cu in self.code_units: | |
|
296 | nameparts = cu.name.split(os.sep) | |
|
297 | if 'IPython' not in nameparts: | |
|
298 | continue | |
|
299 | ix = nameparts.index('IPython') | |
|
300 | cu.name = '.'.join(nameparts[ix:]) | |
|
301 | ||
|
302 | # Reimplement the html_report method with our custom reporter | |
|
303 | cov._harvest_data() | |
|
304 | cov.config.from_args(omit='*%stests' % os.sep, html_dir=html_dir, | |
|
305 | html_title='IPython test coverage', | |
|
306 | ) | |
|
307 | reporter = CustomHtmlReporter(cov, cov.config) | |
|
308 | reporter.report(None) | |
|
309 | print('done.') | |
|
310 | ||
|
311 | # Coverage XML report | |
|
312 | elif coverage_out == 'xml': | |
|
313 | cov.xml_report(outfile='ipy_coverage.xml') | |
|
314 | ||
|
315 | if failed: | |
|
263 | 316 | # Ensure that our exit code indicates failure |
|
264 | 317 | sys.exit(1) |
|
265 | 318 | |
@@ -278,14 +331,20 b' def main():' | |||
|
278 | 331 | help='Run test sections in parallel.') |
|
279 | 332 | parser.add_argument('--xunit', action='store_true', |
|
280 | 333 | help='Produce Xunit XML results') |
|
281 |
parser.add_argument('--coverage', |
|
|
282 |
help=' |
|
|
334 | parser.add_argument('--coverage', nargs='?', const=True, default=False, | |
|
335 | help="Measure test coverage. Specify 'html' or " | |
|
336 | "'xml' to get reports.") | |
|
283 | 337 | |
|
284 | 338 | options = parser.parse_args() |
|
285 | 339 | |
|
340 | try: | |
|
341 | jobs = int(options.fast) | |
|
342 | except TypeError: | |
|
343 | jobs = options.fast | |
|
344 | ||
|
286 | 345 | # This starts subprocesses |
|
287 |
run_iptestall(inc_slow=options.all, jobs= |
|
|
288 | xunit=options.xunit, coverage=options.coverage) | |
|
346 | run_iptestall(inc_slow=options.all, jobs=jobs, | |
|
347 | xunit_out=options.xunit, coverage_out=options.coverage) | |
|
289 | 348 | |
|
290 | 349 | |
|
291 | 350 | if __name__ == '__main__': |
General Comments 0
You need to be logged in to leave comments.
Login now