11import dataclasses
22import graphlib
3+ import pathlib
34import typing
45
56import pytest
67from packaging .requirements import Requirement
78from packaging .utils import canonicalize_name
89from packaging .version import Version
910
10- from fromager .dependency_graph import DependencyNode , TrackingTopologicalSorter
11+ from fromager import context
12+ from fromager .dependency_graph import (
13+ DependencyGraph ,
14+ DependencyNode ,
15+ TrackingTopologicalSorter ,
16+ )
1117from fromager .requirements_file import RequirementType
1218
1319
@@ -54,6 +60,8 @@ def test_dependencynode_dataclass() -> None:
5460 repr (a )
5561 == "DependencyNode(canonicalized_name='a', version=<Version('1.0')>, download_url='', pre_built=False, constraint=None)"
5662 )
63+ assert a .requirement == Requirement ("a==1.0" )
64+
5765 with pytest .raises (dataclasses .FrozenInstanceError ):
5866 a .version = Version ("2.0" ) # type: ignore[misc]
5967 with pytest .raises ((TypeError , AttributeError )):
@@ -63,6 +71,8 @@ def test_dependencynode_dataclass() -> None:
6371 assert root .canonicalized_name == ""
6472 assert root .version == Version ("0.0" )
6573 assert root .key == ""
74+ with pytest .raises (RuntimeError ):
75+ assert root .requirement
6676
6777
6878def test_iter_requirements () -> None :
@@ -295,3 +305,89 @@ def test_tracking_topology_sorter_not_active_error() -> None:
295305 with pytest .raises (ValueError ) as excinfo :
296306 topo .get_available ()
297307 assert "topology is not active" in str (excinfo .value )
308+
309+
310+ def node2str (nodes : set [DependencyNode ]) -> set [str ]:
311+ return {node .key for node in nodes }
312+
313+
314+ def test_e2e_parallel_graph (
315+ tmp_context : context .WorkContext , e2e_path : pathlib .Path
316+ ) -> None :
317+ graph = DependencyGraph .from_file (e2e_path / "build-parallel" / "graph.json" )
318+ assert len (graph ) == 16
319+
320+ topo = graph .get_build_topology (tmp_context )
321+ assert node2str (topo .dependency_nodes ) == {
322+ "cython==3.1.1" ,
323+ "flit-core==3.12.0" ,
324+ "packaging==25.0" ,
325+ "setuptools-scm==8.3.1" ,
326+ "setuptools==80.8.0" ,
327+ "wheel==0.46.1" ,
328+ }
329+
330+ steps = [node2str (batch ) for batch in topo .static_batches ()]
331+ assert steps == [
332+ {
333+ "flit-core==3.12.0" ,
334+ "setuptools==80.8.0" ,
335+ },
336+ {
337+ "cython==3.1.1" ,
338+ "imapclient==3.0.1" ,
339+ "jinja2==3.1.6" ,
340+ "markupsafe==3.0.2" ,
341+ "more-itertools==10.7.0" ,
342+ "packaging==25.0" ,
343+ },
344+ {
345+ "setuptools-scm==8.3.1" ,
346+ "wheel==0.46.1" ,
347+ },
348+ {
349+ "imapautofiler==1.14.0" ,
350+ "jaraco-classes==3.4.0" ,
351+ "jaraco-context==6.0.1" ,
352+ "jaraco-functools==4.1.0" ,
353+ "keyring==25.6.0" ,
354+ "pyyaml==6.0.2" ,
355+ },
356+ ]
357+
358+ # same graph, but mark cython as exclusive
359+ topo = graph .get_build_topology (tmp_context )
360+ node = graph .nodes ["cython==3.1.1" ]
361+ topo .add (node , exclusive = True )
362+
363+ steps = [node2str (batch ) for batch in topo .static_batches ()]
364+ assert steps == [
365+ {
366+ "flit-core==3.12.0" ,
367+ "setuptools==80.8.0" ,
368+ },
369+ {
370+ "imapclient==3.0.1" ,
371+ "jinja2==3.1.6" ,
372+ "markupsafe==3.0.2" ,
373+ "more-itertools==10.7.0" ,
374+ "packaging==25.0" ,
375+ },
376+ {
377+ "setuptools-scm==8.3.1" ,
378+ "wheel==0.46.1" ,
379+ },
380+ {
381+ "imapautofiler==1.14.0" ,
382+ "jaraco-classes==3.4.0" ,
383+ "jaraco-context==6.0.1" ,
384+ "jaraco-functools==4.1.0" ,
385+ "keyring==25.6.0" ,
386+ },
387+ {
388+ "cython==3.1.1" ,
389+ },
390+ {
391+ "pyyaml==6.0.2" ,
392+ },
393+ ]
0 commit comments