Show More
@@ -43,10 +43,6 b' from nose.plugins import Plugin' | |||||
43 |
|
43 | |||
44 | # Our own imports |
|
44 | # Our own imports | |
45 | from IPython.utils.importstring import import_item |
|
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 | from IPython.testing.plugin.ipdoctest import IPythonDoctest |
|
46 | from IPython.testing.plugin.ipdoctest import IPythonDoctest | |
51 | from IPython.external.decorators import KnownFailure, knownfailureif |
|
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 | def check_exclusions_exist(): |
|
301 | def check_exclusions_exist(): | |
|
302 | from IPython.utils.path import get_ipython_package_dir | |||
|
303 | from IPython.utils.warn import warn | |||
306 | parent = os.path.dirname(get_ipython_package_dir()) |
|
304 | parent = os.path.dirname(get_ipython_package_dir()) | |
307 | for sec in test_sections: |
|
305 | for sec in test_sections: | |
308 | for pattern in sec.exclusions: |
|
306 | for pattern in sec.exclusions: | |
@@ -416,11 +414,12 b' def run_iptest():' | |||||
416 | # objects should, individual shells shouldn't). But for now, this |
|
414 | # objects should, individual shells shouldn't). But for now, this | |
417 | # workaround allows the test suite for the inprocess module to complete. |
|
415 | # workaround allows the test suite for the inprocess module to complete. | |
418 | if section.name != 'kernel.inprocess': |
|
416 | if section.name != 'kernel.inprocess': | |
|
417 | from IPython.testing import globalipapp | |||
419 | globalipapp.start_ipython() |
|
418 | globalipapp.start_ipython() | |
420 |
|
419 | |||
421 | # Now nose can run |
|
420 | # Now nose can run | |
422 | TestProgram(argv=argv, addplugins=plugins) |
|
421 | TestProgram(argv=argv, addplugins=plugins) | |
423 |
|
422 | |||
424 |
|
||||
425 | if __name__ == '__main__': |
|
423 | if __name__ == '__main__': | |
426 | run_iptest() |
|
424 | run_iptest() | |
|
425 |
@@ -21,6 +21,7 b' from __future__ import print_function' | |||||
21 | import argparse |
|
21 | import argparse | |
22 | import multiprocessing.pool |
|
22 | import multiprocessing.pool | |
23 | import os |
|
23 | import os | |
|
24 | import shutil | |||
24 | import signal |
|
25 | import signal | |
25 | import sys |
|
26 | import sys | |
26 | import subprocess |
|
27 | import subprocess | |
@@ -39,6 +40,8 b' class IPTestController(object):' | |||||
39 | section = None |
|
40 | section = None | |
40 | #: list, command line arguments to be executed |
|
41 | #: list, command line arguments to be executed | |
41 | cmd = None |
|
42 | cmd = None | |
|
43 | #: str, Python command to execute in subprocess | |||
|
44 | pycmd = None | |||
42 | #: dict, extra environment variables to set for the subprocess |
|
45 | #: dict, extra environment variables to set for the subprocess | |
43 | env = None |
|
46 | env = None | |
44 | #: list, TemporaryDirectory instances to clear up when the process finishes |
|
47 | #: list, TemporaryDirectory instances to clear up when the process finishes | |
@@ -50,28 +53,38 b' class IPTestController(object):' | |||||
50 | def __init__(self, section): |
|
53 | def __init__(self, section): | |
51 | """Create new test runner.""" |
|
54 | """Create new test runner.""" | |
52 | self.section = section |
|
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 | self.env = {} |
|
59 | self.env = {} | |
55 | self.dirs = [] |
|
60 | self.dirs = [] | |
56 | ipydir = TemporaryDirectory() |
|
61 | ipydir = TemporaryDirectory() | |
57 | self.dirs.append(ipydir) |
|
62 | self.dirs.append(ipydir) | |
58 | self.env['IPYTHONDIR'] = ipydir.name |
|
63 | self.env['IPYTHONDIR'] = ipydir.name | |
59 | workingdir = TemporaryDirectory() |
|
64 | self.workingdir = workingdir = TemporaryDirectory() | |
60 | self.dirs.append(workingdir) |
|
65 | self.dirs.append(workingdir) | |
61 | self.env['IPTEST_WORKING_DIR'] = workingdir.name |
|
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 | def add_xunit(self): |
|
70 | def add_xunit(self): | |
64 | xunit_file = os.path.abspath(self.section + '.xunit.xml') |
|
71 | xunit_file = os.path.abspath(self.section + '.xunit.xml') | |
65 | self.cmd.extend(['--with-xunit', '--xunit-file', xunit_file]) |
|
72 | self.cmd.extend(['--with-xunit', '--xunit-file', xunit_file]) | |
66 |
|
73 | |||
67 |
def add_coverage(self |
|
74 | def add_coverage(self): | |
68 | self.cmd.append('--with-coverage') |
|
75 | coverage_rc = ("[run]\n" | |
69 | for include in test_sections[self.section].includes: |
|
76 | "data_file = {data_file}\n" | |
70 | self.cmd.extend(['--cover-package', include]) |
|
77 | "source =\n" | |
71 | if xml: |
|
78 | " {source}\n" | |
72 | coverage_xml = os.path.abspath(self.section + ".coverage.xml") |
|
79 | ).format(data_file=os.path.abspath('.coverage.'+self.section), | |
73 | self.cmd.extend(['--cover-xml', '--cover-xml-file', coverage_xml]) |
|
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 | def launch(self): |
|
89 | def launch(self): | |
77 | # print('*** ENV:', self.env) # dbg |
|
90 | # print('*** ENV:', self.env) # dbg | |
@@ -80,6 +93,7 b' class IPTestController(object):' | |||||
80 | env.update(self.env) |
|
93 | env.update(self.env) | |
81 | output = subprocess.PIPE if self.buffer_output else None |
|
94 | output = subprocess.PIPE if self.buffer_output else None | |
82 | stdout = subprocess.STDOUT if self.buffer_output else None |
|
95 | stdout = subprocess.STDOUT if self.buffer_output else None | |
|
96 | self.cmd[2] = self.pycmd | |||
83 | self.process = subprocess.Popen(self.cmd, stdout=output, |
|
97 | self.process = subprocess.Popen(self.cmd, stdout=output, | |
84 | stderr=stdout, env=env) |
|
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 | if xunit: |
|
145 | if xunit: | |
132 | controller.add_xunit() |
|
146 | controller.add_xunit() | |
133 | if coverage: |
|
147 | if coverage: | |
134 |
controller.add_coverage( |
|
148 | controller.add_coverage() | |
135 | res.append(controller) |
|
149 | res.append(controller) | |
136 | return res |
|
150 | return res | |
137 |
|
151 | |||
@@ -178,7 +192,7 b' def report():' | |||||
178 |
|
192 | |||
179 | return ''.join(out) |
|
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 | """Run the entire IPython test suite by calling nose and trial. |
|
196 | """Run the entire IPython test suite by calling nose and trial. | |
183 |
|
197 | |||
184 | This function constructs :class:`IPTester` instances for all IPython |
|
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 | if jobs != 1: |
|
214 | if jobs != 1: | |
201 | IPTestController.buffer_output = True |
|
215 | IPTestController.buffer_output = True | |
202 |
|
216 | |||
203 | controllers = test_controllers_to_run(inc_slow=inc_slow, xunit=xunit, |
|
217 | controllers = test_controllers_to_run(inc_slow=inc_slow, xunit=xunit_out, | |
204 | coverage=coverage) |
|
218 | coverage=coverage_out) | |
205 |
|
219 | |||
206 | # Run all test runners, tracking execution time |
|
220 | # Run all test runners, tracking execution time | |
207 | failed = [] |
|
221 | failed = [] | |
@@ -257,9 +271,48 b' def run_iptestall(inc_slow=False, jobs=1, xunit=False, coverage=False):' | |||||
257 | print('-'*40) |
|
271 | print('-'*40) | |
258 | print('Runner failed:', controller.section) |
|
272 | print('Runner failed:', controller.section) | |
259 | print('You may wish to rerun this one individually, with:') |
|
273 | print('You may wish to rerun this one individually, with:') | |
260 | failed_call_args = [py3compat.cast_unicode(x) for x in controller.cmd] |
|
274 | print(' iptest', *controller.cmd[3:]) | |
261 | print(u' '.join(failed_call_args)) |
|
|||
262 | print() |
|
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 | # Ensure that our exit code indicates failure |
|
316 | # Ensure that our exit code indicates failure | |
264 | sys.exit(1) |
|
317 | sys.exit(1) | |
265 |
|
318 | |||
@@ -278,14 +331,20 b' def main():' | |||||
278 | help='Run test sections in parallel.') |
|
331 | help='Run test sections in parallel.') | |
279 | parser.add_argument('--xunit', action='store_true', |
|
332 | parser.add_argument('--xunit', action='store_true', | |
280 | help='Produce Xunit XML results') |
|
333 | help='Produce Xunit XML results') | |
281 |
parser.add_argument('--coverage', |
|
334 | parser.add_argument('--coverage', nargs='?', const=True, default=False, | |
282 |
help=' |
|
335 | help="Measure test coverage. Specify 'html' or " | |
|
336 | "'xml' to get reports.") | |||
283 |
|
337 | |||
284 | options = parser.parse_args() |
|
338 | options = parser.parse_args() | |
285 |
|
339 | |||
|
340 | try: | |||
|
341 | jobs = int(options.fast) | |||
|
342 | except TypeError: | |||
|
343 | jobs = options.fast | |||
|
344 | ||||
286 | # This starts subprocesses |
|
345 | # This starts subprocesses | |
287 |
run_iptestall(inc_slow=options.all, jobs= |
|
346 | run_iptestall(inc_slow=options.all, jobs=jobs, | |
288 | xunit=options.xunit, coverage=options.coverage) |
|
347 | xunit_out=options.xunit, coverage_out=options.coverage) | |
289 |
|
348 | |||
290 |
|
349 | |||
291 | if __name__ == '__main__': |
|
350 | if __name__ == '__main__': |
General Comments 0
You need to be logged in to leave comments.
Login now