summaryrefslogtreecommitdiff
path: root/spec/ruby/core/fiber/raise_spec.rb
blob: b3e021e6360b7a2c4e08866aebe6f1ce50109850 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative '../../shared/kernel/raise'

describe "Fiber#raise" do
  it_behaves_like :kernel_raise, :raise, FiberSpecs::NewFiberToRaise
end

describe "Fiber#raise" do
  it 'raises RuntimeError by default' do
    -> { FiberSpecs::NewFiberToRaise.raise }.should raise_error(RuntimeError)
  end

  it "raises FiberError if Fiber is not born" do
    fiber = Fiber.new { true }
    -> { fiber.raise }.should raise_error(FiberError, "cannot raise exception on unborn fiber")
  end

  it "raises FiberError if Fiber is dead" do
    fiber = Fiber.new { true }
    fiber.resume
    -> { fiber.raise }.should raise_error(FiberError, /dead fiber called|attempt to resume a terminated fiber/)
  end

  it 'accepts error class' do
    -> { FiberSpecs::NewFiberToRaise.raise FiberSpecs::CustomError }.should raise_error(FiberSpecs::CustomError)
  end

  it 'accepts error message' do
    -> { FiberSpecs::NewFiberToRaise.raise "error message" }.should raise_error(RuntimeError, "error message")
  end

  it 'does not accept array of backtrace information only' do
    -> { FiberSpecs::NewFiberToRaise.raise ['foo'] }.should raise_error(TypeError)
  end

  it 'does not accept integer' do
    -> { FiberSpecs::NewFiberToRaise.raise 100 }.should raise_error(TypeError)
  end

  it 'accepts error class with error message' do
    -> { FiberSpecs::NewFiberToRaise.raise FiberSpecs::CustomError, 'test error' }.should raise_error(FiberSpecs::CustomError, 'test error')
  end

  it 'accepts error class with with error message and backtrace information' do
    -> {
      FiberSpecs::NewFiberToRaise.raise FiberSpecs::CustomError, 'test error', ['foo', 'boo']
    }.should raise_error(FiberSpecs::CustomError) { |e|
      e.message.should == 'test error'
      e.backtrace.should == ['foo', 'boo']
    }
  end

  it 'does not accept only error message and backtrace information' do
    -> { FiberSpecs::NewFiberToRaise.raise 'test error', ['foo', 'boo'] }.should raise_error(TypeError)
  end

  it "raises a FiberError if invoked from a different Thread" do
    fiber = Fiber.new { Fiber.yield }
    fiber.resume
    Thread.new do
      -> {
        fiber.raise
      }.should raise_error(FiberError, "fiber called across threads")
    end.join
  end

  it "kills Fiber" do
    fiber = Fiber.new { Fiber.yield :first; :second }
    fiber.resume
    -> { fiber.raise }.should raise_error
    -> { fiber.resume }.should raise_error(FiberError, /dead fiber called|attempt to resume a terminated fiber/)
  end

  it "returns to calling fiber after raise" do
    fiber_one = Fiber.new do
      Fiber.yield :yield_one
      :unreachable
    end

    fiber_two = Fiber.new do
      results = []
      results << fiber_one.resume
      begin
        fiber_one.raise
      rescue
        results << :rescued
      end
      results
    end

    fiber_two.resume.should == [:yield_one, :rescued]
  end

  ruby_version_is "3.4" do
    it "raises on the resumed fiber" do
      root_fiber = Fiber.current
      f1 = Fiber.new { root_fiber.transfer }
      f2 = Fiber.new { f1.resume }
      f2.transfer

      -> do
        f2.raise(RuntimeError, "Expected error")
      end.should raise_error(RuntimeError, "Expected error")
    end

    it "raises on itself" do
      -> do
        Fiber.current.raise(RuntimeError, "Expected error")
      end.should raise_error(RuntimeError, "Expected error")
    end

    it "should raise on parent fiber" do
      f2 = nil
      f1 = Fiber.new do
        # This is equivalent to Kernel#raise:
        f2.raise(RuntimeError, "Expected error")
      end
      f2 = Fiber.new do
        f1.resume
      end

      -> do
        f2.resume
      end.should raise_error(RuntimeError, "Expected error")
    end
  end
end


describe "Fiber#raise" do
  it "transfers and raises on a transferring fiber" do
    require "fiber"
    root = Fiber.current
    fiber = Fiber.new { root.transfer }
    fiber.transfer
    -> { fiber.raise "msg" }.should raise_error(RuntimeError, "msg")
  end
end