@@ -13,9 +13,9 @@ use itertools::Itertools;
1313use pixi_build_backend_passthrough:: PassthroughBackend ;
1414use pixi_build_frontend:: { BackendOverride , InMemoryOverriddenBackends } ;
1515use pixi_command_dispatcher:: {
16- BuildEnvironment , CacheDirs , CommandDispatcher , Executor , InstallPixiEnvironmentSpec ,
17- InstantiateToolEnvironmentSpec , PackageIdentifier , PixiEnvironmentSpec ,
18- SourceBuildCacheStatusSpec ,
16+ BuildEnvironment , CacheDirs , CommandDispatcher , Executor , GetOutputDependenciesSpec ,
17+ InstallPixiEnvironmentSpec , InstantiateToolEnvironmentSpec , PackageIdentifier ,
18+ PixiEnvironmentSpec , SourceBuildCacheStatusSpec ,
1919} ;
2020use pixi_config:: default_channel_config;
2121use pixi_record:: PinnedPathSpec ;
@@ -609,3 +609,173 @@ async fn source_build_cache_status_clear_works() {
609609 "Original Arc should be deallocated after cache clear"
610610 ) ;
611611}
612+
613+ /// Tests that `get_output_dependencies` correctly retrieves build, host, and run
614+ /// dependencies for a specific output from a source package using the in-memory
615+ /// backend.
616+ #[ tokio:: test]
617+ pub async fn test_get_output_dependencies ( ) {
618+ // Setup: Create a dispatcher with the in-memory backend
619+ let root_dir = workspaces_dir ( ) . join ( "output-dependencies" ) ;
620+ let tempdir = tempfile:: tempdir ( ) . unwrap ( ) ;
621+ let ( tool_platform, tool_virtual_packages) = tool_platform ( ) ;
622+
623+ let dispatcher = CommandDispatcher :: builder ( )
624+ . with_root_dir ( root_dir. clone ( ) )
625+ . with_cache_dirs ( default_cache_dirs ( ) . with_workspace ( tempdir. path ( ) . to_path_buf ( ) ) )
626+ . with_executor ( Executor :: Serial )
627+ . with_tool_platform ( tool_platform, tool_virtual_packages. clone ( ) )
628+ . with_backend_overrides ( BackendOverride :: from_memory (
629+ PassthroughBackend :: instantiator ( ) ,
630+ ) )
631+ . finish ( ) ;
632+
633+ // Pin the source spec to a path
634+ let pinned_source = PinnedPathSpec {
635+ path : "test-package" . into ( ) ,
636+ }
637+ . into ( ) ;
638+
639+ // Create the spec for getting output dependencies
640+ let spec = GetOutputDependenciesSpec {
641+ source : pinned_source,
642+ output_name : PackageName :: new_unchecked ( "test-package" ) ,
643+ channel_config : default_channel_config ( ) ,
644+ channels : vec ! [ ] ,
645+ build_environment : BuildEnvironment :: simple ( tool_platform, tool_virtual_packages) ,
646+ variants : None ,
647+ enabled_protocols : Default :: default ( ) ,
648+ } ;
649+
650+ // Act: Get the output dependencies
651+ let result = dispatcher
652+ . get_output_dependencies ( spec)
653+ . await
654+ . map_err ( |e| format_diagnostic ( & e) )
655+ . expect ( "get_output_dependencies should succeed" ) ;
656+
657+ // Assert: Verify the dependencies are returned correctly
658+ assert ! (
659+ result. build_dependencies. is_some( ) ,
660+ "Build dependencies should be present"
661+ ) ;
662+ assert ! (
663+ result. host_dependencies. is_some( ) ,
664+ "Host dependencies should be present"
665+ ) ;
666+
667+ let build_deps = result. build_dependencies . unwrap ( ) ;
668+ let host_deps = result. host_dependencies . unwrap ( ) ;
669+ let run_deps = result. run_dependencies ;
670+
671+ // Verify build dependencies (cmake, make)
672+ let build_dep_names: Vec < _ > = build_deps
673+ . depends
674+ . iter ( )
675+ . map ( |dep| dep. name . as_str ( ) )
676+ . sorted ( )
677+ . collect ( ) ;
678+ assert_eq ! (
679+ build_dep_names,
680+ vec![ "cmake" , "make" ] ,
681+ "Build dependencies should include cmake and make"
682+ ) ;
683+
684+ // Verify host dependencies (zlib, openssl)
685+ let host_dep_names: Vec < _ > = host_deps
686+ . depends
687+ . iter ( )
688+ . map ( |dep| dep. name . as_str ( ) )
689+ . sorted ( )
690+ . collect ( ) ;
691+ assert_eq ! (
692+ host_dep_names,
693+ vec![ "openssl" , "zlib" ] ,
694+ "Host dependencies should include zlib and openssl"
695+ ) ;
696+
697+ // Verify run dependencies (python, numpy)
698+ let run_dep_names: Vec < _ > = run_deps
699+ . depends
700+ . iter ( )
701+ . map ( |dep| dep. name . as_str ( ) )
702+ . sorted ( )
703+ . collect ( ) ;
704+ assert_eq ! (
705+ run_dep_names,
706+ vec![ "numpy" , "python" ] ,
707+ "Run dependencies should include python and numpy"
708+ ) ;
709+ }
710+
711+ /// Tests that `get_output_dependencies` returns an appropriate error when the
712+ /// specified output is not found in the source package.
713+ #[ tokio:: test]
714+ pub async fn test_get_output_dependencies_output_not_found ( ) {
715+ // Setup: Create a dispatcher with the in-memory backend
716+ let root_dir = workspaces_dir ( ) . join ( "output-dependencies" ) ;
717+ let tempdir = tempfile:: tempdir ( ) . unwrap ( ) ;
718+ let ( tool_platform, tool_virtual_packages) = tool_platform ( ) ;
719+
720+ let dispatcher = CommandDispatcher :: builder ( )
721+ . with_root_dir ( root_dir. clone ( ) )
722+ . with_cache_dirs ( default_cache_dirs ( ) . with_workspace ( tempdir. path ( ) . to_path_buf ( ) ) )
723+ . with_executor ( Executor :: Serial )
724+ . with_tool_platform ( tool_platform, tool_virtual_packages. clone ( ) )
725+ . with_backend_overrides ( BackendOverride :: from_memory (
726+ PassthroughBackend :: instantiator ( ) ,
727+ ) )
728+ . finish ( ) ;
729+
730+ // Pin the source spec to a path
731+ let pinned_source = PinnedPathSpec {
732+ path : "test-package" . into ( ) ,
733+ }
734+ . into ( ) ;
735+
736+ // Create the spec with a non-existent output name
737+ let spec = GetOutputDependenciesSpec {
738+ source : pinned_source,
739+ output_name : PackageName :: new_unchecked ( "non-existent-output" ) ,
740+ channel_config : default_channel_config ( ) ,
741+ channels : vec ! [ ] ,
742+ build_environment : BuildEnvironment :: simple ( tool_platform, tool_virtual_packages) ,
743+ variants : None ,
744+ enabled_protocols : Default :: default ( ) ,
745+ } ;
746+
747+ // Act: Try to get the output dependencies
748+ let error = dispatcher
749+ . get_output_dependencies ( spec)
750+ . await
751+ . expect_err ( "Expected OutputNotFound error" ) ;
752+
753+ // Assert: Verify we got the expected error type
754+ use pixi_command_dispatcher:: { CommandDispatcherError , GetOutputDependenciesError } ;
755+ match error {
756+ CommandDispatcherError :: Failed ( GetOutputDependenciesError :: OutputNotFound {
757+ output_name,
758+ available_outputs,
759+ } ) => {
760+ assert_eq ! (
761+ output_name. as_source( ) ,
762+ "non-existent-output" ,
763+ "Error should contain the requested output name"
764+ ) ;
765+ assert_eq ! (
766+ available_outputs. len( ) ,
767+ 1 ,
768+ "Should have one available output"
769+ ) ;
770+ assert_eq ! (
771+ available_outputs[ 0 ] . as_source( ) ,
772+ "test-package" ,
773+ "Available outputs should include test-package"
774+ ) ;
775+ }
776+ other => panic ! (
777+ "Expected OutputNotFound error, got: {}" ,
778+ format_diagnostic( & other)
779+ ) ,
780+ }
781+ }
0 commit comments