You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
4.7 KiB
145 lines
4.7 KiB
from binascii import hexlify
|
|
import errno
|
|
import os
|
|
import sys
|
|
from os.path import join as pjoin
|
|
from tempfile import TemporaryDirectory
|
|
from threading import Thread, Event
|
|
from unittest.mock import patch
|
|
|
|
from jupyterlab_server import LabServerApp, LabConfig
|
|
|
|
from ..servertest import ServerTestBase
|
|
from ..server import url_path_join
|
|
|
|
import jupyter_core
|
|
from traitlets.config import Config
|
|
from tornado.ioloop import IOLoop
|
|
|
|
|
|
here = os.path.dirname(__file__)
|
|
|
|
|
|
class LabTestBase(ServerTestBase):
|
|
Application = LabServerApp
|
|
"""The application being tested. Sub-classes should change this."""
|
|
|
|
@classmethod
|
|
def setup_class(cls):
|
|
cls.tmp_dir = TemporaryDirectory()
|
|
|
|
def tmp(*parts):
|
|
path = os.path.join(cls.tmp_dir.name, *parts)
|
|
try:
|
|
os.makedirs(path)
|
|
except OSError as e:
|
|
if e.errno != errno.EEXIST:
|
|
raise
|
|
return path
|
|
|
|
cls.home_dir = tmp('home')
|
|
cls.data_dir = tmp('data')
|
|
cls.config_dir = tmp('config')
|
|
cls.runtime_dir = tmp('runtime')
|
|
cls.lab_dir = tmp('lab')
|
|
cls.app_settings_dir = tmp('appsettings')
|
|
cls.lab_schemas = tmp('labschemas')
|
|
cls.lab_settings = tmp('labsettings')
|
|
cls.lab_workspaces = tmp('labworkspaces')
|
|
cls.env_patch = patch.dict('os.environ', {
|
|
'HOME': cls.home_dir,
|
|
'PYTHONPATH': os.pathsep.join(sys.path),
|
|
'IPYTHONDIR': pjoin(cls.home_dir, '.ipython'),
|
|
'JUPYTER_NO_CONFIG': '1', # needed in the future
|
|
'JUPYTER_CONFIG_DIR': cls.config_dir,
|
|
'JUPYTER_DATA_DIR': cls.data_dir,
|
|
'JUPYTER_RUNTIME_DIR': cls.runtime_dir,
|
|
'JUPYTERLAB_DIR': cls.lab_dir,
|
|
'JUPYTERLAB_SETTINGS_DIR': cls.lab_settings
|
|
})
|
|
cls.env_patch.start()
|
|
cls.lab_config = LabConfig(
|
|
app_settings_dir=cls.app_settings_dir,
|
|
schemas_dir=cls.lab_schemas,
|
|
user_settings_dir=cls.lab_settings,
|
|
workspaces_dir=cls.lab_workspaces)
|
|
cls.notebook_dir = tmp('notebooks')
|
|
cls.path_patch = patch.multiple(
|
|
jupyter_core.paths,
|
|
SYSTEM_JUPYTER_PATH=[tmp('share', 'jupyter')],
|
|
ENV_JUPYTER_PATH=[tmp('env', 'share', 'jupyter')],
|
|
SYSTEM_CONFIG_PATH=[tmp('etc', 'jupyter')],
|
|
ENV_CONFIG_PATH=[tmp('env', 'etc', 'jupyter')],
|
|
)
|
|
cls.path_patch.start()
|
|
|
|
cls.config = cls.config or Config()
|
|
cls.config.NotebookNotary.db_file = ':memory:'
|
|
|
|
cls.token = hexlify(os.urandom(4)).decode('ascii')
|
|
|
|
started = Event()
|
|
|
|
def start_thread():
|
|
if 'asyncio' in sys.modules:
|
|
import asyncio
|
|
asyncio.set_event_loop(asyncio.new_event_loop())
|
|
app = cls.notebook = cls.Application(
|
|
app_dir=cls.lab_dir,
|
|
port=cls.port,
|
|
port_retries=0,
|
|
open_browser=False,
|
|
config_dir=cls.config_dir,
|
|
data_dir=cls.data_dir,
|
|
runtime_dir=cls.runtime_dir,
|
|
notebook_dir=cls.notebook_dir,
|
|
base_url=cls.url_prefix,
|
|
config=cls.config,
|
|
allow_root=True,
|
|
token=cls.token,
|
|
lab_config=cls.lab_config
|
|
)
|
|
# don't register signal handler during tests
|
|
app.init_signal = lambda: None
|
|
# clear log handlers and propagate to root for nose to capture it
|
|
# needs to be redone after initialize, which reconfigures logging
|
|
app.log.propagate = True
|
|
app.log.handlers = []
|
|
app.initialize(argv=[])
|
|
app.log.propagate = True
|
|
app.log.handlers = []
|
|
loop = IOLoop.current()
|
|
loop.add_callback(started.set)
|
|
try:
|
|
app.start()
|
|
finally:
|
|
# set the event, so failure to start doesn't cause a hang
|
|
started.set()
|
|
app.session_manager.close()
|
|
cls.notebook_thread = Thread(target=start_thread)
|
|
cls.notebook_thread.daemon = True
|
|
cls.notebook_thread.start()
|
|
started.wait()
|
|
cls.wait_until_alive()
|
|
|
|
|
|
class APITester(object):
|
|
"""Wrapper for REST API requests"""
|
|
url = '/'
|
|
|
|
def __init__(self, request):
|
|
self.request = request
|
|
|
|
def _req(self, verb, path, body=None):
|
|
response = self.request(verb,
|
|
url_path_join(self.url, path), data=body)
|
|
|
|
if 400 <= response.status_code < 600:
|
|
try:
|
|
response.reason = response.json()['message']
|
|
except Exception:
|
|
pass
|
|
response.raise_for_status()
|
|
|
|
return response
|