diff options
author | Wojtek Kosior <koszko@koszko.org> | 2022-02-05 20:15:47 +0100 |
---|---|---|
committer | Wojtek Kosior <koszko@koszko.org> | 2022-02-05 20:15:47 +0100 |
commit | b5eb89e10762fb7e71c2e8e0cf28ab7c5206884a (patch) | |
tree | 71a38c9eaed59db455b2ce2c531840f30d60341e | |
parent | 8a036bc73353300ab84eb1d72807d9376cc7b4f5 (diff) | |
download | hydrilla-builder-b5eb89e10762fb7e71c2e8e0cf28ab7c5206884a.tar.gz hydrilla-builder-b5eb89e10762fb7e71c2e8e0cf28ab7c5206884a.zip |
test&fix error handling
-rw-r--r-- | src/hydrilla_builder/build.py | 10 | ||||
-rw-r--r-- | src/test/test_hydrilla_builder.py | 146 |
2 files changed, 120 insertions, 36 deletions
diff --git a/src/hydrilla_builder/build.py b/src/hydrilla_builder/build.py index e7c6962..652e537 100644 --- a/src/hydrilla_builder/build.py +++ b/src/hydrilla_builder/build.py @@ -164,12 +164,12 @@ def generate_spdx_report(root): mocked_output = FileBuffer() if reuse_main(args=['--root', str(root), 'lint'], out=mocked_output) != 0: - stderr.write(mocked_out.get_bytes()) + stderr.write(mocked_output.get_bytes().decode()) raise ReuseError('Attempt to generate an SPDX report for a REUSE-incompliant package.') mocked_output = FileBuffer() if reuse_main(args=['--root', str(root), 'spdx'], out=mocked_output) != 0: - stderr.write(mocked_out.get_bytes()) + stderr.write(mocked_output.get_bytes().decode()) raise ReuseError("Couldn't generate an SPDX report for package.") return mocked_output.get_bytes() @@ -249,10 +249,10 @@ class Build: path = path.resolve() if not path.is_relative_to(self.srcdir): - raise FileReferenceException(f"Attempt to load '{filename}' which lies outside package source directory.") + raise FileReferenceError(f"Attempt to load '{filename}' which lies outside package source directory.") - if path == self.index_json_path: - raise FileReferenceException("Attempt to load 'index.json' which is a reserved filename.") + if str(path.relative_to(self.srcdir)) == 'index.json': + raise FileReferenceError("Attempt to load 'index.json' which is a reserved filename.") file_ref = self.files_by_path.get(path) if file_ref is None: diff --git a/src/test/test_hydrilla_builder.py b/src/test/test_hydrilla_builder.py index b2f19f0..f4a4d2f 100644 --- a/src/test/test_hydrilla_builder.py +++ b/src/test/test_hydrilla_builder.py @@ -6,11 +6,17 @@ import pytest import json +import shutil from tempfile import TemporaryDirectory from pathlib import Path from hashlib import sha256, sha1 from zipfile import ZipFile +from typing import Callable, Optional + +from jsonschema import ValidationError + +from hydrilla_builder import build here = Path(__file__).resolve().parent @@ -164,6 +170,45 @@ class CaseSettings: self.expected_source_description ] +ModifyCb = Callable[[CaseSettings, dict], Optional[str]] + +def prepare_modified(tmpdir: Path, modify_cb: ModifyCb) -> CaseSettings: + """ + Use sample source package directory with an alternative, modified + index.json. + """ + settings = CaseSettings() + + for fn in settings.src_filenames: + copy_path = tmpdir / 'srcdir_copy' / fn + copy_path.parent.mkdir(parents=True, exist_ok=True) + shutil.copy(settings.srcdir / fn, copy_path) + + settings.srcdir = tmpdir / 'srcdir_copy' + + with open(settings.srcdir / 'index.json', 'rt') as file_handle: + obj = json.loads(build.strip_json_comments(file_handle.read())) + + contents = modify_cb(settings, obj) + + # Replace the other index.json with new one + settings.index_json_path = tmpdir / 'replacement.json' + + if contents is None: + contents = json.dumps(obj) + + contents = contents.encode() + + settings.contents['index.json'] = contents + + settings.sha256_hashes['index.json'] = sha256(contents).digest().hex() + settings.sha1_hashes['index.json'] = sha1(contents).digest().hex() + + with open(settings.index_json_path, 'wb') as file_handle: + file_handle.write(contents) + + return settings + @pytest.fixture() def tmpdir() -> str: with TemporaryDirectory() as tmpdir: @@ -173,18 +218,11 @@ def prepare_default(tmpdir: Path) -> CaseSettings: """Use sample source package directory as exists in VCS.""" return CaseSettings() -def prepare_external_index_json(tmpdir: Path) -> dict: +def modify_index_good(settings: CaseSettings, obj: dict) -> None: """ - Use sample source package directory with an alternative, modified - index.json. + Modify index.json object to make a slightly different but *also correct* one + that can be used to test some different cases. """ - settings = CaseSettings() - - from hydrilla_builder.build import strip_json_comments - - with open(settings.srcdir / 'index.json', 'rt') as file_handle: - obj = json.loads(strip_json_comments(file_handle.read())) - # Add comments that should be preserved. for dictionary in (obj, settings.expected_source_description): dictionary['comment'] = 'index_json comment' @@ -233,28 +271,12 @@ def prepare_external_index_json(tmpdir: Path) -> dict: to_process.extend(processed.values()) processed['spurious_property'] = 'some value' - # Replace the other index.json with new one - settings.index_json_path = tmpdir / 'replacement.json' - - contents = json.dumps(obj).encode() - - settings.contents['index.json'] = contents - - settings.sha256_hashes['index.json'] = sha256(contents).digest().hex() - settings.sha1_hashes['index.json'] = sha1(contents).digest().hex() - - with open(settings.index_json_path, 'wb') as file_handle: - file_handle.write(contents) - - return settings - @pytest.mark.parametrize('prepare_source_example', [ - prepare_default, prepare_external_index_json + prepare_default, + lambda tmpdir: prepare_modified(tmpdir, modify_index_good) ]) def test_build(tmpdir, prepare_source_example): """Build the sample source package and verify the produced files.""" - from hydrilla_builder.build import Build - # First, build the package dstdir = Path(tmpdir) / 'dstdir' tmpdir = Path(tmpdir) / 'example' @@ -264,8 +286,8 @@ def test_build(tmpdir, prepare_source_example): settings = prepare_source_example(tmpdir) - build = Build(settings.srcdir, settings.index_json_path) - build.write_package_files(dstdir) + build.Build(settings.srcdir, settings.index_json_path)\ + .write_package_files(dstdir) # Verify directories under destination directory assert {'file', 'resource', 'mapping', 'source'} == \ @@ -354,4 +376,66 @@ def test_build(tmpdir, prepare_source_example): with open(source_dir / 'hello.json', 'rt') as file_handle: assert json.load(file_handle) == settings.expected_source_description -# TODO: also check error handling +def modify_index_missing_file(dummy: CaseSettings, obj: dict) -> None: + """ + Modify index.json to expect missing report.spdx file and cause an error. + """ + del obj['reuse_generate_spdx_report'] + +def modify_index_schema_error(dummy: CaseSettings, obj: dict) -> None: + """Modify index.json to be incompliant with the schema.""" + del obj['definitions'] + +def modify_index_bad_comment(dummy: CaseSettings, obj: dict) -> str: + """Modify index.json to have an invalid '/' in it.""" + return json.dumps(obj) + '/something\n' + +def modify_index_bad_json(dummy: CaseSettings, obj: dict) -> str: + """Modify index.json to not be valid json even after comment stripping.""" + return json.dumps(obj) + '???/\n' + +def modify_index_missing_license(settings: CaseSettings, obj: dict) -> None: + """Remove a file to make package REUSE-incompliant.""" + (settings.srcdir / 'README.txt.license').unlink() + +def modify_index_file_outside(dummy: CaseSettings, obj: dict) -> None: + """Make index.json illegally reference a file outside srcdir.""" + obj['copyright'].append({'file': '../abc'}) + +def modify_index_reference_itself(dummy: CaseSettings, obj: dict) -> None: + """Make index.json illegally reference index.json.""" + obj['copyright'].append({'file': 'index.json'}) + +def modify_index_report_excluded(dummy: CaseSettings, obj: dict) -> None: + """ + Make index.json require generation of index.json but not include it among + copyright files. + """ + obj['copyright'] = [fr for fr in obj['copyright'] + if fr['file'] != 'report.spdx'] + +@pytest.mark.parametrize('break_index_json', [ + (modify_index_missing_file, FileNotFoundError), + (modify_index_schema_error, ValidationError), + (modify_index_bad_comment, json.JSONDecodeError), + (modify_index_bad_json, json.JSONDecodeError), + (modify_index_missing_license, build.ReuseError), + (modify_index_file_outside, build.FileReferenceError), + (modify_index_reference_itself, build.FileReferenceError), + (modify_index_report_excluded, build.FileReferenceError) +]) +def test_build_error(tmpdir: str, break_index_json: tuple[ModifyCb, type]): + """Build the sample source package and verify the produced files.""" + dstdir = Path(tmpdir) / 'dstdir' + tmpdir = Path(tmpdir) / 'example' + + dstdir.mkdir(exist_ok=True) + tmpdir.mkdir(exist_ok=True) + + modify_cb, error_type = break_index_json + + settings = prepare_modified(tmpdir, modify_cb) + + with pytest.raises(error_type): + build.Build(settings.srcdir, settings.index_json_path)\ + .write_package_files(dstdir) |