@@ -671,6 +671,155 @@ d_dir/
671671 ) ;
672672}
673673
674+ #[ cfg( unix) ]
675+ #[ test]
676+ fn suggest_value_hint_file_path_symlink_to_dir ( ) {
677+ use std:: os:: unix:: fs:: symlink;
678+
679+ let mut cmd = Command :: new ( "dynamic" ) . arg (
680+ clap:: Arg :: new ( "input" )
681+ . long ( "input" )
682+ . short ( 'i' )
683+ . value_hint ( clap:: ValueHint :: FilePath ) ,
684+ ) ;
685+
686+ let testdir = snapbox:: dir:: DirRoot :: mutable_temp ( ) . unwrap ( ) ;
687+ let testdir_path = testdir. path ( ) . unwrap ( ) ;
688+
689+ fs:: create_dir_all ( testdir_path. join ( "real_dir" ) ) . unwrap ( ) ;
690+ fs:: write ( testdir_path. join ( "real_dir/file.txt" ) , "" ) . unwrap ( ) ;
691+ symlink ( "real_dir" , testdir_path. join ( "link_dir" ) ) . unwrap ( ) ;
692+
693+ // Symlink to directory should appear with trailing slash
694+ assert_data_eq ! (
695+ complete!( cmd, "--input [TAB]" , current_dir = Some ( testdir_path) ) ,
696+ snapbox:: str ![ [ r#"
697+ link_dir/
698+ real_dir/
699+ "# ] ] ,
700+ ) ;
701+
702+ // Should be able to complete through the symlink
703+ assert_data_eq ! (
704+ complete!( cmd, "--input link_dir/[TAB]" , current_dir = Some ( testdir_path) ) ,
705+ snapbox:: str ![ "link_dir/file.txt" ] ,
706+ ) ;
707+ }
708+
709+ #[ cfg( unix) ]
710+ #[ test]
711+ fn suggest_value_hint_file_path_symlink_to_file ( ) {
712+ use std:: os:: unix:: fs:: symlink;
713+
714+ let mut cmd = Command :: new ( "dynamic" ) . arg (
715+ clap:: Arg :: new ( "input" )
716+ . long ( "input" )
717+ . short ( 'i' )
718+ . value_hint ( clap:: ValueHint :: FilePath ) ,
719+ ) ;
720+
721+ let testdir = snapbox:: dir:: DirRoot :: mutable_temp ( ) . unwrap ( ) ;
722+ let testdir_path = testdir. path ( ) . unwrap ( ) ;
723+
724+ fs:: write ( testdir_path. join ( "real_file.txt" ) , "" ) . unwrap ( ) ;
725+ symlink ( "real_file.txt" , testdir_path. join ( "link_file.txt" ) ) . unwrap ( ) ;
726+
727+ // Symlink to file should appear without trailing slash
728+ assert_data_eq ! (
729+ complete!( cmd, "--input [TAB]" , current_dir = Some ( testdir_path) ) ,
730+ snapbox:: str ![ [ r#"
731+ link_file.txt
732+ real_file.txt
733+ "# ] ] ,
734+ ) ;
735+ }
736+
737+ #[ cfg( unix) ]
738+ #[ test]
739+ fn suggest_value_hint_dir_path_symlink ( ) {
740+ use std:: os:: unix:: fs:: symlink;
741+
742+ let mut cmd = Command :: new ( "dynamic" ) . arg (
743+ clap:: Arg :: new ( "input" )
744+ . long ( "input" )
745+ . short ( 'i' )
746+ . value_hint ( clap:: ValueHint :: DirPath ) ,
747+ ) ;
748+
749+ let testdir = snapbox:: dir:: DirRoot :: mutable_temp ( ) . unwrap ( ) ;
750+ let testdir_path = testdir. path ( ) . unwrap ( ) ;
751+
752+ fs:: create_dir_all ( testdir_path. join ( "real_dir" ) ) . unwrap ( ) ;
753+ fs:: write ( testdir_path. join ( "real_file.txt" ) , "" ) . unwrap ( ) ;
754+ symlink ( "real_dir" , testdir_path. join ( "link_dir" ) ) . unwrap ( ) ;
755+ symlink ( "real_file.txt" , testdir_path. join ( "link_file.txt" ) ) . unwrap ( ) ;
756+
757+ // Symlink to directory should have trailing slash
758+ assert_data_eq ! (
759+ complete!( cmd, "--input [TAB]" , current_dir = Some ( testdir_path) ) ,
760+ snapbox:: str ![ [ r#"
761+ .
762+ link_dir/
763+ real_dir/
764+ "# ] ] ,
765+ ) ;
766+ }
767+
768+ #[ cfg( unix) ]
769+ #[ test]
770+ fn suggest_value_hint_file_path_broken_symlink ( ) {
771+ use std:: os:: unix:: fs:: symlink;
772+
773+ let mut cmd = Command :: new ( "dynamic" ) . arg (
774+ clap:: Arg :: new ( "input" )
775+ . long ( "input" )
776+ . short ( 'i' )
777+ . value_hint ( clap:: ValueHint :: FilePath ) ,
778+ ) ;
779+
780+ let testdir = snapbox:: dir:: DirRoot :: mutable_temp ( ) . unwrap ( ) ;
781+ let testdir_path = testdir. path ( ) . unwrap ( ) ;
782+
783+ fs:: write ( testdir_path. join ( "real_file.txt" ) , "" ) . unwrap ( ) ;
784+ symlink ( "nonexistent" , testdir_path. join ( "broken_link" ) ) . unwrap ( ) ;
785+
786+ // Broken symlink should not appear for FilePath (target doesn't exist)
787+ // but should not cause a crash
788+ assert_data_eq ! (
789+ complete!( cmd, "--input [TAB]" , current_dir = Some ( testdir_path) ) ,
790+ snapbox:: str ![ "real_file.txt" ] ,
791+ ) ;
792+ }
793+
794+ #[ cfg( unix) ]
795+ #[ test]
796+ fn suggest_value_hint_any_path_broken_symlink ( ) {
797+ use std:: os:: unix:: fs:: symlink;
798+
799+ let mut cmd = Command :: new ( "dynamic" ) . arg (
800+ clap:: Arg :: new ( "input" )
801+ . long ( "input" )
802+ . short ( 'i' )
803+ . value_hint ( clap:: ValueHint :: AnyPath ) ,
804+ ) ;
805+
806+ let testdir = snapbox:: dir:: DirRoot :: mutable_temp ( ) . unwrap ( ) ;
807+ let testdir_path = testdir. path ( ) . unwrap ( ) ;
808+
809+ fs:: write ( testdir_path. join ( "real_file.txt" ) , "" ) . unwrap ( ) ;
810+ symlink ( "nonexistent" , testdir_path. join ( "broken_link" ) ) . unwrap ( ) ;
811+
812+ // Broken symlink should appear for AnyPath since filter is |_| true
813+ assert_data_eq ! (
814+ complete!( cmd, "--input [TAB]" , current_dir = Some ( testdir_path) ) ,
815+ snapbox:: str ![ [ r#"
816+ .
817+ broken_link
818+ real_file.txt
819+ "# ] ] ,
820+ ) ;
821+ }
822+
674823#[ test]
675824fn suggest_custom_arg_value ( ) {
676825 fn custom_completer ( ) -> Vec < CompletionCandidate > {
0 commit comments