Skip to content

Commit

Permalink
🧪 TESTS: Add Windows OS to CI
Browse files Browse the repository at this point in the history
This involved numerous fixes to the code and tests, to use posix strings.
There was also an issue of the tempdir being created on a different drive.
Therefore, a pytest cmdline option was added to set the location of this directory.
Additionally, pip caching was set up on the GitHub CI, and and pytest-timeout config

Co-authored-by: phaustin <https://github.com/phaustin>
Co-authored-by: foster999 <https://github.com/foster999>"
  • Loading branch information
chrisjsewell committed Aug 28, 2020
1 parent 1f0c7a7 commit 6fb0cbe
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 101 deletions.
53 changes: 42 additions & 11 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v1
uses: actions/setup-python@v2
with:
python-version: 3.8
- uses: pre-commit/action@v2.0.0
Expand All @@ -34,12 +34,13 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install wheel
pip install -e .[testing]
- name: Install Headless Chrome dependencies
Expand All @@ -56,7 +57,6 @@ jobs:
texlive-latex-extra \
latexmk
# Tests
- name: Run pytest
run: |
pytest --durations=10 --cov=jupyter_book --cov-report=xml --cov-report=term-missing
Expand Down Expand Up @@ -87,12 +87,13 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install wheel
pip install "sphinx${{ matrix.sphinx }}"
pip install -e .[testing]
Expand All @@ -101,10 +102,33 @@ jobs:
sudo apt-get update
sudo apt-get install -yq $(cat .github/workflows/pyppeteer_reqs.txt)
# Tests
- name: Run pytest
run: pytest --durations=10 -m 'not requires_tex'

windows:

name: Tests on Windows
runs-on: windows-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python 3.7
uses: actions/setup-python@v2
with:
python-version: 3.7
- uses: actions/cache@v2
with:
path: ~\AppData\Local\pip\Cache
key: ${{ runner.os }}-pip-${{ hashFiles('setup.py') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install Python dependencies
run: |
pytest --durations=10 -m 'not requires_tex'
python -m pip install --upgrade pip
pip install wheel
pip install --upgrade-strategy eager -e .[testing]
- name: Run pytest
run: pytest --durations=10 -m 'not requires_chrome and not requires_tex' --jb-tempdir local_path

# Build the book on OSX to make sure that building the docs works there too
build-book-osx:
Expand All @@ -114,13 +138,20 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.7
uses: actions/setup-python@v1
uses: actions/setup-python@v2
with:
python-version: 3.7
- name: Install dependencies
- uses: actions/cache@v2
with:
path: ~\AppData\Local\pip\Cache
key: ${{ runner.os }}-pip-${{ hashFiles('setup.py') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install -e .[sphinx]
pip install wheel
pip install --upgrade-strategy eager -e .[sphinx]
- name: Build the book
run: |
Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ exclude .flake8
exclude tox.ini
exclude codecov.yml
exclude RELEASES.md
exclude conftest.py

include LICENSE
include CHANGELOG.md
Expand Down
40 changes: 40 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""On GH Actions windows-latest, the supplied tmpdir is on a different Drive to the CWD,
and so relative path computations fail.
Therefore, here we allow for the directory to be directly supplied,
via an environmental variable.
"""
import shutil
from pathlib import Path
from uuid import uuid4

import pytest


def pytest_addoption(parser):
"""Define pytest command-line option"""
group = parser.getgroup("jupyter_book")
group.addoption(
"--jb-tempdir",
dest="jb_tempdir",
default=None,
help="Specify a directory in which to create tempdirs",
)


def pytest_report_header(config):
path = "<TEMP>"
if config.getoption("jb_tempdir"):
path = Path(config.getoption("jb_tempdir")).absolute().as_posix()
return [f"JB TEMPDIR: {path}"]


@pytest.fixture()
def temp_with_override(pytestconfig, tmpdir):
if pytestconfig.getoption("jb_tempdir"):
path = Path(pytestconfig.getoption("jb_tempdir")).resolve().absolute()
path = path / str(uuid4())
path.mkdir(parents=True)
yield path
shutil.rmtree(path)
else:
yield Path(tmpdir.dirname) / tmpdir.basename
10 changes: 6 additions & 4 deletions jupyter_book/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,12 +385,14 @@ def find_config_path(path: Path) -> Tuple[Path, bool]:
else:
current_dir = path.parent

root_dir = current_dir.root
while str(current_dir) != root_dir:
config_file = os.path.join(str(current_dir), "_config.yml")
if os.path.isfile(config_file):
if (current_dir / "_config.yml").is_file():
return (current_dir, True)

while current_dir != current_dir.parent:
if (current_dir / "_config.yml").is_file():
return (current_dir, True)
current_dir = current_dir.parent

if not path.is_dir():
return (path.parent, False)
return (path, False)
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"pytest>=3.6,<4",
"pytest-cov",
"pytest-xdist",
"pytest-timeout",
"beautifulsoup4",
"matplotlib",
"pytest-regressions",
Expand Down
22 changes: 9 additions & 13 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,38 @@


@pytest.fixture()
def build_resources(tmpdir):
def build_resources(temp_with_override):
"""Copys ./books and ./books/tocs to a temporary directory and yields the paths
as `pathlib.Path` objects.
"""
src = Path(__file__).parent.resolve().joinpath("books").absolute()
dst = tmpdir.join("books")
dst = temp_with_override / "books"
shutil.copytree(src, dst)
books = Path(dst)
tocs = books / "toc"
yield books, tocs
yield Path(dst), Path(dst) / "toc"
shutil.rmtree(dst)


@pytest.fixture()
def pages(tmpdir):
def pages(temp_with_override):
"""Copys ./pages to a temporary directory and yields the path as a `pathlib.Path`
object.
"""
src = Path(__file__).parent.joinpath("pages").absolute()
dst = tmpdir.join("pages")
dst = temp_with_override / "pages"
shutil.copytree(src, dst)
pages = Path(dst)
yield pages
yield Path(dst)
shutil.rmtree(dst)


@pytest.fixture()
def docs(tmpdir):
def docs(temp_with_override):
"""Copys ../docs to a temporary directory and yields the path as a `pathlib.Path`
object.
"""
src = Path(__file__).parent.parent.joinpath("docs").absolute()
dst = tmpdir.join("docs")
dst = temp_with_override / "docs"
shutil.copytree(src, dst)
docs = Path(dst)
yield docs
yield Path(dst)
shutil.rmtree(dst)


Expand Down
44 changes: 23 additions & 21 deletions tests/test_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ def test_version(cli: CliRunner):
assert "Jupyter Book" in result.output, result.output


def test_create(tmpdir, cli):
book = Path(tmpdir) / "new_book"
result = cli.invoke(commands.create, str(book))
def test_create(temp_with_override: Path, cli):
book = temp_with_override / "new_book"
result = cli.invoke(commands.create, book.as_posix())
assert result.exit_code == 0
assert book.joinpath("_config.yml").exists()
assert len(list(book.iterdir())) == 9
Expand All @@ -31,13 +31,13 @@ def test_validate_yaml():
assert validate_yaml({"title": ""}, raise_on_errors=False) is None


def test_build_from_template(tmpdir, cli):
def test_build_from_template(temp_with_override, cli):
"""Test building the book template and a few test configs."""
# Create the book from the template
book = Path(tmpdir) / "new_book"
_ = cli.invoke(commands.create, str(book))
build_result = cli.invoke(commands.build, str(book))
assert build_result.exit_code == 0
book = temp_with_override / "new_book"
_ = cli.invoke(commands.create, book.as_posix())
build_result = cli.invoke(commands.build, book.as_posix())
assert build_result.exit_code == 0, build_result.output
html = book.joinpath("_build", "html")
assert html.joinpath("index.html").exists()
assert html.joinpath("intro.html").exists()
Expand All @@ -47,9 +47,9 @@ def test_custom_config(cli, build_resources):
"""Test a variety of custom configuration values."""
books, _ = build_resources
config = books.joinpath("config")
result = cli.invoke(commands.build, str(config))
result = cli.invoke(commands.build, config.as_posix())
assert result.exit_code == 0
html = config.joinpath("_build", "html", "index.html").read_text()
html = config.joinpath("_build", "html", "index.html").read_text(encoding="utf8")
soup = BeautifulSoup(html, "html.parser")
assert '<h1 class="site-logo" id="site-title">TEST PROJECT NAME</h1>' in html
assert '<div class="sphinx-tabs docutils container">' in html
Expand All @@ -62,11 +62,12 @@ def test_custom_config(cli, build_resources):


@pytest.mark.parametrize("toc", ["_toc.yml", "_toc_startwithlist.yml"])
def test_toc_builds(cli, build_resources, toc, tmpdir):
def test_toc_builds(cli, build_resources, toc):
"""Test building the book template with several different TOC files."""
books, tocs = build_resources
toc = str(tocs / toc)
result = cli.invoke(commands.build, [str(tocs), "--toc", toc, "-W"])
result = cli.invoke(
commands.build, [tocs.as_posix(), "--toc", (tocs / toc).as_posix(), "-W"]
)
assert result.exit_code == 0


Expand All @@ -79,17 +80,17 @@ def test_toc_rebuild(cli, build_resources):
index_html = tocs.joinpath("_build", "html", "index.html")

# Not using -W because we expect warnings for pages not listed in TOC
result = cli.invoke(commands.build, [str(tocs), "--toc", str(toc)])
html = BeautifulSoup(index_html.read_text(), "html.parser")
result = cli.invoke(commands.build, [tocs.as_posix(), "--toc", toc.as_posix()])
html = BeautifulSoup(index_html.read_text(encoding="utf8"), "html.parser")
tags = html.find_all("a", "reference internal")
assert result.exit_code == 0
assert tags[1].attrs["href"] == "content1.html"
assert tags[2].attrs["href"] == "content2.html"

toc.write_text("- file: index\n- file: content2\n- file: content1\n")
result = cli.invoke(commands.build, [str(tocs), "--toc", str(toc)])
result = cli.invoke(commands.build, [tocs.as_posix(), "--toc", toc.as_posix()])
assert result.exit_code == 0
html = BeautifulSoup(index_html.read_text(), "html.parser")
html = BeautifulSoup(index_html.read_text(encoding="utf8"), "html.parser")
tags = html.find_all("a", "reference internal")
# The rendered TOC should reflect the order in the modified _toc.yml
assert tags[1].attrs["href"] == "content2.html"
Expand All @@ -107,9 +108,10 @@ def test_toc_rebuild(cli, build_resources):
)
def test_corrupt_toc(build_resources, cli, toc, msg):
books, tocs = build_resources
toc = str(tocs / toc)
with pytest.raises(RuntimeError):
result = cli.invoke(commands.build, [str(tocs), "--toc", toc, "-W"])
result = cli.invoke(
commands.build, [tocs.as_posix(), "--toc", (tocs / toc).as_posix(), "-W"]
)
assert result.exit_code == 1
assert msg in result.output
raise result.exception
Expand Down Expand Up @@ -167,7 +169,7 @@ def test_build_page(pages, cli):
assert result.exit_code == 0
assert html.joinpath("single_page.html").exists()
assert not html.joinpath("extra_page.html").exists()
assert 'url=single_page.html" />' in index.read_text()
assert 'url=single_page.html" />' in index.read_text(encoding="utf8")


def test_build_page_nested(build_resources, cli):
Expand All @@ -181,7 +183,7 @@ def test_build_page_nested(build_resources, cli):
assert result.exit_code == 0
assert html.joinpath("markdown.html").exists()
assert not html.joinpath("extra_page.html").exists()
assert 'url=markdown.html" />' in index.read_text()
assert 'url=markdown.html" />' in index.read_text(encoding="utf8")


@pytest.mark.skipif(sphinx.version_info[0] == 2, reason="randomly fails on CI")
Expand Down
Loading

0 comments on commit 6fb0cbe

Please sign in to comment.