@@ -46,6 +46,14 @@ private def standing_command
4646 {% end % }
4747end
4848
49+ private def path_search_command
50+ {% if flag?(:win32 ) % }
51+ {" cmd.exe" }
52+ {% else % }
53+ {" true" }
54+ {% end % }
55+ end
56+
4957private def newline
5058 {% if flag?(:win32 ) % }
5159 " \r\n "
@@ -379,6 +387,107 @@ describe Process do
379387 end
380388 end
381389 {% end % }
390+
391+ if {{ flag?(:win32 ) }}
392+ it " finds binary in parent `$PATH`, not `env`" do
393+ Process .run(* print_env_command, env: {" PATH" => " " })
394+ end
395+ else
396+ # FIXME: This behaviour is incorrect. It should lookup the command in
397+ # the parent process' `$PATH`, without any changes from `env`.
398+ # https://github.com/crystal-lang/crystal/issues/6464#issuecomment-3391000914
399+ it " finds binary in `env`" do
400+ expect_raises(File ::NotFoundError ) do
401+ Process .run(* print_env_command, env: {" PATH" => " " })
402+ end
403+ end
404+ end
405+
406+ it " errors on invalid key" do
407+ {% begin % }
408+ expect_raises({% if flag?(:win32 ) % }ArgumentError , " Invalid env key" {% else % } RuntimeError , " Invalid argument" {% end % }) do
409+ Process .run(* print_env_command, env: {" " => " baz" })
410+ end
411+ expect_raises({% if flag?(:win32 ) % }ArgumentError , " Invalid env key" {% else % } RuntimeError , " Invalid argument" {% end % }) do
412+ Process .run(* print_env_command, env: {" foo=bar" => " baz" })
413+ end
414+ {% end % }
415+ end
416+
417+ it " errors on zero char in key" do
418+ expect_raises({{ flag?(:win32 ) }} ? ArgumentError : RuntimeError , " String `key` contains null byte" ) do
419+ Process .run(* print_env_command, env: {" foo\0 " => " baz" })
420+ end
421+ end
422+
423+ it " errors on zero char in value" do
424+ expect_raises({{ flag?(:win32 ) }} ? ArgumentError : RuntimeError , " String `value` contains null byte" ) do
425+ Process .run(* print_env_command, env: {" foo" => " baz\0 " })
426+ end
427+ end
428+ end
429+
430+ it " errors with empty command" do
431+ {% begin % }
432+ expect_raises({% if flag?(:win32 ) % } IO ::Error , " The parameter is incorrect" {% else % } File ::NotFoundError {% end % }) do
433+ Process .run(" " )
434+ end
435+ {% end % }
436+ end
437+
438+ it " errors with too long command" do
439+ pending! unless {{ flag?(:linux ) }}
440+
441+ path_max = {% if LibC .has_constant?(:PATH_MAX ) % }
442+ LibC ::PATH_MAX
443+ {% else % }
444+ 10_000
445+ {% end % }
446+
447+ expect_raises(IO ::Error , /File ?name too long/ ) do
448+ Process .run(" a" * (path_max + 1 ))
449+ end
450+
451+ # The pathname itself is not too long, but it will be when combined with
452+ # any path prefix.
453+ expect_raises(IO ::Error , /File ?name too long/ ) do
454+ Process .run(" a" * path_max)
455+ end
456+ end
457+
458+ describe " $PATH" do
459+ it " works with unset $PATH" do
460+ with_env(" PATH" : nil ) do
461+ Process .run(* path_search_command)
462+ end
463+ end
464+
465+ it " errors with empty $PATH" do
466+ pending! if {{ flag?(:win32 ) }}
467+ with_env(" PATH" : " " ) do
468+ expect_raises(File ::NotFoundError ) do
469+ Process .run(* path_search_command)
470+ end
471+ end
472+ end
473+
474+ it " empty path entry means current directory" do
475+ pending! unless {{ flag?(:unix ) }}
476+
477+ with_tempfile(" crystal-spec-run" ) do |dir |
478+ Dir .mkdir dir
479+ File .write(Path [dir, " foo" ], " #!/bin/sh\n echo bar" )
480+ File .chmod(Path [dir, " foo" ], 0o555 )
481+ unless {{ flag?(:darwin ) }}
482+ expect_raises(File ::NotFoundError ) do
483+ Process .run(" foo" , chdir: dir)
484+ end
485+ end
486+ with_env(" PATH" : " :" ) do
487+ Process .run(" foo" , chdir: dir)
488+ end
489+ end
490+ end
382491 end
383492
384493 it " can link processes together" do
0 commit comments