ruby-prof 0.15.9 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES +27 -1
- data/README.rdoc +83 -31
- data/bin/ruby-prof +4 -4
- data/doc/LICENSE.html +1 -1
- data/doc/README_rdoc.html +92 -33
- data/doc/Rack.html +1 -1
- data/doc/Rack/RubyProf.html +17 -14
- data/doc/RubyProf.html +30 -29
- data/doc/RubyProf/AbstractPrinter.html +1 -1
- data/doc/RubyProf/AggregateCallInfo.html +1 -1
- data/doc/RubyProf/CallInfo.html +1 -1
- data/doc/RubyProf/CallInfoPrinter.html +1 -1
- data/doc/RubyProf/CallInfoVisitor.html +1 -1
- data/doc/RubyProf/CallStackPrinter.html +1 -1
- data/doc/RubyProf/CallTreePrinter.html +349 -67
- data/doc/RubyProf/Cmd.html +5 -5
- data/doc/RubyProf/DotPrinter.html +2 -2
- data/doc/RubyProf/FlatPrinter.html +1 -1
- data/doc/RubyProf/FlatPrinterWithLineNumbers.html +1 -1
- data/doc/RubyProf/GraphHtmlPrinter.html +1 -1
- data/doc/RubyProf/GraphPrinter.html +1 -1
- data/doc/RubyProf/MethodInfo.html +2 -2
- data/doc/RubyProf/MultiPrinter.html +11 -9
- data/doc/RubyProf/Profile.html +94 -44
- data/doc/RubyProf/ProfileTask.html +1 -1
- data/doc/RubyProf/Thread.html +43 -1
- data/doc/created.rid +16 -16
- data/doc/examples/flat_txt.html +1 -1
- data/doc/examples/graph_html.html +1 -1
- data/doc/examples/graph_txt.html +3 -3
- data/doc/index.html +85 -30
- data/doc/js/navigation.js.gz +0 -0
- data/doc/js/search_index.js +1 -1
- data/doc/js/search_index.js.gz +0 -0
- data/doc/js/searcher.js +2 -2
- data/doc/js/searcher.js.gz +0 -0
- data/doc/table_of_contents.html +117 -68
- data/examples/cachegrind.out.1 +114 -0
- data/examples/cachegrind.out.1.32313213 +114 -0
- data/examples/graph.txt +1 -1
- data/ext/ruby_prof/extconf.rb +6 -2
- data/ext/ruby_prof/rp_measure_cpu_time.c +29 -31
- data/ext/ruby_prof/rp_method.c +1 -1
- data/ext/ruby_prof/rp_thread.c +57 -52
- data/ext/ruby_prof/ruby_prof.c +122 -66
- data/ext/ruby_prof/ruby_prof.h +2 -0
- data/lib/ruby-prof.rb +14 -13
- data/lib/ruby-prof/assets/call_stack_printer.js.html +1 -1
- data/lib/ruby-prof/compatibility.rb +9 -8
- data/lib/ruby-prof/method_info.rb +1 -1
- data/lib/ruby-prof/printers/call_tree_printer.rb +88 -50
- data/lib/ruby-prof/printers/dot_printer.rb +1 -1
- data/lib/ruby-prof/printers/multi_printer.rb +6 -4
- data/lib/ruby-prof/profile.rb +0 -1
- data/lib/ruby-prof/rack.rb +53 -16
- data/lib/ruby-prof/thread.rb +11 -0
- data/lib/ruby-prof/version.rb +1 -1
- data/test/exclude_threads_test.rb +2 -3
- data/test/fiber_test.rb +21 -7
- data/test/measure_cpu_time_test.rb +84 -24
- data/test/multi_printer_test.rb +5 -4
- data/test/pause_resume_test.rb +7 -7
- data/test/printers_test.rb +6 -4
- data/test/rack_test.rb +26 -1
- data/test/test_helper.rb +28 -3
- data/test/thread_test.rb +1 -0
- metadata +5 -3
@@ -18,15 +18,17 @@ module RubyProf
|
|
18
18
|
def print(options)
|
19
19
|
@profile = options.delete(:profile) || "profile"
|
20
20
|
@directory = options.delete(:path) || File.expand_path(".")
|
21
|
+
|
21
22
|
File.open(stack_profile, "w") do |f|
|
22
23
|
@stack_printer.print(f, options.merge(:graph => "#{@profile}.graph.html"))
|
23
24
|
end
|
25
|
+
|
24
26
|
File.open(graph_profile, "w") do |f|
|
25
27
|
@graph_printer.print(f, options)
|
26
28
|
end
|
27
|
-
|
28
|
-
|
29
|
-
|
29
|
+
|
30
|
+
@tree_printer.print(options.merge(:path => @directory, :profile => @profile))
|
31
|
+
|
30
32
|
File.open(flat_profile, "w") do |f|
|
31
33
|
@flat_printer.print(f, options)
|
32
34
|
end
|
@@ -44,7 +46,7 @@ module RubyProf
|
|
44
46
|
|
45
47
|
# the name of the callgrind profile file
|
46
48
|
def tree_profile
|
47
|
-
"#{@directory}/#{@profile}.
|
49
|
+
"#{@directory}/#{@profile}.callgrind.out.#{$$}"
|
48
50
|
end
|
49
51
|
|
50
52
|
# the name of the flat profile file
|
data/lib/ruby-prof/profile.rb
CHANGED
data/lib/ruby-prof/rack.rb
CHANGED
@@ -16,30 +16,61 @@ module Rack
|
|
16
16
|
::RubyProf::GraphHtmlPrinter => 'graph.html',
|
17
17
|
::RubyProf::CallStackPrinter => 'call_stack.html'}
|
18
18
|
|
19
|
-
@skip_paths = options[:skip_paths] || [%r{^/assets}, %r{\.css
|
19
|
+
@skip_paths = options[:skip_paths] || [%r{^/assets}, %r{\.(css|js|png|jpeg|jpg|gif)$}]
|
20
|
+
@only_paths = options[:only_paths]
|
20
21
|
end
|
21
22
|
|
22
23
|
def call(env)
|
23
24
|
request = Rack::Request.new(env)
|
24
25
|
|
25
|
-
if
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
26
|
+
if should_profile?(request.path)
|
27
|
+
begin
|
28
|
+
result = nil
|
29
|
+
data = ::RubyProf::Profile.profile(profiling_options) do
|
30
|
+
result = @app.call(env)
|
31
|
+
end
|
32
32
|
|
33
|
-
|
34
|
-
|
33
|
+
path = request.path.gsub('/', '-')
|
34
|
+
path.slice!(0)
|
35
35
|
|
36
|
-
|
37
|
-
|
36
|
+
print(data, path)
|
37
|
+
result
|
38
|
+
end
|
39
|
+
else
|
40
|
+
@app.call(env)
|
38
41
|
end
|
39
42
|
end
|
40
43
|
|
41
44
|
private
|
42
45
|
|
46
|
+
def should_profile?(path)
|
47
|
+
return false if paths_match?(path, @skip_paths)
|
48
|
+
|
49
|
+
@only_paths ? paths_match?(path, @only_paths) : true
|
50
|
+
end
|
51
|
+
|
52
|
+
def paths_match?(path, paths)
|
53
|
+
paths.any? { |skip_path| skip_path =~ path }
|
54
|
+
end
|
55
|
+
|
56
|
+
def profiling_options
|
57
|
+
options = {}
|
58
|
+
options[:measure_mode] = ::RubyProf.measure_mode
|
59
|
+
options[:exclude_threads] =
|
60
|
+
if @options[:ignore_existing_threads]
|
61
|
+
Thread.list.select{|t| t != Thread.current}
|
62
|
+
else
|
63
|
+
::RubyProf.exclude_threads
|
64
|
+
end
|
65
|
+
if @options[:request_thread_only]
|
66
|
+
options[:include_threads] = [Thread.current]
|
67
|
+
end
|
68
|
+
if @options[:merge_fibers]
|
69
|
+
options[:merge_fibers] = true
|
70
|
+
end
|
71
|
+
options
|
72
|
+
end
|
73
|
+
|
43
74
|
def print(data, path)
|
44
75
|
@printer_klasses.each do |printer_klass, base_name|
|
45
76
|
printer = printer_klass.new(data)
|
@@ -48,11 +79,17 @@ module Rack
|
|
48
79
|
base_name = base_name.call
|
49
80
|
end
|
50
81
|
|
51
|
-
|
52
|
-
|
53
|
-
|
82
|
+
if printer_klass == ::RubyProf::MultiPrinter
|
83
|
+
printer.print(@options.merge(:profile => "#{path}-#{base_name}"))
|
84
|
+
elsif printer_klass == ::RubyProf::CallTreePrinter
|
85
|
+
printer.print(@options.merge(:profile => "#{path}-#{base_name}"))
|
86
|
+
else
|
87
|
+
file_name = ::File.join(@tmpdir, "#{path}-#{base_name}")
|
88
|
+
::File.open(file_name, 'wb') do |file|
|
89
|
+
printer.print(file, @options)
|
90
|
+
end
|
54
91
|
end
|
55
92
|
end
|
56
93
|
end
|
57
94
|
end
|
58
|
-
end
|
95
|
+
end
|
data/lib/ruby-prof/thread.rb
CHANGED
@@ -26,5 +26,16 @@ module RubyProf
|
|
26
26
|
sum
|
27
27
|
end
|
28
28
|
end
|
29
|
+
|
30
|
+
def wait_time
|
31
|
+
# wait_time, like self:time, is always method local
|
32
|
+
# thus we need to sum over all methods and call infos
|
33
|
+
self.methods.inject(0) do |sum, method_info|
|
34
|
+
method_info.call_infos.each do |call_info|
|
35
|
+
sum += call_info.wait_time
|
36
|
+
end
|
37
|
+
sum
|
38
|
+
end
|
39
|
+
end
|
29
40
|
end
|
30
41
|
end
|
data/lib/ruby-prof/version.rb
CHANGED
@@ -26,7 +26,8 @@ class ExcludeThreadsTest < TestCase
|
|
26
26
|
thread2_proc
|
27
27
|
end
|
28
28
|
|
29
|
-
|
29
|
+
# exclude_threads already includes the minitest thread pool
|
30
|
+
RubyProf.exclude_threads += [ thread2 ]
|
30
31
|
|
31
32
|
RubyProf.start
|
32
33
|
|
@@ -35,8 +36,6 @@ class ExcludeThreadsTest < TestCase
|
|
35
36
|
|
36
37
|
result = RubyProf.stop
|
37
38
|
|
38
|
-
RubyProf::exclude_threads = nil
|
39
|
-
|
40
39
|
assert_equal(2, result.threads.length)
|
41
40
|
|
42
41
|
output = Array.new
|
data/test/fiber_test.rb
CHANGED
@@ -37,18 +37,18 @@ class FiberTest < TestCase
|
|
37
37
|
@fiber_ids = Set.new
|
38
38
|
@root_fiber = Fiber.current.object_id
|
39
39
|
@thread_id = Thread.current.object_id
|
40
|
-
@result = RubyProf.profile { fiber_test }
|
41
40
|
end
|
42
41
|
|
43
42
|
def test_fibers
|
44
|
-
|
45
|
-
|
46
|
-
assert_equal(
|
43
|
+
result = RubyProf.profile { fiber_test }
|
44
|
+
profiled_fiber_ids = result.threads.map(&:fiber_id)
|
45
|
+
assert_equal(2, result.threads.length)
|
46
|
+
assert_equal([@thread_id], result.threads.map(&:id).uniq)
|
47
47
|
assert_equal(@fiber_ids, Set.new(profiled_fiber_ids))
|
48
48
|
|
49
49
|
assert profiled_fiber_ids.include?(@root_fiber)
|
50
|
-
assert(root_fiber_profile =
|
51
|
-
assert(enum_fiber_profile =
|
50
|
+
assert(root_fiber_profile = result.threads.detect{|t| t.fiber_id == @root_fiber})
|
51
|
+
assert(enum_fiber_profile = result.threads.detect{|t| t.fiber_id != @root_fiber})
|
52
52
|
|
53
53
|
assert_in_delta(0.3, root_fiber_profile.total_time, 0.05)
|
54
54
|
assert_in_delta(0.2, enum_fiber_profile.total_time, 0.05)
|
@@ -58,8 +58,22 @@ class FiberTest < TestCase
|
|
58
58
|
|
59
59
|
assert_in_delta(0.2, method_next.total_time, 0.05)
|
60
60
|
assert_in_delta(0.2, method_each.total_time, 0.05)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_merged_fibers
|
64
|
+
result = RubyProf.profile(merge_fibers: true) { fiber_test }
|
65
|
+
assert_equal(1, result.threads.length)
|
66
|
+
|
67
|
+
profile = result.threads.first
|
68
|
+
assert_equal 0, profile.fiber_id
|
69
|
+
|
70
|
+
assert_in_delta(0.3, profile.total_time, 0.05)
|
61
71
|
|
62
|
-
|
72
|
+
assert(method_next = profile.methods.detect{|m| m.full_name == "Enumerator#next"})
|
73
|
+
assert(method_each = profile.methods.detect{|m| m.full_name == "Enumerator#each"})
|
74
|
+
|
75
|
+
assert_in_delta(0.2, method_next.total_time, 0.05)
|
76
|
+
assert_in_delta(0.2, method_each.total_time, 0.05)
|
63
77
|
end
|
64
78
|
|
65
79
|
end
|
@@ -8,8 +8,11 @@ class MeasureCpuTimeTest < TestCase
|
|
8
8
|
RubyProf::measure_mode = RubyProf::CPU_TIME
|
9
9
|
end
|
10
10
|
|
11
|
+
def teardown
|
12
|
+
RubyProf::measure_mode = RubyProf::WALL_TIME
|
13
|
+
end
|
14
|
+
|
11
15
|
def test_mode
|
12
|
-
RubyProf::measure_mode = RubyProf::CPU_TIME
|
13
16
|
assert_equal(RubyProf::CPU_TIME, RubyProf::measure_mode)
|
14
17
|
end
|
15
18
|
|
@@ -19,12 +22,12 @@ class MeasureCpuTimeTest < TestCase
|
|
19
22
|
|
20
23
|
def test_class_methods
|
21
24
|
result = RubyProf.profile do
|
22
|
-
RubyProf::C7.
|
25
|
+
RubyProf::C7.busy_wait
|
23
26
|
end
|
24
27
|
|
25
28
|
# Length should be greater 2:
|
26
29
|
# MeasureCpuTimeTest#test_class_methods
|
27
|
-
# <Class::RubyProf::C1>#
|
30
|
+
# <Class::RubyProf::C1>#busy_wait
|
28
31
|
# ....
|
29
32
|
|
30
33
|
methods = result.threads.first.methods.sort.reverse[0..1]
|
@@ -32,21 +35,21 @@ class MeasureCpuTimeTest < TestCase
|
|
32
35
|
|
33
36
|
# Check the names
|
34
37
|
assert_equal('MeasureCpuTimeTest#test_class_methods', methods[0].full_name)
|
35
|
-
assert_equal('<Class::RubyProf::C7>#
|
38
|
+
assert_equal('<Class::RubyProf::C7>#busy_wait', methods[1].full_name)
|
36
39
|
|
37
40
|
# Check times
|
38
|
-
assert_in_delta(0.1, methods[0].total_time, 0.
|
39
|
-
assert_in_delta(0, methods[0].wait_time, 0.
|
40
|
-
assert_in_delta(0, methods[0].self_time, 0.
|
41
|
+
assert_in_delta(0.1, methods[0].total_time, 0.05)
|
42
|
+
assert_in_delta(0, methods[0].wait_time, 0.05)
|
43
|
+
assert_in_delta(0, methods[0].self_time, 0.05)
|
41
44
|
|
42
|
-
assert_in_delta(0.1, methods[1].total_time, 0.
|
43
|
-
assert_in_delta(0, methods[1].wait_time, 0.
|
44
|
-
assert_in_delta(0, methods[1].self_time, 0.
|
45
|
+
assert_in_delta(0.1, methods[1].total_time, 0.05)
|
46
|
+
assert_in_delta(0, methods[1].wait_time, 0.05)
|
47
|
+
assert_in_delta(0, methods[1].self_time, 0.05)
|
45
48
|
end
|
46
49
|
|
47
50
|
def test_instance_methods
|
48
51
|
result = RubyProf.profile do
|
49
|
-
RubyProf::C7.new.
|
52
|
+
RubyProf::C7.new.busy_wait
|
50
53
|
end
|
51
54
|
|
52
55
|
methods = result.threads.first.methods.sort.reverse[0..1]
|
@@ -54,12 +57,12 @@ class MeasureCpuTimeTest < TestCase
|
|
54
57
|
|
55
58
|
# Methods at this point:
|
56
59
|
# MeasureCpuTimeTest#test_instance_methods
|
57
|
-
# C7#
|
60
|
+
# C7#busy_wait
|
58
61
|
# ...
|
59
62
|
|
60
63
|
names = methods.map(&:full_name)
|
61
64
|
assert_equal('MeasureCpuTimeTest#test_instance_methods', names[0])
|
62
|
-
assert_equal('RubyProf::C7#
|
65
|
+
assert_equal('RubyProf::C7#busy_wait', names[1])
|
63
66
|
|
64
67
|
|
65
68
|
# Check times
|
@@ -69,24 +72,24 @@ class MeasureCpuTimeTest < TestCase
|
|
69
72
|
|
70
73
|
assert_in_delta(0.2, methods[1].total_time, 0.03)
|
71
74
|
assert_in_delta(0, methods[1].wait_time, 0.03)
|
72
|
-
assert_in_delta(0, methods[1].self_time, 0.
|
75
|
+
assert_in_delta(0, methods[1].self_time, 0.2)
|
73
76
|
end
|
74
77
|
|
75
78
|
def test_module_methods
|
76
79
|
result = RubyProf.profile do
|
77
|
-
RubyProf::C8.
|
80
|
+
RubyProf::C8.busy_wait
|
78
81
|
end
|
79
82
|
|
80
83
|
# Methods:
|
81
84
|
# MeasureCpuTimeTest#test_module_methods
|
82
|
-
# M1#
|
85
|
+
# M1#busy_wait
|
83
86
|
# ...
|
84
87
|
|
85
88
|
methods = result.threads.first.methods.sort.reverse[0..1]
|
86
89
|
assert_equal(2, methods.length)
|
87
90
|
|
88
91
|
assert_equal('MeasureCpuTimeTest#test_module_methods', methods[0].full_name)
|
89
|
-
assert_equal('RubyProf::M7#
|
92
|
+
assert_equal('RubyProf::M7#busy_wait', methods[1].full_name)
|
90
93
|
|
91
94
|
# Check times
|
92
95
|
assert_in_delta(0.3, methods[0].total_time, 0.1)
|
@@ -100,26 +103,26 @@ class MeasureCpuTimeTest < TestCase
|
|
100
103
|
|
101
104
|
def test_module_instance_methods
|
102
105
|
result = RubyProf.profile do
|
103
|
-
RubyProf::C8.new.
|
106
|
+
RubyProf::C8.new.busy_wait
|
104
107
|
end
|
105
108
|
|
106
109
|
# Methods:
|
107
110
|
# MeasureCpuTimeTest#test_module_instance_methods
|
108
|
-
# M7#
|
111
|
+
# M7#busy_wait
|
109
112
|
# ...
|
110
113
|
|
111
114
|
methods = result.threads.first.methods.sort.reverse[0..1]
|
112
115
|
assert_equal(2, methods.length)
|
113
116
|
names = methods.map(&:full_name)
|
114
117
|
assert_equal('MeasureCpuTimeTest#test_module_instance_methods', names[0])
|
115
|
-
assert_equal('RubyProf::M7#
|
118
|
+
assert_equal('RubyProf::M7#busy_wait', names[1])
|
116
119
|
|
117
120
|
# Check times
|
118
121
|
assert_in_delta(0.3, methods[0].total_time, 0.1)
|
119
122
|
assert_in_delta(0, methods[0].wait_time, 0.1)
|
120
123
|
assert_in_delta(0, methods[0].self_time, 0.1)
|
121
124
|
|
122
|
-
assert_in_delta(0.3, methods[1].total_time, 0.
|
125
|
+
assert_in_delta(0.3, methods[1].total_time, 0.1)
|
123
126
|
assert_in_delta(0, methods[1].wait_time, 0.01)
|
124
127
|
assert_in_delta(0, methods[1].self_time, 0.1)
|
125
128
|
end
|
@@ -128,19 +131,19 @@ class MeasureCpuTimeTest < TestCase
|
|
128
131
|
c3 = RubyProf::C3.new
|
129
132
|
|
130
133
|
class << c3
|
131
|
-
def
|
134
|
+
def busy_wait
|
132
135
|
end
|
133
136
|
end
|
134
137
|
|
135
138
|
result = RubyProf.profile do
|
136
|
-
c3.
|
139
|
+
c3.busy_wait
|
137
140
|
end
|
138
141
|
|
139
142
|
methods = result.threads.first.methods.sort.reverse
|
140
143
|
assert_equal(2, methods.length)
|
141
144
|
|
142
145
|
assert_equal('MeasureCpuTimeTest#test_singleton', methods[0].full_name)
|
143
|
-
assert_equal('<Object::RubyProf::C3>#
|
146
|
+
assert_equal('<Object::RubyProf::C3>#busy_wait', methods[1].full_name)
|
144
147
|
|
145
148
|
assert_in_delta(0, methods[0].total_time, 0.01)
|
146
149
|
assert_in_delta(0, methods[0].wait_time, 0.01)
|
@@ -150,4 +153,61 @@ class MeasureCpuTimeTest < TestCase
|
|
150
153
|
assert_in_delta(0, methods[1].wait_time, 0.01)
|
151
154
|
assert_in_delta(0, methods[1].self_time, 0.01)
|
152
155
|
end
|
156
|
+
|
157
|
+
|
158
|
+
def test_sleeping_does_accumulate_wall_time
|
159
|
+
RubyProf::measure_mode = RubyProf::WALL_TIME
|
160
|
+
result = RubyProf.profile do
|
161
|
+
sleep 0.1
|
162
|
+
end
|
163
|
+
methods = result.threads.first.methods.sort.reverse
|
164
|
+
assert_equal(["MeasureCpuTimeTest#test_sleeping_does_accumulate_wall_time", "Kernel#sleep"], methods.map(&:full_name))
|
165
|
+
assert_in_delta(0.1, methods[1].total_time, 0.01)
|
166
|
+
assert_equal(0, methods[1].wait_time)
|
167
|
+
assert_in_delta(0.1, methods[1].self_time, 0.01)
|
168
|
+
end
|
169
|
+
|
170
|
+
def test_sleeping_does_not_accumulate_significant_cpu_time
|
171
|
+
result = RubyProf.profile do
|
172
|
+
sleep 0.1
|
173
|
+
end
|
174
|
+
methods = result.threads.first.methods.sort.reverse
|
175
|
+
assert_equal(["MeasureCpuTimeTest#test_sleeping_does_not_accumulate_significant_cpu_time", "Kernel#sleep"], methods.map(&:full_name))
|
176
|
+
assert_in_delta(0, methods[1].total_time, 0.01)
|
177
|
+
assert_equal(0, methods[1].wait_time)
|
178
|
+
assert_in_delta(0, methods[1].self_time, 0.01)
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_waiting_for_threads_does_not_accumulate_cpu_time
|
182
|
+
background_thread = nil
|
183
|
+
result = RubyProf.profile do
|
184
|
+
background_thread = Thread.new{ sleep 0.1 }
|
185
|
+
background_thread.join
|
186
|
+
end
|
187
|
+
# check number of threads
|
188
|
+
assert_equal(2, result.threads.length)
|
189
|
+
fg, bg = result.threads
|
190
|
+
assert(fg.methods.map(&:full_name).include?("Thread#join"))
|
191
|
+
assert(bg.methods.map(&:full_name).include?("Kernel#sleep"))
|
192
|
+
assert_in_delta(0, fg.total_time, 0.01)
|
193
|
+
assert_in_delta(0, bg.total_time, 0.01)
|
194
|
+
end
|
195
|
+
|
196
|
+
def test_waiting_for_threads_does_accumulate_wall_time
|
197
|
+
RubyProf::measure_mode = RubyProf::WALL_TIME
|
198
|
+
background_thread = nil
|
199
|
+
result = RubyProf.profile do
|
200
|
+
background_thread = Thread.new{ sleep 0.1 }
|
201
|
+
background_thread.join
|
202
|
+
end
|
203
|
+
# check number of threads
|
204
|
+
assert_equal(2, result.threads.length)
|
205
|
+
fg, bg = result.threads
|
206
|
+
assert(fg.methods.map(&:full_name).include?("Thread#join"))
|
207
|
+
assert(bg.methods.map(&:full_name).include?("Kernel#sleep"))
|
208
|
+
assert_in_delta(0.1, fg.total_time, 0.01)
|
209
|
+
assert_in_delta(0.1, fg.wait_time, 0.01)
|
210
|
+
assert_in_delta(0.1, bg.total_time, 0.01)
|
211
|
+
end
|
212
|
+
|
153
213
|
end
|