import multiprocessing import os import signal import pytest from pkgcheck import PkgcheckException, scan from pkgcheck import objects class TestScanApi: @pytest.fixture(autouse=True) def _setup(self, testconfig): self.base_args = ["--config", testconfig] self.scan_args = ["--config", "no", "--cache", "no"] def test_argparse_error(self, repo): with pytest.raises(PkgcheckException, match="unrecognized arguments"): scan(["-r", repo.location, "--foo"]) def test_no_scan_args(self): pipe = scan(base_args=self.base_args) assert pipe.options.target_repo.repo_id == "standalone" def test_no_base_args(self, repo): assert [] == list(scan(self.scan_args + ["-r", repo.location])) def test_keyword_import(self): """Keyword classes are importable from the top-level module.""" from pkgcheck import NonsolvableDeps, Result assert issubclass(NonsolvableDeps, Result) def test_module_attributes(self): """All keyword class names are shown for the top-level module.""" import pkgcheck assert set(objects.KEYWORDS) < set(dir(pkgcheck)) def test_sigint_handling(self, repo): """Verify SIGINT is properly handled by the parallelized pipeline.""" def run(queue): """Pipeline test run in a separate process that gets interrupted.""" import sys import time from functools import partial from unittest.mock import patch from pkgcheck import scan def sleep(): """Notify testing process then sleep.""" queue.put("ready") time.sleep(100) with patch("pkgcheck.pipeline.Pipeline.__iter__") as fake_iter: fake_iter.side_effect = partial(sleep) try: iter(scan([repo.location])) except KeyboardInterrupt: queue.put(None) sys.exit(0) queue.put(None) sys.exit(1) mp_ctx = multiprocessing.get_context("fork") queue = mp_ctx.SimpleQueue() p = mp_ctx.Process(target=run, args=(queue,)) p.start() # wait for pipeline object to be fully initialized then send SIGINT for _ in iter(queue.get, None): os.kill(p.pid, signal.SIGINT) p.join() assert p.exitcode == 0