diff --git a/.github/workflows/test_linux.yml b/.github/workflows/test_linux.yml index 35266d9..64e58a7 100644 --- a/.github/workflows/test_linux.yml +++ b/.github/workflows/test_linux.yml @@ -5,7 +5,7 @@ name: Python package on: push: - branches: [ "main", "feature-github-actions" ] + branches: [ "main", "test-tar-from-tex" ] pull_request: branches: [ "main" ] @@ -30,6 +30,11 @@ jobs: python -m pip install flake8 pytest python -m pip install hatch if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Install TeX Live + uses: zauguin/install-texlive@v3 + with: + packages: > + latex-bin latexmk scheme-basic - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names @@ -38,4 +43,7 @@ jobs: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with hatch run pytest run: | + # TEMPDIR is needed tor tex compilation based tests + export TMPDIR=$(mktemp -d -p .) hatch run pytest -v + rm -fr ${TMPDIR} diff --git a/pyproject.toml b/pyproject.toml index 0779937..9f0ac98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,3 +87,7 @@ exclude_lines = [ addopts = [ "--import-mode=importlib", ] + +markers = [ + "slow: marks tests as slow (deselect with '-m \"not slow\"')", +] diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..c0ff71f --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,39 @@ +# vim: set ai et ts=4 sw=4 tw=80: +# SPDX-FileCopyrightText: 2024-present Atri Bhattacharya +# +# SPDX-License-Identifier: MIT + +"""Common fixtures""" + +import os +import shutil + +import pytest + +from tartex.tartex import TarTeX + + +@pytest.fixture +def sample_texfile(): + """Pytest fixture: TarTeX with just a tex file for parameter""" + return TarTeX(["some_file.tex"]) + + +# Data copying to tmpdir to allow using with pytest +# Note that relative paths to the data do not work as it is never certain which +# location pytest is run from +# Based on https://stackoverflow.com/a/29631801 +@pytest.fixture +def datadir(tmpdir, request): + ''' + Fixture responsible for searching a folder with the same name of test + module and, if available, moving all contents to a temporary directory so + tests can use them freely. + ''' + filename = request.module.__file__ + test_dir, _ = os.path.splitext(filename) + + if os.path.isdir(test_dir): + shutil.copytree(test_dir, tmpdir, dirs_exist_ok=True) + + return tmpdir diff --git a/tests/test_args.py b/tests/test_args.py index 3964763..68b2f47 100644 --- a/tests/test_args.py +++ b/tests/test_args.py @@ -8,7 +8,7 @@ import pytest from tartex.__about__ import __version__ -from tartex.tartex import TAR_DEFAULT_COMP, TarTeX, make_tar +from tartex.tartex import TarTeX, make_tar class TestArgs: @@ -21,11 +21,10 @@ def test_no_args(self): assert exc.value.code == 2 - def test_only_file(self): + def test_only_file(self, sample_texfile): """Test success with one arg: file name""" - t = TarTeX(["some_file.tex"]) - assert t.main_file.stem == "some_file" - assert t.tar_file.name == "some_file.tar" + assert sample_texfile.main_file.stem == "some_file" + assert sample_texfile.tar_file.name == "some_file.tar" def test_version(self, capsys): """Test version string against version from __about.py__""" @@ -37,38 +36,13 @@ def test_version(self, capsys): assert f"{__version__}" in output assert exc.value.code == 0 - def test_taropts_conflict(self, capsys): - """Test exit status when both --bzip2 and --gzip are passed""" + @pytest.mark.parametrize(("tar_opt1", "tar_opt2"), + [("-J", "-z"), ("-j", "-J"), ("-z", "-J")]) + def test_taropts_conflict(self, capsys, tar_opt1, tar_opt2): + """Test exit status when two conflicting tar options are passed""" with pytest.raises(SystemExit) as exc: - TarTeX(["--bzip2", "--gzip"]) + TarTeX([tar_opt1, tar_opt2, "some_file.tex"]) assert exc.value.code == 2 output = capsys.readouterr().err assert "not allowed with" in output - - -class TestTarExt: - """ - Class of tests checking automatic tar compression detection based on user - specified outout tarfile name - """ - def test_default(self): - """"Test default tarball extension""" - t = TarTeX(["some_file.tex"]) - assert t.tar_ext == TAR_DEFAULT_COMP - - def test_output_bzip2(self): - """Test user output file as .tar.bz2""" - t = TarTeX(["-o", "dest.tar.bz2", "some_file.tex"]) - assert t.tar_ext == "bz2" - t = TarTeX(["-o", "dest.tar.bz2", "-J", "some_file.tex"]) - assert t.tar_ext == "xz" - assert t.tar_file.name == "dest.tar" - - def test_output_xz(self): - """Test user output file as .tar.xz""" - t = TarTeX(["-o", "dest.tar.xz", "some_file.tex"]) - assert t.tar_ext == "xz" - t = TarTeX(["-o", "dest.tar.xz", "-j", "some_file.tex"]) - assert t.tar_ext == "bz2" - assert t.tar_file.name == "dest.tar" diff --git a/tests/test_basic_latex.py b/tests/test_basic_latex.py new file mode 100644 index 0000000..44e00b0 --- /dev/null +++ b/tests/test_basic_latex.py @@ -0,0 +1,154 @@ +# SPDX-FileCopyrightText: 2024-present Atri Bhattacharya +# +# SPDX-License-Identifier: MIT +# +"""Tests for tarball generation from basic latex files""" +import os +import tarfile as tar +from pathlib import Path + +import pytest + +from tartex.tartex import TAR_DEFAULT_COMP, TarTeX + + +@pytest.fixture +def default_target(datadir): + return Path(datadir) / "test" + + +@pytest.fixture +def default_tartex_obj(datadir, default_target): + return TarTeX( + [ + (Path(datadir) / "basic_latex.tex").as_posix(), + "-v", + "-s", + "-o", + default_target.as_posix(), + ] + ) + + +class TestBasicLaTeX: + """Tests checking tar file generation from a basic latex file""" + + def test_gen_tar(self, default_target, default_tartex_obj): + """Should include a single file in tarball""" + output = default_target.with_suffix(".tar.gz") + t = default_tartex_obj + t.tar_files() + assert output.exists() is True + with tar.open(output) as rat: + assert len(rat.getnames()) == 1 + + def test_diff_target_dir(self, tmpdir, datadir): + # Make a new dir inside tmpdir + destdir = tmpdir / "dest" + os.mkdir(destdir) + t = TarTeX([(Path(datadir) / "basic_latex.tex").as_posix(), + "-v", + "-s", + "-o", + str(destdir / "output.tar.gz")]) + t.tar_files() + dest = t.tar_file.with_suffix(f".tar.{t.tar_ext}") + print(dest) + assert t.tar_file.with_suffix(f".tar.{t.tar_ext}").exists() + + +# These tests involve repeatedly compiling LaTeX files, thus can be slow +@pytest.mark.slow +class TestTarConflict: + """Tests checking resolutions for tar file name conflicts""" + + def test_sol_default(self, default_tartex_obj, monkeypatch): + """Test when user response is not workable""" + t_con = default_tartex_obj + t_con.tar_files() + + # Monkeypatch empty response for input + monkeypatch.setattr("builtins.input", lambda _: "") + + # Trying to create tar file again will lead to conflic res dialog + # Blank user input (from monkeypatch) will raise SystemExit + with pytest.raises(SystemExit) as exc: + t_con.tar_files() + + assert "Not overwriting existing tar file" in exc.value.code + + def test_sol_quit(self, default_tartex_obj, monkeypatch): + """Test when user response is not workable""" + t_con = default_tartex_obj + t_con.tar_files() + + # Monkeypatch empty response for input + monkeypatch.setattr("builtins.input", lambda _: "q") + + # Trying to create tar file again will lead to conflic res dialog + # Blank user input (from monkeypatch) will raise SystemExit + with pytest.raises(SystemExit) as exc: + t_con.tar_files() + + assert "Not overwriting existing tar file" in exc.value.code + + def test_sol_overwrite(self, default_tartex_obj, monkeypatch): + """Test overwrite resolution""" + t_con = default_tartex_obj + t_con.tar_files() + + # Monkeypatch empty response for input + monkeypatch.setattr("builtins.input", lambda _: "o") + t_con.tar_files() + output = t_con.tar_file.with_suffix(f".tar.{TAR_DEFAULT_COMP}") + assert output.exists() is True + with tar.open(output) as rat: + assert len(rat.getnames()) == 1 + + def test_sol_newname_ok(self, default_tartex_obj, tmpdir, monkeypatch): + """Test entering new name that works""" + t_con = default_tartex_obj + t_con.tar_files() + + output = str(tmpdir / "new.tar.gz") + # Monkeypatch responses for choosing a new file name + user_inputs = iter(['c', output]) + monkeypatch.setattr("builtins.input", lambda _: next(user_inputs)) + t_con.tar_files() + assert Path(output).exists() is True + with tar.open(output) as rat: + assert len(rat.getnames()) == 1 + + def test_sol_newname_old(self, default_tartex_obj, tmpdir, monkeypatch): + """Test error when entering new name that is same as the old name""" + t_con = default_tartex_obj + t_con.tar_files() + + output = str(tmpdir / "test.tar.gz") + assert Path(output).exists() is True + + # Monkeypatch responses for choosing file name same as original + user_inputs = iter(['c', output]) + monkeypatch.setattr("builtins.input", lambda _: next(user_inputs)) + with pytest.raises(SystemExit) as exc: + t_con.tar_files() + + assert "New name entered is also the same" in exc.value.code + + def test_sol_newext(self, default_tartex_obj, tmpdir, monkeypatch): + """Test new name with just the file ext changed""" + t_con = default_tartex_obj + t_con.tar_files() + + output = str(tmpdir / "test.tar.gz") + assert Path(output).exists() is True + + output = output.replace(".gz", ".xz") + # Monkeypatch responses for choosing file name same as original + user_inputs = iter(['c', output]) + monkeypatch.setattr("builtins.input", lambda _: next(user_inputs)) + t_con.tar_files() + assert Path(output).exists() is True + assert t_con.tar_ext == "xz" + with tar.open(output) as rat: + assert len(rat.getnames()) == 1 diff --git a/tests/test_basic_latex/basic_latex.tex b/tests/test_basic_latex/basic_latex.tex new file mode 100644 index 0000000..18dc370 --- /dev/null +++ b/tests/test_basic_latex/basic_latex.tex @@ -0,0 +1,6 @@ +\documentclass{article} + +\begin{document} +\section{Test document section} +This is a test document. +\end{document} diff --git a/tests/test_bib.py b/tests/test_bib.py new file mode 100644 index 0000000..0960967 --- /dev/null +++ b/tests/test_bib.py @@ -0,0 +1,26 @@ +# vim:set et sw=4 ts=4 tw=80: +""" +Test inclusion and exclusion of .bib files +""" + +import tarfile as tar +from tartex.tartex import TarTeX, TAR_DEFAULT_COMP + + +def test_bib(datadir, tmpdir): + """Test if tar file contains .bib when -b is passed""" + t = TarTeX( + [ + str(datadir / "main_bib.tex"), + "-b", + "-s", + "-v", + "-o", + f"{str(tmpdir)}/main_bib", + ] + ) + t.tar_files() + with tar.open(t.tar_file.with_suffix(f".tar.{TAR_DEFAULT_COMP}")) as f: + assert "refs.bib" in f.getnames() # Check: .bib file is in tarball + # Check main_bib.bbl file is in tarball even though not in srcdir + assert "main_bib.bbl" in f.getnames() diff --git a/tests/test_bib/main_bib.tex b/tests/test_bib/main_bib.tex new file mode 100644 index 0000000..39449f7 --- /dev/null +++ b/tests/test_bib/main_bib.tex @@ -0,0 +1,19 @@ +\documentclass[11pt,a4paper]{article} + +\author{No Author} +\title{Test bib} +\date{\today} +\begin{document} +\maketitle +\tableofcontents + +\section{Only Section} + +This is a test document with a single citation \cite{testarticle}. + +\subsection{Empty subsection} + +\bibliographystyle{plain} +\bibliography{refs} + +\end{document} diff --git a/tests/test_bib/refs.bib b/tests/test_bib/refs.bib new file mode 100644 index 0000000..75e46ea --- /dev/null +++ b/tests/test_bib/refs.bib @@ -0,0 +1,8 @@ +@ARTICLE{testarticle, + author = {No Name}, + title = {No Title}, + journal = {No Journal}, + year = {2024}, + otherinfo = {Purely for testing} +} + diff --git a/tests/test_multidir_latex.py b/tests/test_multidir_latex.py new file mode 100644 index 0000000..4da66a1 --- /dev/null +++ b/tests/test_multidir_latex.py @@ -0,0 +1,67 @@ +# vim: set ai et ts=4 sw=4 tw=80: +# SPDX-FileCopyrightText: 2024-present Atri Bhattacharya +# +# SPDX-License-Identifier: MIT + +"""Tests for LaTeX project consisting of multiple dirs""" + +import os +import tarfile as tar +from pathlib import Path + +import pytest + +from tartex.tartex import TAR_DEFAULT_COMP, TarTeX + + +@pytest.fixture +def multidir_target(datadir): + return Path(datadir) / "multi" + + +@pytest.fixture +def multidir_tartex_obj(datadir, multidir_target): + return TarTeX( + [ + (Path(datadir) / "main.tex").as_posix(), + "-v", + "-s", + "-o", + multidir_target.as_posix(), + ] + ) + + +@pytest.fixture +def src_files(datadir): + """Return sorted list of files in source dir """ + src_files = [str(Path(dname).relative_to(datadir) / f) + for dname, _, files in os.walk(datadir) + for f in files] + + src_files.sort() # Sort for comparison with tar output + return src_files + + +class TestMultiDir: + """Tests for LaTeX projects with files spread across multiple dirs""" + + def test_tar(self, datadir, src_files, multidir_tartex_obj): + """Test tar file creation""" + t = multidir_tartex_obj + + t.tar_files() + output = t.tar_file.with_suffix(f".tar.{TAR_DEFAULT_COMP}") + assert output.exists() + + with tar.open(output, "r") as rat: + # Check if files in tarball have the same dir structure as src_files + assert src_files == sorted(rat.getnames()) + + def test_list(self, datadir, src_files, capsys): + """Test printing list of files""" + t = TarTeX([(Path(datadir) / "main.tex").as_posix(), "-l", "-s"]) + t.tar_files() + + # Tar file must not be created with "-l" + assert not t.tar_file.with_suffix(".tar.{TAR_DEFAULT_COMP}").exists() diff --git a/tests/test_multidir_latex/figures/peppers.png b/tests/test_multidir_latex/figures/peppers.png new file mode 100644 index 0000000..a8d4272 Binary files /dev/null and b/tests/test_multidir_latex/figures/peppers.png differ diff --git a/tests/test_multidir_latex/main.tex b/tests/test_multidir_latex/main.tex new file mode 100644 index 0000000..c4ef898 --- /dev/null +++ b/tests/test_multidir_latex/main.tex @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{graphicx} + +\begin{document} +This is a test document with multiple sections included from different +directories. + +% Section 1 +\input{section_1/text} +% Section 2 +\input{section_2/text} +%A Figure +\begin{figure}[htb] + \centering + \includegraphics[width=0.5\textwidth]{figures/peppers} + \caption{Peppers from USC SIPI database.} +\end{figure} + +\end{document} diff --git a/tests/test_multidir_latex/section_1/text.tex b/tests/test_multidir_latex/section_1/text.tex new file mode 100644 index 0000000..0284de4 --- /dev/null +++ b/tests/test_multidir_latex/section_1/text.tex @@ -0,0 +1,2 @@ +\section{Section 1} +This is text for section 1. diff --git a/tests/test_multidir_latex/section_2/subsection/text.tex b/tests/test_multidir_latex/section_2/subsection/text.tex new file mode 100644 index 0000000..ee00510 --- /dev/null +++ b/tests/test_multidir_latex/section_2/subsection/text.tex @@ -0,0 +1,2 @@ +\subsection{A sub-section} +This is a subsection. diff --git a/tests/test_multidir_latex/section_2/text.tex b/tests/test_multidir_latex/section_2/text.tex new file mode 100644 index 0000000..e5a5ebe --- /dev/null +++ b/tests/test_multidir_latex/section_2/text.tex @@ -0,0 +1,3 @@ +\section{Section 2} +This is text for section 2. +\input{section_2/subsection/text} diff --git a/tests/test_target.py b/tests/test_target.py new file mode 100644 index 0000000..e6666d4 --- /dev/null +++ b/tests/test_target.py @@ -0,0 +1,42 @@ +# vim: set ai et ts=4 sw=4 tw=80: +# SPDX-FileCopyrightText: 2024-present Atri Bhattacharya +# +# SPDX-License-Identifier: MIT + +"""Tests for argument parsing""" + +import pytest + +from tartex.tartex import TAR_DEFAULT_COMP, TarTeX + + +@pytest.fixture +def target_tar(): + def _target(tar_ext, cmp_opt=""): + ttx_opts = ["-v", "-s", "-o", f"dest.tar.{tar_ext}", "some_file.tex"] + if cmp_opt: + ttx_opts.append(cmp_opt) + return TarTeX(ttx_opts) + + return _target + + +class TestTarExt: + """ + Class of tests checking automatic tar compression detection based on user + specified outout tarfile name + """ + + def test_default_compress(self, sample_texfile): + """Test default tarball extension""" + assert sample_texfile.tar_ext == TAR_DEFAULT_COMP + + def test_output_file_compress(self, target_tar): + """Test user output file for different tar.foo extensions""" + # bz2 + assert target_tar("bz2").tar_ext == "bz2" + # tar.bz2 overridden by "-J" + assert target_tar("bz2", "-J").tar_ext != "bz2" + # xz + assert target_tar("xz").tar_ext == "xz" + assert target_tar("xz").tar_file.name == "dest.tar"