childprocess 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +6 -0
- data/.gitignore +28 -0
- data/.rspec +1 -0
- data/.travis.yml +36 -0
- data/CHANGELOG.md +44 -0
- data/Gemfile +15 -0
- data/LICENSE +20 -0
- data/README.md +192 -0
- data/Rakefile +61 -0
- data/appveyor.yml +43 -0
- data/childprocess.gemspec +30 -0
- data/lib/childprocess.rb +205 -0
- data/lib/childprocess/abstract_io.rb +36 -0
- data/lib/childprocess/abstract_process.rb +192 -0
- data/lib/childprocess/errors.rb +26 -0
- data/lib/childprocess/jruby.rb +56 -0
- data/lib/childprocess/jruby/io.rb +16 -0
- data/lib/childprocess/jruby/process.rb +159 -0
- data/lib/childprocess/jruby/pump.rb +53 -0
- data/lib/childprocess/tools/generator.rb +146 -0
- data/lib/childprocess/unix.rb +9 -0
- data/lib/childprocess/unix/fork_exec_process.rb +70 -0
- data/lib/childprocess/unix/io.rb +21 -0
- data/lib/childprocess/unix/lib.rb +186 -0
- data/lib/childprocess/unix/platform/i386-linux.rb +12 -0
- data/lib/childprocess/unix/platform/i386-solaris.rb +11 -0
- data/lib/childprocess/unix/platform/x86_64-linux.rb +12 -0
- data/lib/childprocess/unix/platform/x86_64-macosx.rb +11 -0
- data/lib/childprocess/unix/posix_spawn_process.rb +134 -0
- data/lib/childprocess/unix/process.rb +89 -0
- data/lib/childprocess/version.rb +3 -0
- data/lib/childprocess/windows.rb +33 -0
- data/lib/childprocess/windows/handle.rb +91 -0
- data/lib/childprocess/windows/io.rb +25 -0
- data/lib/childprocess/windows/lib.rb +416 -0
- data/lib/childprocess/windows/process.rb +130 -0
- data/lib/childprocess/windows/process_builder.rb +175 -0
- data/lib/childprocess/windows/structs.rb +149 -0
- data/spec/abstract_io_spec.rb +12 -0
- data/spec/childprocess_spec.rb +391 -0
- data/spec/io_spec.rb +228 -0
- data/spec/jruby_spec.rb +24 -0
- data/spec/pid_behavior.rb +12 -0
- data/spec/platform_detection_spec.rb +86 -0
- data/spec/spec_helper.rb +261 -0
- data/spec/unix_spec.rb +57 -0
- data/spec/windows_spec.rb +23 -0
- metadata +179 -0
    
        data/spec/jruby_spec.rb
    ADDED
    
    | @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            require File.expand_path('../spec_helper', __FILE__)
         | 
| 2 | 
            +
            require "pid_behavior"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            if ChildProcess.jruby? && !ChildProcess.windows?
         | 
| 5 | 
            +
              describe ChildProcess::JRuby::IO do
         | 
| 6 | 
            +
                let(:io) { ChildProcess::JRuby::IO.new }
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                it "raises an ArgumentError if given IO does not respond to :to_outputstream" do
         | 
| 9 | 
            +
                  expect { io.stdout = nil }.to raise_error(ArgumentError)
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              describe ChildProcess::JRuby::Process do
         | 
| 14 | 
            +
                if ChildProcess.unix?
         | 
| 15 | 
            +
                  it_behaves_like "a platform that provides the child's pid"
         | 
| 16 | 
            +
                else
         | 
| 17 | 
            +
                  it "raises an error when trying to access the child's pid" do
         | 
| 18 | 
            +
                    process = exit_with(0)
         | 
| 19 | 
            +
                    process.start
         | 
| 20 | 
            +
                    expect { process.pid }.to raise_error(NotImplementedError)
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
            end
         | 
| @@ -0,0 +1,12 @@ | |
| 1 | 
            +
            require File.expand_path('../spec_helper', __FILE__)
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            shared_examples_for "a platform that provides the child's pid" do
         | 
| 4 | 
            +
              it "knows the child's pid" do
         | 
| 5 | 
            +
                Tempfile.open("pid-spec") do |file|
         | 
| 6 | 
            +
                  process = write_pid(file.path).start
         | 
| 7 | 
            +
                  process.wait
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  expect(process.pid).to eq rewind_and_read(file).chomp.to_i
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
            end
         | 
| @@ -0,0 +1,86 @@ | |
| 1 | 
            +
            require File.expand_path('../spec_helper', __FILE__)
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Q: Should platform detection concern be extracted from ChildProcess?
         | 
| 4 | 
            +
            describe ChildProcess do
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              describe ".arch" do
         | 
| 7 | 
            +
                subject { described_class.arch }
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                before(:each) { described_class.instance_variable_set(:@arch, nil) }
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                after(:each) { described_class.instance_variable_set(:@arch, nil) }
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                shared_examples 'expected_arch_for_host_cpu' do |host_cpu, expected_arch|
         | 
| 14 | 
            +
                  context "when host_cpu is '#{host_cpu}'" do
         | 
| 15 | 
            +
                    before :each do
         | 
| 16 | 
            +
                      allow(RbConfig::CONFIG).
         | 
| 17 | 
            +
                        to receive(:[]).
         | 
| 18 | 
            +
                        with('host_cpu').
         | 
| 19 | 
            +
                        and_return(expected_arch)
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    it { is_expected.to eq expected_arch }
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                # Normal cases: not macosx - depends only on host_cpu
         | 
| 27 | 
            +
                context "when os is *not* 'macosx'" do
         | 
| 28 | 
            +
                  before :each do
         | 
| 29 | 
            +
                    allow(described_class).to receive(:os).and_return(:not_macosx)
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  [
         | 
| 33 | 
            +
                    { host_cpu: 'i386',    expected_arch: 'i386'    },
         | 
| 34 | 
            +
                    { host_cpu: 'i486',    expected_arch: 'i386'    },
         | 
| 35 | 
            +
                    { host_cpu: 'i586',    expected_arch: 'i386'    },
         | 
| 36 | 
            +
                    { host_cpu: 'i686',    expected_arch: 'i386'    },
         | 
| 37 | 
            +
                    { host_cpu: 'amd64',   expected_arch: 'x86_64'  },
         | 
| 38 | 
            +
                    { host_cpu: 'x86_64',  expected_arch: 'x86_64'  },
         | 
| 39 | 
            +
                    { host_cpu: 'ppc',     expected_arch: 'powerpc' },
         | 
| 40 | 
            +
                    { host_cpu: 'powerpc', expected_arch: 'powerpc' },
         | 
| 41 | 
            +
                    { host_cpu: 'unknown', expected_arch: 'unknown' },
         | 
| 42 | 
            +
                  ].each do |args|
         | 
| 43 | 
            +
                    include_context 'expected_arch_for_host_cpu', args.values
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                # Special cases: macosx - when host_cpu is i686, have to re-check
         | 
| 48 | 
            +
                context "when os is 'macosx'" do
         | 
| 49 | 
            +
                  before :each do
         | 
| 50 | 
            +
                    allow(described_class).to receive(:os).and_return(:macosx)
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  context "when host_cpu is 'i686' " do
         | 
| 54 | 
            +
                    shared_examples 'expected_arch_on_macosx_i686' do |is_64, expected_arch|
         | 
| 55 | 
            +
                      context "when Ruby is #{is_64 ? 64 : 32}-bit" do
         | 
| 56 | 
            +
                        before :each do
         | 
| 57 | 
            +
                          allow(described_class).
         | 
| 58 | 
            +
                            to receive(:is_64_bit?).
         | 
| 59 | 
            +
                            and_return(is_64)
         | 
| 60 | 
            +
                        end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                        include_context 'expected_arch_for_host_cpu', 'i686', expected_arch
         | 
| 63 | 
            +
                      end
         | 
| 64 | 
            +
                    end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                    [
         | 
| 67 | 
            +
                      { is_64: true,  expected_arch: 'x86_64' },
         | 
| 68 | 
            +
                      { is_64: false, expected_arch: 'i386'   }
         | 
| 69 | 
            +
                    ].each do |args|
         | 
| 70 | 
            +
                      include_context 'expected_arch_on_macosx_i686', args.values
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  [
         | 
| 75 | 
            +
                    { host_cpu: 'amd64',   expected_arch: 'x86_64'  },
         | 
| 76 | 
            +
                    { host_cpu: 'x86_64',  expected_arch: 'x86_64'  },
         | 
| 77 | 
            +
                    { host_cpu: 'ppc',     expected_arch: 'powerpc' },
         | 
| 78 | 
            +
                    { host_cpu: 'powerpc', expected_arch: 'powerpc' },
         | 
| 79 | 
            +
                    { host_cpu: 'unknown', expected_arch: 'unknown' },
         | 
| 80 | 
            +
                  ].each do |args|
         | 
| 81 | 
            +
                    include_context 'expected_arch_for_host_cpu', args.values
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
              end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
            end
         | 
    
        data/spec/spec_helper.rb
    ADDED
    
    | @@ -0,0 +1,261 @@ | |
| 1 | 
            +
            $LOAD_PATH.unshift(File.dirname(__FILE__))
         | 
| 2 | 
            +
            $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            unless defined?(JRUBY_VERSION)
         | 
| 5 | 
            +
              require 'coveralls'
         | 
| 6 | 
            +
              Coveralls.wear!
         | 
| 7 | 
            +
            end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            require 'childprocess'
         | 
| 10 | 
            +
            require 'rspec'
         | 
| 11 | 
            +
            require 'tempfile'
         | 
| 12 | 
            +
            require 'socket'
         | 
| 13 | 
            +
            require 'stringio'
         | 
| 14 | 
            +
            require 'ostruct'
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            module ChildProcessSpecHelper
         | 
| 17 | 
            +
              RUBY = defined?(Gem) ? Gem.ruby : 'ruby'
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              def ruby_process(*args)
         | 
| 20 | 
            +
                @process = ChildProcess.build(RUBY , *args)
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              def sleeping_ruby(seconds = nil)
         | 
| 24 | 
            +
                if seconds
         | 
| 25 | 
            +
                  ruby_process("-e", "sleep #{seconds}")
         | 
| 26 | 
            +
                else
         | 
| 27 | 
            +
                  ruby_process("-e", "sleep")
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              def invalid_process
         | 
| 32 | 
            +
                @process = ChildProcess.build("unlikelytoexist")
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              def ignored(signal)
         | 
| 36 | 
            +
                code = <<-RUBY
         | 
| 37 | 
            +
                  trap(#{signal.inspect}, "IGNORE")
         | 
| 38 | 
            +
                  sleep
         | 
| 39 | 
            +
                RUBY
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                ruby_process tmp_script(code)
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              def write_env(path)
         | 
| 45 | 
            +
                code = <<-RUBY
         | 
| 46 | 
            +
                  File.open(#{path.inspect}, "w") { |f| f << ENV.inspect }
         | 
| 47 | 
            +
                RUBY
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                ruby_process tmp_script(code)
         | 
| 50 | 
            +
              end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              def write_argv(path, *args)
         | 
| 53 | 
            +
                code = <<-RUBY
         | 
| 54 | 
            +
                  File.open(#{path.inspect}, "w") { |f| f << ARGV.inspect }
         | 
| 55 | 
            +
                RUBY
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                ruby_process(tmp_script(code), *args)
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
              def write_pid(path)
         | 
| 61 | 
            +
                code = <<-RUBY
         | 
| 62 | 
            +
                  File.open(#{path.inspect}, "w") { |f| f << Process.pid }
         | 
| 63 | 
            +
                RUBY
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                ruby_process tmp_script(code)
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
              def write_pid_in_sleepy_grand_child(path)
         | 
| 69 | 
            +
                code = <<-RUBY
         | 
| 70 | 
            +
                  system "ruby", "-e", 'File.open(#{path.inspect}, "w") { |f| f << Process.pid; f.flush }; sleep'
         | 
| 71 | 
            +
                RUBY
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                ruby_process tmp_script(code)
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              def exit_with(exit_code)
         | 
| 77 | 
            +
                ruby_process(tmp_script("exit(#{exit_code})"))
         | 
| 78 | 
            +
              end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
              def with_env(hash)
         | 
| 81 | 
            +
                hash.each { |k,v| ENV[k] = v }
         | 
| 82 | 
            +
                begin
         | 
| 83 | 
            +
                  yield
         | 
| 84 | 
            +
                ensure
         | 
| 85 | 
            +
                  hash.each_key { |k| ENV[k] = nil }
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
              end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
              def tmp_script(code)
         | 
| 90 | 
            +
                # use an ivar to avoid GC
         | 
| 91 | 
            +
                @tf = Tempfile.new("childprocess-temp")
         | 
| 92 | 
            +
                @tf << code
         | 
| 93 | 
            +
                @tf.close
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                puts code if $DEBUG
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                @tf.path
         | 
| 98 | 
            +
              end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
              def cat
         | 
| 101 | 
            +
                if ChildProcess.os == :windows
         | 
| 102 | 
            +
                  ruby(<<-CODE)
         | 
| 103 | 
            +
                        STDIN.sync = STDOUT.sync = true
         | 
| 104 | 
            +
                        IO.copy_stream(STDIN, STDOUT)
         | 
| 105 | 
            +
                      CODE
         | 
| 106 | 
            +
                else
         | 
| 107 | 
            +
                  ChildProcess.build("cat")
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
              end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
              def echo
         | 
| 112 | 
            +
                if ChildProcess.os == :windows
         | 
| 113 | 
            +
                  ruby(<<-CODE)
         | 
| 114 | 
            +
                        STDIN.sync  = true
         | 
| 115 | 
            +
                        STDOUT.sync = true
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                        puts "hello"
         | 
| 118 | 
            +
                      CODE
         | 
| 119 | 
            +
                else
         | 
| 120 | 
            +
                  ChildProcess.build("echo", "hello")
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
              end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
              def ruby(code)
         | 
| 125 | 
            +
                ruby_process(tmp_script(code))
         | 
| 126 | 
            +
              end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
              def with_executable_at(path, &blk)
         | 
| 129 | 
            +
                if ChildProcess.os == :windows
         | 
| 130 | 
            +
                  path << ".cmd"
         | 
| 131 | 
            +
                  content = "#{RUBY} -e 'sleep 10' \n @echo foo"
         | 
| 132 | 
            +
                else
         | 
| 133 | 
            +
                  content = "#!/bin/sh\nsleep 10\necho foo"
         | 
| 134 | 
            +
                end
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                File.open(path, 'w', 0744) { |io| io << content }
         | 
| 137 | 
            +
                proc = ChildProcess.build(path)
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                begin
         | 
| 140 | 
            +
                  yield proc
         | 
| 141 | 
            +
                ensure
         | 
| 142 | 
            +
                  proc.stop if proc.alive?
         | 
| 143 | 
            +
                  File.delete path
         | 
| 144 | 
            +
                end
         | 
| 145 | 
            +
              end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
              def exit_timeout
         | 
| 148 | 
            +
                10
         | 
| 149 | 
            +
              end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
              def random_free_port
         | 
| 152 | 
            +
                server = TCPServer.new('127.0.0.1', 0)
         | 
| 153 | 
            +
                port   = server.addr[1]
         | 
| 154 | 
            +
                server.close
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                port
         | 
| 157 | 
            +
              end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
              def with_tmpdir(&blk)
         | 
| 160 | 
            +
                name = "#{Time.now.strftime("%Y%m%d")}-#{$$}-#{rand(0x100000000).to_s(36)}"
         | 
| 161 | 
            +
                FileUtils.mkdir_p(name)
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                begin
         | 
| 164 | 
            +
                  yield File.expand_path(name)
         | 
| 165 | 
            +
                ensure
         | 
| 166 | 
            +
                  FileUtils.rm_rf name
         | 
| 167 | 
            +
                end
         | 
| 168 | 
            +
              end
         | 
| 169 | 
            +
             | 
| 170 | 
            +
              def wait_until(timeout = 10, &blk)
         | 
| 171 | 
            +
                end_time       = Time.now + timeout
         | 
| 172 | 
            +
                last_exception = nil
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                until Time.now >= end_time
         | 
| 175 | 
            +
                  begin
         | 
| 176 | 
            +
                    result = yield
         | 
| 177 | 
            +
                    return result if result
         | 
| 178 | 
            +
                  rescue RSpec::Expectations::ExpectationNotMetError => ex
         | 
| 179 | 
            +
                    last_exception = ex
         | 
| 180 | 
            +
                  end
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                  sleep 0.01
         | 
| 183 | 
            +
                end
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                msg = "timed out after #{timeout} seconds"
         | 
| 186 | 
            +
                msg << ":\n#{last_exception.message}" if last_exception
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                raise msg
         | 
| 189 | 
            +
              end
         | 
| 190 | 
            +
             | 
| 191 | 
            +
              def can_bind?(host, port)
         | 
| 192 | 
            +
                TCPServer.new(host, port).close
         | 
| 193 | 
            +
                true
         | 
| 194 | 
            +
              rescue
         | 
| 195 | 
            +
                false
         | 
| 196 | 
            +
              end
         | 
| 197 | 
            +
             | 
| 198 | 
            +
              def rewind_and_read(io)
         | 
| 199 | 
            +
                io.rewind
         | 
| 200 | 
            +
                io.read
         | 
| 201 | 
            +
              end
         | 
| 202 | 
            +
             | 
| 203 | 
            +
              def alive?(pid)
         | 
| 204 | 
            +
                if ChildProcess.windows?
         | 
| 205 | 
            +
                  ChildProcess::Windows::Lib.alive?(pid)
         | 
| 206 | 
            +
                else
         | 
| 207 | 
            +
                  begin
         | 
| 208 | 
            +
                    Process.getpgid pid
         | 
| 209 | 
            +
                    true
         | 
| 210 | 
            +
                  rescue Errno::ESRCH
         | 
| 211 | 
            +
                    false
         | 
| 212 | 
            +
                  end
         | 
| 213 | 
            +
                end
         | 
| 214 | 
            +
              end
         | 
| 215 | 
            +
             | 
| 216 | 
            +
              def capture_std
         | 
| 217 | 
            +
                orig_out = STDOUT.clone
         | 
| 218 | 
            +
                orig_err = STDERR.clone
         | 
| 219 | 
            +
             | 
| 220 | 
            +
                out = Tempfile.new 'captured-stdout'
         | 
| 221 | 
            +
                err = Tempfile.new 'captured-stderr'
         | 
| 222 | 
            +
                out.sync = true
         | 
| 223 | 
            +
                err.sync = true
         | 
| 224 | 
            +
             | 
| 225 | 
            +
                STDOUT.reopen out
         | 
| 226 | 
            +
                STDERR.reopen err
         | 
| 227 | 
            +
             | 
| 228 | 
            +
                yield
         | 
| 229 | 
            +
             | 
| 230 | 
            +
                OpenStruct.new stdout: rewind_and_read(out), stderr: rewind_and_read(err)
         | 
| 231 | 
            +
              ensure
         | 
| 232 | 
            +
                STDOUT.reopen orig_out
         | 
| 233 | 
            +
                STDERR.reopen orig_err
         | 
| 234 | 
            +
              end
         | 
| 235 | 
            +
             | 
| 236 | 
            +
              def generate_log_messages
         | 
| 237 | 
            +
                ChildProcess.logger.level = Logger::DEBUG
         | 
| 238 | 
            +
             | 
| 239 | 
            +
                process = exit_with(0).start
         | 
| 240 | 
            +
                process.wait
         | 
| 241 | 
            +
                process.poll_for_exit(0.1)
         | 
| 242 | 
            +
              end
         | 
| 243 | 
            +
             | 
| 244 | 
            +
            end # ChildProcessSpecHelper
         | 
| 245 | 
            +
             | 
| 246 | 
            +
            Thread.abort_on_exception = true
         | 
| 247 | 
            +
             | 
| 248 | 
            +
            RSpec.configure do |c|
         | 
| 249 | 
            +
              c.include(ChildProcessSpecHelper)
         | 
| 250 | 
            +
              c.after(:each) {
         | 
| 251 | 
            +
                defined?(@process) && @process.alive? && @process.stop
         | 
| 252 | 
            +
              }
         | 
| 253 | 
            +
             | 
| 254 | 
            +
              if ChildProcess.jruby? && ChildProcess.new("true").instance_of?(ChildProcess::JRuby::Process)
         | 
| 255 | 
            +
                c.filter_run_excluding :process_builder => false
         | 
| 256 | 
            +
              end
         | 
| 257 | 
            +
             | 
| 258 | 
            +
              if ChildProcess.linux? && ChildProcess.posix_spawn?
         | 
| 259 | 
            +
                c.filter_run_excluding :posix_spawn_on_linux => false
         | 
| 260 | 
            +
              end
         | 
| 261 | 
            +
            end
         | 
    
        data/spec/unix_spec.rb
    ADDED
    
    | @@ -0,0 +1,57 @@ | |
| 1 | 
            +
            require File.expand_path('../spec_helper', __FILE__)
         | 
| 2 | 
            +
            require "pid_behavior"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            if ChildProcess.unix? && !ChildProcess.jruby? && !ChildProcess.posix_spawn?
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              describe ChildProcess::Unix::Process do
         | 
| 7 | 
            +
                it_behaves_like "a platform that provides the child's pid"
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                it "handles ECHILD race condition where process dies between timeout and KILL" do
         | 
| 10 | 
            +
                  process = sleeping_ruby
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  allow(process).to receive(:fork).and_return('fakepid')
         | 
| 13 | 
            +
                  allow(process).to receive(:send_term)
         | 
| 14 | 
            +
                  allow(process).to receive(:poll_for_exit).and_raise(ChildProcess::TimeoutError)
         | 
| 15 | 
            +
                  allow(process).to receive(:send_kill).and_raise(Errno::ECHILD.new)
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  process.start
         | 
| 18 | 
            +
                  expect { process.stop }.not_to raise_error
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  allow(process).to receive(:alive?).and_return(false)
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  process.send(:send_signal, 'TERM')
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                it "handles ESRCH race condition where process dies between timeout and KILL" do
         | 
| 26 | 
            +
                  process = sleeping_ruby
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  allow(process).to receive(:fork).and_return('fakepid')
         | 
| 29 | 
            +
                  allow(process).to receive(:send_term)
         | 
| 30 | 
            +
                  allow(process).to receive(:poll_for_exit).and_raise(ChildProcess::TimeoutError)
         | 
| 31 | 
            +
                  allow(process).to receive(:send_kill).and_raise(Errno::ESRCH.new)
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  process.start
         | 
| 34 | 
            +
                  expect { process.stop }.not_to raise_error
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  allow(process).to receive(:alive?).and_return(false)
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  process.send(:send_signal, 'TERM')
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              describe ChildProcess::Unix::IO do
         | 
| 43 | 
            +
                let(:io) { ChildProcess::Unix::IO.new }
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                it "raises an ArgumentError if given IO does not respond to :to_io" do
         | 
| 46 | 
            +
                  expect { io.stdout = nil }.to raise_error(ArgumentError, /to respond to :to_io/)
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                it "raises a TypeError if #to_io does not return an IO" do
         | 
| 50 | 
            +
                  fake_io = Object.new
         | 
| 51 | 
            +
                  def fake_io.to_io() StringIO.new end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  expect { io.stdout = fake_io }.to raise_error(TypeError, /expected IO, got/)
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            end
         | 
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            require File.expand_path('../spec_helper', __FILE__)
         | 
| 2 | 
            +
            require "pid_behavior"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            if ChildProcess.windows?
         | 
| 5 | 
            +
              describe ChildProcess::Windows::Process do
         | 
| 6 | 
            +
                it_behaves_like "a platform that provides the child's pid"
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              describe ChildProcess::Windows::IO do
         | 
| 10 | 
            +
                let(:io) { ChildProcess::Windows::IO.new }
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                it "raises an ArgumentError if given IO does not respond to :fileno" do
         | 
| 13 | 
            +
                  expect { io.stdout = nil }.to raise_error(ArgumentError, /must have :fileno or :to_io/)
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                it "raises an ArgumentError if the #to_io does not return an IO " do
         | 
| 17 | 
            +
                  fake_io = Object.new
         | 
| 18 | 
            +
                  def fake_io.to_io() StringIO.new end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  expect { io.stdout = fake_io }.to raise_error(ArgumentError, /must have :fileno or :to_io/)
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
            end
         |