rubymirrors 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +7 -0
- data/README.md +30 -0
- data/Rakefile +13 -0
- data/lib/abstract_reflection.rb +112 -0
- data/lib/abstract_reflection/class_mirror.rb +150 -0
- data/lib/abstract_reflection/compiler_mirror.rb +31 -0
- data/lib/abstract_reflection/field_mirror.rb +35 -0
- data/lib/abstract_reflection/gc_mirror.rb +19 -0
- data/lib/abstract_reflection/method_mirror.rb +253 -0
- data/lib/abstract_reflection/mirror.rb +108 -0
- data/lib/abstract_reflection/object_mirror.rb +48 -0
- data/lib/abstract_reflection/stack_frame_mirror.rb +61 -0
- data/lib/abstract_reflection/thread_mirror.rb +64 -0
- data/lib/maglev/reflection.rb +49 -0
- data/lib/maglev/reflection/class_mirror.rb +157 -0
- data/lib/maglev/reflection/core_ext/class_organizer.rb +20 -0
- data/lib/maglev/reflection/core_ext/maglev.rb +5 -0
- data/lib/maglev/reflection/core_ext/method.rb +154 -0
- data/lib/maglev/reflection/core_ext/module.rb +41 -0
- data/lib/maglev/reflection/core_ext/object.rb +4 -0
- data/lib/maglev/reflection/core_ext/thread.rb +226 -0
- data/lib/maglev/reflection/field_mirror.rb +39 -0
- data/lib/maglev/reflection/field_mirror/fixed_instance_variable_mirror.rb +25 -0
- data/lib/maglev/reflection/method_mirror.rb +149 -0
- data/lib/maglev/reflection/mirror.rb +6 -0
- data/lib/maglev/reflection/object_mirror.rb +18 -0
- data/lib/maglev/reflection/stack_frame_mirror.rb +104 -0
- data/lib/maglev/reflection/thread_mirror.rb +116 -0
- data/lib/rubinius/reflection.rb +6 -0
- data/lib/ruby/reflection.rb +74 -0
- data/lib/ruby/reflection/class_mirror.rb +89 -0
- data/lib/ruby/reflection/field_mirror.rb +32 -0
- data/lib/ruby/reflection/field_mirror/class_variable_mirror.rb +25 -0
- data/lib/ruby/reflection/field_mirror/constant_mirror.rb +36 -0
- data/lib/ruby/reflection/field_mirror/instance_variable_mirror.rb +25 -0
- data/lib/ruby/reflection/method_mirror.rb +122 -0
- data/lib/ruby/reflection/mirror.rb +12 -0
- data/lib/ruby/reflection/object_mirror.rb +25 -0
- data/lib/ruby/reflection/stack_frame_mirror.rb +49 -0
- data/lib/ruby/reflection/support/shift_reset.rb +29 -0
- data/lib/ruby/reflection/thread_mirror.rb +47 -0
- data/rubymirrors.gemspec +12 -0
- data/spec/class_spec.rb +92 -0
- data/spec/field_spec.rb +119 -0
- data/spec/fixtures/class_spec.rb +29 -0
- data/spec/fixtures/field_spec.rb +9 -0
- data/spec/fixtures/method_spec.rb +14 -0
- data/spec/fixtures/object_spec.rb +5 -0
- data/spec/fixtures/reflect_spec.rb +14 -0
- data/spec/fixtures/stack_frame_spec.rb +5 -0
- data/spec/fixtures/thread_spec.rb +5 -0
- data/spec/frame_spec.rb +80 -0
- data/spec/method_spec.rb +166 -0
- data/spec/object_spec.rb +18 -0
- data/spec/reflection_spec.rb +74 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/spec_helper/mspec_patch.rb +29 -0
- data/spec/spec_helper/multiple_reflections.rb +14 -0
- data/spec/thread_spec.rb +142 -0
- metadata +173 -0
data/spec/frame_spec.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
require 'fixtures/stack_frame_spec'
|
3
|
+
|
4
|
+
describe "StackFrameMirror" do
|
5
|
+
before(:each) do
|
6
|
+
@r = reflection
|
7
|
+
@t = Thread.start do
|
8
|
+
t = FrameFixture.new
|
9
|
+
t.my_stop("argument_value")
|
10
|
+
t.my_return
|
11
|
+
end
|
12
|
+
Thread.pass until @t.stop?
|
13
|
+
@m = @r.reflect(@t)
|
14
|
+
@s = @m.stack
|
15
|
+
@f = @s.detect {|frame| frame.name == "my_stop" }
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should return StackFrameMirrors when reflecting on a Threads stack" do
|
19
|
+
@s.each {|a| a.should be_kind_of Reflection::StackFrameMirror }
|
20
|
+
end
|
21
|
+
|
22
|
+
it "the step offset" do
|
23
|
+
@f.step_offset.should be_kind_of Fixnum
|
24
|
+
end
|
25
|
+
|
26
|
+
it "the source offset" do
|
27
|
+
offs = @f.source_offset
|
28
|
+
offs.should be_kind_of Fixnum
|
29
|
+
offs.should < @f.method.source.size
|
30
|
+
end
|
31
|
+
|
32
|
+
it "the receiver" do
|
33
|
+
@f.receiver.target_class.name.should == "FrameFixture"
|
34
|
+
end
|
35
|
+
|
36
|
+
it "the self should be the receiver for a regular method" do
|
37
|
+
@f.self.reflectee.should == @f.receiver.reflectee
|
38
|
+
end
|
39
|
+
|
40
|
+
it "the arguments" do
|
41
|
+
@f.arguments.keys.should == ["argument"]
|
42
|
+
@f.arguments.values.collect(&:name).should == ["argument_value".inspect]
|
43
|
+
end
|
44
|
+
|
45
|
+
it "the locals" do
|
46
|
+
@f.locals.keys.should include "local"
|
47
|
+
@f.locals.values.collect(&:name).should include "local_value".inspect
|
48
|
+
end
|
49
|
+
|
50
|
+
it "the variable context" do
|
51
|
+
pending
|
52
|
+
end
|
53
|
+
|
54
|
+
it "restart the frame" do
|
55
|
+
prev_step_offset = @f.step_offset
|
56
|
+
@f.restart
|
57
|
+
@f.step_offset.should < prev_step_offset
|
58
|
+
@m.stack.first.should == @f
|
59
|
+
end
|
60
|
+
|
61
|
+
it "pop the frame" do
|
62
|
+
frame_before_f = @s[@s.index(@f) + 1]
|
63
|
+
@f.pop
|
64
|
+
@m.stack.first.should == frame_before_f
|
65
|
+
end
|
66
|
+
|
67
|
+
it "step over the current call" do
|
68
|
+
@f.restart
|
69
|
+
prev_step_offset = @f.step_offset
|
70
|
+
@f.step(:over)
|
71
|
+
@f.step_offset.should == prev_step_offset + 1
|
72
|
+
end
|
73
|
+
|
74
|
+
it "step into the next call" do
|
75
|
+
@f.restart
|
76
|
+
@f.step(:into)
|
77
|
+
@m.stack[0].should_not == @f
|
78
|
+
@m.stack[1].should == @f
|
79
|
+
end
|
80
|
+
end
|
data/spec/method_spec.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
require 'fixtures/method_spec'
|
3
|
+
|
4
|
+
describe "MethodMirror" do
|
5
|
+
describe "runtime reflection" do
|
6
|
+
describe "structural queries" do
|
7
|
+
before(:each) do
|
8
|
+
@r = Reflection.new(nil)
|
9
|
+
@f = MethodSpecFixture
|
10
|
+
m = MethodSpecFixture.instance_method(:source_location)
|
11
|
+
@m = @r.reflect(m)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "file" do
|
15
|
+
@m.file.should == @f.new.source_location[0]
|
16
|
+
end
|
17
|
+
|
18
|
+
it "line" do
|
19
|
+
@m.line.should == (@f.new.source_location[1] - 1)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "selector" do
|
23
|
+
@m.selector.should == @f.new.source_location[2]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "defining class" do
|
27
|
+
@m.defining_class.name.should == @f.new.source_location[3].name
|
28
|
+
end
|
29
|
+
|
30
|
+
it "source" do
|
31
|
+
@m.source.should =~ /[__FILE__, __LINE__, __method__.to_s, self.class]/
|
32
|
+
end
|
33
|
+
|
34
|
+
it "line=" do
|
35
|
+
@m.line = 12
|
36
|
+
@m.line.should == 12
|
37
|
+
end
|
38
|
+
|
39
|
+
it "file=" do
|
40
|
+
@m.file = "no_file.rb"
|
41
|
+
@m.file.should == "no_file.rb"
|
42
|
+
end
|
43
|
+
|
44
|
+
it "source=" do
|
45
|
+
@m.source = @m.source.sub("__FILE__", "__FILE__.to_s")
|
46
|
+
@m.source.should =~ /[__FILE__.to_s, __LINE__, __method__.to_s, self.class]/
|
47
|
+
@m.defining_class.name.should == MethodSpecFixture.name
|
48
|
+
m = @r.reflect MethodSpecFixture.instance_method(:source_location)
|
49
|
+
m.source.should =~ /[__FILE__.to_s, __LINE__, __method__.to_s, self.class]/
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "runtime behavior queries" do
|
54
|
+
def method_b(a, aa, b = 1, bb = 2, *args, &block)
|
55
|
+
to_s
|
56
|
+
super
|
57
|
+
end
|
58
|
+
|
59
|
+
before do
|
60
|
+
@m = @r.reflect(method(:method_b))
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "arguments" do
|
64
|
+
it "argument list" do
|
65
|
+
@m.arguments.should include("a", "aa", "b", "bb", "args", "block")
|
66
|
+
end
|
67
|
+
|
68
|
+
it "block argument" do
|
69
|
+
@m.block_argument.should == "block"
|
70
|
+
end
|
71
|
+
|
72
|
+
it "required arguments" do
|
73
|
+
@m.required_arguments.should include("a", "aa")
|
74
|
+
end
|
75
|
+
|
76
|
+
it "optional arguments" do
|
77
|
+
@m.optional_arguments.should include("b", "bb")
|
78
|
+
end
|
79
|
+
|
80
|
+
it "splat argument" do
|
81
|
+
@m.splat_argument.should == "args"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it "step_locations" do
|
86
|
+
@m.step_offsets.each do |l|
|
87
|
+
l.should be_kind_of(Fixnum)
|
88
|
+
l.should < @m.source.length
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
it "message sends and their offsets" do
|
93
|
+
@m.send_offsets.should be_kind_of(Hash)
|
94
|
+
@m.send_offsets.keys.should include "to_s"
|
95
|
+
@m.send_offsets.values.first.should be_kind_of(Fixnum)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "ast" do
|
99
|
+
pending
|
100
|
+
end
|
101
|
+
|
102
|
+
it "bytecodes" do
|
103
|
+
pending
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "protection" do
|
107
|
+
before do
|
108
|
+
@cm = @r.reflect(MethodSpecFixture)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "is public" do
|
112
|
+
m = @cm.method(:method_p_public)
|
113
|
+
m.public?.should.be_true
|
114
|
+
m.protected?.should.be_false
|
115
|
+
m.private?.should.be_false
|
116
|
+
end
|
117
|
+
|
118
|
+
it "is private" do
|
119
|
+
m = @cm.method(:method_p_private)
|
120
|
+
m.public?.should.be_false
|
121
|
+
m.protected?.should.be_false
|
122
|
+
m.private?.should.be_true
|
123
|
+
end
|
124
|
+
|
125
|
+
it "is protected" do
|
126
|
+
m = @cm.method(:method_p_protected)
|
127
|
+
m.public?.should.be_false
|
128
|
+
m.protected?.should.be_true
|
129
|
+
m.private?.should.be_false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
it "returns native code if it was JITted" do
|
134
|
+
pending
|
135
|
+
end
|
136
|
+
|
137
|
+
it "returns average execution time" do
|
138
|
+
@m.execution_time_average.should be_kind_of(Time)
|
139
|
+
end
|
140
|
+
|
141
|
+
it "can return an approximation about the overall execution time it has been on the stack for" do
|
142
|
+
@m.execution_time.should be_kind_of(Time)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "returns how many percent of the total process' execution time this method was active" do
|
146
|
+
@m.execution_time_share.should be_kind_of(Number)
|
147
|
+
@m.execution_time_share.should < 1
|
148
|
+
@m.execution_time_share.should > 0
|
149
|
+
end
|
150
|
+
|
151
|
+
it "returns invocation count" do
|
152
|
+
ic = @m.invocation_count
|
153
|
+
MethodSpecFixture.new.send(@m.selector)
|
154
|
+
@m.invocation_count.should == (ic + 1)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "can delete a method from its home class" do
|
158
|
+
c = MethodSpecFixture
|
159
|
+
m = @r.reflect c.instance_method(:removeable_method)
|
160
|
+
c.instance_methods(false).map(&:to_s).should include("removeable_method")
|
161
|
+
m.delete
|
162
|
+
c.instance_methods(false).map(&:to_s).should_not include("removeable_method")
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
data/spec/object_spec.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
require 'fixtures/object_spec'
|
3
|
+
|
4
|
+
describe "ObjectMirror" do
|
5
|
+
before do
|
6
|
+
@r = reflection
|
7
|
+
end
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
@o = ObjectFixture.new
|
11
|
+
@m = @r.reflect(@o)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "can query instance variables" do
|
15
|
+
vars = @m.variables
|
16
|
+
vars.collect(&:name).should == ["@ivar"]
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
require 'fixtures/reflect_spec'
|
3
|
+
|
4
|
+
describe Reflection do
|
5
|
+
it "returns the type of codebase it can work on" do
|
6
|
+
Reflection.codebase.should be_kind_of(Class)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "queries" do
|
10
|
+
before do
|
11
|
+
@r = reflection
|
12
|
+
end
|
13
|
+
|
14
|
+
it "finds known modules" do
|
15
|
+
modules = @r.modules.collect(&:name)
|
16
|
+
modules.should include("ReflectModule")
|
17
|
+
modules.should_not include("ReflectClass")
|
18
|
+
end
|
19
|
+
|
20
|
+
it "finds known classes" do
|
21
|
+
classes = @r.classes.collect(&:name)
|
22
|
+
classes.should include("ReflectClass")
|
23
|
+
classes.should_not include("ReflectModule")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "finds known instances of something" do
|
27
|
+
class MyObj; end
|
28
|
+
class My2Obj < MyObj; end
|
29
|
+
a = MyObj.new
|
30
|
+
b = My2Obj.new
|
31
|
+
instances = @r.instances_of(MyObj).collect(&:name)
|
32
|
+
instances.should include(a.inspect)
|
33
|
+
instances.should_not include(b.inspect)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "can get vm objects id" do
|
37
|
+
o = Object.new
|
38
|
+
@r.object_by_id(o.object_id).name.should == o.inspect
|
39
|
+
end
|
40
|
+
|
41
|
+
it "can find implementors of a method" do
|
42
|
+
l = @r.implementations_of("unique_reflect_fixture_method")
|
43
|
+
l.should be_kind_of Array
|
44
|
+
l.size.should == 1
|
45
|
+
l.first.selector.should == "unique_reflect_fixture_method"
|
46
|
+
l.first.defining_class.name.should == "ReflectClass"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "can find senders of a method" do
|
50
|
+
l = @r.senders_of("unique_reflect_sent_method")
|
51
|
+
l.should be_kind_of Array
|
52
|
+
l.size.should == 1
|
53
|
+
l.first.selector.should == "unique_reflect_fixture_method"
|
54
|
+
l.first.defining_class.name.should == "ReflectClass"
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns all available threads" do
|
58
|
+
t = Thread.start {}
|
59
|
+
@r.threads.collect(&:name).should include(t.inspect)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "reports a platform" do
|
63
|
+
@r.platform.should == RUBY_PLATFORM
|
64
|
+
end
|
65
|
+
|
66
|
+
it "reports a ruby implementation" do
|
67
|
+
@r.engine.should == RUBY_ENGINE
|
68
|
+
end
|
69
|
+
|
70
|
+
it "reports the implementation's version" do
|
71
|
+
@r.version.should == Object.const_get(:"#{RUBY_ENGINE.upcase}_VERSION")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
RUBY_ENGINE = 'ruby' unless defined? RUBY_ENGINE
|
2
|
+
mirror_api = ENV["MIRRORS"] || RUBY_ENGINE
|
3
|
+
mirror_api = 'rubinius' if mirror_api == 'rbx'
|
4
|
+
|
5
|
+
$:.push File.expand_path("..", __FILE__)
|
6
|
+
$:.push File.expand_path("../../lib", __FILE__)
|
7
|
+
|
8
|
+
require "#{mirror_api}/reflection"
|
9
|
+
include Object.const_get(mirror_api.capitalize)
|
10
|
+
|
11
|
+
require 'spec_helper/mspec_patch'
|
12
|
+
require 'spec_helper/multiple_reflections'
|
13
|
+
|
14
|
+
class PendingError < StandardError; end
|
15
|
+
MSpec.store :guarding_exceptions, [Reflection::CapabilitiesExceeded, PendingError]
|
16
|
+
def pending; raise PendingError; end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class GuardException
|
2
|
+
def initialize(example, exception)
|
3
|
+
@message = example && example.description || "<no description>"
|
4
|
+
@exception = exception
|
5
|
+
@method = exception.backtrace.first
|
6
|
+
end
|
7
|
+
|
8
|
+
def finish(*args)
|
9
|
+
print "Skipped '#{@message}'\n\t#{@exception}\n\tin #{@method}).\n\n"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module MSpec
|
14
|
+
class << self
|
15
|
+
alias mspec_protect protect
|
16
|
+
|
17
|
+
def protect(location, &block)
|
18
|
+
wrapped_block = proc do
|
19
|
+
begin
|
20
|
+
instance_eval(&block)
|
21
|
+
rescue *MSpec.retrieve(:guarding_exceptions)
|
22
|
+
MSpec.expectation
|
23
|
+
MSpec.register :finish, GuardException.new(MSpec.current.state, $!)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
mspec_protect(location, &wrapped_block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
class Object
|
4
|
+
def reflection
|
5
|
+
map = { File => proc { Reflection.new("fixtures/reflect") },
|
6
|
+
NilClass => proc { Reflection.new(nil) },
|
7
|
+
URI => proc do
|
8
|
+
run_drb_vm
|
9
|
+
Reflection.new(URI::Generic.new("drb", "127.0.0.1", "9128"))
|
10
|
+
end }
|
11
|
+
map[Reflection.codebase][]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
data/spec/thread_spec.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
require 'fixtures/thread_spec'
|
3
|
+
|
4
|
+
describe "ThreadMirror" do
|
5
|
+
before(:each) do
|
6
|
+
@r = reflection
|
7
|
+
@t = Thread.start do
|
8
|
+
t = ThreadFixture.new
|
9
|
+
t.stop
|
10
|
+
t.return
|
11
|
+
end
|
12
|
+
@m = reflection.reflect(@t)
|
13
|
+
sleep 0.1 until @t.stop?
|
14
|
+
end
|
15
|
+
|
16
|
+
after(:each) do
|
17
|
+
@t.kill
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "should query" do
|
21
|
+
it "the thread state" do
|
22
|
+
@m.status.should == "sleep"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "the thread return value" do
|
26
|
+
@m.run
|
27
|
+
sleep 0.1 until @t.stop?
|
28
|
+
@m.return_value.should == ThreadFixture.new.return
|
29
|
+
end
|
30
|
+
|
31
|
+
it "the stack" do
|
32
|
+
@m.stack.collect(&:name).should include "stop"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "should manipulate" do
|
37
|
+
it "should be able to resume the thread" do
|
38
|
+
@m.run
|
39
|
+
sleep 0.1 while @t.status
|
40
|
+
@m.status.should == "dead"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should install exception blocks" do
|
45
|
+
@t = Thread.start do
|
46
|
+
t = ThreadFixture.new
|
47
|
+
t.stop
|
48
|
+
t.t_raise
|
49
|
+
end
|
50
|
+
@m = @r.reflect(@t)
|
51
|
+
handled = false
|
52
|
+
@m.handle_exception Exception do |e|
|
53
|
+
handled = true
|
54
|
+
end
|
55
|
+
@m.run
|
56
|
+
begin @t.join rescue RuntimeError end
|
57
|
+
handled.should == true
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should handle the right thread exception blocks" do
|
61
|
+
t1 = Thread.start do
|
62
|
+
t = ThreadFixture.new
|
63
|
+
t.stop
|
64
|
+
t.t_raise
|
65
|
+
end
|
66
|
+
t2 = Thread.start do
|
67
|
+
t = ThreadFixture.new
|
68
|
+
t.stop
|
69
|
+
t.t_raise
|
70
|
+
end
|
71
|
+
m1 = @r.reflect(t1)
|
72
|
+
m2 = @r.reflect(t2)
|
73
|
+
handled = false
|
74
|
+
m1.handle_exception Exception do |e|
|
75
|
+
handled = true
|
76
|
+
end
|
77
|
+
m2.run
|
78
|
+
begin t2.join rescue RuntimeError end
|
79
|
+
handled.should == false
|
80
|
+
m1.run
|
81
|
+
begin t1.join rescue RuntimeError end
|
82
|
+
handled.should == true
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should handle the many thread exception blocks" do
|
86
|
+
t1 = Thread.start do
|
87
|
+
t = ThreadFixture.new
|
88
|
+
t.stop
|
89
|
+
t.t_raise
|
90
|
+
end
|
91
|
+
t2 = Thread.start do
|
92
|
+
t = ThreadFixture.new
|
93
|
+
t.stop
|
94
|
+
t.t_raise
|
95
|
+
end
|
96
|
+
m1 = @r.reflect(t1)
|
97
|
+
m2 = @r.reflect(t2)
|
98
|
+
handles = []
|
99
|
+
m1.handle_exception Exception do |e|
|
100
|
+
handles << "m1"
|
101
|
+
end
|
102
|
+
m2.handle_exception Exception do |e|
|
103
|
+
handles << "m2"
|
104
|
+
end
|
105
|
+
m1.run
|
106
|
+
begin t1.join rescue RuntimeError end
|
107
|
+
handles.should include "m1"
|
108
|
+
handles.should_not include "m2"
|
109
|
+
m2.run
|
110
|
+
begin t2.join rescue RuntimeError end
|
111
|
+
handles.should include "m1"
|
112
|
+
handles.should include "m2"
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "implement shift/reset" do
|
116
|
+
it "can be used to return what you want" do
|
117
|
+
retval = @r.reflect(Thread.current).reset do
|
118
|
+
@r.reflect(Thread.current).shift {|cc| cc.call "shifted" } + " test"
|
119
|
+
end
|
120
|
+
|
121
|
+
retval.should == "shifted test"
|
122
|
+
end
|
123
|
+
|
124
|
+
it "can drop the continuation" do
|
125
|
+
retval = @r.reflect(Thread.current).reset do
|
126
|
+
@r.reflect(Thread.current).shift {|cc| 1 } + 1
|
127
|
+
end
|
128
|
+
|
129
|
+
retval.should == 1
|
130
|
+
end
|
131
|
+
|
132
|
+
it "can be run and re-run" do
|
133
|
+
retval = @r.reflect(Thread.current).reset do
|
134
|
+
@r.reflect(Thread.current).shift do |cc|
|
135
|
+
cc.call(cc.call(1))
|
136
|
+
end + 1
|
137
|
+
end
|
138
|
+
|
139
|
+
retval.should == 3
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|