diff --git a/Makefile b/Makefile index 358e456b..239a63f0 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,16 @@ build-test-all: $(foreach I,$(ALL_IMAGES),arch_patch/$(I) build/$(I) test/$(I) ) check-outdated/%: ## check the outdated conda packages in a stack and produce a report (experimental) @TEST_IMAGE="$(OWNER)/$(notdir $@)" pytest test/test_outdated.py +cont-clean-all: cont-stop-all cont-rm-all ## clean all containers (stop + rm) + +cont-stop-all: ## stop all containers + @echo "Stopping all containers ..." + -docker stop -t0 $(shell docker ps -a -q) 2> /dev/null + +cont-rm-all: ## remove all containers + @echo "Removing all containers ..." + -docker rm --force $(shell docker ps -a -q) 2> /dev/null + dev/%: ARGS?= dev/%: DARGS?= dev/%: PORT?=8888 @@ -61,6 +71,20 @@ dev/%: ## run a foreground container for a stack dev-env: ## install libraries required to build docs and run tests pip install -r requirements-dev.txt +img-clean: img-rm-dang img-rm ## clean dangling and jupyter images + +img-list: ## list jupyter images + @echo "Listing $(OWNER) images ..." + docker images "$(OWNER)/*" + +img-rm: ## remove jupyter images + @echo "Removing $(OWNER) images ..." + -docker rmi --force $(shell docker images --quiet "$(OWNER)/*") 2> /dev/null + +img-rm-dang: ## remove dangling images (tagged None) + @echo "Removing dangling images ..." + -docker rmi --force $(shell docker images -f "dangling=true" -q) 2> /dev/null + docs: ## build HTML documentation make -C docs html @@ -71,11 +95,17 @@ n-docs-diff: ## number of docs/ files changed since branch from master n-other-diff: ## number of files outside docs/ changed since branch from master @git diff --name-only $(DIFF_RANGE) -- ':!docs/' | wc -l | awk '{print $$1}' -run/%: ## run a bash in interactive mode in a stack - docker run -it --rm $(OWNER)/$(notdir $@) $(SHELL) +pull/%: DARGS?= +pull/%: ## pull a jupyter image + docker pull $(DARGS) $(OWNER)/$(notdir $@) +run/%: DARGS?= +run/%: ## run a bash in interactive mode in a stack + docker run -it --rm $(DARGS) $(OWNER)/$(notdir $@) $(SHELL) + +run-sudo/%: DARGS?= run-sudo/%: ## run a bash in interactive mode as root in a stack - docker run -it --rm -u root $(OWNER)/$(notdir $@) $(SHELL) + docker run -it --rm -u root $(DARGS) $(OWNER)/$(notdir $@) $(SHELL) tx-en: ## rebuild en locale strings and push to master (req: GH_TOKEN) @git config --global user.email "travis@travis-ci.org" diff --git a/minimal-notebook/test/test_nbconvert.py b/minimal-notebook/test/test_nbconvert.py index 653deae9..5dc61f0d 100644 --- a/minimal-notebook/test/test_nbconvert.py +++ b/minimal-notebook/test/test_nbconvert.py @@ -16,7 +16,7 @@ def test_nbconvert(container, format): cont_data_dir = "/home/jovyan/data" test_file = "notebook1" output_dir = "/tmp" - LOGGER.info(f"Converting example notebook to {format.upper()} ...") + LOGGER.info(f"Test that an example notebook can be converted to {format.upper()} ...") command = f"jupyter nbconvert {cont_data_dir}/{test_file}.ipynb --output-dir {output_dir} --to {format}" c = container.run( volumes={host_data_dir: {"bind": cont_data_dir, "mode": "ro"}}, @@ -24,8 +24,8 @@ def test_nbconvert(container, format): command=["start.sh", "bash", "-c", command], ) rv = c.wait(timeout=30) - assert rv == 0 or rv["StatusCode"] == 0 + assert rv == 0 or rv["StatusCode"] == 0, f"Command {command} failed" logs = c.logs(stdout=True).decode("utf-8") LOGGER.debug(logs) - assert f"{output_dir}/{test_file}.{format}" in logs - + expected_file = f"{output_dir}/{test_file}.{format}" + assert expected_file in logs, f"Expected file {expected_file} not generated" diff --git a/scipy-notebook/test/data/matplotlib_1.py b/scipy-notebook/test/data/matplotlib_1.py new file mode 100644 index 00000000..9b04e5de --- /dev/null +++ b/scipy-notebook/test/data/matplotlib_1.py @@ -0,0 +1,24 @@ +# Matplotlit: Create a simple plot example. +# Refs: https://matplotlib.org/3.1.1/gallery/lines_bars_and_markers/simple_plot.html + +# Optional test with [Matplotlib Jupyter Integration](https://github.com/matplotlib/ipympl) +# %matplotlib widget +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import os + +# Data for plotting +t = np.arange(0.0, 2.0, 0.01) +s = 1 + np.sin(2 * np.pi * t) + +fig, ax = plt.subplots() +ax.plot(t, s) + +ax.set(xlabel='time (s)', ylabel='voltage (mV)', + title='About as simple as it gets, folks') +ax.grid() +# Note that the test can be run headless by checking if an image is produced +file_path = os.path.join("/tmp", "test.png") +fig.savefig(file_path) +print(f"File {file_path} saved") \ No newline at end of file diff --git a/scipy-notebook/test/test_matplotlib.py b/scipy-notebook/test/test_matplotlib.py new file mode 100644 index 00000000..ed5a319d --- /dev/null +++ b/scipy-notebook/test/test_matplotlib.py @@ -0,0 +1,35 @@ +# Copyright (c) Jupyter Development Team. +# Distributed under the terms of the Modified BSD License. + +import logging + +import pytest +import os + +LOGGER = logging.getLogger(__name__) + + +def test_matplotlib(container): + """Test that matplotlib is able to plot a graph and write it as an image""" + host_data_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data") + cont_data_dir = "/home/jovyan/data" + test_file = "matplotlib_1.py" + output_dir = "/tmp" + LOGGER.info(f"Test that matplotlib is able to plot a graph and write it as an image ...") + command = "sleep infinity" + running_container = container.run( + volumes={host_data_dir: {"bind": cont_data_dir, "mode": "ro"}}, + tty=True, + command=["start.sh", "bash", "-c", command], + ) + command = f"python {cont_data_dir}/{test_file}" + cmd = running_container.exec_run(command) + assert cmd.exit_code == 0, f"Command {command} failed" + LOGGER.debug(cmd.output.decode("utf-8")) + # Checking if the file is generated + # https://stackoverflow.com/a/15895594/4413446 + expected_file = f"{output_dir}/test.png" + command = f"test -s {expected_file}" + cmd = running_container.exec_run(command) + assert cmd.exit_code == 0, f"Command {command} failed" + LOGGER.debug(cmd.output.decode("utf-8")) diff --git a/scipy-notebook/test/test_pandas.py b/scipy-notebook/test/test_pandas.py index f054963a..0da7aaf1 100644 --- a/scipy-notebook/test/test_pandas.py +++ b/scipy-notebook/test/test_pandas.py @@ -21,6 +21,6 @@ def test_pandas(container, name, command): LOGGER.info(f"Testing pandas: {name} ...") c = container.run(tty=True, command=["start.sh", "python", "-c", command]) rv = c.wait(timeout=30) - assert rv == 0 or rv["StatusCode"] == 0 + assert rv == 0 or rv["StatusCode"] == 0, f"Command {command} failed" logs = c.logs(stdout=True).decode("utf-8") LOGGER.debug(logs) diff --git a/tensorflow-notebook/test/test_tensorflow.py b/tensorflow-notebook/test/test_tensorflow.py index 680343c7..f5a6910f 100644 --- a/tensorflow-notebook/test/test_tensorflow.py +++ b/tensorflow-notebook/test/test_tensorflow.py @@ -25,6 +25,6 @@ def test_tensorflow(container, name, command): LOGGER.info(f"Testing tensorflow: {name} ...") c = container.run(tty=True, command=["start.sh", "python", "-c", command]) rv = c.wait(timeout=30) - assert rv == 0 or rv["StatusCode"] == 0 + assert rv == 0 or rv["StatusCode"] == 0, f"Command {command} failed" logs = c.logs(stdout=True).decode("utf-8") LOGGER.debug(logs) diff --git a/test/test_notebook.py b/test/test_notebook.py index 51746fed..548c272d 100644 --- a/test/test_notebook.py +++ b/test/test_notebook.py @@ -1,9 +1,10 @@ # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. + def test_secured_server(container, http_client): """Notebook server should eventually request user login.""" container.run() - resp = http_client.get('http://localhost:8888') + resp = http_client.get("http://localhost:8888") resp.raise_for_status() - assert 'login_submit' in resp.text + assert "login_submit" in resp.text, "User login not requested"