summaryrefslogtreecommitdiff
path: root/spec/ruby/shared/process
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/shared/process')
-rw-r--r--spec/ruby/shared/process/abort.rb36
-rw-r--r--spec/ruby/shared/process/exit.rb94
-rw-r--r--spec/ruby/shared/process/fork.rb90
3 files changed, 220 insertions, 0 deletions
diff --git a/spec/ruby/shared/process/abort.rb b/spec/ruby/shared/process/abort.rb
new file mode 100644
index 0000000000..1a25aeffc2
--- /dev/null
+++ b/spec/ruby/shared/process/abort.rb
@@ -0,0 +1,36 @@
+describe :process_abort, shared: true do
+ before :each do
+ @stderr, $stderr = $stderr, IOStub.new
+ end
+
+ after :each do
+ $stderr = @stderr
+ end
+
+ it "raises a SystemExit exception" do
+ lambda { @object.abort }.should raise_error(SystemExit)
+ end
+
+ it "sets the exception message to the given message" do
+ lambda { @object.abort "message" }.should raise_error { |e| e.message.should == "message" }
+ end
+
+ it "sets the exception status code of 1" do
+ lambda { @object.abort }.should raise_error { |e| e.status.should == 1 }
+ end
+
+ it "prints the specified message to STDERR" do
+ lambda { @object.abort "a message" }.should raise_error(SystemExit)
+ $stderr.should =~ /a message/
+ end
+
+ it "coerces the argument with #to_str" do
+ str = mock('to_str')
+ str.should_receive(:to_str).any_number_of_times.and_return("message")
+ lambda { @object.abort str }.should raise_error(SystemExit, "message")
+ end
+
+ it "raises TypeError when given a non-String object" do
+ lambda { @object.abort 123 }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/shared/process/exit.rb b/spec/ruby/shared/process/exit.rb
new file mode 100644
index 0000000000..7d567c8195
--- /dev/null
+++ b/spec/ruby/shared/process/exit.rb
@@ -0,0 +1,94 @@
+describe :process_exit, shared: true do
+ it "raises a SystemExit with status 0" do
+ lambda { @object.exit }.should raise_error(SystemExit) { |e|
+ e.status.should == 0
+ }
+ end
+
+ it "raises a SystemExit with the specified status" do
+ [-2**16, -2**8, -8, -1, 0, 1 , 8, 2**8, 2**16].each do |value|
+ lambda { @object.exit(value) }.should raise_error(SystemExit) { |e|
+ e.status.should == value
+ }
+ end
+ end
+
+ it "raises a SystemExit with the specified boolean status" do
+ { true => 0, false => 1 }.each do |value, status|
+ lambda { @object.exit(value) }.should raise_error(SystemExit) { |e|
+ e.status.should == status
+ }
+ end
+ end
+
+ it "tries to convert the passed argument to an Integer using #to_int" do
+ obj = mock('5')
+ obj.should_receive(:to_int).and_return(5)
+ lambda { @object.exit(obj) }.should raise_error(SystemExit) { |e|
+ e.status.should == 5
+ }
+ end
+
+ it "converts the passed Float argument to an Integer" do
+ { -2.2 => -2, -0.1 => 0, 5.5 => 5, 827.999 => 827 }.each do |value, status|
+ lambda { @object.exit(value) }.should raise_error(SystemExit) { |e|
+ e.status.should == status
+ }
+ end
+ end
+
+ it "raises TypeError if can't convert the argument to an Integer" do
+ lambda { @object.exit(Object.new) }.should raise_error(TypeError)
+ lambda { @object.exit('0') }.should raise_error(TypeError)
+ lambda { @object.exit([0]) }.should raise_error(TypeError)
+ lambda { @object.exit(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises the SystemExit in the main thread if it reaches the top-level handler of another thread" do
+ ScratchPad.record []
+
+ ready = false
+ t = Thread.new {
+ Thread.pass until ready
+
+ begin
+ @object.exit 42
+ rescue SystemExit => e
+ ScratchPad << :in_thread
+ raise e
+ end
+ }
+
+ begin
+ ready = true
+ sleep
+ rescue SystemExit
+ ScratchPad << :in_main
+ end
+
+ ScratchPad.recorded.should == [:in_thread, :in_main]
+
+ # the thread also keeps the exception as its value
+ lambda { t.value }.should raise_error(SystemExit)
+ end
+end
+
+describe :process_exit!, shared: true do
+ it "exits with the given status" do
+ out = ruby_exe("#{@object}.send(:exit!, 21)", args: '2>&1')
+ out.should == ""
+ $?.exitstatus.should == 21
+ end
+
+ it "exits when called from a thread" do
+ out = ruby_exe("Thread.new { #{@object}.send(:exit!, 21) }.join; sleep", args: '2>&1')
+ out.should == ""
+ $?.exitstatus.should == 21
+ end
+
+ it "exits when called from a fiber" do
+ out = ruby_exe("Fiber.new { #{@object}.send(:exit!, 21) }.resume", args: '2>&1')
+ out.should == ""
+ $?.exitstatus.should == 21
+ end
+end
diff --git a/spec/ruby/shared/process/fork.rb b/spec/ruby/shared/process/fork.rb
new file mode 100644
index 0000000000..c2c2aee9bf
--- /dev/null
+++ b/spec/ruby/shared/process/fork.rb
@@ -0,0 +1,90 @@
+describe :process_fork, shared: true do
+ platform_is :windows do
+ it "returns false from #respond_to?" do
+ # Workaround for Kernel::Method being public and losing the "non-respond_to? magic"
+ mod = @object.class.name == "KernelSpecs::Method" ? Object.new : @object
+ mod.respond_to?(:fork).should be_false
+ mod.respond_to?(:fork, true).should be_false
+ end
+
+ it "raises a NotImplementedError when called" do
+ lambda { @object.fork }.should raise_error(NotImplementedError)
+ end
+ end
+
+ platform_is_not :windows do
+ before :each do
+ @file = tmp('i_exist')
+ rm_r @file
+ end
+
+ after :each do
+ rm_r @file
+ end
+
+ it "returns status zero" do
+ pid = Process.fork { exit! 0 }
+ _, result = Process.wait2(pid)
+ result.exitstatus.should == 0
+ end
+
+ it "returns status zero" do
+ pid = Process.fork { exit 0 }
+ _, result = Process.wait2(pid)
+ result.exitstatus.should == 0
+ end
+
+ it "returns status zero" do
+ pid = Process.fork {}
+ _, result = Process.wait2(pid)
+ result.exitstatus.should == 0
+ end
+
+ it "returns status non-zero" do
+ pid = Process.fork { exit! 42 }
+ _, result = Process.wait2(pid)
+ result.exitstatus.should == 42
+ end
+
+ it "returns status non-zero" do
+ pid = Process.fork { exit 42 }
+ _, result = Process.wait2(pid)
+ result.exitstatus.should == 42
+ end
+
+ it "returns nil for the child process" do
+ child_id = @object.fork
+ if child_id == nil
+ touch(@file) { |f| f.write 'rubinius' }
+ Process.exit!
+ else
+ Process.waitpid(child_id)
+ end
+ File.exist?(@file).should == true
+ end
+
+ it "runs a block in a child process" do
+ pid = @object.fork {
+ touch(@file) { |f| f.write 'rubinius' }
+ Process.exit!
+ }
+ Process.waitpid(pid)
+ File.exist?(@file).should == true
+ end
+
+ it "marks threads from the parent as killed" do
+ t = Thread.new { sleep }
+ pid = @object.fork {
+ touch(@file) do |f|
+ f.write Thread.current.alive?
+ f.write t.alive?
+ end
+ Process.exit!
+ }
+ Process.waitpid(pid)
+ t.kill
+ t.join
+ File.read(@file).should == "truefalse"
+ end
+ end
+end