From 36d96ccb5cfe6af447f5a1e9d07e6b029e8363df Mon Sep 17 00:00:00 2001 From: Guthrie McAfee Armstrong Date: Thu, 9 Jun 2022 17:44:53 -0400 Subject: [PATCH] Add unit tests --- .gitignore | 3 +- dangerzone/tests/__init__.py | 0 dangerzone/tests/test_cli.py | 125 +++++++++++++++++++++++++ dangerzone/tests/test_global_common.py | 58 ++++++++++++ poetry.lock | 2 +- pyproject.toml | 1 + 6 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 dangerzone/tests/__init__.py create mode 100644 dangerzone/tests/test_cli.py create mode 100644 dangerzone/tests/test_global_common.py diff --git a/.gitignore b/.gitignore index 6620ae7..12b8da9 100644 --- a/.gitignore +++ b/.gitignore @@ -133,7 +133,8 @@ dmypy.json deb_dist .DS_Store install/windows/Dangerzone.wxs -test_docs/sample-safe.pdf +**/*-safe.pdf +test_docs/out/ share/container.tar share/container.tar.gz share/image-id.txt diff --git a/dangerzone/tests/__init__.py b/dangerzone/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dangerzone/tests/test_cli.py b/dangerzone/tests/test_cli.py new file mode 100644 index 0000000..8883635 --- /dev/null +++ b/dangerzone/tests/test_cli.py @@ -0,0 +1,125 @@ +from __future__ import annotations + +import os.path +import sys +from pathlib import Path +from unittest import TestCase + +from click.testing import CliRunner, Result + +from dangerzone.cli import cli_main + + +# TODO --output-filename with spaces +# TODO explore any symlink edge cases +# TODO simulate ctrl-c, ctrl-d, SIGINT/SIGKILL/SIGTERM... (man 7 signal), etc? +# TODO validate output PDFs https://github.com/pdfminer/pdfminer.six +# TODO trigger "Invalid json returned from container" +# TODO trigger "pdf-to-pixels failed" +# TODO simulate container runtime missing +# TODO simulate container connection error +# TODO simulate container connection loss +# FIXME "/" path separator is platform-dependent, use pathlib instead + + +class CliTestCase(TestCase): + SAMPLE_DIRECTORY = "test_docs" + BASIC_SAMPLE = f"{SAMPLE_DIRECTORY}/sample.pdf" + SAFE_SUFFIX = "-safe.pdf" + + def setUp(self): + sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + sys.dangerzone_dev = True + self.runner = CliRunner() + # TODO Use pathlib or similar for safer file handling here + samples_dir = Path(self.SAMPLE_DIRECTORY) + self.samples: list[Path | str] = [ + p + for p in samples_dir.rglob("*") + if p.is_file() and not p.name.endswith(self.SAFE_SUFFIX) + ] + print(f"{self.BASIC_SAMPLE} --output-filename {self.SAMPLE_DIRECTORY}/out/my-output.pdf") + if len(self.samples) < 10: + raise RuntimeWarning(f"Only {len(self.samples)} samples found.") + + def invoke_runner(self, *args, **kwargs) -> Result: + return self.runner.invoke(cli_main, *args, **kwargs) + + +class CliBasicTestCase(CliTestCase): + def test_no_args(self): + """``$ dangerzone-cli``""" + result = self.invoke_runner() + self.assertNotEqual(result.exit_code, 0) + + def test_help(self): + """``$ dangerzone-cli --help``""" + result = self.invoke_runner("--help") + self.assertEqual(result.exit_code, 0) + + def test_version(self): + """``$ dangerzone-cli --version``""" + # Note: fails for now, "--version" is not yet implemented. + result = self.invoke_runner("--version") + self.assertEqual(result.exit_code, 0) + + +class CliConversionTestCase(CliTestCase): + def test_invalid_lang(self): + result = self.invoke_runner(f"{self.BASIC_SAMPLE} --ocr-lang piglatin") + self.assertNotEqual(result.exit_code, 0) + + def test_samples(self): + for sample in self.samples: + with self.subTest(f"Convert {sample}"): + result = self.invoke_runner(f'"{sample}"') + self.assertEqual(result.exit_code, 0) + + def test_output_filename(self): + result = self.invoke_runner(f"{self.BASIC_SAMPLE} --output-filename {self.SAMPLE_DIRECTORY}/out/my-output.pdf") + self.assertEqual(result.exit_code, 0) + + def test_output_filename_new_dir(self): + result = self.invoke_runner(f"{self.BASIC_SAMPLE} --output-filename fake-directory/my-output.pdf") + self.assertEqual(result.exit_code, 0) + + def test_sample_not_found(self): + result = self.invoke_runner("fake-directory/fake-file.pdf") + self.assertEquals(result.exit_code, 1) + + def test_lang_mismatch(self): + """Try to OCR sample.pdf (Lorem ipsum) as traditional Chinese characters.""" + # TODO how should we handle these cases? + with self.assertWarns(RuntimeWarning): + self.invoke_runner(f"{self.BASIC_SAMPLE} --ocr-lang chi_tra") + + def test_lang_eng(self): + # Rewrite this case if samples in other languages or scripts are added. + result = self.invoke_runner(f'"{self.BASIC_SAMPLE}" --ocr-lang eng') + self.assertEqual(result.exit_code, 0) + + def test_bulk(self): + """ + Try to convert all sample documents in one run. + Fails for now, since bulk conversion is not yet implemented. + """ + # FIXME Once bulk conversion is implemented, return here to expand and quote self.samples correctly. + result = self.invoke_runner(self.samples) + self.assertEqual(result.exit_code, 0) + + def test_bulk_input_one_name(self): + """ + Try to convert all sample documents in one run and supplies --output-filename This should fail. + """ + # FIXME Once bulk conversion is implemented, return here to expand and quote self.samples correctly. + result = self.invoke_runner(self.samples + ["--output-filename sample-safe.pdf"]) # more samples than names + self.assertNotEqual(result.exit_code, 0) + + def test_bulk_ocr_eng(self): + """ + Try to convert all sample documents in one run and with English OCR. + Fails for now, since bulk conversion is not yet implemented. + """ + # FIXME Once bulk conversion is implemented, return here to expand and quote self.samples correctly. + result = self.invoke_runner(self.samples + ["--ocr-lang eng"]) + self.assertEqual(result.exit_code, 0) diff --git a/dangerzone/tests/test_global_common.py b/dangerzone/tests/test_global_common.py new file mode 100644 index 0000000..fe7c352 --- /dev/null +++ b/dangerzone/tests/test_global_common.py @@ -0,0 +1,58 @@ +import io +import os +import platform +import subprocess +import sys +import unittest +from io import StringIO +from pathlib import Path +from unittest import TestCase, mock + +from strip_ansi import strip_ansi # type: ignore + +import dangerzone.global_common as global_common + + +class TestGlobalCommon(TestCase): + + VERSION_FILE_NAME = "version.txt" + + def setUp(self): + self.global_common = global_common.GlobalCommon() + sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + sys.dangerzone_dev = True + + def test_get_resource_path(self): + share_dir = Path("share").resolve() + resource_path = Path( + self.global_common.get_resource_path(self.VERSION_FILE_NAME) + ).parent + self.assertTrue( + share_dir.samefile(resource_path), + msg=f"{share_dir} is not the same file as {resource_path}", + ) + + @unittest.skipUnless(platform.system() == "Windows", "STARTUPINFO is for Windows") + def test_get_subprocess_startupinfo(self): + startupinfo = self.global_common.get_subprocess_startupinfo() + self.assertIsInstance(startupinfo, subprocess.STARTUPINFO) + + @unittest.mock.patch("sys.stdout", new_callable=io.StringIO) + def test_display_banner(self, mock_stdout: StringIO): + self.global_common.display_banner() # call the test subject + banner = mock_stdout.getvalue() + plain_lines = [strip_ansi(line) for line in banner.splitlines()] + with self.subTest("banner top border"): + self.assertEqual("╭──────────────────────────╮", plain_lines[0]) + with self.subTest("banner bottom border"): + self.assertEqual("╰──────────────────────────╯", plain_lines[14]) + with self.subTest("banner consistent dimensions"): + width = len(plain_lines[0]) + for line in plain_lines: + self.assertEqual(len(line), width) + + @unittest.mock.patch("sys.stdout", new_callable=io.StringIO) + def test_display_banner_dims(self, mock_stdout: StringIO): + self.global_common.display_banner() # call the test subject + banner = mock_stdout.getvalue() + banner_lines = banner.splitlines() diff --git a/poetry.lock b/poetry.lock index e1a1372..d25278d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -367,7 +367,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [metadata] lock-version = "1.1" python-versions = ">=3.7,<3.10" -content-hash = "786373e6750c907d6353d3c96c2d8aebffb1b0271035a793469055e2d6502bac" +content-hash = "06c3321d6ee6b4e8dc1512f6dbc9ef8d911347aac642f1a709a55d8eec1cc09a" [metadata.files] altgraph = [ diff --git a/pyproject.toml b/pyproject.toml index 58231e6..b0377d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,7 @@ black = "*" isort = "*" mypy = "*" PySide2-stubs = "*" +strip-ansi = "*" [tool.poetry.scripts] dangerzone = 'dangerzone:main'