@@ -30,6 +30,14 @@ private def stdin_to_stdout_command
3030 {% end % }
3131end
3232
33+ private def stdin_to_stderr_command (status = 0 )
34+ {% if flag?(:win32 ) % }
35+ {" powershell.exe" , {" -C" , " while ($line = [Console]::In.ReadLine()) { [Console]::Error.WriteLine($line) }; exit #{ status } " }}
36+ {% else % }
37+ {" /bin/sh" , {" -c" , " cat 1>&2; exit #{ status } " }}
38+ {% end % }
39+ end
40+
3341private def print_env_command
3442 {% if flag?(:win32 ) % }
3543 # cmd adds these by itself, clear them out before printing.
@@ -769,6 +777,244 @@ describe Process do
769777 end
770778 end
771779
780+ describe " .capture_result" do
781+ it " captures stdout" do
782+ result = Process .capture_result(to_ary(shell_command(" echo hello" )))
783+ result.status.success?.should be_true
784+ result.output?.should eq " hello#{ newline } "
785+ result.error?.should eq " "
786+ end
787+
788+ it " captures stdout from stdin" do
789+ result = Process .capture_result(to_ary(stdin_to_stdout_command), input: IO ::Memory .new(" hello" ))
790+ result.status.success?.should be_true
791+ result.output.chomp.should eq " hello"
792+ end
793+
794+ it " ignores stdout if output is IO" do
795+ io = IO ::Memory .new
796+ result = Process .capture_result(to_ary(stdin_to_stdout_command), input: IO ::Memory .new(" hello" ), output: io)
797+ result.status.success?.should be_true
798+ result.output?.should be_nil
799+ result.error?.should eq " "
800+ io.to_s.chomp.should eq " hello"
801+ end
802+
803+ it " ignores stdout if output is FileDescriptor" do
804+ reader, writer = IO .pipe
805+ result = Process .capture_result(to_ary(stdin_to_stdout_command), input: IO ::Memory .new(" hello\n " ), output: writer)
806+ result.status.success?.should be_true
807+ result.output?.should be_nil
808+ result.error?.should eq " "
809+ reader.gets.should eq " hello"
810+ end
811+
812+ it " captures stderr" do
813+ result = Process .capture_result(to_ary(shell_command(" 1>&2 echo hello" )))
814+ result.status.success?.should be_true
815+ result.output?.should eq " "
816+ result.error?.should eq " hello#{ newline } "
817+ end
818+
819+ it " ignores stderr if error is IO" do
820+ io = IO ::Memory .new
821+ result = Process .capture_result(to_ary(shell_command(" 1>&2 echo hello" )), error: io)
822+ result.status.success?.should be_true
823+ result.output?.should eq " "
824+ result.error?.should be_nil
825+ io.to_s.should eq " hello#{ newline } "
826+ end
827+
828+ it " ignores stderr if error is FileDescriptor" do
829+ reader, writer = IO .pipe
830+ result = Process .capture_result(to_ary(shell_command(" 1>&2 echo hello" )), error: writer)
831+ result.status.success?.should be_true
832+ result.output?.should eq " "
833+ result.error?.should be_nil
834+ reader.gets.should eq " hello"
835+ end
836+
837+ it " doesn't capture closed stdout" do
838+ result = Process .capture_result(to_ary(shell_command(" echo hello" )), output: :close )
839+ result.output?.should be_nil
840+ result.error?.should_not be_nil
841+ end
842+
843+ it " doesn't capture closed stderr" do
844+ # FIXME: Autocasting breaks in the interpreter
845+ result = Process .capture_result(to_ary(shell_command(" 1>&2 echo hello" )), error: Process ::Redirect ::Close )
846+ result.status.success?.should be_true
847+ result.output?.should eq " "
848+ result.error?.should be_nil
849+ end
850+
851+ pending " truncates error output" , tags: %w[slow] do
852+ dashes32 = " -" * (32 << 10 )
853+ input = IO ::Memory .new(" #{ dashes32 } X#{ dashes32 } " )
854+ result = Process .capture_result(to_ary(stdin_to_stderr_command), input: input)
855+ result.status.success?.should be_true
856+ result.output?.should eq " "
857+ error = result.error.should be_a(String )
858+ error.should contain " \n ...omitted 1 bytes...\n "
859+ error.count(" -" ).should eq(32 << 11 )
860+ end
861+
862+ it " reports status" do
863+ Process .capture_result(to_ary(exit_code_command(0 ))).status.exit_code.should eq(0 )
864+ Process .capture_result(to_ary(exit_code_command(123 ))).status.exit_code.should eq(123 )
865+ end
866+
867+ it " raises if process cannot execute" do
868+ expect_raises(File ::NotFoundError , " Error executing process: 'foobarbaz'" ) do
869+ Process .capture_result([" foobarbaz" ])
870+ end
871+ end
872+ end
873+
874+ describe " .capture_result?" do
875+ it " captures stdout" do
876+ result = Process .capture_result?(to_ary(shell_command(" echo hello" ))).should be_a(Process ::Result )
877+ result.status.success?.should be_true
878+ result.output?.should eq " hello#{ newline } "
879+ result.error?.should eq " "
880+ end
881+
882+ it " captures stdout from stdin" do
883+ result = Process .capture_result?(to_ary(stdin_to_stdout_command), input: IO ::Memory .new(" hello" )).should be_a(Process ::Result )
884+ result.status.success?.should be_true
885+ result.output.chomp.should eq " hello"
886+ end
887+
888+ it " ignores stdout if output is IO" do
889+ io = IO ::Memory .new
890+ result = Process .capture_result?(to_ary(stdin_to_stdout_command), input: IO ::Memory .new(" hello" ), output: io).should be_a(Process ::Result )
891+ result.status.success?.should be_true
892+ result.output?.should be_nil
893+ result.error?.should eq " "
894+ io.to_s.chomp.should eq " hello"
895+ end
896+
897+ it " ignores stdout if output is FileDescriptor" do
898+ reader, writer = IO .pipe
899+ result = Process .capture_result?(to_ary(stdin_to_stdout_command), input: IO ::Memory .new(" hello\n " ), output: writer).should be_a(Process ::Result )
900+ result.status.success?.should be_true
901+ result.output?.should be_nil
902+ result.error?.should eq " "
903+ reader.gets.should eq " hello"
904+ end
905+
906+ it " captures stderr" do
907+ result = Process .capture_result?(to_ary(shell_command(" 1>&2 echo hello" ))).should be_a(Process ::Result )
908+ result.status.success?.should be_true
909+ result.output?.should eq " "
910+ result.error?.should eq " hello#{ newline } "
911+ end
912+
913+ it " ignores stderr if error is IO" do
914+ io = IO ::Memory .new
915+ result = Process .capture_result?(to_ary(shell_command(" 1>&2 echo hello" )), error: io).should be_a(Process ::Result )
916+ result.status.success?.should be_true
917+ result.output?.should eq " "
918+ result.error?.should be_nil
919+ io.to_s.should eq " hello#{ newline } "
920+ end
921+
922+ it " ignores stderr if error is FileDescriptor" do
923+ reader, writer = IO .pipe
924+ result = Process .capture_result?(to_ary(shell_command(" 1>&2 echo hello" )), error: writer).should be_a(Process ::Result )
925+ result.status.success?.should be_true
926+ result.output?.should eq " "
927+ result.error?.should be_nil
928+ reader.gets.should eq " hello"
929+ end
930+
931+ it " doesn't capture closed stdout" do
932+ result = Process .capture_result?(to_ary(shell_command(" echo hello" )), output: :close ).should be_a(Process ::Result )
933+ result.output?.should be_nil
934+ result.error?.should_not be_nil
935+ end
936+
937+ it " doesn't capture closed stderr" do
938+ # FIXME: Autocasting breaks in the interpreter
939+ result = Process .capture_result?(to_ary(shell_command(" 1>&2 echo hello" )), error: Process ::Redirect ::Close ).should be_a(Process ::Result )
940+ result.status.success?.should be_true
941+ result.output?.should eq " "
942+ result.error?.should be_nil
943+ end
944+
945+ pending " truncates error output" , tags: %w[slow] do
946+ dashes32 = " -" * (32 << 10 )
947+ input = IO ::Memory .new(" #{ dashes32 } X#{ dashes32 } " )
948+ result = Process .capture_result?(to_ary(stdin_to_stderr_command), input: input).should be_a(Process ::Result )
949+ result.status.success?.should be_true
950+ result.output?.should eq " "
951+ error = result.error.should be_a(String )
952+ error.should contain " \n ...omitted 1 bytes...\n "
953+ error.count(" -" ).should eq(32 << 11 )
954+ end
955+
956+ it " reports status" do
957+ result = Process .capture_result?(to_ary(exit_code_command(0 ))).should be_a(Process ::Result )
958+ result.status.exit_code.should eq(0 )
959+ result = Process .capture_result?(to_ary(exit_code_command(123 ))).should be_a(Process ::Result )
960+ result.status.exit_code.should eq(123 )
961+ end
962+
963+ it " raises if process cannot execute" do
964+ Process .capture_result?([" foobarbaz" ]).should be_nil
965+ end
966+ end
967+
968+ describe " .capture" do
969+ it " captures stdout" do
970+ Process .capture(to_ary(shell_command(" echo hello" ))).should eq " hello#{ newline } "
971+ end
972+
973+ it " captures stdout from stdin" do
974+ Process .capture(to_ary(stdin_to_stdout_command), input: IO ::Memory .new(" hello" )).chomp.should eq " hello"
975+ end
976+
977+ it " raises on non-zero exit status" do
978+ error = expect_raises(Process ::ExitError , /^Command \[ .*exit 1.*\] failed: Process exited with status 1$/ ) do
979+ Process .capture(to_ary(exit_code_command(1 )))
980+ end
981+ error.result.status.exit_code.should eq 1
982+ end
983+
984+ it " raises if process cannot execute" do
985+ expect_raises(File ::NotFoundError , " Error executing process: 'foobarbaz'" ) do
986+ Process .capture([" foobarbaz" ])
987+ end
988+ end
989+
990+ it " captures stderr in error message" do
991+ error = expect_raises(Process ::ExitError ) do
992+ Process .capture(to_ary(stdin_to_stderr_command(status: 1 )), input: IO ::Memory .new(" hello" ))
993+ end
994+ error.result.error.chomp.should eq " hello"
995+ end
996+ end
997+
998+ describe " .capture?" do
999+ it " captures stdout" do
1000+ Process .capture?(to_ary(shell_command(" echo hello" ))).should eq " hello#{ newline } "
1001+ end
1002+
1003+ it " captures stdout from stdin" do
1004+ Process .capture?(to_ary(stdin_to_stdout_command), input: IO ::Memory .new(" hello" )).try(& .chomp).should eq " hello"
1005+ end
1006+
1007+ it " returns nil on unsuccessful exit" do
1008+ Process .capture?(to_ary(exit_code_command(1 ))).should be_nil
1009+ end
1010+
1011+ it " raises if process cannot execute" do
1012+ expect_raises(File ::NotFoundError , " Error executing process: 'foobarbaz'" ) do
1013+ Process .capture([" foobarbaz" ])
1014+ end
1015+ end
1016+ end
1017+
7721018 describe " .on_interrupt" do
7731019 it " compiles" do
7741020 typeof (Process .on_interrupt { })
0 commit comments