diff --git a/spec/std/process/utils_spec.cr b/spec/std/process/utils_spec.cr new file mode 100644 index 000000000000..e971a0209e4a --- /dev/null +++ b/spec/std/process/utils_spec.cr @@ -0,0 +1,120 @@ +require "spec" + +describe Process do + describe ".executable_path" do + it "searches executable" do + Process.executable_path.should be_a(String | Nil) + end + end + + describe ".quote_posix" do + it { Process.quote_posix("").should eq "''" } + it { Process.quote_posix(" ").should eq "' '" } + it { Process.quote_posix("$hi").should eq "'$hi'" } + it { Process.quote_posix(orig = "aZ5+,-./:=@_").should eq orig } + it { Process.quote_posix(orig = "cafe").should eq orig } + it { Process.quote_posix("café").should eq "'café'" } + it { Process.quote_posix("I'll").should eq %('I'"'"'ll') } + it { Process.quote_posix("'").should eq %(''"'"'') } + it { Process.quote_posix("\\").should eq "'\\'" } + + context "join" do + it { Process.quote_posix([] of String).should eq "" } + it { Process.quote_posix(["my file.txt", "another.txt"]).should eq "'my file.txt' another.txt" } + it { Process.quote_posix(["foo ", "", " ", " bar"]).should eq "'foo ' '' ' ' ' bar'" } + it { Process.quote_posix(["foo'", "\"bar"]).should eq %('foo'"'"'' '"bar') } + end + end + + describe ".quote_windows" do + it { Process.quote_windows("").should eq %("") } + it { Process.quote_windows(" ").should eq %(" ") } + it { Process.quote_windows(orig = "%hi%").should eq orig } + it { Process.quote_windows(%q(C:\"foo" project.txt)).should eq %q("C:\\\"foo\" project.txt") } + it { Process.quote_windows(%q(C:\"foo"_project.txt)).should eq %q(C:\\\"foo\"_project.txt) } + it { Process.quote_windows(%q(C:\Program Files\Foo Bar\foobar.exe)).should eq %q("C:\Program Files\Foo Bar\foobar.exe") } + it { Process.quote_windows(orig = "café").should eq orig } + it { Process.quote_windows(%(")).should eq %q(\") } + it { Process.quote_windows(%q(a\\b\ c\)).should eq %q("a\\b\ c\\") } + it { Process.quote_windows(orig = %q(a\\b\c\)).should eq orig } + + context "join" do + it { Process.quote_windows([] of String).should eq "" } + it { Process.quote_windows(["my file.txt", "another.txt"]).should eq %("my file.txt" another.txt) } + it { Process.quote_windows(["foo ", "", " ", " bar"]).should eq %("foo " "" " " " bar") } + end + end + + {% if flag?(:unix) %} + describe ".parse_arguments" do + it "uses the native platform rules" do + Process.parse_arguments(%q[a\ b'c']).should eq [%q[a bc]] + end + end + {% elsif flag?(:win32) %} + describe ".parse_arguments" do + it "uses the native platform rules" do + Process.parse_arguments(%q[a\ b'c']).should eq [%q[a\], %q[b'c']] + end + end + {% else %} + pending ".parse_arguments" + {% end %} + + describe ".parse_arguments_posix" do + it { Process.parse_arguments_posix(%q[]).should eq([] of String) } + it { Process.parse_arguments_posix(%q[ ]).should eq([] of String) } + it { Process.parse_arguments_posix(%q[foo]).should eq [%q[foo]] } + it { Process.parse_arguments_posix(%q[foo bar]).should eq [%q[foo], %q[bar]] } + it { Process.parse_arguments_posix(%q["foo bar" 'foo bar' baz]).should eq [%q[foo bar], %q[foo bar], %q[baz]] } + it { Process.parse_arguments_posix(%q["foo bar"'foo bar'baz]).should eq [%q[foo barfoo barbaz]] } + it { Process.parse_arguments_posix(%q[foo\ bar]).should eq [%q[foo bar]] } + it { Process.parse_arguments_posix(%q["foo\ bar"]).should eq [%q[foo\ bar]] } + it { Process.parse_arguments_posix(%q['foo\ bar']).should eq [%q[foo\ bar]] } + it { Process.parse_arguments_posix(%q[\]).should eq [%q[\]] } + it { Process.parse_arguments_posix(%q["foo bar" '\hello/' Fizz\ Buzz]).should eq [%q[foo bar], %q[\hello/], %q[Fizz Buzz]] } + it { Process.parse_arguments_posix(%q[foo"bar"baz]).should eq [%q[foobarbaz]] } + it { Process.parse_arguments_posix(%q[foo'bar'baz]).should eq [%q[foobarbaz]] } + it { Process.parse_arguments_posix(%q[this 'is a "'very wei"rd co"m"mand please" don't do t'h'a't p"leas"e]).should eq [%q[this], %q[is a "very], %q[weird command please], %q[dont do that], %q[please]] } + + it "raises an error when double quote is unclosed" do + expect_raises ArgumentError, "Unmatched quote" do + Process.parse_arguments_posix(%q["foo]) + end + end + + it "raises an error if single quote is unclosed" do + expect_raises ArgumentError, "Unmatched quote" do + Process.parse_arguments_posix(%q['foo]) + end + end + end + + describe ".parse_arguments_windows" do + it { Process.parse_arguments_windows(%q[]).should eq([] of String) } + it { Process.parse_arguments_windows(%q[ ]).should eq([] of String) } + it { Process.parse_arguments_windows(%q[foo]).should eq [%q[foo]] } + it { Process.parse_arguments_windows(%q[foo bar]).should eq [%q[foo], %q[bar]] } + it { Process.parse_arguments_windows(%q["foo bar" 'foo bar' baz]).should eq [%q[foo bar], %q['foo], %q[bar'], %q[baz]] } + it { Process.parse_arguments_windows(%q["foo bar"baz]).should eq [%q[foo barbaz]] } + it { Process.parse_arguments_windows(%q[foo"bar baz"]).should eq [%q[foobar baz]] } + it { Process.parse_arguments_windows(%q[foo\bar]).should eq [%q[foo\bar]] } + it { Process.parse_arguments_windows(%q[foo\ bar]).should eq [%q[foo\], %q[bar]] } + it { Process.parse_arguments_windows(%q[foo\\bar]).should eq [%q[foo\\bar]] } + it { Process.parse_arguments_windows(%q[foo\\\bar]).should eq [%q[foo\\\bar]] } + it { Process.parse_arguments_windows(%q[ /LIBPATH:C:\crystal\lib ]).should eq [%q[/LIBPATH:C:\crystal\lib]] } + it { Process.parse_arguments_windows(%q[a\\\b d"e f"g h]).should eq [%q[a\\\b], %q[de fg], %q[h]] } + it { Process.parse_arguments_windows(%q[a\\\"b c d]).should eq [%q[a\"b], %q[c], %q[d]] } + it { Process.parse_arguments_windows(%q[a\\\\"b c" d e]).should eq [%q[a\\b c], %q[d], %q[e]] } + it { Process.parse_arguments_windows(%q["foo bar" '\hello/' Fizz\ Buzz]).should eq [%q[foo bar], %q['\hello/'], %q[Fizz\], %q[Buzz]] } + it { Process.parse_arguments_windows(%q[this 'is a "'very wei"rd co"m"mand please" don't do t'h'a't p"leas"e"]).should eq [%q[this], %q['is], %q[a], %q['very weird], %q[command], %q[please don't do t'h'a't please]] } + + it "raises an error if double quote is unclosed" do + expect_raises ArgumentError, "Unmatched quote" do + Process.parse_arguments_windows(%q["foo]) + Process.parse_arguments_windows(%q[\"foo]) + Process.parse_arguments_windows(%q["f\"oo\\\"]) + end + end + end +end diff --git a/spec/std/process_spec.cr b/spec/std/process_spec.cr index e051e328c66e..c28d74d83882 100644 --- a/spec/std/process_spec.cr +++ b/spec/std/process_spec.cr @@ -61,262 +61,263 @@ private def newline end describe Process do - it "runs true" do - process = Process.new(*exit_code_command(0)) - process.wait.exit_code.should eq(0) - end - - it "runs false" do - process = Process.new(*exit_code_command(1)) - process.wait.exit_code.should eq(1) - end - - it "raises if command doesn't exist" do - expect_raises(File::NotFoundError, "Error executing process: 'foobarbaz'") do - Process.new("foobarbaz") + describe ".new" do + it "raises if command doesn't exist" do + expect_raises(File::NotFoundError, "Error executing process: 'foobarbaz'") do + Process.new("foobarbaz") + end end - end - pending_win32 "raises if command is not executable" do - with_tempfile("crystal-spec-run") do |path| - File.touch path - expect_raises(File::AccessDeniedError, "Error executing process: '#{path.inspect_unquoted}'") do - Process.new(path) + pending_win32 "raises if command is not executable" do + with_tempfile("crystal-spec-run") do |path| + File.touch path + expect_raises(File::AccessDeniedError, "Error executing process: '#{path.inspect_unquoted}'") do + Process.new(path) + end end end - end - it "raises if command could not be executed" do - with_tempfile("crystal-spec-run") do |path| - File.touch path - command = File.join(path, "foo") - expect_raises(IO::Error, "Error executing process: '#{command.inspect_unquoted}'") do - Process.new(command) + it "raises if command could not be executed" do + with_tempfile("crystal-spec-run") do |path| + File.touch path + command = File.join(path, "foo") + expect_raises(IO::Error, "Error executing process: '#{command.inspect_unquoted}'") do + Process.new(command) + end end end end - it "run waits for the process" do - Process.run(*exit_code_command(0)).exit_code.should eq(0) - end - - it "runs true in block" do - Process.run(*exit_code_command(0)) { } - $?.exit_code.should eq(0) - end + describe "#wait" do + it "successful exit code" do + process = Process.new(*exit_code_command(0)) + process.wait.exit_code.should eq(0) + end - it "receives arguments in array" do - command, args = exit_code_command(123) - Process.run(command, args.to_a).exit_code.should eq(123) + it "unsuccessful exit code" do + process = Process.new(*exit_code_command(1)) + process.wait.exit_code.should eq(1) + end end - it "receives arguments in tuple" do - command, args = exit_code_command(123) - Process.run(command, args.as(Tuple)).exit_code.should eq(123) - end + describe ".run" do + it "waits for the process" do + Process.run(*exit_code_command(0)).exit_code.should eq(0) + end - it "redirects output to /dev/null" do - # This doesn't test anything but no output should be seen while running tests - command, args = {% if flag?(:win32) %} - {"cmd.exe", {"/c", "dir"}} - {% else %} - {"/bin/ls", [] of String} - {% end %} - Process.run(command, args, output: Process::Redirect::Close).exit_code.should eq(0) - end + it "runs true in block" do + Process.run(*exit_code_command(0)) { } + $?.exit_code.should eq(0) + end - it "gets output" do - value = Process.run(*shell_command("echo hello")) do |proc| - proc.output.gets_to_end + it "receives arguments in array" do + command, args = exit_code_command(123) + Process.run(command, args.to_a).exit_code.should eq(123) end - value.should eq("hello#{newline}") - end - pending_win32 "sends input in IO" do - value = Process.run(*stdin_to_stdout_command, input: IO::Memory.new("hello")) do |proc| - proc.input?.should be_nil - proc.output.gets_to_end + it "receives arguments in tuple" do + command, args = exit_code_command(123) + Process.run(command, args.as(Tuple)).exit_code.should eq(123) end - value.should eq("hello") - end - it "sends output to IO" do - output = IO::Memory.new - Process.run(*shell_command("echo hello"), output: output) - output.to_s.should eq("hello#{newline}") - end + it "redirects output to /dev/null" do + # This doesn't test anything but no output should be seen while running tests + command, args = {% if flag?(:win32) %} + {"cmd.exe", {"/c", "dir"}} + {% else %} + {"/bin/ls", [] of String} + {% end %} + Process.run(command, args, output: Process::Redirect::Close).exit_code.should eq(0) + end - it "sends error to IO" do - error = IO::Memory.new - Process.run(*shell_command("1>&2 echo hello"), error: error) - error.to_s.should eq("hello#{newline}") - end + it "gets output" do + value = Process.run(*shell_command("echo hello")) do |proc| + proc.output.gets_to_end + end + value.should eq("hello#{newline}") + end - it "controls process in block" do - value = Process.run(*stdin_to_stdout_command, error: :inherit) do |proc| - proc.input.puts "hello" - proc.input.close - proc.output.gets_to_end + pending_win32 "sends input in IO" do + value = Process.run(*stdin_to_stdout_command, input: IO::Memory.new("hello")) do |proc| + proc.input?.should be_nil + proc.output.gets_to_end + end + value.should eq("hello") end - value.should eq("hello#{newline}") - end - it "closes ios after block" do - Process.run(*stdin_to_stdout_command) { } - $?.exit_code.should eq(0) - end + it "sends output to IO" do + output = IO::Memory.new + Process.run(*shell_command("echo hello"), output: output) + output.to_s.should eq("hello#{newline}") + end - {% if flag?(:unix) %} - it "chroot raises when unprivileged" do - status, output, _ = compile_and_run_source <<-'CRYSTAL' - begin - Process.chroot(".") - puts "FAIL" - rescue ex - puts ex.inspect - end - CRYSTAL + it "sends error to IO" do + error = IO::Memory.new + Process.run(*shell_command("1>&2 echo hello"), error: error) + error.to_s.should eq("hello#{newline}") + end - status.success?.should be_true - output.should eq("#\n") + it "controls process in block" do + value = Process.run(*stdin_to_stdout_command, error: :inherit) do |proc| + proc.input.puts "hello" + proc.input.close + proc.output.gets_to_end + end + value.should eq("hello#{newline}") end - {% end %} - it "sets working directory" do - parent = File.dirname(Dir.current) - command = {% if flag?(:win32) %} - "cmd.exe /c echo %cd%" - {% else %} - "pwd" - {% end %} - value = Process.run(command, shell: true, chdir: parent, output: Process::Redirect::Pipe) do |proc| - proc.output.gets_to_end + it "closes ios after block" do + Process.run(*stdin_to_stdout_command) { } + $?.exit_code.should eq(0) end - value.should eq "#{parent}#{newline}" - end - pending_win32 "disallows passing arguments to nowhere" do - expect_raises ArgumentError, /args.+@/ do - Process.run("foo bar", {"baz"}, shell: true) + it "sets working directory" do + parent = File.dirname(Dir.current) + command = {% if flag?(:win32) %} + "cmd.exe /c echo %cd%" + {% else %} + "pwd" + {% end %} + value = Process.run(command, shell: true, chdir: parent, output: Process::Redirect::Pipe) do |proc| + proc.output.gets_to_end + end + value.should eq "#{parent}#{newline}" end - end - pending_win32 "looks up programs in the $PATH with a shell" do - proc = Process.run(*exit_code_command(0), shell: true, output: Process::Redirect::Close) - proc.exit_code.should eq(0) - end + pending_win32 "disallows passing arguments to nowhere" do + expect_raises ArgumentError, /args.+@/ do + Process.run("foo bar", {"baz"}, shell: true) + end + end - pending_win32 "allows passing huge argument lists to a shell" do - proc = Process.new(%(echo "${@}"), {"a", "b"}, shell: true, output: Process::Redirect::Pipe) - output = proc.output.gets_to_end - proc.wait - output.should eq "a b\n" - end + pending_win32 "looks up programs in the $PATH with a shell" do + proc = Process.run(*exit_code_command(0), shell: true, output: Process::Redirect::Close) + proc.exit_code.should eq(0) + end - pending_win32 "does not run shell code in the argument list" do - proc = Process.new("echo", {"`echo hi`"}, shell: true, output: Process::Redirect::Pipe) - output = proc.output.gets_to_end - proc.wait - output.should eq "`echo hi`\n" - end + pending_win32 "allows passing huge argument lists to a shell" do + proc = Process.new(%(echo "${@}"), {"a", "b"}, shell: true, output: Process::Redirect::Pipe) + output = proc.output.gets_to_end + proc.wait + output.should eq "a b\n" + end - describe "environ" do - it "clears the environment" do - value = Process.run(*print_env_command, clear_env: true) do |proc| - proc.output.gets_to_end - end - value.should eq("") + pending_win32 "does not run shell code in the argument list" do + proc = Process.new("echo", {"`echo hi`"}, shell: true, output: Process::Redirect::Pipe) + output = proc.output.gets_to_end + proc.wait + output.should eq "`echo hi`\n" end - it "clears and sets an environment variable" do - value = Process.run(*print_env_command, clear_env: true, env: {"FOO" => "bar"}) do |proc| - proc.output.gets_to_end + describe "environ" do + it "clears the environment" do + value = Process.run(*print_env_command, clear_env: true) do |proc| + proc.output.gets_to_end + end + value.should eq("") end - value.should eq("FOO=bar#{newline}") - end - it "sets an environment variable" do - value = Process.run(*print_env_command, env: {"FOO" => "bar"}) do |proc| - proc.output.gets_to_end + it "clears and sets an environment variable" do + value = Process.run(*print_env_command, clear_env: true, env: {"FOO" => "bar"}) do |proc| + proc.output.gets_to_end + end + value.should eq("FOO=bar#{newline}") end - value.should match /(*ANYCRLF)^FOO=bar$/m - end - it "sets an empty environment variable" do - value = Process.run(*print_env_command, env: {"FOO" => ""}) do |proc| - proc.output.gets_to_end + it "sets an environment variable" do + value = Process.run(*print_env_command, env: {"FOO" => "bar"}) do |proc| + proc.output.gets_to_end + end + value.should match /(*ANYCRLF)^FOO=bar$/m end - value.should match /(*ANYCRLF)^FOO=$/m - end - it "deletes existing environment variable" do - ENV["FOO"] = "bar" - value = Process.run(*print_env_command, env: {"FOO" => nil}) do |proc| - proc.output.gets_to_end + it "sets an empty environment variable" do + value = Process.run(*print_env_command, env: {"FOO" => ""}) do |proc| + proc.output.gets_to_end + end + value.should match /(*ANYCRLF)^FOO=$/m end - value.should_not match /(*ANYCRLF)^FOO=/m - ensure - ENV.delete("FOO") - end - {% if flag?(:win32) %} - it "deletes existing environment variable case-insensitive" do + it "deletes existing environment variable" do ENV["FOO"] = "bar" - value = Process.run(*print_env_command, env: {"foo" => nil}) do |proc| + value = Process.run(*print_env_command, env: {"FOO" => nil}) do |proc| proc.output.gets_to_end end - value.should_not match /(*ANYCRLF)^FOO=/mi + value.should_not match /(*ANYCRLF)^FOO=/m ensure ENV.delete("FOO") end - {% end %} - it "preserves existing environment variable" do - ENV["FOO"] = "bar" - value = Process.run(*print_env_command) do |proc| - proc.output.gets_to_end - end - value.should match /(*ANYCRLF)^FOO=bar$/m - ensure - ENV.delete("FOO") - end + {% if flag?(:win32) %} + it "deletes existing environment variable case-insensitive" do + ENV["FOO"] = "bar" + value = Process.run(*print_env_command, env: {"foo" => nil}) do |proc| + proc.output.gets_to_end + end + value.should_not match /(*ANYCRLF)^FOO=/mi + ensure + ENV.delete("FOO") + end + {% end %} - it "preserves and sets an environment variable" do - ENV["FOO"] = "bar" - value = Process.run(*print_env_command, env: {"FOO2" => "bar2"}) do |proc| - proc.output.gets_to_end + it "preserves existing environment variable" do + ENV["FOO"] = "bar" + value = Process.run(*print_env_command) do |proc| + proc.output.gets_to_end + end + value.should match /(*ANYCRLF)^FOO=bar$/m + ensure + ENV.delete("FOO") end - value.should match /(*ANYCRLF)^FOO=bar$/m - value.should match /(*ANYCRLF)^FOO2=bar2$/m - ensure - ENV.delete("FOO") - end - it "overrides existing environment variable" do - ENV["FOO"] = "bar" - value = Process.run(*print_env_command, env: {"FOO" => "different"}) do |proc| - proc.output.gets_to_end + it "preserves and sets an environment variable" do + ENV["FOO"] = "bar" + value = Process.run(*print_env_command, env: {"FOO2" => "bar2"}) do |proc| + proc.output.gets_to_end + end + value.should match /(*ANYCRLF)^FOO=bar$/m + value.should match /(*ANYCRLF)^FOO2=bar2$/m + ensure + ENV.delete("FOO") end - value.should match /(*ANYCRLF)^FOO=different$/m - ensure - ENV.delete("FOO") - end - {% if flag?(:win32) %} - it "overrides existing environment variable case-insensitive" do + it "overrides existing environment variable" do ENV["FOO"] = "bar" - value = Process.run(*print_env_command, env: {"fOo" => "different"}) do |proc| + value = Process.run(*print_env_command, env: {"FOO" => "different"}) do |proc| proc.output.gets_to_end end - value.should_not match /(*ANYCRLF)^FOO=/m - value.should match /(*ANYCRLF)^fOo=different$/m + value.should match /(*ANYCRLF)^FOO=different$/m ensure ENV.delete("FOO") end - {% end %} + + {% if flag?(:win32) %} + it "overrides existing environment variable case-insensitive" do + ENV["FOO"] = "bar" + value = Process.run(*print_env_command, env: {"fOo" => "different"}) do |proc| + proc.output.gets_to_end + end + value.should_not match /(*ANYCRLF)^FOO=/m + value.should match /(*ANYCRLF)^fOo=different$/m + ensure + ENV.delete("FOO") + end + {% end %} + end + + pending_win32 "can link processes together" do + buffer = IO::Memory.new + Process.run(*stdin_to_stdout_command) do |cat| + Process.run(*stdin_to_stdout_command, input: cat.output, output: buffer) do + 1000.times { cat.input.puts "line" } + cat.close + end + end + buffer.to_s.lines.size.should eq(1000) + end end - describe "signal" do + describe "#signal" do pending_win32 "kills a process" do process = Process.new(*standing_command) process.signal(Signal::KILL).should be_nil @@ -330,46 +331,16 @@ describe Process do end end - pending_win32 "gets the pgid of a process id" do + pending_win32 "#terminate" do process = Process.new(*standing_command) - Process.pgid(process.pid).should be_a(Int64) - process.signal(Signal::KILL) - Process.pgid.should eq(Process.pgid(Process.pid)) - end + process.exists?.should be_true + process.terminated?.should be_false - pending_win32 "can link processes together" do - buffer = IO::Memory.new - Process.run(*stdin_to_stdout_command) do |cat| - Process.run(*stdin_to_stdout_command, input: cat.output, output: buffer) do - 1000.times { cat.input.puts "line" } - cat.close - end - end - buffer.to_s.lines.size.should eq(1000) + process.terminate + process.wait end - {% unless flag?(:preview_mt) || flag?(:win32) %} - it "executes the new process with exec" do - with_tempfile("crystal-spec-exec") do |path| - File.exists?(path).should be_false - - fork = Process.fork do - Process.exec("/usr/bin/env", {"touch", path}) - end - fork.wait - - File.exists?(path).should be_true - end - end - - it "gets error from exec" do - expect_raises(File::NotFoundError, "Error executing process: 'foobarbaz'") do - Process.exec("foobarbaz") - end - end - {% end %} - - pending_win32 "checks for existence" do + pending_win32 ".exists?" do # We can't reliably check whether it ever returns false, since we can't predict # how PIDs are used by the system, a new process might be spawned in between # reaping the one we would spawn and checking for it, using the now available @@ -391,129 +362,53 @@ describe Process do process.terminated?.should be_true end - pending_win32 "terminates the process" do + pending_win32 ".pgid" do process = Process.new(*standing_command) - process.exists?.should be_true - process.terminated?.should be_false - - process.terminate - process.wait - end - - describe "executable_path" do - it "searches executable" do - Process.executable_path.should be_a(String | Nil) - end - end - - describe "quote_posix" do - it { Process.quote_posix("").should eq "''" } - it { Process.quote_posix(" ").should eq "' '" } - it { Process.quote_posix("$hi").should eq "'$hi'" } - it { Process.quote_posix(orig = "aZ5+,-./:=@_").should eq orig } - it { Process.quote_posix(orig = "cafe").should eq orig } - it { Process.quote_posix("café").should eq "'café'" } - it { Process.quote_posix("I'll").should eq %('I'"'"'ll') } - it { Process.quote_posix("'").should eq %(''"'"'') } - it { Process.quote_posix("\\").should eq "'\\'" } - - context "join" do - it { Process.quote_posix([] of String).should eq "" } - it { Process.quote_posix(["my file.txt", "another.txt"]).should eq "'my file.txt' another.txt" } - it { Process.quote_posix(["foo ", "", " ", " bar"]).should eq "'foo ' '' ' ' ' bar'" } - it { Process.quote_posix(["foo'", "\"bar"]).should eq %('foo'"'"'' '"bar') } - end + Process.pgid(process.pid).should be_a(Int64) + process.signal(Signal::KILL) + Process.pgid.should eq(Process.pgid(Process.pid)) end - describe "quote_windows" do - it { Process.quote_windows("").should eq %("") } - it { Process.quote_windows(" ").should eq %(" ") } - it { Process.quote_windows(orig = "%hi%").should eq orig } - it { Process.quote_windows(%q(C:\"foo" project.txt)).should eq %q("C:\\\"foo\" project.txt") } - it { Process.quote_windows(%q(C:\"foo"_project.txt)).should eq %q(C:\\\"foo\"_project.txt) } - it { Process.quote_windows(%q(C:\Program Files\Foo Bar\foobar.exe)).should eq %q("C:\Program Files\Foo Bar\foobar.exe") } - it { Process.quote_windows(orig = "café").should eq orig } - it { Process.quote_windows(%(")).should eq %q(\") } - it { Process.quote_windows(%q(a\\b\ c\)).should eq %q("a\\b\ c\\") } - it { Process.quote_windows(orig = %q(a\\b\c\)).should eq orig } - - context "join" do - it { Process.quote_windows([] of String).should eq "" } - it { Process.quote_windows(["my file.txt", "another.txt"]).should eq %("my file.txt" another.txt) } - it { Process.quote_windows(["foo ", "", " ", " bar"]).should eq %("foo " "" " " " bar") } - end - end + {% unless flag?(:preview_mt) || flag?(:win32) %} + describe ".fork" do + it "executes the new process with exec" do + with_tempfile("crystal-spec-exec") do |path| + File.exists?(path).should be_false - {% if flag?(:unix) %} - describe ".parse_arguments" do - it "uses the native platform rules" do - Process.parse_arguments(%q[a\ b'c']).should eq [%q[a bc]] - end - end - {% elsif flag?(:win32) %} - describe ".parse_arguments" do - it "uses the native platform rules" do - Process.parse_arguments(%q[a\ b'c']).should eq [%q[a\], %q[b'c']] - end - end - {% else %} - pending ".parse_arguments" - {% end %} + fork = Process.fork do + Process.exec("/usr/bin/env", {"touch", path}) + end + fork.wait - describe ".parse_arguments_posix" do - it { Process.parse_arguments_posix(%q[]).should eq([] of String) } - it { Process.parse_arguments_posix(%q[ ]).should eq([] of String) } - it { Process.parse_arguments_posix(%q[foo]).should eq [%q[foo]] } - it { Process.parse_arguments_posix(%q[foo bar]).should eq [%q[foo], %q[bar]] } - it { Process.parse_arguments_posix(%q["foo bar" 'foo bar' baz]).should eq [%q[foo bar], %q[foo bar], %q[baz]] } - it { Process.parse_arguments_posix(%q["foo bar"'foo bar'baz]).should eq [%q[foo barfoo barbaz]] } - it { Process.parse_arguments_posix(%q[foo\ bar]).should eq [%q[foo bar]] } - it { Process.parse_arguments_posix(%q["foo\ bar"]).should eq [%q[foo\ bar]] } - it { Process.parse_arguments_posix(%q['foo\ bar']).should eq [%q[foo\ bar]] } - it { Process.parse_arguments_posix(%q[\]).should eq [%q[\]] } - it { Process.parse_arguments_posix(%q["foo bar" '\hello/' Fizz\ Buzz]).should eq [%q[foo bar], %q[\hello/], %q[Fizz Buzz]] } - it { Process.parse_arguments_posix(%q[foo"bar"baz]).should eq [%q[foobarbaz]] } - it { Process.parse_arguments_posix(%q[foo'bar'baz]).should eq [%q[foobarbaz]] } - it { Process.parse_arguments_posix(%q[this 'is a "'very wei"rd co"m"mand please" don't do t'h'a't p"leas"e]).should eq [%q[this], %q[is a "very], %q[weird command please], %q[dont do that], %q[please]] } - - it "raises an error when double quote is unclosed" do - expect_raises ArgumentError, "Unmatched quote" do - Process.parse_arguments_posix(%q["foo]) + File.exists?(path).should be_true + end end end - it "raises an error if single quote is unclosed" do - expect_raises ArgumentError, "Unmatched quote" do - Process.parse_arguments_posix(%q['foo]) + describe ".exec" do + it "gets error from exec" do + expect_raises(File::NotFoundError, "Error executing process: 'foobarbaz'") do + Process.exec("foobarbaz") + end end end - end + {% end %} - describe ".parse_arguments_windows" do - it { Process.parse_arguments_windows(%q[]).should eq([] of String) } - it { Process.parse_arguments_windows(%q[ ]).should eq([] of String) } - it { Process.parse_arguments_windows(%q[foo]).should eq [%q[foo]] } - it { Process.parse_arguments_windows(%q[foo bar]).should eq [%q[foo], %q[bar]] } - it { Process.parse_arguments_windows(%q["foo bar" 'foo bar' baz]).should eq [%q[foo bar], %q['foo], %q[bar'], %q[baz]] } - it { Process.parse_arguments_windows(%q["foo bar"baz]).should eq [%q[foo barbaz]] } - it { Process.parse_arguments_windows(%q[foo"bar baz"]).should eq [%q[foobar baz]] } - it { Process.parse_arguments_windows(%q[foo\bar]).should eq [%q[foo\bar]] } - it { Process.parse_arguments_windows(%q[foo\ bar]).should eq [%q[foo\], %q[bar]] } - it { Process.parse_arguments_windows(%q[foo\\bar]).should eq [%q[foo\\bar]] } - it { Process.parse_arguments_windows(%q[foo\\\bar]).should eq [%q[foo\\\bar]] } - it { Process.parse_arguments_windows(%q[ /LIBPATH:C:\crystal\lib ]).should eq [%q[/LIBPATH:C:\crystal\lib]] } - it { Process.parse_arguments_windows(%q[a\\\b d"e f"g h]).should eq [%q[a\\\b], %q[de fg], %q[h]] } - it { Process.parse_arguments_windows(%q[a\\\"b c d]).should eq [%q[a\"b], %q[c], %q[d]] } - it { Process.parse_arguments_windows(%q[a\\\\"b c" d e]).should eq [%q[a\\b c], %q[d], %q[e]] } - it { Process.parse_arguments_windows(%q["foo bar" '\hello/' Fizz\ Buzz]).should eq [%q[foo bar], %q['\hello/'], %q[Fizz\], %q[Buzz]] } - it { Process.parse_arguments_windows(%q[this 'is a "'very wei"rd co"m"mand please" don't do t'h'a't p"leas"e"]).should eq [%q[this], %q['is], %q[a], %q['very weird], %q[command], %q[please don't do t'h'a't please]] } - - it "raises an error if double quote is unclosed" do - expect_raises ArgumentError, "Unmatched quote" do - Process.parse_arguments_windows(%q["foo]) - Process.parse_arguments_windows(%q[\"foo]) - Process.parse_arguments_windows(%q["f\"oo\\\"]) + describe ".chroot" do + {% if flag?(:unix) %} + it "raises when unprivileged" do + status, output, _ = compile_and_run_source <<-'CRYSTAL' + begin + Process.chroot(".") + puts "FAIL" + rescue ex + puts ex.inspect + end + CRYSTAL + + status.success?.should be_true + output.should eq("#\n") end - end + {% end %} end end