childprocess 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
data/childprocess.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{childprocess}
8
- s.version = "0.1.0"
8
+ s.version = "0.1.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jari Bakken"]
@@ -25,42 +25,49 @@ Gem::Specification.new do |s|
25
25
  "VERSION",
26
26
  "childprocess.gemspec",
27
27
  "lib/childprocess.rb",
28
+ "lib/childprocess/abstract_io.rb",
28
29
  "lib/childprocess/abstract_process.rb",
29
30
  "lib/childprocess/errors.rb",
30
31
  "lib/childprocess/ironruby.rb",
31
32
  "lib/childprocess/ironruby/process.rb",
32
33
  "lib/childprocess/jruby.rb",
34
+ "lib/childprocess/jruby/io.rb",
33
35
  "lib/childprocess/jruby/process.rb",
36
+ "lib/childprocess/jruby/redirector.rb",
34
37
  "lib/childprocess/unix.rb",
38
+ "lib/childprocess/unix/io.rb",
35
39
  "lib/childprocess/unix/process.rb",
36
40
  "lib/childprocess/windows.rb",
37
41
  "lib/childprocess/windows/api.rb",
38
42
  "lib/childprocess/windows/constants.rb",
39
43
  "lib/childprocess/windows/functions.rb",
40
44
  "lib/childprocess/windows/handle.rb",
45
+ "lib/childprocess/windows/io.rb",
41
46
  "lib/childprocess/windows/process.rb",
42
47
  "lib/childprocess/windows/structs.rb",
43
48
  "spec/childprocess_spec.rb",
49
+ "spec/jruby_spec.rb",
44
50
  "spec/spec.opts",
45
51
  "spec/spec_helper.rb",
46
- "spec/unix_process_spec.rb"
52
+ "spec/unix_spec.rb"
47
53
  ]
48
54
  s.homepage = %q{http://github.com/jarib/childprocess}
49
55
  s.rdoc_options = ["--charset=UTF-8"]
50
56
  s.require_paths = ["lib"]
51
- s.rubygems_version = %q{1.3.6}
57
+ s.rubygems_version = %q{1.3.7}
52
58
  s.summary = %q{Cross-platform ruby library for managing child processes.}
53
59
  s.test_files = [
54
60
  "spec/childprocess_spec.rb",
61
+ "spec/jruby_spec.rb",
55
62
  "spec/spec_helper.rb",
56
- "spec/unix_process_spec.rb"
63
+ "spec/unix_spec.rb"
57
64
  ]
58
65
 
59
66
  if s.respond_to? :specification_version then
60
67
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
61
68
  s.specification_version = 3
62
69
 
63
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
70
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
64
71
  s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
65
72
  s.add_development_dependency(%q<yard>, [">= 0"])
66
73
  s.add_runtime_dependency(%q<ffi>, ["~> 0.6.3"])
data/lib/childprocess.rb CHANGED
@@ -1,5 +1,6 @@
1
- require 'childprocess/abstract_process'
2
1
  require 'childprocess/errors'
2
+ require 'childprocess/abstract_process'
3
+ require 'childprocess/abstract_io'
3
4
 
4
5
  module ChildProcess
5
6
  autoload :Unix, 'childprocess/unix'
@@ -35,6 +36,14 @@ module ChildProcess
35
36
  end
36
37
  end
37
38
 
39
+ def unix?
40
+ !jruby? && [:macosx, :linux, :unix].include?(os)
41
+ end
42
+
43
+ def jruby?
44
+ platform == :jruby
45
+ end
46
+
38
47
  def os
39
48
  @os ||= (
40
49
  require "rbconfig"
@@ -0,0 +1,22 @@
1
+ module ChildProcess
2
+ class AbstractIO
3
+ attr_reader :stderr, :stdout
4
+
5
+ def stderr=(io)
6
+ check_type io
7
+ @stderr = io
8
+ end
9
+
10
+ def stdout=(io)
11
+ check_type io
12
+ @stdout = io
13
+ end
14
+
15
+ private
16
+
17
+ def check_type(io)
18
+ raise SubclassResponsibility, "check_type"
19
+ end
20
+
21
+ end
22
+ end
@@ -21,6 +21,14 @@ module ChildProcess
21
21
  @exit_code = nil
22
22
  end
23
23
 
24
+ #
25
+ # Returns a ChildProcess::AbstractIO subclass to configure the child's IO streams.
26
+ #
27
+
28
+ def io
29
+ raise SubclassResponsibility, "io"
30
+ end
31
+
24
32
  #
25
33
  # Launch the child process
26
34
  #
@@ -57,15 +65,28 @@ module ChildProcess
57
65
  #
58
66
  # Is this process running?
59
67
  #
68
+ # @return [Boolean]
69
+ #
60
70
 
61
71
  def alive?
62
72
  started? && !exited?
63
73
  end
64
74
 
75
+ #
76
+ # Returns true if the process has exited and the exit code was not 0.
77
+ #
78
+ # @return [Boolean]
79
+ #
80
+
65
81
  def crashed?
66
82
  exited? && @exit_code != 0
67
83
  end
68
84
 
85
+ #
86
+ # Wait for the process to exit, raising a ChildProcess::TimeoutError if
87
+ # the timeout expires.
88
+ #
89
+
69
90
  def poll_for_exit(timeout)
70
91
  log "polling #{timeout} seconds for exit"
71
92
 
@@ -3,4 +3,7 @@ module ChildProcess
3
3
  end
4
4
  end
5
5
 
6
+ require "java"
7
+ require "childprocess/jruby/redirector"
8
+ require "childprocess/jruby/io"
6
9
  require "childprocess/jruby/process"
@@ -0,0 +1,16 @@
1
+ module ChildProcess
2
+ module JRuby
3
+ class IO < AbstractIO
4
+ private
5
+
6
+ def check_type(output)
7
+ unless output.respond_to?(:to_outputstream) && output.respond_to?(:write)
8
+ raise ArgumentError, "expected #{output.inspect} to respond to :to_outputstream"
9
+ end
10
+ end
11
+
12
+ end # IO
13
+ end # Unix
14
+ end # ChildProcess
15
+
16
+
@@ -3,11 +3,16 @@ require "java"
3
3
  module ChildProcess
4
4
  module JRuby
5
5
  class Process < AbstractProcess
6
+ def io
7
+ @io ||= JRuby::IO.new
8
+ end
9
+
6
10
  def exited?
7
11
  return true if @exit_code
8
12
 
9
13
  assert_started
10
14
  @exit_code = @process.exitValue
15
+ true
11
16
  rescue java.lang.IllegalThreadStateException
12
17
  false
13
18
  ensure
@@ -26,35 +31,35 @@ module ChildProcess
26
31
  private
27
32
 
28
33
  def launch_process
29
- # background_args! if @detach
30
-
31
34
  pb = java.lang.ProcessBuilder.new(@args)
32
35
 
33
- # not sure why this is necessary
34
36
  env = pb.environment
35
37
  ENV.each { |k,v| env.put(k, v) }
36
38
 
37
39
  @process = pb.start
38
-
39
- # Firefox 3.6 on Snow Leopard has a lot output on stderr, which makes
40
- # the launch act funny if we don't do something to the streams
41
- # Closing the streams solves the problem for now, but on other platforms
42
- # we might need to actually read them.
43
-
44
- @process.getErrorStream.close
45
- @process.getInputStream.close
40
+ setup_io
46
41
  end
47
42
 
48
- def background_args!
49
- case ChildProcess.os
50
- when :windows
51
- args = %w[start /wait /b]
52
- @args.unshift(*args) unless @args[0] == start
43
+ def setup_io
44
+ if @io
45
+ redirect @process.getErrorStream, @io.stderr
46
+ redirect @process.getInputStream, @io.stdout
53
47
  else
54
- @args.push "&" unless @args.last == "&"
48
+ @process.getErrorStream.close
49
+ @process.getInputStream.close
55
50
  end
56
51
  end
57
52
 
53
+ def redirect(input, output)
54
+ if output.nil?
55
+ input.close
56
+ return
57
+ end
58
+
59
+ output = output.to_outputstream
60
+ Thread.new { Redirector.new(input, output).run }
61
+ end
62
+
58
63
  end # Process
59
64
  end # JRuby
60
65
  end # ChildProcess
@@ -0,0 +1,29 @@
1
+ module ChildProcess
2
+ module JRuby
3
+ class Redirector
4
+ BUFFER_SIZE = 2048
5
+
6
+ def initialize(input, output)
7
+ @input = input
8
+ @output = output
9
+ @buffer = Java.byte[BUFFER_SIZE].new
10
+ end
11
+
12
+ def run
13
+ read, avail = 0, 0
14
+
15
+ while(read != -1)
16
+ avail = [@input.available, 1].max
17
+ read = @input.read(@buffer, 0, avail)
18
+
19
+ if read > 0
20
+ @output.write(@buffer, 0, read)
21
+ end
22
+ end
23
+ rescue java.io.IOException => ex
24
+ $stderr.puts ex.message, ex.backtrace if $DEBUG
25
+ end
26
+
27
+ end # Redirector
28
+ end # JRuby
29
+ end # ChildProcess
@@ -3,4 +3,5 @@ module ChildProcess
3
3
  end
4
4
  end
5
5
 
6
+ require "childprocess/unix/io"
6
7
  require "childprocess/unix/process"
@@ -0,0 +1,21 @@
1
+ module ChildProcess
2
+ module Unix
3
+ class IO < AbstractIO
4
+ private
5
+
6
+ def check_type(io)
7
+ unless io.respond_to? :to_io
8
+ raise ArgumentError, "expected #{io.inspect} to respond to :to_io"
9
+ end
10
+
11
+ result = io.to_io
12
+ unless result && result.kind_of?(::IO)
13
+ raise TypeError, "expected IO, got #{result.inspect}:#{result.class}"
14
+ end
15
+ end
16
+
17
+ end # IO
18
+ end # Unix
19
+ end # ChildProcess
20
+
21
+
@@ -2,6 +2,10 @@ module ChildProcess
2
2
  module Unix
3
3
  class Process < AbstractProcess
4
4
 
5
+ def io
6
+ @io ||= Unix::IO.new
7
+ end
8
+
5
9
  def stop(timeout = 3)
6
10
  assert_started
7
11
  send_term
@@ -63,10 +67,14 @@ module ChildProcess
63
67
  end
64
68
 
65
69
  def launch_process
70
+ if @io
71
+ stdout = @io.stdout
72
+ stderr = @io.stderr
73
+ end
74
+
66
75
  @pid = fork {
67
- unless $DEBUG
68
- [STDOUT, STDERR].each { |io| io.reopen("/dev/null") }
69
- end
76
+ STDOUT.reopen(stdout || "/dev/null")
77
+ STDERR.reopen(stderr || "/dev/null")
70
78
 
71
79
  exec(*@args)
72
80
  }
@@ -1,19 +1,34 @@
1
1
  require "ffi"
2
+ require "rbconfig"
2
3
 
3
4
  module ChildProcess
4
5
  module Windows
5
6
  module Lib
6
7
  extend FFI::Library
7
8
 
8
- ffi_lib "kernel32"
9
+ def self.msvcrt_name
10
+ host_part = RbConfig::CONFIG['host_os'].split("_")[1]
11
+ manifest = File.join(RbConfig::CONFIG['bindir'], 'ruby.exe.manifest')
12
+
13
+ if host_part && host_part.to_i > 80 && File.exists?(manifest)
14
+ "msvcr#{host_part}"
15
+ else
16
+ "msvcrt"
17
+ end
18
+ end
19
+
20
+ ffi_lib "kernel32", msvcrt_name
9
21
  ffi_convention :stdcall
10
- end
11
- end
12
- end
22
+
23
+
24
+ end # Library
25
+ end # Windows
26
+ end # ChildProcess
13
27
 
14
28
  require "childprocess/windows/constants"
15
29
  require "childprocess/windows/structs"
16
30
  require "childprocess/windows/functions"
17
31
  require "childprocess/windows/handle"
18
32
  require "childprocess/windows/api"
19
- require "childprocess/windows/process"
33
+ require "childprocess/windows/io"
34
+ require "childprocess/windows/process"
@@ -19,6 +19,10 @@ module ChildProcess::Windows
19
19
 
20
20
  DETACHED_PROCESS = 0x00000008
21
21
 
22
+ STARTF_USESTDHANDLES = 0x00000100
23
+ INVALID_HANDLE_VALUE = 0xFFFFFFFF
24
+
25
+
22
26
  module Lib
23
27
  enum :wait_status, [ :wait_object_0, 0,
24
28
  :wait_timeout, 0x102,
@@ -13,6 +13,15 @@ module ChildProcess
13
13
  si = StartupInfo.new
14
14
  pi = ProcessInfo.new
15
15
 
16
+ if opts[:stdout] || opts[:stderr]
17
+ si[:dwFlags] ||= 0
18
+ si[:dwFlags] |= STARTF_USESTDHANDLES
19
+ inherit = true
20
+
21
+ si[:hStdOutput] = get_os_file_handle(opts[:stdout].fileno) if opts[:stdout]
22
+ si[:hStdError] = get_os_file_handle(opts[:stderr].fileno) if opts[:stderr]
23
+ end
24
+
16
25
  ok = create_process(nil, cmd_ptr, nil, nil, inherit, flags, nil, nil, si, pi)
17
26
  ok or raise Error, last_error_message
18
27
 
@@ -34,6 +43,33 @@ module ChildProcess
34
43
  buf.read_string(size).strip
35
44
  end
36
45
 
46
+ def self.get_os_file_handle(fd_or_io)
47
+ case fd_or_io
48
+ when IO
49
+ handle = _get_osfhandle(fd.fileno)
50
+ when Fixnum
51
+ handle = _get_osfhandle(fd_or_io)
52
+ else
53
+ if fd_or_io.respond_to?(:to_io)
54
+ io = fd_or_io.to_io
55
+
56
+ unless io.kind_of?(IO)
57
+ raise TypeError, "expected #to_io to return an instance of IO"
58
+ end
59
+
60
+ handle = _get_osfhandle(io.fileno)
61
+ else
62
+ raise TypeError, "invalid type: #{fd_or_io.inspect}"
63
+ end
64
+ end
65
+
66
+ if handle == INVALID_HANDLE_VALUE
67
+ raise Error, Lib.last_error_message
68
+ end
69
+
70
+ handle
71
+ end
72
+
37
73
  #
38
74
  # BOOL WINAPI CreateProcess(
39
75
  # __in_opt LPCTSTR lpApplicationName,
@@ -139,6 +175,14 @@ module ChildProcess
139
175
 
140
176
  attach_function :terminate_process, :TerminateProcess, [:pointer, :uint], :bool
141
177
 
178
+ #
179
+ # long _get_osfhandle(
180
+ # int fd
181
+ # );
182
+ #
183
+
184
+ attach_function :_get_osfhandle, :_get_osfhandle, [:int], :long
185
+
142
186
  end # Lib
143
187
  end # Windows
144
188
  end # ChildProcess
@@ -1,87 +1,89 @@
1
- module ChildProcess::Windows
2
- class Handle
1
+ module ChildProcess
2
+ module Windows
3
+ class Handle
3
4
 
4
- class << self
5
- private :new
5
+ class << self
6
+ private :new
6
7
 
7
- def open(pid, access = PROCESS_ALL_ACCESS)
8
- handle = Lib.open_process(access, false, pid)
8
+ def open(pid, access = PROCESS_ALL_ACCESS)
9
+ handle = Lib.open_process(access, false, pid)
9
10
 
10
- if handle.null?
11
- raise Error, Lib.last_error_message
12
- end
11
+ if handle.null?
12
+ raise Error, Lib.last_error_message
13
+ end
13
14
 
14
- h = new(handle, pid)
15
- return h unless block_given?
15
+ h = new(handle, pid)
16
+ return h unless block_given?
16
17
 
17
- begin
18
- yield h
19
- ensure
20
- h.close
18
+ begin
19
+ yield h
20
+ ensure
21
+ h.close
22
+ end
21
23
  end
22
24
  end
23
- end
24
25
 
25
- def initialize(handle, pid)
26
- unless handle.kind_of?(FFI::Pointer)
27
- raise TypeError, "invalid handle: #{handle.inspect}"
28
- end
26
+ def initialize(handle, pid)
27
+ unless handle.kind_of?(FFI::Pointer)
28
+ raise TypeError, "invalid handle: #{handle.inspect}"
29
+ end
29
30
 
30
- if handle.null?
31
- raise ArgumentError, "handle is null: #{handle.inspect}"
32
- end
31
+ if handle.null?
32
+ raise ArgumentError, "handle is null: #{handle.inspect}"
33
+ end
33
34
 
34
- @pid = pid
35
- @handle = handle
36
- @closed = false
37
- end
35
+ @pid = pid
36
+ @handle = handle
37
+ @closed = false
38
+ end
38
39
 
39
- def exit_code
40
- code_pointer = FFI::MemoryPointer.new :ulong
41
- ok = Lib.get_exit_code(@handle, code_pointer)
40
+ def exit_code
41
+ code_pointer = FFI::MemoryPointer.new :ulong
42
+ ok = Lib.get_exit_code(@handle, code_pointer)
42
43
 
43
- if ok
44
- code_pointer.get_ulong(0)
45
- else
46
- close
47
- raise Error, Lib.last_error_message
44
+ if ok
45
+ code_pointer.get_ulong(0)
46
+ else
47
+ close
48
+ raise Error, Lib.last_error_message
49
+ end
48
50
  end
49
- end
50
-
51
- def send(signal)
52
- case signal
53
- when 0
54
- exit_code == PROCESS_STILL_ALIVE
55
- when WIN_SIGINT
56
- Lib.generate_console_ctrl_event(CTRL_C_EVENT, @pid)
57
- when WIN_SIGBREAK
58
- Lib.generate_console_ctrl_event(CTRL_BREAK_EVENT, @pid)
59
- when WIN_SIGKILL
60
- ok = Lib.terminate_process(@handle, @pid)
61
- ok or raise Error, Lib.last_error_message
62
- else
63
- thread_id = FFI::MemoryPointer.new(:ulong)
64
- module_handle = Lib.get_module_handle("kernel32")
65
- proc_address = Lib.get_proc_address(module_handle, "ExitProcess")
66
-
67
- thread = Lib.create_remote_thread(@handle, 0, 0, proc_address, 0, 0, thread_id)
68
- thread or raise Error, Lib.last_error_message
69
-
70
- Lib.wait_for_single_object(thread, 5)
71
- true
51
+
52
+ def send(signal)
53
+ case signal
54
+ when 0
55
+ exit_code == PROCESS_STILL_ALIVE
56
+ when WIN_SIGINT
57
+ Lib.generate_console_ctrl_event(CTRL_C_EVENT, @pid)
58
+ when WIN_SIGBREAK
59
+ Lib.generate_console_ctrl_event(CTRL_BREAK_EVENT, @pid)
60
+ when WIN_SIGKILL
61
+ ok = Lib.terminate_process(@handle, @pid)
62
+ ok or raise Error, Lib.last_error_message
63
+ else
64
+ thread_id = FFI::MemoryPointer.new(:ulong)
65
+ module_handle = Lib.get_module_handle("kernel32")
66
+ proc_address = Lib.get_proc_address(module_handle, "ExitProcess")
67
+
68
+ thread = Lib.create_remote_thread(@handle, 0, 0, proc_address, 0, 0, thread_id)
69
+ thread or raise Error, Lib.last_error_message
70
+
71
+ Lib.wait_for_single_object(thread, 5)
72
+ true
73
+ end
72
74
  end
73
- end
74
75
 
75
- def close
76
- return if @closed
76
+ def close
77
+ return if @closed
77
78
 
78
- Lib.close_handle(@handle)
79
- @closed = true
80
- end
79
+ Lib.close_handle(@handle)
80
+ @closed = true
81
+ end
81
82
 
82
- def wait(milliseconds = nil)
83
- Lib.wait_for_single_object(@handle, milliseconds || INFINITE)
84
- end
83
+ def wait(milliseconds = nil)
84
+ Lib.wait_for_single_object(@handle, milliseconds || INFINITE)
85
+ end
85
86
 
86
- end # Handle
87
- end # WindowsProcess
87
+ end # Handle
88
+ end # Windows
89
+ end # ChildProcess
@@ -0,0 +1,25 @@
1
+ module ChildProcess
2
+ module Windows
3
+ class IO < AbstractIO
4
+ private
5
+
6
+ def check_type(io)
7
+ return if has_fileno?(io)
8
+ return if has_to_io?(io)
9
+
10
+ raise ArgumentError, "#{io.inspect}:#{io.class} must have :fileno or :to_io"
11
+ end
12
+
13
+ def has_fileno?(io)
14
+ io.respond_to?(:fileno) && io.fileno
15
+ end
16
+
17
+ def has_to_io?
18
+ io.respond_to?(:to_io) && io.to_io.kind_of?(::IO)
19
+ end
20
+
21
+ end # IO
22
+ end # Windows
23
+ end # ChildProcess
24
+
25
+
@@ -2,6 +2,10 @@ module ChildProcess
2
2
  module Windows
3
3
  class Process < AbstractProcess
4
4
 
5
+ def io
6
+ @io ||= Windows::IO.new
7
+ end
8
+
5
9
  def stop(timeout = 3)
6
10
  assert_started
7
11
 
@@ -33,11 +37,19 @@ module ChildProcess
33
37
  private
34
38
 
35
39
  def launch_process
36
- @pid = Lib.create_proc(
37
- @args.join(' '),
40
+ opts = {
38
41
  :inherit => false,
39
- :detach => @detach
40
- )
42
+ :detach => @detach,
43
+ }
44
+
45
+ if @io
46
+ opts[:stdout] = @io.stdout
47
+ opts[:stderr] = @io.stderr
48
+ end
49
+
50
+ command = @args.map { |e| e.inspect }.join(' ')
51
+
52
+ @pid = Lib.create_proc(command, opts)
41
53
  @handle = Handle.open(@pid)
42
54
 
43
55
  self
@@ -32,6 +32,7 @@ module ChildProcess::Windows
32
32
  :dwXCountChars, :ulong,
33
33
  :dwYCountChars, :ulong,
34
34
  :dwFillAttribute, :ulong,
35
+ :dwFlags, :ulong,
35
36
  :wShowWindow, :ushort,
36
37
  :cbReserved2, :ushort,
37
38
  :lpReserved2, :pointer,
@@ -63,8 +63,37 @@ describe ChildProcess do
63
63
  end
64
64
 
65
65
  it "lets a detached child live on" do
66
- # hmm. how do we spec this?
66
+ pending "how do we spec this?"
67
+ end
68
+
69
+ it "can redirect stdout and stderr" do
70
+ process = ruby(<<-CODE)
71
+ [STDOUT, STDERR].each_with_index do |io, idx|
72
+ io.sync = true
73
+ io.puts idx
74
+ end
75
+ sleep 0.2
76
+ CODE
77
+
78
+ out = Tempfile.new("stdout-spec")
79
+ err = Tempfile.new("stderr-spec")
80
+
81
+ begin
82
+ process.io.stdout = out
83
+ process.io.stderr = err
84
+
85
+ process.start
86
+ process.poll_for_exit(EXIT_TIMEOUT)
87
+
88
+ out.rewind
89
+ err.rewind
90
+
91
+ out.read.should == "0\n"
92
+ err.read.should == "1\n"
93
+ ensure
94
+ out.close
95
+ err.close
96
+ end
67
97
  end
68
98
 
69
- it_should_behave_like "unix process" if ChildProcess.platform == :unix
70
99
  end
@@ -0,0 +1,12 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ if ChildProcess.jruby?
4
+ describe ChildProcess::JRuby::IO do
5
+ let(:io) { ChildProcess::JRuby::IO.new }
6
+
7
+ it "raises an ArgumentError if given IO does not respond to :to_outputstream" do
8
+ lambda { io.stdout = nil }.should raise_error(ArgumentError)
9
+ end
10
+ end
11
+
12
+ end
data/spec/spec_helper.rb CHANGED
@@ -85,6 +85,7 @@ module ChildProcessSpecHelper
85
85
 
86
86
  end # ChildProcessSpecHelper
87
87
 
88
+ Thread.abort_on_exception = true
88
89
 
89
90
  Spec::Runner.configure do |config|
90
91
  config.include(ChildProcessSpecHelper)
@@ -92,5 +93,3 @@ Spec::Runner.configure do |config|
92
93
  @process && @process.alive? && @process.stop
93
94
  }
94
95
  end
95
-
96
- require "unix_process_spec"
data/spec/unix_spec.rb ADDED
@@ -0,0 +1,49 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ if ChildProcess.unix?
4
+ describe ChildProcess::Unix::Process do
5
+ it "handles ECHILD race condition where process dies between timeout and KILL" do
6
+ process = sleeping_ruby
7
+
8
+ process.stub!(:fork).and_return('fakepid')
9
+ process.stub!(:send_term)
10
+ process.stub!(:poll_for_exit).and_raise(ChildProcess::TimeoutError)
11
+ process.stub!(:send_kill).and_raise(Errno::ECHILD)
12
+
13
+ process.start
14
+ lambda { process.stop }.should_not raise_error
15
+
16
+ process.stub(:alive?).and_return(false)
17
+ end
18
+
19
+ it "handles ESRCH race condition where process dies between timeout and KILL" do
20
+ process = sleeping_ruby
21
+
22
+ process.stub!(:fork).and_return('fakepid')
23
+ process.stub!(:send_term)
24
+ process.stub!(:poll_for_exit).and_raise(ChildProcess::TimeoutError)
25
+ process.stub!(:send_kill).and_raise(Errno::ESRCH)
26
+
27
+ process.start
28
+ lambda { process.stop }.should_not raise_error
29
+
30
+ process.stub(:alive?).and_return(false)
31
+ end
32
+ end
33
+
34
+ describe ChildProcess::Unix::IO do
35
+ let(:io) { ChildProcess::Unix::IO.new }
36
+
37
+ it "raises an ArgumentError if given IO does not respond to :to_io" do
38
+ lambda { io.stdout = nil }.should raise_error(ArgumentError, /to respond to :to_io/)
39
+ end
40
+
41
+ it "raises an ArgumentError if the IO's fileno is nil" do
42
+ fake_io = Object.new
43
+ def fake_io.to_io() StringIO.new end
44
+
45
+ lambda { io.stdout = fake_io }.should raise_error(TypeError, /expected IO, got/)
46
+ end
47
+ end
48
+
49
+ end
metadata CHANGED
@@ -3,13 +3,13 @@ name: childprocess
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
- - 0
7
- - 1
8
- - 0
9
- version: 0.1.0
6
+ - 0
7
+ - 1
8
+ - 1
9
+ version: 0.1.1
10
10
  platform: ruby
11
11
  authors:
12
- - Jari Bakken
12
+ - Jari Bakken
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
@@ -17,46 +17,49 @@ cert_chain: []
17
17
  date: 2010-10-17 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies:
20
- - !ruby/object:Gem::Dependency
21
- name: rspec
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- segments:
28
- - 1
29
- - 2
30
- - 9
31
- version: 1.2.9
32
- type: :development
33
- version_requirements: *id001
34
- - !ruby/object:Gem::Dependency
35
- name: yard
36
- prerelease: false
37
- requirement: &id002 !ruby/object:Gem::Requirement
38
- requirements:
39
- - - ">="
40
- - !ruby/object:Gem::Version
41
- segments:
42
- - 0
43
- version: "0"
44
- type: :development
45
- version_requirements: *id002
46
- - !ruby/object:Gem::Dependency
47
- name: ffi
48
- prerelease: false
49
- requirement: &id003 !ruby/object:Gem::Requirement
50
- requirements:
51
- - - ~>
52
- - !ruby/object:Gem::Version
53
- segments:
54
- - 0
55
- - 6
56
- - 3
57
- version: 0.6.3
58
- type: :runtime
59
- version_requirements: *id003
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 1
30
+ - 2
31
+ - 9
32
+ version: 1.2.9
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: yard
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: ffi
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ segments:
57
+ - 0
58
+ - 6
59
+ - 3
60
+ version: 0.6.3
61
+ type: :runtime
62
+ version_requirements: *id003
60
63
  description: This gem aims at being a simple and reliable solution for controlling external programs running in the background on any Ruby / OS combination.
61
64
  email: jari.bakken@gmail.com
62
65
  executables: []
@@ -64,67 +67,76 @@ executables: []
64
67
  extensions: []
65
68
 
66
69
  extra_rdoc_files:
67
- - LICENSE
68
- - README.rdoc
70
+ - LICENSE
71
+ - README.rdoc
69
72
  files:
70
- - .document
71
- - .gitignore
72
- - LICENSE
73
- - README.rdoc
74
- - Rakefile
75
- - VERSION
76
- - childprocess.gemspec
77
- - lib/childprocess.rb
78
- - lib/childprocess/abstract_process.rb
79
- - lib/childprocess/errors.rb
80
- - lib/childprocess/ironruby.rb
81
- - lib/childprocess/ironruby/process.rb
82
- - lib/childprocess/jruby.rb
83
- - lib/childprocess/jruby/process.rb
84
- - lib/childprocess/unix.rb
85
- - lib/childprocess/unix/process.rb
86
- - lib/childprocess/windows.rb
87
- - lib/childprocess/windows/api.rb
88
- - lib/childprocess/windows/constants.rb
89
- - lib/childprocess/windows/functions.rb
90
- - lib/childprocess/windows/handle.rb
91
- - lib/childprocess/windows/process.rb
92
- - lib/childprocess/windows/structs.rb
93
- - spec/childprocess_spec.rb
94
- - spec/spec.opts
95
- - spec/spec_helper.rb
96
- - spec/unix_process_spec.rb
73
+ - .document
74
+ - .gitignore
75
+ - LICENSE
76
+ - README.rdoc
77
+ - Rakefile
78
+ - VERSION
79
+ - childprocess.gemspec
80
+ - lib/childprocess.rb
81
+ - lib/childprocess/abstract_io.rb
82
+ - lib/childprocess/abstract_process.rb
83
+ - lib/childprocess/errors.rb
84
+ - lib/childprocess/ironruby.rb
85
+ - lib/childprocess/ironruby/process.rb
86
+ - lib/childprocess/jruby.rb
87
+ - lib/childprocess/jruby/io.rb
88
+ - lib/childprocess/jruby/process.rb
89
+ - lib/childprocess/jruby/redirector.rb
90
+ - lib/childprocess/unix.rb
91
+ - lib/childprocess/unix/io.rb
92
+ - lib/childprocess/unix/process.rb
93
+ - lib/childprocess/windows.rb
94
+ - lib/childprocess/windows/api.rb
95
+ - lib/childprocess/windows/constants.rb
96
+ - lib/childprocess/windows/functions.rb
97
+ - lib/childprocess/windows/handle.rb
98
+ - lib/childprocess/windows/io.rb
99
+ - lib/childprocess/windows/process.rb
100
+ - lib/childprocess/windows/structs.rb
101
+ - spec/childprocess_spec.rb
102
+ - spec/jruby_spec.rb
103
+ - spec/spec.opts
104
+ - spec/spec_helper.rb
105
+ - spec/unix_spec.rb
97
106
  has_rdoc: true
98
107
  homepage: http://github.com/jarib/childprocess
99
108
  licenses: []
100
109
 
101
110
  post_install_message:
102
111
  rdoc_options:
103
- - --charset=UTF-8
112
+ - --charset=UTF-8
104
113
  require_paths:
105
- - lib
114
+ - lib
106
115
  required_ruby_version: !ruby/object:Gem::Requirement
116
+ none: false
107
117
  requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- segments:
111
- - 0
112
- version: "0"
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ segments:
121
+ - 0
122
+ version: "0"
113
123
  required_rubygems_version: !ruby/object:Gem::Requirement
124
+ none: false
114
125
  requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- segments:
118
- - 0
119
- version: "0"
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ segments:
129
+ - 0
130
+ version: "0"
120
131
  requirements: []
121
132
 
122
133
  rubyforge_project:
123
- rubygems_version: 1.3.6
134
+ rubygems_version: 1.3.7
124
135
  signing_key:
125
136
  specification_version: 3
126
137
  summary: Cross-platform ruby library for managing child processes.
127
138
  test_files:
128
- - spec/childprocess_spec.rb
129
- - spec/spec_helper.rb
130
- - spec/unix_process_spec.rb
139
+ - spec/childprocess_spec.rb
140
+ - spec/jruby_spec.rb
141
+ - spec/spec_helper.rb
142
+ - spec/unix_spec.rb
@@ -1,29 +0,0 @@
1
- shared_examples_for "unix process" do
2
- it "handles ECHILD race condition where process dies between timeout and KILL" do
3
- process = sleeping_ruby
4
-
5
- process.stub!(:fork).and_return('fakepid')
6
- process.stub!(:send_term)
7
- process.stub!(:poll_for_exit).and_raise(ChildProcess::TimeoutError)
8
- process.stub!(:send_kill).and_raise(Errno::ECHILD)
9
-
10
- process.start
11
- lambda { process.stop }.should_not raise_error
12
-
13
- process.stub(:alive?).and_return(false)
14
- end
15
-
16
- it "handles ESRCH race condition where process dies between timeout and KILL" do
17
- process = sleeping_ruby
18
-
19
- process.stub!(:fork).and_return('fakepid')
20
- process.stub!(:send_term)
21
- process.stub!(:poll_for_exit).and_raise(ChildProcess::TimeoutError)
22
- process.stub!(:send_kill).and_raise(Errno::ESRCH)
23
-
24
- process.start
25
- lambda { process.stop }.should_not raise_error
26
-
27
- process.stub(:alive?).and_return(false)
28
- end
29
- end