From a7fc9ad7c3bfbec98619c21ac299f1bc3fd9ac73 Mon Sep 17 00:00:00 2001 From: Anders Rillbert Date: Wed, 2 Feb 2022 19:55:29 +0100 Subject: [PATCH 1/8] update API and docs to match --- lib/sproc/core.rb | 3 +++ test/core_test.rb | 7 ++++--- test/usage_ex_test.rb | 7 ++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/sproc/core.rb b/lib/sproc/core.rb index be9ad96..6c6d9c7 100644 --- a/lib/sproc/core.rb +++ b/lib/sproc/core.rb @@ -202,14 +202,17 @@ def task_info # assert(completed.exit_zero?) # end def self.wait_on_all(running_proc, polling_interval = 100, &block) + ok = true until running_proc.empty? done = get_finished(running_proc) running_proc -= done + done.each { |sp| ok &&= sp.exit_zero? } next unless block done.each(&block) if block sleep polling_interval / 1000 end + ok end # Wait for subprocesses to complete and give a block an opportunity to diff --git a/test/core_test.rb b/test/core_test.rb index aa37b0a..6e860b0 100644 --- a/test/core_test.rb +++ b/test/core_test.rb @@ -37,8 +37,8 @@ def test_set_environment_bash } test_str = '"${my_env_var}"' - # For command subtitustion, we need a to start the process - # under a shell + # For command subtitustion, we need to start the subprocess + # within a shell sp = SProc.new(type: SProc::BASH, env: env) info = sp.exec_sync("echo", test_str).task_info @@ -69,6 +69,7 @@ def test_completion_status sp = SProc.new(type: SProc::NONE).exec_async("ping", [@count_flag, "1", "127.0.0.1"]) assert_equal(false, sp.exit_zero?) sp.wait_on_completion + assert_equal(true, sp.exit_zero?) # expect this to complete with exit code != 0 since host does not # exist @@ -87,7 +88,7 @@ def test_start_two_parallel_processes p_array = msg_array.collect do |str| SProc.new(type: SProc::NONE).exec_async("echo", str) end - SProc.wait_on_all(p_array) + assert(SProc.wait_on_all(p_array)) p_array.each_with_index do |p, i| info = p.task_info assert_equal("echo #{msg_array[i]}", info[:cmd_str]) diff --git a/test/usage_ex_test.rb b/test/usage_ex_test.rb index 36cf962..a0e0dc3 100644 --- a/test/usage_ex_test.rb +++ b/test/usage_ex_test.rb @@ -90,10 +90,11 @@ def test_single_async ti = sp.task_info # the wall time is not filled in until completion assert_equal(0, ti.wall_time) - # we can access the underlying ruby 'ProcessStatus' object if - # we really need to + # we do not expect an underlying ruby 'ProcessStatus' object + # while the subprocess is running assert_equal(true, ti.process_status.nil?) - # we don't know if the popen_thread has been created here yet + # we don't know if the popen_thread has been created here yet but + # if it has, it must be alive assert_equal(true, ti.popen_thread.alive?) unless ti.popen_thread.nil? # Wait for the sub-process to complete From 374080adbbf2dc84f210d39b1ab9819d9175e101 Mon Sep 17 00:00:00 2001 From: Anders Rillbert Date: Thu, 3 Feb 2022 09:01:22 +0100 Subject: [PATCH 2/8] start adding tests for different process errors --- test/data/error_gen_script.rb | 4 ++++ test/negative_test.rb | 30 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 test/data/error_gen_script.rb create mode 100644 test/negative_test.rb diff --git a/test/data/error_gen_script.rb b/test/data/error_gen_script.rb new file mode 100644 index 0000000..201cdef --- /dev/null +++ b/test/data/error_gen_script.rb @@ -0,0 +1,4 @@ + +if $PROGRAM_NAME == $0 + puts ARGV +end \ No newline at end of file diff --git a/test/negative_test.rb b/test/negative_test.rb new file mode 100644 index 0000000..20d3f2c --- /dev/null +++ b/test/negative_test.rb @@ -0,0 +1,30 @@ +require "minitest/autorun" +require_relative "../lib/sproc/osinfo" +require_relative "../lib/sproc/core" + +module SProc + # test the sequential process class + class TestSequentialProcess < Minitest::Test + def setup + # avoid keeping data in stdout buffer before writing it out + $stdout.sync = true + # since not even a simple cmd like 'ping' has the same flags + # under windows/linux (grrr), we need to pass different flags + # depending on os + @count_flag = case OSInfo.host_os + when OSInfo::WINDOWS then "-n" + when OSInfo::LINUX then "-c" + else raise "Unsupported OS!" + end + @script_path = Pathname.new("#{__dir__}").join("data/testscript.rb") + end + + def test_exception + s = SProc.new.exec_sync("ruby",[@script_path, "hej", "hopp"]) + # p s.task_info.inspect + # puts "stdout: #{s.task_info.stdout}" + # puts "exit code: #{s.task_info.process_status.exitstatus}" + assert_equal(ExecutionState::COMPLETED,s.execution_state) + end + end +end \ No newline at end of file From bcc6a280d6641f4a5d1b415831f1c770c5ac8337 Mon Sep 17 00:00:00 2001 From: Anders Rillbert Date: Fri, 4 Feb 2022 22:04:25 +0100 Subject: [PATCH 3/8] - use mutex/cond variable to ensure that the popen3 call has been made before returning from one of the SProc::exec_... methods. - move signal sending to separate object - reimplement execution state enum to use classes --- lib/sproc/core.rb | 209 +++++++++++++++++++++++++++++++++--------- test/core_test.rb | 10 +- test/usage_ex_test.rb | 10 +- 3 files changed, 176 insertions(+), 53 deletions(-) diff --git a/lib/sproc/core.rb b/lib/sproc/core.rb index 6c6d9c7..3fbcea6 100644 --- a/lib/sproc/core.rb +++ b/lib/sproc/core.rb @@ -2,6 +2,57 @@ require "open3" module SProc + class Signal + def initialize(sproc) + @sproc = sproc + end + + # send the 'kill' signal to the subprocess. + # + # return:: true if the signal was sent, false if the sending failed + def kill + signal("KILL") + end + + # send the 'abort' signal to the subprocess. + # + # return:: true if the signal was sent, false if the sending failed + def abort + signal("ABRT") + end + + # send the 'interrup' signal to the subprocess. + # + # return:: true if the signal was sent, false if the sending failed + def interrupt + signal("INT") + end + + # send the 'terminate' signal to the subprocess. + # + # return:: true if the signal was sent, false if the sending failed + def terminate + signal("TERM") + end + + private + + # Try to send a signal to the subprocess and returns true + # if the sending succeeded. If the sending fails + # for any reason at all (process not started, wrong argument type, ...) + # false is returned. + # + # return:: true if the signal was sent, false otherwise + def signal(sig) + return false if @sproc.pid.nil? + + Process.kill(sig, @sproc.pid) + true + rescue + false + end + end + # Defines the supported shell environments in which a subprocess # can be run. module ShellType @@ -18,19 +69,40 @@ module ShellType # running within an SProc instance. module ExecutionState # The process is initiated but does not yet run - NOT_STARTED = 0 + class NotStarted; end + # The process is running - RUNNING = 1 + class Running; end + # The process has previously been running but is now aborted - ABORTED = 2 + class Aborted; end + # The process has previously been running but has now run to completion - COMPLETED = 3 + class Completed; end + # The process failed to start and thus, have never been running - FAILED_TO_START = 4 + class FailedToStart; end end # Execute a command in a subprocess, either synchronuously or asyncronously. class SProc + include ShellType + include ExecutionState + + # A SProc::Signal instance that can be used to send signals to the subprocess. + # + # === Example + # + # # start an async subprocess running "sleep" + # s = SProc.new.exec_async("sleep", "1"]) + # + # # clobber the subprocess and wait for it to die + # s.signal.kill + # s.wait_on_completion + # + # ... + attr_reader :signal + # A struct that represents queryable info about the task run by this SProc # # cmd_str:: the invokation string used to start the process @@ -47,8 +119,20 @@ class SProc :process_status, # the ProcessStatus object (see Ruby docs) :popen_thread, # the thread created by the popen call, nil before started :stdout, # a String containing all output from the process' stdout - :stderr # a String containing all output from the process' stderr - ) + :stderr, # a String containing all output from the process' stderr + :pid # the process id of the sub process or nil if no process id has yet been allocated + ) { + def to_s + str = " " + end + } @logger = nil class << self @@ -60,9 +144,6 @@ def logger self.class.logger end - include ShellType - include ExecutionState - # prepare to run a sub process # type:: the ShellType used to run the process within # stdout_callback:: a callback that will receive all stdout output @@ -87,6 +168,7 @@ def initialize(type: ShellType::NONE, stdout_callback: nil, @runner = TaskRunner.new(@run_opts) @execution_thread = nil @env = env + @signal = Signal.new(self) end # Start the sub-process and block until it has completed. @@ -116,44 +198,37 @@ def exec_async(cmd, *args, **opts) exec(false, @env, cmd, *args, **opts) end + # return:: the process id of the running/completed subprocess or nil + # if no subprocess has started. + def pid + task_info[:pid] + end + # return:: +true+ if this process has completed with exit code 0 # (success). +false+ otherwise def exit_zero? - return false unless execution_state == ExecutionState::COMPLETED + return false unless execution_state == ExecutionState::Completed task_info[:process_status].exitstatus.zero? end - # Block the caller as long as this subprocess is running. - # If this SProc has not been started, the call returns - # immediately - # - # return:: the TaskInfo struct of the completed process or - # nil if the subprocess has not yet been started. - def wait_on_completion - return nil if @execution_thread.nil? - - @execution_thread.join - task_info - end - # Return the execution state of this SProc. Note that it is not # identical with the life-cycle of the underlying ProcessStatus object # # return:: current ExecutionState def execution_state - return ExecutionState::NOT_STARTED if @execution_thread.nil? + return ExecutionState::NotStarted if @execution_thread.nil? # Count this SProc as running as long as the thread # that executes it is alive (this includes book keeping # chores within this class as well) - return ExecutionState::RUNNING if @execution_thread.alive? + return ExecutionState::Running if @execution_thread.alive? status = task_info[:process_status] # an execution thread that has run but not generated a task_info # means that we tried to start a process but failed - return ExecutionState::FAILED_TO_START if status.nil? + return ExecutionState::FailedToStart if status.nil? # a process can terminate for different reasons: # - its done @@ -161,10 +236,10 @@ def execution_state # - an uncaught signal # this should take care of uncaught signals - return ExecutionState::ABORTED if status.signaled? + return ExecutionState::Aborted if status.signaled? # If the process completed (either successfully or not) - return ExecutionState::COMPLETED if status.exited? + return ExecutionState::Completed if status.exited? # We don't currently handle a process that has been stopped... raise NotImplementedError("Unhandled process 'stopped' status!") if status.stopped? @@ -174,11 +249,28 @@ def execution_state end # return:: the TaskInfo representing this SProc, nil if - # process has not started + # the subprocess has not started def task_info @runner.task_info end + # Block the caller as long as this subprocess is running. + # If this SProc has not been started, the call returns + # immediately + # + # return:: the TaskInfo struct of the completed process or + # nil if the subprocess has not yet been started. + def wait_on_completion + return nil if @execution_thread.nil? + + @execution_thread.join + task_info + end + + def to_s + " " + end + # Blocks until all processes in the given array are completed/aborted. # # If the caller submits a block, that block is called once for each @@ -272,9 +364,9 @@ def self.wait_or_back_to_back(running_proc, polling_interval = 100) # return:: an array of SProc objects not running (but previously started) def self.get_finished(sproc_array) sproc_array.select do |p| - [ExecutionState::COMPLETED, - ExecutionState::ABORTED, - ExecutionState::FAILED_TO_START].include?(p.execution_state) + [ExecutionState::Completed, + ExecutionState::Aborted, + ExecutionState::FailedToStart].include?(p.execution_state) end end @@ -290,6 +382,9 @@ def exec(synch, env, cmd, *args, **opts) @execution_thread = Thread.new do @runner.execute(env, cmd, *args, **opts) end + @runner.mutex.synchronize { + @runner.resource.wait(@runner.mutex) + } @execution_thread.join if synch self end @@ -301,6 +396,7 @@ def exec(synch, env, cmd, *args, **opts) # :nodoc: all class TaskRunner attr_reader :task_info + attr_accessor :mutex, :resource include ShellType @@ -314,8 +410,10 @@ class TaskRunner }.freeze def initialize(opts) - @task_info = TaskInfo.new("", nil, 0, nil, nil, "", "") + @task_info = TaskInfo.new("", nil, 0, nil, nil, "", "", nil) @opts = DEFAULT_OPTS.dup.merge!(opts) + @mutex = Mutex.new + @resource = ConditionVariable.new end # Runs the process and blocks until it is completed or aborted. @@ -327,10 +425,16 @@ def execute(env, cmd, *args, **opts) shell_out_via_popen(env, cmd, *args, **opts) rescue => e @task_info[:exception] = e + @resource.signal + ensure + @task_info[:wall_time] = (Process.clock_gettime( + Process::CLOCK_MONOTONIC + ) - start_time) end - @task_info[:wall_time] = (Process.clock_gettime( - Process::CLOCK_MONOTONIC - ) - start_time) + end + + def to_s + task_info.to_s + "" end private @@ -354,12 +458,31 @@ def shell_out_via_popen(env, cmd, *args, **opts) SProc.logger&.debug { "Start: #{task_info[:cmd_str]}" } SProc.logger&.debug { "Supplying env: #{env}" } unless env.nil? SProc.logger&.debug { "Spawn options: #{opts}" } unless opts.nil? - Open3.popen3(env, *args) do |stdin, stdout, stderr, thread| - @task_info[:popen_thread] = thread - threads = do_while_process_running(stdin, stdout, stderr) - @task_info[:process_status] = thread.value - threads.each(&:join) - end + + # non-blocking kick-off of the subprocess + stdin, stdout, stderr, wait_thread = Open3.popen3(env, *args) + # get info that is available while the process is running + @task_info[:popen_thread] = wait_thread + @task_info[:pid] = wait_thread.pid + @resource.signal + + # kick-off the processing of the output io streams for the process + threads = do_while_process_running(stdin, stdout, stderr) + # block here until the process is completed/aborted + @task_info[:process_status] = wait_thread.value + # block until the remaining stream info has been processed + threads.each(&:join) + # explicitly close the streams + stdin.close + stdout.close + stderr.close + + # Open3.popen3(env, *args) do |stdin, stdout, stderr, thread| + # @task_info[:popen_thread] = thread + # threads = do_while_process_running(stdin, stdout, stderr) + # @task_info[:process_status] = thread.value + # threads.each(&:join) + # end end def get_args_native(cmd, *args, **opts) diff --git a/test/core_test.rb b/test/core_test.rb index 6e860b0..fe728e5 100644 --- a/test/core_test.rb +++ b/test/core_test.rb @@ -79,7 +79,7 @@ def test_completion_status # expect this to never start a process since cmd not exists sp = SProc.new(type: SProc::NONE).exec_sync("pinggg", [@count_flag, "1", "fake_host"]) assert_equal(false, sp.exit_zero?) - assert_equal(ExecutionState::FAILED_TO_START, sp.execution_state) + assert_equal(ExecutionState::FailedToStart, sp.execution_state) end def test_start_two_parallel_processes @@ -113,7 +113,7 @@ def test_block_yield_wait_all nof_finished += 1 info = p.task_info case p.execution_state - when ExecutionState::ABORTED + when ExecutionState::Aborted err_str = ["Error: #{info[:stderr]}"] err_str << "Process Exception: #{info[:exception]}" err_str << "Did not expect any process to be aborted!!!" @@ -122,7 +122,7 @@ def test_block_yield_wait_all end assert_equal(4, nof_finished) p_array.each do |p| - assert_equal(p.execution_state, ExecutionState::COMPLETED) + assert_equal(p.execution_state, ExecutionState::Completed) end end @@ -141,7 +141,7 @@ def test_back_to_back # finishes p_total = SProc.wait_or_back_to_back(p_array) do |p| nof_finished += 1 - raise "Aouch" if p.execution_state == ExecutionState::ABORTED + raise "Aouch" if p.execution_state == ExecutionState::Aborted # create new processes as long as there are messages left unless messages.empty? @@ -154,7 +154,7 @@ def test_back_to_back assert_equal(9, nof_finished) assert_equal(9, p_total.count) p_total.each do |p| - assert_equal(p.execution_state, ExecutionState::COMPLETED) + assert_equal(p.execution_state, ExecutionState::Completed) end end end diff --git a/test/usage_ex_test.rb b/test/usage_ex_test.rb index a0e0dc3..cabe5f2 100644 --- a/test/usage_ex_test.rb +++ b/test/usage_ex_test.rb @@ -34,7 +34,7 @@ def test_single_sync # we expect this to succeed (ie exit with '0') assert_equal(true, sp.exit_zero?) - assert_equal(ExecutionState::COMPLETED, sp.execution_state) + assert_equal(ExecutionState::Completed, sp.execution_state) # we can access more info about the completed process via # its associated TaskInfo struct: @@ -59,13 +59,13 @@ def test_single_sync # expect this to complete with exit code != 0 since host does not # exist sp.exec_sync("ping", [@count_flag, "2", "fake_host"]) - assert_equal(ExecutionState::COMPLETED, sp.execution_state) + assert_equal(ExecutionState::Completed, sp.execution_state) assert_equal(false, sp.exit_zero?) assert_equal(true, sp.task_info.exception.nil?) # expect this to never start a process since the cmd not exists sp.exec_sync("pinggg", [@count_flag, "1", "fake_host"]) - assert_equal(ExecutionState::FAILED_TO_START, sp.execution_state) + assert_equal(ExecutionState::FailedToStart, sp.execution_state) assert_equal(false, sp.exit_zero?) # A call to non-existing command will return ERRNO::ENOENT assert_equal(false, sp.task_info.exception.nil?) @@ -85,7 +85,7 @@ def test_single_async # ping should take at least 1 sec to complete so we # expect the subprocess to run when these asserts are executed - assert_equal(ExecutionState::RUNNING, sp.execution_state) + assert_equal(ExecutionState::Running, sp.execution_state) assert_equal(false, sp.exit_zero?) ti = sp.task_info # the wall time is not filled in until completion @@ -101,7 +101,7 @@ def test_single_async sp.wait_on_completion # Now we expect the same as during a corresponding synchronous # invokation - assert_equal(ExecutionState::COMPLETED, sp.execution_state) + assert_equal(ExecutionState::Completed, sp.execution_state) assert_equal(true, sp.exit_zero?) ti = sp.task_info assert_equal("ping #{@count_flag} 2 127.0.0.1", ti.cmd_str) From 67be105d14a81d8c71303b547c1e0cb2432f7313 Mon Sep 17 00:00:00 2001 From: Anders Rillbert Date: Fri, 4 Feb 2022 22:06:54 +0100 Subject: [PATCH 4/8] add tests for signal sending and uncaught exceptions in the subprocess --- test/data/error_gen_script.rb | 25 ++++++++++++++++++++-- test/negative_test.rb | 40 ++++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/test/data/error_gen_script.rb b/test/data/error_gen_script.rb index 201cdef..51043fd 100644 --- a/test/data/error_gen_script.rb +++ b/test/data/error_gen_script.rb @@ -1,4 +1,25 @@ +require "open3" + +# begin +# stdin, stdout, stderr, wait_thread = Open3.popen3({}, "ruby", "hej") +# rescue => e +# puts e.inspect +# end +# exit 1 if $PROGRAM_NAME == $0 - puts ARGV -end \ No newline at end of file + exit 0 if ARGV.empty? + + if ARGV.length == 1 + case ARGV[0] + when "--success" + puts "Ok!" + exit 0 + when "--throw-exception" + raise "Throwing exception" + when "--wait-on-signal" + puts "sleeping" + sleep 3 + end + end +end diff --git a/test/negative_test.rb b/test/negative_test.rb index 20d3f2c..f19cef9 100644 --- a/test/negative_test.rb +++ b/test/negative_test.rb @@ -4,27 +4,33 @@ module SProc # test the sequential process class - class TestSequentialProcess < Minitest::Test + class NegativeTest < Minitest::Test def setup - # avoid keeping data in stdout buffer before writing it out $stdout.sync = true - # since not even a simple cmd like 'ping' has the same flags - # under windows/linux (grrr), we need to pass different flags - # depending on os - @count_flag = case OSInfo.host_os - when OSInfo::WINDOWS then "-n" - when OSInfo::LINUX then "-c" - else raise "Unsupported OS!" - end - @script_path = Pathname.new("#{__dir__}").join("data/testscript.rb") + @script_path = Pathname.new(__dir__.to_s).join("data/error_gen_script.rb") + end + + def test_ok + s = SProc.new( + stdout_callback: lambda { |line| assert_equal("Ok!", line.chomp) } + ).exec_sync("ruby", [@script_path, "--success"]) + assert_equal(ExecutionState::Completed, s.execution_state) + assert(s.exit_zero?) end def test_exception - s = SProc.new.exec_sync("ruby",[@script_path, "hej", "hopp"]) - # p s.task_info.inspect - # puts "stdout: #{s.task_info.stdout}" - # puts "exit code: #{s.task_info.process_status.exitstatus}" - assert_equal(ExecutionState::COMPLETED,s.execution_state) + s = SProc.new.exec_sync("ruby", [@script_path, "--throw-exception"]) + assert_equal(ExecutionState::Completed, s.execution_state) + end + + def test_exit_on_signal + s = SProc.new.exec_async("ruby", [@script_path, "--wait-on-signal"]) + assert(s.execution_state == ExecutionState::Running) + + # clobber the subprocess + s.signal.kill + s.wait_on_completion + assert(s.execution_state == ExecutionState::Aborted) end end -end \ No newline at end of file +end From ee2be5c09a52fd1286ad4104124ae04b6f24319e Mon Sep 17 00:00:00 2001 From: Anders Rillbert Date: Fri, 4 Feb 2022 22:10:57 +0100 Subject: [PATCH 5/8] add tests --- test/negative_test.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/negative_test.rb b/test/negative_test.rb index f19cef9..517c8e3 100644 --- a/test/negative_test.rb +++ b/test/negative_test.rb @@ -18,7 +18,13 @@ def test_ok assert(s.exit_zero?) end - def test_exception + def test_non_existent_cmd + s = SProc.new.exec_async("rubbbby") + assert_equal(ExecutionState::FailedToStart, s.execution_state) + assert_equal(Errno::ENOENT, s.task_info.exception.class) + end + + def test_exception_in_subprocess s = SProc.new.exec_sync("ruby", [@script_path, "--throw-exception"]) assert_equal(ExecutionState::Completed, s.execution_state) end From e180410b628d9f199497d964793620717a85511d Mon Sep 17 00:00:00 2001 From: Anders Rillbert Date: Fri, 4 Feb 2022 22:12:46 +0100 Subject: [PATCH 6/8] run standard --- lib/sproc/core.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sproc/core.rb b/lib/sproc/core.rb index 3fbcea6..4c5aabc 100644 --- a/lib/sproc/core.rb +++ b/lib/sproc/core.rb @@ -90,7 +90,7 @@ class SProc include ExecutionState # A SProc::Signal instance that can be used to send signals to the subprocess. - # + # # === Example # # # start an async subprocess running "sleep" From d11d19abe88e89e2137b3e6ce7f1a4217c442c62 Mon Sep 17 00:00:00 2001 From: Anders Rillbert Date: Fri, 4 Feb 2022 22:32:26 +0100 Subject: [PATCH 7/8] add debug print --- test/negative_test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/negative_test.rb b/test/negative_test.rb index 517c8e3..5d9bd19 100644 --- a/test/negative_test.rb +++ b/test/negative_test.rb @@ -36,6 +36,7 @@ def test_exit_on_signal # clobber the subprocess s.signal.kill s.wait_on_completion + puts s assert(s.execution_state == ExecutionState::Aborted) end end From 9f48fd812aa548be212c4cc393d7c684927fe4e0 Mon Sep 17 00:00:00 2001 From: Anders Rillbert Date: Fri, 4 Feb 2022 22:44:54 +0100 Subject: [PATCH 8/8] do not run signal test on Windows --- test/negative_test.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/negative_test.rb b/test/negative_test.rb index 5d9bd19..4165988 100644 --- a/test/negative_test.rb +++ b/test/negative_test.rb @@ -30,13 +30,14 @@ def test_exception_in_subprocess end def test_exit_on_signal + skip "Uses POSIX signals, does not work on Windows..." if OSInfo.on_windows? || OSInfo.on_mixed_env? + s = SProc.new.exec_async("ruby", [@script_path, "--wait-on-signal"]) assert(s.execution_state == ExecutionState::Running) # clobber the subprocess s.signal.kill s.wait_on_completion - puts s assert(s.execution_state == ExecutionState::Aborted) end end