Show More
@@ -34,7 +34,6 b' from nose.core import TestProgram' | |||
|
34 | 34 | from nose.plugins import Plugin |
|
35 | 35 | from nose.util import safe_str |
|
36 | 36 | |
|
37 | from IPython.utils.process import is_cmd_found | |
|
38 | 37 | from IPython.utils.py3compat import bytes_to_str |
|
39 | 38 | from IPython.utils.importstring import import_item |
|
40 | 39 | from IPython.testing.plugin.ipdoctest import IPythonDoctest |
@@ -132,9 +131,6 b" have['requests'] = test_for('requests')" | |||
|
132 | 131 | have['sphinx'] = test_for('sphinx') |
|
133 | 132 | have['jsonschema'] = test_for('jsonschema') |
|
134 | 133 | have['terminado'] = test_for('terminado') |
|
135 | have['casperjs'] = is_cmd_found('casperjs') | |
|
136 | have['phantomjs'] = is_cmd_found('phantomjs') | |
|
137 | have['slimerjs'] = is_cmd_found('slimerjs') | |
|
138 | 134 | |
|
139 | 135 | min_zmq = (13,) |
|
140 | 136 |
@@ -247,218 +247,15 b' class PyTestController(TestController):' | |||
|
247 | 247 | super(PyTestController, self).launch(buffer_output=buffer_output) |
|
248 | 248 | |
|
249 | 249 | |
|
250 | js_prefix = 'js/' | |
|
251 | ||
|
252 | def get_js_test_dir(): | |
|
253 | import IPython.html.tests as t | |
|
254 | return os.path.join(os.path.dirname(t.__file__), '') | |
|
255 | ||
|
256 | def all_js_groups(): | |
|
257 | import glob | |
|
258 | test_dir = get_js_test_dir() | |
|
259 | all_subdirs = glob.glob(test_dir + '[!_]*/') | |
|
260 | return [js_prefix+os.path.relpath(x, test_dir) for x in all_subdirs] | |
|
261 | ||
|
262 | class JSController(TestController): | |
|
263 | """Run CasperJS tests """ | |
|
264 | ||
|
265 | requirements = ['zmq', 'tornado', 'jinja2', 'casperjs', 'sqlite3', | |
|
266 | 'jsonschema'] | |
|
267 | ||
|
268 | def __init__(self, section, xunit=True, engine='phantomjs', url=None): | |
|
269 | """Create new test runner.""" | |
|
270 | TestController.__init__(self) | |
|
271 | self.engine = engine | |
|
272 | self.section = section | |
|
273 | self.xunit = xunit | |
|
274 | self.url = url | |
|
275 | self.slimer_failure = re.compile('^FAIL.*', flags=re.MULTILINE) | |
|
276 | js_test_dir = get_js_test_dir() | |
|
277 | includes = '--includes=' + os.path.join(js_test_dir,'util.js') | |
|
278 | test_cases = os.path.join(js_test_dir, self.section[len(js_prefix):]) | |
|
279 | self.cmd = ['casperjs', 'test', includes, test_cases, '--engine=%s' % self.engine] | |
|
280 | ||
|
281 | def setup(self): | |
|
282 | self.ipydir = TemporaryDirectory() | |
|
283 | self.nbdir = TemporaryDirectory() | |
|
284 | self.dirs.append(self.ipydir) | |
|
285 | self.dirs.append(self.nbdir) | |
|
286 | os.makedirs(os.path.join(self.nbdir.name, os.path.join(u'sub βir1', u'sub βir 1a'))) | |
|
287 | os.makedirs(os.path.join(self.nbdir.name, os.path.join(u'sub βir2', u'sub βir 1b'))) | |
|
288 | ||
|
289 | if self.xunit: | |
|
290 | self.add_xunit() | |
|
291 | ||
|
292 | # If a url was specified, use that for the testing. | |
|
293 | if self.url: | |
|
294 | try: | |
|
295 | alive = requests.get(self.url).status_code == 200 | |
|
296 | except: | |
|
297 | alive = False | |
|
298 | ||
|
299 | if alive: | |
|
300 | self.cmd.append("--url=%s" % self.url) | |
|
301 | else: | |
|
302 | raise Exception('Could not reach "%s".' % self.url) | |
|
303 | else: | |
|
304 | # start the ipython notebook, so we get the port number | |
|
305 | self.server_port = 0 | |
|
306 | self._init_server() | |
|
307 | if self.server_port: | |
|
308 | self.cmd.append("--port=%i" % self.server_port) | |
|
309 | else: | |
|
310 | # don't launch tests if the server didn't start | |
|
311 | self.cmd = [sys.executable, '-c', 'raise SystemExit(1)'] | |
|
312 | ||
|
313 | def add_xunit(self): | |
|
314 | xunit_file = os.path.abspath(self.section.replace('/','.') + '.xunit.xml') | |
|
315 | self.cmd.append('--xunit=%s' % xunit_file) | |
|
316 | ||
|
317 | def launch(self, buffer_output): | |
|
318 | # If the engine is SlimerJS, we need to buffer the output because | |
|
319 | # SlimerJS does not support exit codes, so CasperJS always returns 0. | |
|
320 | if self.engine == 'slimerjs' and not buffer_output: | |
|
321 | return super(JSController, self).launch(capture_output=True) | |
|
322 | ||
|
323 | else: | |
|
324 | return super(JSController, self).launch(buffer_output=buffer_output) | |
|
325 | ||
|
326 | def wait(self, *pargs, **kwargs): | |
|
327 | """Wait for the JSController to finish""" | |
|
328 | ret = super(JSController, self).wait(*pargs, **kwargs) | |
|
329 | # If this is a SlimerJS controller, check the captured stdout for | |
|
330 | # errors. Otherwise, just return the return code. | |
|
331 | if self.engine == 'slimerjs': | |
|
332 | stdout = bytes_to_str(self.stdout) | |
|
333 | if ret != 0: | |
|
334 | # This could still happen e.g. if it's stopped by SIGINT | |
|
335 | return ret | |
|
336 | return bool(self.slimer_failure.search(strip_ansi(stdout))) | |
|
337 | else: | |
|
338 | return ret | |
|
339 | ||
|
340 | def print_extra_info(self): | |
|
341 | print("Running tests with notebook directory %r" % self.nbdir.name) | |
|
342 | ||
|
343 | @property | |
|
344 | def will_run(self): | |
|
345 | should_run = all(have[a] for a in self.requirements + [self.engine]) | |
|
346 | return should_run | |
|
347 | ||
|
348 | def _init_server(self): | |
|
349 | "Start the notebook server in a separate process" | |
|
350 | self.server_command = command = [sys.executable, | |
|
351 | '-m', 'IPython.html', | |
|
352 | '--no-browser', | |
|
353 | '--ipython-dir', self.ipydir.name, | |
|
354 | '--notebook-dir', self.nbdir.name, | |
|
355 | ] | |
|
356 | # ipc doesn't work on Windows, and darwin has crazy-long temp paths, | |
|
357 | # which run afoul of ipc's maximum path length. | |
|
358 | if sys.platform.startswith('linux'): | |
|
359 | command.append('--KernelManager.transport=ipc') | |
|
360 | self.stream_capturer = c = StreamCapturer() | |
|
361 | c.start() | |
|
362 | env = os.environ.copy() | |
|
363 | if self.engine == 'phantomjs': | |
|
364 | env['IPYTHON_ALLOW_DRAFT_WEBSOCKETS_FOR_PHANTOMJS'] = '1' | |
|
365 | self.server = subprocess.Popen(command, | |
|
366 | stdout=c.writefd, | |
|
367 | stderr=subprocess.STDOUT, | |
|
368 | cwd=self.nbdir.name, | |
|
369 | env=env, | |
|
370 | ) | |
|
371 | self.server_info_file = os.path.join(self.ipydir.name, | |
|
372 | 'profile_default', 'security', 'nbserver-%i.json' % self.server.pid | |
|
373 | ) | |
|
374 | self._wait_for_server() | |
|
375 | ||
|
376 | def _wait_for_server(self): | |
|
377 | """Wait 30 seconds for the notebook server to start""" | |
|
378 | for i in range(300): | |
|
379 | if self.server.poll() is not None: | |
|
380 | return self._failed_to_start() | |
|
381 | if os.path.exists(self.server_info_file): | |
|
382 | try: | |
|
383 | self._load_server_info() | |
|
384 | except ValueError: | |
|
385 | # If the server is halfway through writing the file, we may | |
|
386 | # get invalid JSON; it should be ready next iteration. | |
|
387 | pass | |
|
388 | else: | |
|
389 | return | |
|
390 | time.sleep(0.1) | |
|
391 | print("Notebook server-info file never arrived: %s" % self.server_info_file, | |
|
392 | file=sys.stderr | |
|
393 | ) | |
|
394 | ||
|
395 | def _failed_to_start(self): | |
|
396 | """Notebook server exited prematurely""" | |
|
397 | captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace') | |
|
398 | print("Notebook failed to start: ", file=sys.stderr) | |
|
399 | print(self.server_command) | |
|
400 | print(captured, file=sys.stderr) | |
|
401 | ||
|
402 | def _load_server_info(self): | |
|
403 | """Notebook server started, load connection info from JSON""" | |
|
404 | with open(self.server_info_file) as f: | |
|
405 | info = json.load(f) | |
|
406 | self.server_port = info['port'] | |
|
407 | ||
|
408 | def cleanup(self): | |
|
409 | if hasattr(self, 'server'): | |
|
410 | try: | |
|
411 | self.server.terminate() | |
|
412 | except OSError: | |
|
413 | # already dead | |
|
414 | pass | |
|
415 | # wait 10s for the server to shutdown | |
|
416 | try: | |
|
417 | popen_wait(self.server, NOTEBOOK_SHUTDOWN_TIMEOUT) | |
|
418 | except TimeoutExpired: | |
|
419 | # server didn't terminate, kill it | |
|
420 | try: | |
|
421 | print("Failed to terminate notebook server, killing it.", | |
|
422 | file=sys.stderr | |
|
423 | ) | |
|
424 | self.server.kill() | |
|
425 | except OSError: | |
|
426 | # already dead | |
|
427 | pass | |
|
428 | # wait another 10s | |
|
429 | try: | |
|
430 | popen_wait(self.server, NOTEBOOK_SHUTDOWN_TIMEOUT) | |
|
431 | except TimeoutExpired: | |
|
432 | print("Notebook server still running (%s)" % self.server_info_file, | |
|
433 | file=sys.stderr | |
|
434 | ) | |
|
435 | ||
|
436 | self.stream_capturer.halt() | |
|
437 | TestController.cleanup(self) | |
|
438 | ||
|
439 | ||
|
440 | 250 | def prepare_controllers(options): |
|
441 | 251 | """Returns two lists of TestController instances, those to run, and those |
|
442 | 252 | not to run.""" |
|
443 | 253 | testgroups = options.testgroups |
|
444 | if testgroups: | |
|
445 | if 'js' in testgroups: | |
|
446 | js_testgroups = all_js_groups() | |
|
447 | else: | |
|
448 | js_testgroups = [g for g in testgroups if g.startswith(js_prefix)] | |
|
449 | py_testgroups = [g for g in testgroups if not g.startswith('js')] | |
|
450 | else: | |
|
451 | py_testgroups = py_test_group_names | |
|
452 | if not options.all: | |
|
453 | js_testgroups = [] | |
|
454 | else: | |
|
455 | js_testgroups = all_js_groups() | |
|
254 | if not testgroups: | |
|
255 | testgroups = py_test_group_names | |
|
456 | 256 | |
|
457 | engine = 'slimerjs' if options.slimerjs else 'phantomjs' | |
|
458 | c_js = [JSController(name, xunit=options.xunit, engine=engine, url=options.url) for name in js_testgroups] | |
|
459 | c_py = [PyTestController(name, options) for name in py_testgroups] | |
|
257 | controllers = [PyTestController(name, options) for name in testgroups] | |
|
460 | 258 | |
|
461 | controllers = c_py + c_js | |
|
462 | 259 | to_run = [c for c in controllers if c.will_run] |
|
463 | 260 | not_run = [c for c in controllers if not c.will_run] |
|
464 | 261 | return to_run, not_run |
@@ -559,9 +356,6 b' def run_iptestall(options):' | |||
|
559 | 356 | inc_slow : bool |
|
560 | 357 | Include slow tests. By default, these tests aren't run. |
|
561 | 358 | |
|
562 | slimerjs : bool | |
|
563 | Use slimerjs if it's installed instead of phantomjs for casperjs tests. | |
|
564 | ||
|
565 | 359 | url : unicode |
|
566 | 360 | Address:port to use when running the JS tests. |
|
567 | 361 | |
@@ -696,8 +490,6 b" argparser.add_argument('testgroups', nargs='*'," | |||
|
696 | 490 | 'all tests.') |
|
697 | 491 | argparser.add_argument('--all', action='store_true', |
|
698 | 492 | help='Include slow tests not run by default.') |
|
699 | argparser.add_argument('--slimerjs', action='store_true', | |
|
700 | help="Use slimerjs if it's installed instead of phantomjs for casperjs tests.") | |
|
701 | 493 | argparser.add_argument('--url', help="URL to use for the JS tests.") |
|
702 | 494 | argparser.add_argument('-j', '--fast', nargs='?', const=None, default=1, type=int, |
|
703 | 495 | help='Run test sections in parallel. This starts as many ' |
General Comments 0
You need to be logged in to leave comments.
Login now