1515
1616MINIBOM = Path (__file__ ).parent .parent / "fixtures" / "minibom"
1717
18- # scijava-common depends on parsington, making it the right component to test
19- # the BOM's dependency version pinning.
20- _SCJ_COMMON = "org.scijava:scijava-common"
18+ # Components from the minibom (4 total), in same order as the BOM.
19+ _APP_LAUNCHER = "org.scijava:app-launcher:2.3.1"
20+ _PARSINGTON = "org.scijava:parsington:3.1.0"
21+ _SJ_COMMON = "org.scijava:scijava-common:2.99.2"
22+ _SJ_DESKTOP = "org.scijava:scijava-desktop:1.0.0"
2123
22- # parsington 1.0.0 predates the 3.x API that scijava-common 2.99.2 requires,
24+ _ALL_COMPONENTS = [_APP_LAUNCHER , _PARSINGTON , _SJ_COMMON , _SJ_DESKTOP ]
25+
26+ # parsington 1.0.0 predates the 3.x API that scijava-common 2.99.2 uses,
2327# so injecting it should cause a compilation failure.
2428_PARSINGTON_INCOMPATIBLE = "org.scijava:parsington:1.0.0"
2529
@@ -36,13 +40,37 @@ def _make_pipeline(tmp_path: Path, **kwargs) -> Pipeline:
3640 return Pipeline (config )
3741
3842
43+ def _make_pipeline_with_config (tmp_path : Path , config_path : Path , ** kwargs ) -> Pipeline :
44+ """Create a pipeline with a config file."""
45+ from pombast .config ._settings import PombastConfig
46+
47+ pombast_config = PombastConfig .load (config_path )
48+
49+ pipeline_config = PipelineConfig (
50+ bom = str (MINIBOM ),
51+ output_dir = tmp_path / "output" ,
52+ success_cache_dir = tmp_path / ".success-cache" ,
53+ test_binary = False ,
54+ force = True ,
55+ config = pombast_config ,
56+ ** kwargs ,
57+ )
58+ return Pipeline (pipeline_config )
59+
60+
61+ def _assert_components (report , expected : list [str ]) -> None :
62+ """Assert the report contains exactly the given component coordinates, in order."""
63+ actual = [r .component .coordinate for r in report .results ]
64+ assert actual == expected , f"expected { expected } , but found: { actual } "
65+
66+
3967class TestValidate :
4068 def test_passing_bom (self , tmp_path ):
4169 """scijava-common should build successfully against the minibom."""
42- pipeline = _make_pipeline (tmp_path , includes = [_SCJ_COMMON ])
70+ pipeline = _make_pipeline (tmp_path , includes = ["org.scijava:scijava-common" ])
4371 report = pipeline .run ()
4472
45- assert report . results , "expected at least one result"
73+ _assert_components ( report , [ _SJ_COMMON ])
4674 failures = [r for r in report .results if r .status != BuildStatus .SUCCESS ]
4775 assert not failures , (
4876 f"unexpected failures: { [r .component .coordinate for r in failures ]} "
@@ -52,11 +80,12 @@ def test_incompatible_parsington(self, tmp_path):
5280 """Injecting parsington 1.0.0 via -c should cause scijava-common to fail."""
5381 pipeline = _make_pipeline (
5482 tmp_path ,
55- includes = [_SCJ_COMMON ],
83+ includes = ["org.scijava:scijava-common" ],
5684 changes = [_PARSINGTON_INCOMPATIBLE ],
5785 )
5886 report = pipeline .run ()
5987
88+ _assert_components (report , [_SJ_COMMON ])
6089 failures = [
6190 r
6291 for r in report .results
@@ -85,3 +114,109 @@ def test_incompatible_parsington(self, tmp_path):
85114 f"expected compilation failure matching signature { expected_incompatibility_regex } , "
86115 f"but found unexpected build log content."
87116 )
117+
118+ def test_exclude_component (self , tmp_path ):
119+ """Excluding a component via -e should skip it entirely."""
120+ pipeline = _make_pipeline (tmp_path , excludes = ["org.scijava:scijava-common" ])
121+ report = pipeline .run ()
122+
123+ _assert_components (report , [_APP_LAUNCHER , _PARSINGTON , _SJ_DESKTOP ])
124+
125+ def test_exclude_nonexistent (self , tmp_path ):
126+ """Excluding a non-existent component should not affect results."""
127+ pipeline = _make_pipeline (tmp_path , excludes = ["org.nonexistent:nonexistent" ])
128+ report = pipeline .run ()
129+
130+ _assert_components (report , _ALL_COMPONENTS )
131+
132+ def test_prune_with_change (self , tmp_path ):
133+ """With prune, only components that depend on the change should be built."""
134+ pipeline = _make_pipeline (
135+ tmp_path ,
136+ changes = [_PARSINGTON_INCOMPATIBLE ],
137+ prune = True ,
138+ )
139+ report = pipeline .run ()
140+
141+ _assert_components (report , _ALL_COMPONENTS )
142+ built = [r for r in report .results if r .status != BuildStatus .SKIPPED ]
143+ skipped = [r for r in report .results if r .status == BuildStatus .SKIPPED ]
144+ assert [r .component .coordinate for r in built ] == [_SJ_COMMON ], (
145+ f"expected only scijava-common to be built with prune, "
146+ f"but built: { [r .component .coordinate for r in built ]} "
147+ )
148+ assert [r .component .coordinate for r in skipped ] == [
149+ _APP_LAUNCHER ,
150+ _PARSINGTON ,
151+ _SJ_DESKTOP ,
152+ ], (
153+ f"expected app-launcher, parsington, scijava-desktop to be skipped with prune, "
154+ f"but skipped: { [r .component .coordinate for r in skipped ]} "
155+ )
156+
157+ def test_exclude_and_include_interaction (self , tmp_path ):
158+ """When both -i and -e are used, exclude takes precedence for matching components."""
159+ pipeline = _make_pipeline (
160+ tmp_path ,
161+ includes = ["org.scijava:scijava-*" ],
162+ excludes = ["org.scijava:scijava-common" ],
163+ )
164+ report = pipeline .run ()
165+
166+ # "org.scijava:scijava-*" matches scijava-common and scijava-desktop;
167+ # scijava-common is then excluded, leaving only scijava-desktop.
168+ _assert_components (report , [_SJ_DESKTOP ])
169+
170+ def test_repository_option (self , tmp_path ):
171+ """Adding a repository via -r should be accepted without error."""
172+ pipeline = _make_pipeline (
173+ tmp_path ,
174+ includes = ["org.scijava:scijava-common" ],
175+ repositories = ["https://repo1.maven.org/maven2" ],
176+ )
177+ report = pipeline .run ()
178+
179+ _assert_components (report , [_SJ_COMMON ])
180+
181+ def test_multiple_repositories (self , tmp_path ):
182+ """Multiple -r options should all be added to the repository map."""
183+ pipeline = _make_pipeline (
184+ tmp_path ,
185+ includes = ["org.scijava:scijava-common" ],
186+ repositories = [
187+ "https://repo1.maven.org/maven2" ,
188+ "https://repo.jfrog.org/artifactory" ,
189+ ],
190+ )
191+ report = pipeline .run ()
192+
193+ _assert_components (report , [_SJ_COMMON ])
194+
195+ def test_skip_build_option (self , tmp_path ):
196+ """Using -s should skip actual builds; no results are produced."""
197+ pipeline = _make_pipeline (
198+ tmp_path ,
199+ includes = ["org.scijava:scijava-common" ],
200+ skip_build = True ,
201+ )
202+ report = pipeline .run ()
203+
204+ _assert_components (report , [])
205+
206+ def test_config_file_loading (self , tmp_path ):
207+ """Loading configuration from a pombast.toml file should work."""
208+ config_path = MINIBOM / "pombast.toml"
209+
210+ pipeline = _make_pipeline_with_config (tmp_path , config_path )
211+ report = pipeline .run ()
212+
213+ _assert_components (report , _ALL_COMPONENTS )
214+
215+ def test_config_min_java_version (self , tmp_path ):
216+ """Config file min-java-version should be used if not specified on CLI."""
217+ config_path = MINIBOM / "pombast.toml"
218+
219+ pipeline = _make_pipeline_with_config (tmp_path , config_path )
220+ report = pipeline .run ()
221+
222+ _assert_components (report , _ALL_COMPONENTS )
0 commit comments