1+ import importlib
2+ import io
13import os
24import shlex
35import shutil
46import subprocess
57import tempfile
68import textwrap
9+ import typing
710from collections import defaultdict
811from functools import partial
912from io import StringIO
1013from operator import attrgetter
1114from unittest .mock import patch
1215
1316import pytest
14- from pkgcheck import __title__ as project
15- from pkgcheck import base
16- from pkgcheck import checks as checks_mod
17- from pkgcheck import const , objects , reporters , scan
18- from pkgcheck .scripts import run
1917from pkgcore import const as pkgcore_const
2018from pkgcore .ebuild import atom , restricts
2119from pkgcore .restrictions import packages
2422from snakeoil .formatters import PlainTextFormatter
2523from snakeoil .osutils import pjoin
2624
25+ from pkgcheck import __title__ as project
26+ from pkgcheck import base , const , objects , reporters , scan
27+ from pkgcheck import checks as checks_mod
28+ from pkgcheck .results import Result
29+ from pkgcheck .scripts import run
30+
2731from ..misc import Profile
2832
2933
@@ -587,23 +591,45 @@ def _scan_results(self, repo, tmp_path, verbosity):
587591 assert len (results ) == len (results_set )
588592 return results_set
589593
590- def _get_results (self , path : str ):
594+ def _load_expected_results (
595+ self , path : str
596+ ) -> tuple [typing .Callable [[typing .Set [Result ]], bool ] | None , set [Result ]]:
591597 """Return the set of result objects from a given json stream file."""
598+ explicit = []
599+ custom = None
600+ expected_path = self .repos_data / path / "expected.json"
601+ custom_path = self .repos_data / path / "custom.py"
592602 try :
593- with ( self . repos_data / path ) .open () as f :
594- return set (reporters .JsonStream .from_iter (f ))
603+ with expected_path .open () as f :
604+ explicit = list (reporters .JsonStream .from_iter (f ))
595605 except FileNotFoundError :
596- return set ()
606+ pass
607+ except Exception as e :
608+ raise Exception (f"failed loading { expected_path } " ) from e
609+ try :
610+ with custom_path .open () as f :
611+ # we have to use eval since the import pathway isn't valid import lookup
612+ module = eval (f .read ())
613+ custom = getattr (module , "handle" )
614+ except FileNotFoundError :
615+ pass
616+ except Exception as e :
617+ raise Exception (f"failed loading { custom_path } " ) from e
618+ s_explicit = set (explicit )
619+ assert len (s_explicit ) == len (explicit ), f"results of { expected_path } are not unique"
620+ assert custom is not None or len (explicit ), (
621+ f"{ (self .repos_data / path )!r} contains no expected.json nor custom.py"
622+ )
623+ return custom , set (explicit )
597624
598- def _render_results (self , results , ** kwargs ):
625+ def _render_results (self , results , ** kwargs ) -> str :
599626 """Render a given set of result objects into their related string form."""
600- with tempfile .TemporaryFile () as f :
627+ # with tempfile.TemporaryFile() as f:
628+ with io .BytesIO () as f :
601629 with reporters .FancyReporter (out = PlainTextFormatter (f ), ** kwargs ) as reporter :
602630 for result in sorted (results ):
603631 reporter .report (result )
604- f .seek (0 )
605- output = f .read ().decode ()
606- return output
632+ return f .getvalue ().decode ()
607633
608634 @pytest .mark .parametrize ("repo" , repos )
609635 def test_scan_repo (self , repo , tmp_path_factory ):
@@ -615,13 +641,16 @@ def test_scan_repo(self, repo, tmp_path_factory):
615641 for check , keywords in self ._checks [repo ].items ():
616642 for keyword in keywords :
617643 # verify the expected results were seen during the repo scans
618- expected_results = self ._get_results (f"{ repo } /{ check } /{ keyword } /expected.json" )
619- assert expected_results , "regular results must always exist"
644+ custom_check , expected_results = self ._load_expected_results (
645+ f"{ repo } /{ check } /{ keyword } "
646+ )
647+ if custom_check :
648+ custom_check (scan_results )
620649 assert self ._render_results (expected_results ), "failed rendering results"
621650 results .update (expected_results )
622651
623652 # when expected verbose results exist use them, otherwise fallback to using the regular ones
624- expected_verbose_results = self ._get_results (
653+ expected_verbose_results = self ._load_expected_results (
625654 f"{ repo } /{ check } /{ keyword } /expected-verbose.json"
626655 )
627656 if expected_verbose_results :
0 commit comments