mirror of
https://github.com/wassname/jupyter_contrib_nbextensions.git
synced 2026-06-27 16:10:24 +08:00
add basic tests
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Tests for the main app."""
|
||||
|
||||
from __future__ import (
|
||||
absolute_import, division, print_function, unicode_literals,
|
||||
)
|
||||
|
||||
import itertools
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
from unittest import TestCase
|
||||
|
||||
import jupyter_core.paths
|
||||
import nose.tools as nt
|
||||
from jupyter_contrib_core.notebook_compat import nbextensions
|
||||
from jupyter_contrib_core.testing_utils import (
|
||||
get_logger, patch_traitlets_app_logs,
|
||||
)
|
||||
from jupyter_contrib_core.testing_utils.jupyter_env import patch_jupyter_dirs
|
||||
from traitlets.config import Config
|
||||
from traitlets.tests.utils import check_help_all_output, check_help_output
|
||||
|
||||
from jupyter_contrib_nbextensions.application import main as main_app
|
||||
from jupyter_contrib_nbextensions.application import (
|
||||
BaseContribNbextensionsApp, BaseContribNbextensionsInstallApp,
|
||||
ContribNbextensionsApp, InstallContribNbextensionsApp,
|
||||
UninstallContribNbextensionsApp,
|
||||
)
|
||||
|
||||
app_classes = (
|
||||
BaseContribNbextensionsApp, BaseContribNbextensionsInstallApp,
|
||||
ContribNbextensionsApp,
|
||||
InstallContribNbextensionsApp, UninstallContribNbextensionsApp,
|
||||
)
|
||||
|
||||
|
||||
class AppTest(TestCase):
|
||||
"""Tests for the main app."""
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.log = cls.log = get_logger(cls.__name__)
|
||||
cls.log.handlers = []
|
||||
cls.log.propagate = True
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures for each test."""
|
||||
(jupyter_patches, self.jupyter_dirs,
|
||||
remove_jupyter_dirs) = patch_jupyter_dirs()
|
||||
for ptch in jupyter_patches:
|
||||
ptch.start()
|
||||
self.addCleanup(ptch.stop)
|
||||
self.addCleanup(remove_jupyter_dirs)
|
||||
for klass in app_classes:
|
||||
patch_traitlets_app_logs(klass)
|
||||
klass.log_level.default_value = logging.DEBUG
|
||||
|
||||
def _check_install(self, dirs):
|
||||
installed_files = []
|
||||
for tree_dir in dirs.values():
|
||||
in_this_tree = []
|
||||
for root, subdirs, files in os.walk(tree_dir, followlinks=True):
|
||||
in_this_tree.extend([os.path.join(root, f) for f in files])
|
||||
nt.assert_true(
|
||||
in_this_tree,
|
||||
'Install should create file(s) in {}'.format(*dirs.values()))
|
||||
installed_files.extend(in_this_tree)
|
||||
|
||||
return installed_files
|
||||
|
||||
def _check_uninstall(self, dirs, installed_files):
|
||||
# check that nothing remains in the data directory
|
||||
data_installed = [
|
||||
path for path in installed_files
|
||||
if path.startswith(dirs['data']) and os.path.exists(path)]
|
||||
nt.assert_false(
|
||||
data_installed,
|
||||
'Uninstall should remove all data files from {}'.format(
|
||||
dirs['data']))
|
||||
# check the config directory
|
||||
conf_installed = [
|
||||
path for path in installed_files
|
||||
if path.startswith(dirs['conf']) and os.path.exists(path)]
|
||||
for path in conf_installed:
|
||||
with open(path, 'r') as f:
|
||||
conf = Config(json.load(f))
|
||||
confstrip = {}
|
||||
confstrip.update(conf)
|
||||
confstrip.pop('NotebookApp', None)
|
||||
confstrip.pop('version', None)
|
||||
nt.assert_false(confstrip, 'disable should leave config empty.')
|
||||
|
||||
def _get_default_check_kwargs(self, argv, dirs):
|
||||
if argv is None:
|
||||
argv = []
|
||||
if dirs is None:
|
||||
dirs = {
|
||||
'conf': jupyter_core.paths.jupyter_config_dir(),
|
||||
'data': jupyter_core.paths.jupyter_data_dir(),
|
||||
}
|
||||
return argv, dirs
|
||||
|
||||
def _call_main_app(self, argv):
|
||||
main_app(argv=argv)
|
||||
# a bit of a hack to allow initializing a new app instance
|
||||
for klass in app_classes:
|
||||
klass.clear_instance()
|
||||
|
||||
def _check_subproc(self, args):
|
||||
proc = subprocess.Popen(
|
||||
args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
try:
|
||||
output, unused_err = proc.communicate()
|
||||
except:
|
||||
proc.kill()
|
||||
proc.wait()
|
||||
raise
|
||||
print(output.decode()) # this gets it captured by nose
|
||||
retcode = proc.poll()
|
||||
nt.assert_equal(retcode, 0, 'command should exit with code 0')
|
||||
|
||||
def check_app_install(self, argv=None, dirs=None):
|
||||
"""Check files were installed in the correct place."""
|
||||
argv, dirs = self._get_default_check_kwargs(argv, dirs)
|
||||
self._call_main_app(argv=['install'] + argv)
|
||||
installed_files = self._check_install(dirs)
|
||||
self._call_main_app(argv=['uninstall'] + argv)
|
||||
self._check_uninstall(dirs, installed_files)
|
||||
|
||||
def check_cli_install(self, argv=None, dirs=None):
|
||||
argv, dirs = self._get_default_check_kwargs(argv, dirs)
|
||||
args = InstallContribNbextensionsApp.name.split(' ') + argv
|
||||
self._check_subproc(args)
|
||||
installed_files = self._check_install(dirs)
|
||||
args = UninstallContribNbextensionsApp.name.split(' ') + argv
|
||||
self._check_subproc(args)
|
||||
self._check_uninstall(dirs, installed_files)
|
||||
|
||||
def test_00_extra_args(self):
|
||||
"""Check that app complains about extra args."""
|
||||
for subcom in ('install', 'uninstall'):
|
||||
# sys.exit should be called if extra args specified
|
||||
with nt.assert_raises(SystemExit):
|
||||
main_app([subcom, 'arbitrary_extension_name'])
|
||||
for klass in app_classes:
|
||||
klass.clear_instance()
|
||||
|
||||
def test_01_help_output(self):
|
||||
"""Check that app help works."""
|
||||
app_module = 'jupyter_contrib_nbextensions.application'
|
||||
for subcommand in (None, ['install'], ['uninstall']):
|
||||
check_help_output(app_module, subcommand=subcommand)
|
||||
check_help_all_output(app_module, subcommand=subcommand)
|
||||
# sys.exit should be called if empty argv specified
|
||||
with nt.assert_raises(SystemExit):
|
||||
main_app([])
|
||||
|
||||
def test_02_argument_conflict(self):
|
||||
"""Check that install objects to multiple flags."""
|
||||
conflicting_flags = ('--user', '--system', '--sys-prefix')
|
||||
conflicting_flagsets = []
|
||||
for nn in range(2, len(conflicting_flags) + 1):
|
||||
conflicting_flagsets.extend(
|
||||
itertools.combinations(conflicting_flags, nn))
|
||||
for subcommand in ('install', 'uninstall'):
|
||||
for flagset in conflicting_flagsets:
|
||||
self.log.info('testing conflicting flagset {}'.format(flagset))
|
||||
nt.assert_raises(nbextensions.ArgumentConflict,
|
||||
main_app, [subcommand] + list(flagset))
|
||||
|
||||
def test_03_app_install_defaults(self):
|
||||
"""Check that app install works correctly using defaults."""
|
||||
self.check_app_install()
|
||||
|
||||
def test_04_cli_install_defaults(self):
|
||||
"""Check that cli install works correctly using defaults."""
|
||||
self.check_cli_install()
|
||||
|
||||
def test_05_app_install_user(self):
|
||||
"""Check that app install works correctly using --user flag."""
|
||||
self.check_app_install(argv=['--user'])
|
||||
|
||||
def test_06_cli_install_user(self):
|
||||
"""Check that cli install works correctly using --user flag."""
|
||||
self.check_cli_install(argv=['--user'])
|
||||
|
||||
def test_07_app_install_sys_prefix(self):
|
||||
"""Check that app install works correctly using --sys-prefix flag."""
|
||||
self.check_app_install(
|
||||
dirs=self.jupyter_dirs['sys_prefix'], argv=['--sys-prefix'])
|
||||
|
||||
# don't test cli install with --sys-prefix flag, as we can't patch
|
||||
# directories in the subprocess
|
||||
|
||||
def test_08_app_install_system(self):
|
||||
"""Check that app install works correctly using --system flag."""
|
||||
self.check_app_install(
|
||||
dirs=self.jupyter_dirs['system'], argv=['--system'])
|
||||
|
||||
# don't test cli install with --system flag, as we can't patch
|
||||
# directories in the subprocess
|
||||
|
||||
def test_09_app_install_symlink(self):
|
||||
"""Check that app install works correctly using --symlink flag."""
|
||||
self.check_app_install(argv=['--symlink'])
|
||||
|
||||
def test_10_cli_install_symlink(self):
|
||||
"""Check that cli install works correctly using --symlink flag."""
|
||||
self.check_cli_install(argv=['--symlink'])
|
||||
|
||||
def test_11_app_install_nbextensions_dir(self):
|
||||
"""Check that app install works correctly using --nbextensions arg."""
|
||||
self.check_app_install(
|
||||
dirs={'conf': self.jupyter_dirs['system']['conf'],
|
||||
'data': self.jupyter_dirs['custom']['data'], },
|
||||
# we need to set user flag false to avoid ArgumentConflict
|
||||
argv=[
|
||||
'--BaseContribNbextensionsInstallApp.user=False',
|
||||
'--nbextensions={}'.format(
|
||||
os.path.join(
|
||||
self.jupyter_dirs['custom']['data'], 'nbextensions'))])
|
||||
|
||||
# We can't test cli install using nbextensions_dir, since it edits system
|
||||
# config, and we can't patch directories in the subprocess
|
||||
@@ -0,0 +1,158 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Tests for the retiring old IPython-notebook-extensions installations."""
|
||||
|
||||
from __future__ import (
|
||||
absolute_import, division, print_function, unicode_literals,
|
||||
)
|
||||
|
||||
import imp
|
||||
import json
|
||||
import os
|
||||
from unittest import TestCase
|
||||
|
||||
import nose.tools as nt
|
||||
import pip
|
||||
from jupyter_contrib_core.testing_utils import (
|
||||
get_logger, patch_traitlets_app_logs,
|
||||
)
|
||||
from jupyter_contrib_core.testing_utils.jupyter_env import patch_jupyter_dirs
|
||||
|
||||
import jupyter_contrib_nbextensions.application
|
||||
import jupyter_contrib_nbextensions.migrate
|
||||
|
||||
|
||||
def refresh_packages():
|
||||
"""
|
||||
Refresh pip's list of installed packages.
|
||||
|
||||
Rather hackish, taken from https://github.com/pypa/pip/issues/2695
|
||||
"""
|
||||
pip.utils.pkg_resources = imp.reload(pip.utils.pkg_resources)
|
||||
|
||||
|
||||
def get_installed_project_names():
|
||||
"""Return all installed project names as reported by (refreshed) pip."""
|
||||
refresh_packages()
|
||||
return [p.project_name for p in pip.get_installed_distributions()]
|
||||
|
||||
|
||||
class MigrateTest(TestCase):
|
||||
"""Tests for retiring old IPython-notebook-extensions installs."""
|
||||
old_pkg_git_ref = 'master'
|
||||
old_pkg_project_name = 'Python-contrib-nbextensions'
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.log = cls.log = get_logger(cls.__name__)
|
||||
cls.log.handlers = []
|
||||
cls.log.propagate = True
|
||||
|
||||
@classmethod
|
||||
def get_old_pkg_url(cls):
|
||||
return ('https://github.com/ipython-contrib/'
|
||||
'IPython-notebook-extensions/archive/{}.zip').format(
|
||||
cls.old_pkg_git_ref)
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
(jupyter_patches, self.jupyter_dirs,
|
||||
remove_jupyter_dirs) = patch_jupyter_dirs()
|
||||
for ptch in jupyter_patches:
|
||||
ptch.start()
|
||||
self.addCleanup(ptch.stop)
|
||||
self.addCleanup(remove_jupyter_dirs)
|
||||
|
||||
def install_old_pkg(self):
|
||||
old_pkg_project_name = self.old_pkg_project_name
|
||||
# install old repo version
|
||||
pip.main(['install', '-v', self.get_old_pkg_url()])
|
||||
# check pip installed ok
|
||||
nt.assert_in(
|
||||
old_pkg_project_name, get_installed_project_names(),
|
||||
'pip should list {} as installed'.format(old_pkg_project_name))
|
||||
# list all files that got installed
|
||||
installed = {}
|
||||
for sect, dirpath in self.jupyter_dirs['env_vars'].items():
|
||||
installed[sect] = []
|
||||
for root, subdirs, files in os.walk(dirpath):
|
||||
installed[sect].extend([os.path.join(root, f) for f in files])
|
||||
nt.assert_true(installed,
|
||||
'Old install should create files in {}'.format(
|
||||
dirpath))
|
||||
|
||||
return installed
|
||||
|
||||
def check_old_pkg_uninstalled(self, installed):
|
||||
old_pkg_project_name = self.old_pkg_project_name
|
||||
# check pip uninstalled ok
|
||||
nt.assert_not_in(
|
||||
old_pkg_project_name, get_installed_project_names(),
|
||||
'pip should no longer list {} as installed'.format(
|
||||
old_pkg_project_name))
|
||||
# check that nothing remains of the old install
|
||||
remaining = [pp for pp in installed['data'] if os.path.exists(pp)]
|
||||
nt.assert_false(
|
||||
remaining, '{} should remove all data files from {}'.format(
|
||||
'jupyter_contrib_nbextensions.migrate',
|
||||
self.jupyter_dirs['env_vars']['data']))
|
||||
# check that python configs are empty
|
||||
for pp in [pp for pp in installed['conf']
|
||||
if (os.path.exists(pp) and pp.endswith('.py'))]:
|
||||
with open(pp, 'r') as f:
|
||||
contents = f.read()
|
||||
nt.assert_false(
|
||||
contents.strip(),
|
||||
'.py config file {} contains text:\n{}'.format(pp, contents))
|
||||
# check that json configs are essentially empty
|
||||
for pp in [pp for pp in installed['conf']
|
||||
if (os.path.exists(pp) and pp.endswith('.json'))]:
|
||||
with open(pp, 'r') as f:
|
||||
contents = f.read()
|
||||
conf = json.loads(contents)
|
||||
# ignore keys added by jupyter_nbextensions_configurator
|
||||
load_exts = conf.get('load_extensions', {})
|
||||
for req in list(load_exts.keys()):
|
||||
if req.startswith('nbextensions_configurator'):
|
||||
load_exts.pop(req, None)
|
||||
if not load_exts:
|
||||
conf.pop('load_extensions', None)
|
||||
conf.pop('version', None)
|
||||
srvext_to_remove = 'jupyter_nbextensions_configurator'
|
||||
nbapp_sect = conf.get('NotebookApp', {})
|
||||
srvxts_key = 'nbserver_extensions'
|
||||
srvxts = nbapp_sect.get(srvxts_key, {})
|
||||
srvxts.pop(srvext_to_remove, None)
|
||||
if not srvxts:
|
||||
nbapp_sect.pop(srvxts_key, None)
|
||||
srvxts_key = 'server_extensions'
|
||||
srvxts = nbapp_sect.get(srvxts_key, [])
|
||||
while srvext_to_remove in srvxts:
|
||||
srvxts.remove(srvext_to_remove)
|
||||
if not srvxts:
|
||||
nbapp_sect.pop(srvxts_key, None)
|
||||
if not nbapp_sect:
|
||||
conf.pop('NotebookApp', None)
|
||||
nt.assert_false(
|
||||
conf,
|
||||
'config file {} is not empty:\n{}'.format(
|
||||
pp, json.dumps(conf, indent=2, sort_keys=True)))
|
||||
|
||||
def test_01_migrate_main(self):
|
||||
"""Check migrate script removes old install correctly."""
|
||||
installed_files = self.install_old_pkg()
|
||||
# execute the migrate script
|
||||
jupyter_contrib_nbextensions.migrate.main()
|
||||
self.check_old_pkg_uninstalled(installed_files)
|
||||
|
||||
def test_02_migrate_app(self):
|
||||
"""Check migrate application removes old install correctly."""
|
||||
installed_files = self.install_old_pkg()
|
||||
# execute the migrate app
|
||||
klass = jupyter_contrib_nbextensions.application.MigrateContribNbextensionsApp # noqa
|
||||
patch_traitlets_app_logs(klass)
|
||||
jupyter_contrib_nbextensions.application.main(['migrate'])
|
||||
self.check_old_pkg_uninstalled(installed_files)
|
||||
|
||||
def test_03_migrate_blank(self):
|
||||
"""Check migrate can run correctly even without a previous install."""
|
||||
jupyter_contrib_nbextensions.migrate.main()
|
||||
@@ -0,0 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Test the package"""
|
||||
|
||||
from jupyter_contrib_core.testing_utils import raise_on_bad_version
|
||||
|
||||
from jupyter_contrib_nbextensions import __version__
|
||||
|
||||
|
||||
def test_current_version():
|
||||
"""check that version string complies with pep440"""
|
||||
raise_on_bad_version(__version__)
|
||||
Reference in New Issue
Block a user