"""Base class for notebook tests.""" import sys import time import requests from contextlib import contextmanager from subprocess import Popen, STDOUT from unittest import TestCase import nose from IPython.utils.tempdir import TemporaryDirectory MAX_WAITTIME = 30 # seconds to wait for notebook server to start POLL_INTERVAL = 0.1 # time between attempts class NotebookTestBase(TestCase): """A base class for tests that need a running notebook. This creates an empty profile in a temp ipython_dir and then starts the notebook server with a separate temp notebook_dir. """ port = 12341 @classmethod def wait_until_alive(cls): """Wait for the server to be alive""" url = 'http://localhost:%i/api/notebooks' % cls.port for _ in range(int(MAX_WAITTIME/POLL_INTERVAL)): try: requests.get(url) except requests.exceptions.ConnectionError: if cls.notebook.poll() is not None: raise RuntimeError("The notebook server exited with status %s" \ % cls.notebook.poll()) time.sleep(POLL_INTERVAL) else: return raise TimeoutError("The notebook server didn't start up correctly.") @classmethod def wait_until_dead(cls): """Wait for the server process to terminate after shutdown""" for _ in range(int(MAX_WAITTIME/POLL_INTERVAL)): if cls.notebook.poll() is not None: return time.sleep(POLL_INTERVAL) raise TimeoutError("Undead notebook server") @classmethod def setup_class(cls): cls.ipython_dir = TemporaryDirectory() cls.notebook_dir = TemporaryDirectory() notebook_args = [ sys.executable, '-c', 'from IPython.html.notebookapp import launch_new_instance; launch_new_instance()', '--port=%d' % cls.port, '--port-retries=0', # Don't try any other ports '--no-browser', '--ipython-dir=%s' % cls.ipython_dir.name, '--notebook-dir=%s' % cls.notebook_dir.name, ] cls.notebook = Popen(notebook_args, stdout=nose.iptest_stdstreams_fileno(), stderr=STDOUT, ) cls.wait_until_alive() @classmethod def teardown_class(cls): cls.notebook.terminate() cls.ipython_dir.cleanup() cls.notebook_dir.cleanup() cls.wait_until_dead() @classmethod def base_url(cls): return 'http://localhost:%i/' % cls.port @contextmanager def assert_http_error(status, msg=None): try: yield except requests.HTTPError as e: real_status = e.response.status_code assert real_status == status, \ "Expected status %d, got %d" % (real_status, status) if msg: assert msg in str(e), e else: assert False, "Expected HTTP error status"