openlogic-turn 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +54 -0
- data/README.txt +116 -0
- data/Rakefile +40 -0
- data/Release.txt +33 -0
- data/Version.txt +1 -0
- data/bin/turn +4 -0
- data/demo/test_autorun_minitest.rb +26 -0
- data/demo/test_autorun_testunit.rb +26 -0
- data/demo/test_sample.rb +35 -0
- data/demo/test_sample2.rb +33 -0
- data/lib/turn.rb +19 -0
- data/lib/turn/autorun/minitest.rb +155 -0
- data/lib/turn/autorun/testunit.rb +116 -0
- data/lib/turn/bin.rb +4 -0
- data/lib/turn/colorize.rb +65 -0
- data/lib/turn/command.rb +210 -0
- data/lib/turn/components/case.rb +104 -0
- data/lib/turn/components/method.rb +42 -0
- data/lib/turn/components/suite.rb +85 -0
- data/lib/turn/controller.rb +204 -0
- data/lib/turn/core_ext.rb +31 -0
- data/lib/turn/reporter.rb +69 -0
- data/lib/turn/reporters/cue_reporter.rb +167 -0
- data/lib/turn/reporters/dot_reporter.rb +93 -0
- data/lib/turn/reporters/marshal_reporter.rb +17 -0
- data/lib/turn/reporters/outline_reporter.rb +144 -0
- data/lib/turn/reporters/pretty_reporter.rb +184 -0
- data/lib/turn/reporters/progress_reporter.rb +116 -0
- data/lib/turn/runners/crossrunner.rb +42 -0
- data/lib/turn/runners/isorunner.rb +167 -0
- data/lib/turn/runners/loadrunner.rb +48 -0
- data/lib/turn/runners/minirunner.rb +189 -0
- data/lib/turn/runners/solorunner.rb +8 -0
- data/lib/turn/runners/testrunner.rb +166 -0
- data/test/helper.rb +97 -0
- data/test/runner +2 -0
- data/test/test_framework.rb +131 -0
- data/test/test_reporters.rb +44 -0
- data/test/test_runners.rb +45 -0
- metadata +138 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
module Turn
|
2
|
+
|
3
|
+
#
|
4
|
+
class TestMethod
|
5
|
+
attr_accessor :name
|
6
|
+
attr_accessor :file
|
7
|
+
attr_accessor :raised
|
8
|
+
attr_accessor :message
|
9
|
+
attr_accessor :backtrace
|
10
|
+
|
11
|
+
def initialize(name)
|
12
|
+
@name = name
|
13
|
+
@fail = false
|
14
|
+
@error = false
|
15
|
+
@raised = nil
|
16
|
+
@message = nil
|
17
|
+
@backtrace = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def fail!(assertion)
|
21
|
+
@fail, @error = true, false
|
22
|
+
@raised = assertion
|
23
|
+
@message = assertion.message
|
24
|
+
@backtrace = assertion.backtrace
|
25
|
+
end
|
26
|
+
|
27
|
+
def error!(exception)
|
28
|
+
@fail, @error = false, true
|
29
|
+
@raised = exception
|
30
|
+
@message = exception.message
|
31
|
+
@backtrace = exception.backtrace
|
32
|
+
end
|
33
|
+
|
34
|
+
def fail? ; @fail ; end
|
35
|
+
def error? ; @error ; end
|
36
|
+
def pass? ; !(@fail or @error) ; end
|
37
|
+
|
38
|
+
def to_s ; name ; end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Turn
|
2
|
+
|
3
|
+
#
|
4
|
+
class TestSuite
|
5
|
+
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
attr_accessor :name
|
9
|
+
attr_accessor :size
|
10
|
+
attr_accessor :cases
|
11
|
+
|
12
|
+
# This one can be set manually since it
|
13
|
+
# is not calculatable (beyond the case level).
|
14
|
+
attr_accessor :count_assertions
|
15
|
+
|
16
|
+
#
|
17
|
+
def initialize(name=nil)
|
18
|
+
@name = name
|
19
|
+
@size = nil
|
20
|
+
@cases = []
|
21
|
+
|
22
|
+
#@count_tests = nil
|
23
|
+
#@count_assertions = nil
|
24
|
+
#@count_failures = nil
|
25
|
+
#@count_errors = nil
|
26
|
+
#@count_passes = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
def new_case(name, *files)
|
31
|
+
c = TestCase.new(name, *files)
|
32
|
+
@cases << c
|
33
|
+
c
|
34
|
+
end
|
35
|
+
|
36
|
+
def count_tests
|
37
|
+
#@count_tests ||= (
|
38
|
+
sum = 0; each{ |c| sum += c.count_tests }; sum
|
39
|
+
#)
|
40
|
+
end
|
41
|
+
|
42
|
+
def count_assertions
|
43
|
+
#@count_assertions ||= (
|
44
|
+
sum = 0; each{ |c| sum += c.count_assertions }; sum
|
45
|
+
#)
|
46
|
+
end
|
47
|
+
|
48
|
+
def count_failures
|
49
|
+
#@count_failures ||= (
|
50
|
+
sum = 0; each{ |c| sum += c.count_failures }; sum
|
51
|
+
#)
|
52
|
+
end
|
53
|
+
|
54
|
+
def count_errors
|
55
|
+
#@count_errors ||= (
|
56
|
+
sum = 0; each{ |c| sum += c.count_errors }; sum
|
57
|
+
#)
|
58
|
+
end
|
59
|
+
|
60
|
+
def count_passes
|
61
|
+
#@count_passes ||= (
|
62
|
+
sum = 0; each{ |c| sum += c.count_passes }; sum
|
63
|
+
#)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Convenience methods --this is what is typcially wanted.
|
67
|
+
def counts
|
68
|
+
return count_tests, count_assertions, count_failures, count_errors #,count_skips
|
69
|
+
end
|
70
|
+
|
71
|
+
def each(&block)
|
72
|
+
@cases.each(&block)
|
73
|
+
end
|
74
|
+
|
75
|
+
def size
|
76
|
+
@size ||= @cases.size
|
77
|
+
end
|
78
|
+
|
79
|
+
def passed?
|
80
|
+
(count_failures == 0 && count_errors == 0)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Turn
|
4
|
+
require 'turn/components/suite.rb'
|
5
|
+
require 'turn/components/case.rb'
|
6
|
+
require 'turn/components/method.rb'
|
7
|
+
|
8
|
+
# = Controller
|
9
|
+
#
|
10
|
+
#--
|
11
|
+
# TODO: Add support to test run loggging.
|
12
|
+
#++
|
13
|
+
class Controller
|
14
|
+
|
15
|
+
# File glob pattern of tests to run.
|
16
|
+
# Can be an array of files/globs.
|
17
|
+
attr_accessor :tests
|
18
|
+
|
19
|
+
# Files globs to specially exclude.
|
20
|
+
attr_accessor :exclude
|
21
|
+
|
22
|
+
# Regexp pattern that all test name's must
|
23
|
+
# match to be eligible to run.
|
24
|
+
attr_accessor :pattern
|
25
|
+
|
26
|
+
# Add these folders to the $LOAD_PATH.
|
27
|
+
attr_accessor :loadpath
|
28
|
+
|
29
|
+
# Libs to require when running tests.
|
30
|
+
attr_accessor :requires
|
31
|
+
|
32
|
+
# Reporter type.
|
33
|
+
attr_accessor :format
|
34
|
+
|
35
|
+
# Run mode.
|
36
|
+
attr_accessor :runmode
|
37
|
+
|
38
|
+
# Test against live install (i.e. Don't use loadpath option)
|
39
|
+
attr_accessor :live
|
40
|
+
|
41
|
+
# Log results? May be true/false or log file name. (TODO)
|
42
|
+
attr_accessor :log
|
43
|
+
|
44
|
+
# Verbose output?
|
45
|
+
attr_accessor :verbose
|
46
|
+
|
47
|
+
# Test framework, either :minitest or :testunit
|
48
|
+
attr_accessor :framework
|
49
|
+
|
50
|
+
def verbose? ; @verbose ; end
|
51
|
+
def live? ; @live ; end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def initialize
|
56
|
+
yield(self) if block_given?
|
57
|
+
initialize_defaults
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
def initialize_defaults
|
62
|
+
@loadpath ||= ['lib']
|
63
|
+
@tests ||= "test/**/{test,}*{,test}"
|
64
|
+
@exclude ||= []
|
65
|
+
@requires ||= []
|
66
|
+
@live ||= false
|
67
|
+
@log ||= true
|
68
|
+
#@reporter ||= OutlineReporter.new($stdout)
|
69
|
+
#@runner ||= RUBY_VERSION >= "1.9" ? MiniRunner : TestRunner
|
70
|
+
@pattern ||= /.*/
|
71
|
+
end
|
72
|
+
|
73
|
+
# Collect test configuation.
|
74
|
+
#def test_configuration(options={})
|
75
|
+
# #options = configure_options(options, 'test')
|
76
|
+
# #options['loadpath'] ||= metadata.loadpath
|
77
|
+
# options['tests'] ||= self.tests
|
78
|
+
# options['loadpath'] ||= self.loadpath
|
79
|
+
# options['requires'] ||= self.requires
|
80
|
+
# options['live'] ||= self.live
|
81
|
+
# options['exclude'] ||= self.exclude
|
82
|
+
# #options['tests'] = list_option(options['tests'])
|
83
|
+
# options['loadpath'] = list_option(options['loadpath'])
|
84
|
+
# options['exclude'] = list_option(options['exclude'])
|
85
|
+
# options['require'] = list_option(options['require'])
|
86
|
+
# return options
|
87
|
+
#end
|
88
|
+
|
89
|
+
#
|
90
|
+
def list_option(list)
|
91
|
+
case list
|
92
|
+
when nil
|
93
|
+
[]
|
94
|
+
when Array
|
95
|
+
list
|
96
|
+
else
|
97
|
+
list.split(/[:;]/)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
public
|
102
|
+
|
103
|
+
def loadpath=(paths)
|
104
|
+
@loadpath = list_option(paths)
|
105
|
+
end
|
106
|
+
|
107
|
+
def exclude=(paths)
|
108
|
+
@exclude = list_option(paths)
|
109
|
+
end
|
110
|
+
|
111
|
+
def requires=(paths)
|
112
|
+
@requires = list_option(paths)
|
113
|
+
end
|
114
|
+
|
115
|
+
def files
|
116
|
+
@files ||= (
|
117
|
+
fs = tests.map do |t|
|
118
|
+
File.directory?(t) ? Dir[File.join(t, '**', '*')] : Dir[t]
|
119
|
+
end
|
120
|
+
fs = fs.flatten.reject{ |f| File.directory?(f) }
|
121
|
+
ex = exclude.map do |x|
|
122
|
+
File.directory?(x) ? Dir[File.join(x, '**', '*')] : Dir[x]
|
123
|
+
end
|
124
|
+
ex = ex.flatten.reject{ |f| File.directory?(f) }
|
125
|
+
(fs - ex).uniq.map{ |f| File.expand_path(f) }
|
126
|
+
)
|
127
|
+
end
|
128
|
+
|
129
|
+
def start
|
130
|
+
@files = nil # reset files just in case
|
131
|
+
|
132
|
+
if files.empty?
|
133
|
+
$stderr.puts "No tests."
|
134
|
+
return
|
135
|
+
end
|
136
|
+
|
137
|
+
testrun = runner.new(self)
|
138
|
+
|
139
|
+
testrun.start
|
140
|
+
end
|
141
|
+
|
142
|
+
# Select reporter based on output mode.
|
143
|
+
def reporter
|
144
|
+
@reporter ||= (
|
145
|
+
case format
|
146
|
+
when :marshal
|
147
|
+
require 'turn/reporters/marshal_reporter'
|
148
|
+
Turn::MarshalReporter.new($stdout)
|
149
|
+
when :progress
|
150
|
+
require 'turn/reporters/progress_reporter'
|
151
|
+
Turn::ProgressReporter.new($stdout)
|
152
|
+
when :dotted
|
153
|
+
require 'turn/reporters/dot_reporter'
|
154
|
+
Turn::DotReporter.new($stdout)
|
155
|
+
when :pretty
|
156
|
+
require 'turn/reporters/pretty_reporter'
|
157
|
+
Turn::PrettyReporter.new($stdout)
|
158
|
+
when :cue
|
159
|
+
require 'turn/reporters/cue_reporter'
|
160
|
+
Turn::CueReporter.new($stdout)
|
161
|
+
else
|
162
|
+
require 'turn/reporters/outline_reporter'
|
163
|
+
Turn::OutlineReporter.new($stdout)
|
164
|
+
end
|
165
|
+
)
|
166
|
+
end
|
167
|
+
|
168
|
+
# # Insatance of Runner, selected based on format and runmode.
|
169
|
+
def runner
|
170
|
+
@runner ||= (
|
171
|
+
case framework
|
172
|
+
when :minitest
|
173
|
+
require 'turn/runners/minirunner'
|
174
|
+
else
|
175
|
+
require 'turn/runners/testrunner'
|
176
|
+
end
|
177
|
+
|
178
|
+
case runmode
|
179
|
+
when :marshal
|
180
|
+
if framework == :minitest
|
181
|
+
Turn::MiniRunner
|
182
|
+
else
|
183
|
+
Turn::TestRunner
|
184
|
+
end
|
185
|
+
when :solo
|
186
|
+
require 'turn/runners/solorunner'
|
187
|
+
Turn::SoloRunner
|
188
|
+
when :cross
|
189
|
+
require 'turn/runners/crossrunner'
|
190
|
+
Turn::CrossRunner
|
191
|
+
else
|
192
|
+
if framework == :minitest
|
193
|
+
Turn::MiniRunner
|
194
|
+
else
|
195
|
+
Turn::TestRunner
|
196
|
+
end
|
197
|
+
end
|
198
|
+
)
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Borrowed methods from Ruby Facets.
|
2
|
+
|
3
|
+
class String
|
4
|
+
|
5
|
+
# Aligns each line n spaces.
|
6
|
+
def tab(n)
|
7
|
+
gsub(/^ */, ' ' * n)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Preserves relative tabbing.
|
11
|
+
# The first non-empty line ends up with n spaces before nonspace.
|
12
|
+
def tabto(n)
|
13
|
+
if self =~ /^( *)\S/
|
14
|
+
indent(n - $1.length)
|
15
|
+
else
|
16
|
+
self
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Indent left or right by n spaces.
|
21
|
+
# (This used to be called #tab and aliased as #indent.)
|
22
|
+
def indent(n, c=' ')
|
23
|
+
if n >= 0
|
24
|
+
gsub(/^/, c * n)
|
25
|
+
else
|
26
|
+
gsub(/^#{Regexp.escape(c)}{0,#{-n}}/, "")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Turn
|
2
|
+
require 'turn/colorize'
|
3
|
+
require 'turn/core_ext'
|
4
|
+
|
5
|
+
# = Reporter
|
6
|
+
#
|
7
|
+
# There are two distinct way in which a report may be utilized
|
8
|
+
# by a Runner: per-call or per-file. The method #pass, #fail
|
9
|
+
# and #error are generic, and will be used in either case.
|
10
|
+
# A per-call runner will use all the methods of a Reporter,
|
11
|
+
# while a per-file runner will use start_case per file,
|
12
|
+
# and will not use the start_test and finish_test methods,
|
13
|
+
# since those are beyond it's grainularity.
|
14
|
+
#
|
15
|
+
class Reporter
|
16
|
+
|
17
|
+
include Colorize
|
18
|
+
|
19
|
+
attr :io
|
20
|
+
|
21
|
+
def initialize(io)
|
22
|
+
@io = io || $stdout
|
23
|
+
end
|
24
|
+
|
25
|
+
# These methods are called in the process of running the tests.
|
26
|
+
|
27
|
+
def start_suite(test_suite)
|
28
|
+
end
|
29
|
+
|
30
|
+
def start_case(test_case)
|
31
|
+
end
|
32
|
+
|
33
|
+
def start_test(test)
|
34
|
+
end
|
35
|
+
|
36
|
+
def pass(message=nil)
|
37
|
+
end
|
38
|
+
|
39
|
+
def fail(assertion, message=nil)
|
40
|
+
end
|
41
|
+
|
42
|
+
def error(exception, message=nil)
|
43
|
+
end
|
44
|
+
|
45
|
+
def finish_test(test)
|
46
|
+
end
|
47
|
+
|
48
|
+
def finish_case(test_case)
|
49
|
+
end
|
50
|
+
|
51
|
+
def finish_suite(test_suite)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# TODO: backtrace filter probably could use some refinement.
|
57
|
+
def filter_backtrace(bt)
|
58
|
+
return [] unless bt
|
59
|
+
bt.reject!{ |line| line.rindex('minitest') }
|
60
|
+
bt.reject!{ |line| line.rindex('test/unit') }
|
61
|
+
bt.reject!{ |line| line.rindex('lib/turn') }
|
62
|
+
bt.reject!{ |line| line.rindex('bin/turn') }
|
63
|
+
bt.map{ |line| line.sub(Dir.pwd+'/', '') }
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'turn/reporter'
|
2
|
+
|
3
|
+
module Turn
|
4
|
+
|
5
|
+
# = Cue Reporter
|
6
|
+
#
|
7
|
+
# Inspired by Shindo.
|
8
|
+
#
|
9
|
+
class CueReporter < Reporter
|
10
|
+
|
11
|
+
def start_suite(suite)
|
12
|
+
@suite = suite
|
13
|
+
@time = Time.now
|
14
|
+
@stdout = StringIO.new
|
15
|
+
@stderr = StringIO.new
|
16
|
+
#files = suite.collect{ |s| s.file }.join(' ')
|
17
|
+
io.puts "Loaded suite #{suite.name}"
|
18
|
+
#io.puts "Started"
|
19
|
+
end
|
20
|
+
|
21
|
+
def start_case(kase)
|
22
|
+
io.puts(kase.name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def start_test(test)
|
26
|
+
#if @file != test.file
|
27
|
+
# @file = test.file
|
28
|
+
# io.puts(test.file)
|
29
|
+
#end
|
30
|
+
io.print Colorize.blue(" %-69s" % test.name)
|
31
|
+
$stdout = @stdout
|
32
|
+
$stderr = @stderr
|
33
|
+
$stdout.rewind
|
34
|
+
$stderr.rewind
|
35
|
+
end
|
36
|
+
|
37
|
+
def pass(message=nil)
|
38
|
+
io.puts " #{PASS}"
|
39
|
+
if message
|
40
|
+
message = Colorize.green(message)
|
41
|
+
message = message.to_s.tabto(8)
|
42
|
+
io.puts(message)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def fail(assertion, message=nil)
|
47
|
+
io.puts(" #{FAIL}")
|
48
|
+
#message = assertion.location[0] + "\n" + assertion.message #.gsub("\n","\n")
|
49
|
+
message = message || assertion.to_s
|
50
|
+
#if message
|
51
|
+
message = Colorize.red(message)
|
52
|
+
message = message.to_s.tabto(8)
|
53
|
+
io.puts(message)
|
54
|
+
#end
|
55
|
+
|
56
|
+
show_captured_output
|
57
|
+
|
58
|
+
prompt
|
59
|
+
end
|
60
|
+
|
61
|
+
def error(exception, message=nil)
|
62
|
+
#message = exception.to_s.split("\n")[2..-1].join("\n")
|
63
|
+
message = message || exception.to_s
|
64
|
+
io.puts("#{ERROR}")
|
65
|
+
io.puts(message) #if message
|
66
|
+
|
67
|
+
prompt
|
68
|
+
end
|
69
|
+
|
70
|
+
def finish_test(test)
|
71
|
+
$stdout = STDOUT
|
72
|
+
$stderr = STDERR
|
73
|
+
end
|
74
|
+
|
75
|
+
def show_captured_output
|
76
|
+
show_captured_stdout
|
77
|
+
show_captured_stderr
|
78
|
+
end
|
79
|
+
|
80
|
+
def show_captured_stdout
|
81
|
+
@stdout.rewind
|
82
|
+
return if @stdout.eof?
|
83
|
+
STDOUT.puts(<<-output.tabto(8))
|
84
|
+
\nSTDOUT:
|
85
|
+
#{@stdout.read}
|
86
|
+
output
|
87
|
+
end
|
88
|
+
|
89
|
+
def show_captured_stderr
|
90
|
+
@stderr.rewind
|
91
|
+
return if @stderr.eof?
|
92
|
+
STDOUT.puts(<<-output.tabto(8))
|
93
|
+
\nSTDERR:
|
94
|
+
#{@stderr.read}
|
95
|
+
output
|
96
|
+
end
|
97
|
+
|
98
|
+
#def finish_case(kase)
|
99
|
+
#end
|
100
|
+
|
101
|
+
def finish_suite(suite)
|
102
|
+
total = suite.count_tests
|
103
|
+
failure = suite.count_failures
|
104
|
+
error = suite.count_errors
|
105
|
+
pass = total - failure - error
|
106
|
+
|
107
|
+
bar = '=' * 78
|
108
|
+
if COLORIZE
|
109
|
+
bar = if pass == total then Colorize.green(bar)
|
110
|
+
else Colorize.red(bar) end
|
111
|
+
end
|
112
|
+
|
113
|
+
tally = [total, suite.count_assertions]
|
114
|
+
|
115
|
+
io.puts bar
|
116
|
+
io.puts " pass: %d, fail: %d, error: %d" % [pass, failure, error]
|
117
|
+
io.puts " total: %d tests with %d assertions in #{Time.new - @time} seconds" % tally
|
118
|
+
io.puts bar
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def prompt
|
124
|
+
begin
|
125
|
+
io << " [c,i,q,r,t,#,?] "
|
126
|
+
io.flush
|
127
|
+
until inp = $stdin.gets ; sleep 1 ; end
|
128
|
+
answer = inp.strip
|
129
|
+
case answer
|
130
|
+
when 'c', ''
|
131
|
+
when 'r'
|
132
|
+
# how to reload and start over?
|
133
|
+
when 'i'
|
134
|
+
# how to drop into an interactive console?
|
135
|
+
when 't'
|
136
|
+
io.puts $@
|
137
|
+
raise ArgumentError
|
138
|
+
when /^\d+$/
|
139
|
+
io.puts $@[0..answer.to_i]
|
140
|
+
raise ArgumentError
|
141
|
+
when 'q'
|
142
|
+
exit -1
|
143
|
+
when '?'
|
144
|
+
io.puts HELP
|
145
|
+
raise ArgumentError #prompt
|
146
|
+
else
|
147
|
+
raise ArgumentError
|
148
|
+
end
|
149
|
+
rescue ArgumentError
|
150
|
+
retry
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
HELP = %{
|
155
|
+
c continue
|
156
|
+
r restart
|
157
|
+
i irb
|
158
|
+
t backtrace
|
159
|
+
# backtrace lines
|
160
|
+
q quit
|
161
|
+
? help
|
162
|
+
}
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
|