diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 857da2924f..4564890ee1 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -52,9 +52,12 @@ jobs: flag-name: run-${{ matrix.os }}-${{matrix.python-version}} parallel: true github-token : ${{ secrets.GITHUB_TOKEN}} + finish: needs: test runs-on: ubuntu-latest + strategy: + max-parallel: 2 steps: - name: Coveralls Finished env: @@ -63,4 +66,12 @@ jobs: with: parallel-finished: true github-token : ${{ secrets.GITHUB_TOKEN }} + - name: Set up Python 3.8 for notebook + uses: actions/setup-python@v2 + with: + python-version: 3.8 + - name: Test with notebook + run: | + pip install -r requirements-test.txt + pytest test_notebooks/notebook_tests.py diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 5f37c5c2c3..918f16e851 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -5,6 +5,7 @@ on: # branches to consider in the event; optional, defaults to all branches: - mainline + - WHY-2304_notebook_tests jobs: update_release_draft: @@ -13,4 +14,4 @@ jobs: # Drafts your next Release notes as Pull Requests are merged into "mainline" - uses: release-drafter/release-drafter@v5 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/requirements-test.txt b/requirements-test.txt index c2a2a3724b..467b6a7532 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -31,3 +31,4 @@ xlrd==2.0.1 openpyxl==3.0.6 smart-open==4.1.2 tqdm==4.54.0 +nbconvert==6.0.7 \ No newline at end of file diff --git a/test_notebooks/__init__.py b/test_notebooks/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test_notebooks/conftest.py b/test_notebooks/conftest.py new file mode 100644 index 0000000000..b3afe61860 --- /dev/null +++ b/test_notebooks/conftest.py @@ -0,0 +1,13 @@ +import os +import sys +import pytest +_MY_DIR = os.path.realpath(os.path.dirname(__file__)) +# Allow import of the test utilities packages + + +@pytest.fixture(scope="session") +def notebooks_path(): + + notebook_path = os.path.join( + _MY_DIR, os.pardir, os.pardir, "notebooks") + return os.path.abs(notebook_path) diff --git a/test_notebooks/notebook_tests.py b/test_notebooks/notebook_tests.py new file mode 100644 index 0000000000..6e9ffd8cf8 --- /dev/null +++ b/test_notebooks/notebook_tests.py @@ -0,0 +1,51 @@ +from nbconvert.preprocessors import CellExecutionError +from nbconvert.preprocessors import ExecutePreprocessor +import nbformat +import subprocess +import os +import pytest + +TEST_DIR = os.path.dirname(os.path.abspath(__file__)) +PARENT_DIR = os.path.join(TEST_DIR, os.pardir, "..") + + +def process_notebook(notebook_filename, html_directory="notebook-html"): + """Checks if an IPython notebook runs without error from start to finish. If so, writes the notebook to HTML (with outputs) and overwrites the .ipynb file (without outputs).""" + + with open(notebook_filename) as f: + nb = nbformat.read(f, as_version=4) + + ep = ExecutePreprocessor(timeout=600, kernel_name="python3", allow_errors=True) + + try: + # Check that the notebook runs + ep.preprocess(nb, {"metadata": {"path": ""}}) + except CellExecutionError: + raise + + print(f"Successfully executed {notebook_filename}") + return + + +def test_all_notebooks(remove_fail_test=True): + """ + Runs `process_notebook` on all notebooks in the git repository. + """ + + # Get all files included in the git repository + git_files = subprocess.check_output("git ls-tree --full-tree --name-only -r HEAD", shell=True).decode("utf-8").splitlines() + + # Get just the notebooks from the git files + notebooks = [fn for fn in git_files if fn.endswith(".ipynb")] + print(notebooks) + + # Test each notebook + for notebook in notebooks: + print("Testing", notebook) + process_notebook(os.path.join(PARENT_DIR, notebook)) + + return + + +if __name__ == "__main__": + test_all_notebooks()