rubydoctest 0.2.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +10 -0
- data/License.txt +16 -17
- data/Manifest.txt +13 -5
- data/PostInstall.txt +2 -3
- data/README.txt +48 -57
- data/Ruby DocTest.tmproj +197 -0
- data/bin/rubydoctest +41 -25
- data/config/hoe.rb +3 -5
- data/lib/code_block.rb +68 -0
- data/lib/doctest_require.rb +3 -0
- data/lib/lines.rb +143 -0
- data/lib/result.rb +63 -0
- data/lib/rubydoctest.rb +13 -255
- data/lib/rubydoctest/version.rb +3 -3
- data/lib/runner.rb +370 -0
- data/lib/special_directive.rb +44 -0
- data/lib/statement.rb +75 -0
- data/lib/test.rb +29 -0
- data/rubydoctest.gemspec +6 -6
- data/script/console +0 -0
- data/script/destroy +0 -0
- data/script/generate +0 -0
- data/script/rstakeout +92 -0
- data/script/txt2html +0 -0
- data/tasks/doctests.rake +2 -1
- data/textmate/DocTest (Markdown).textmate +7 -0
- data/textmate/DocTest (Ruby).textmate +55 -0
- data/textmate/DocTest (Text).textmate +66 -0
- data/website/index.html +141 -11
- data/website/template.html.erb +1 -1
- metadata +22 -15
- data/test/doctest/file_relative.doctest +0 -5
- data/test/doctest/runner.doctest +0 -58
- data/test/doctest/simple.doctest +0 -30
- data/test/test_helper.rb +0 -2
- data/test/test_rubydoctest.rb +0 -11
data/lib/rubydoctest/version.rb
CHANGED
data/lib/runner.rb
ADDED
@@ -0,0 +1,370 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'rubydoctest'
|
5
|
+
require 'statement'
|
6
|
+
require 'result'
|
7
|
+
require 'special_directive'
|
8
|
+
require 'code_block'
|
9
|
+
require 'test'
|
10
|
+
|
11
|
+
module RubyDocTest
|
12
|
+
class Runner
|
13
|
+
attr_reader :groups, :blocks, :tests
|
14
|
+
|
15
|
+
@@color = {
|
16
|
+
:html => {
|
17
|
+
:red => %{<font color="red">%s</font>},
|
18
|
+
:yellow => %{<font color="#C0C000">%s</font>},
|
19
|
+
:green => %{<font color="green">%s</font>}
|
20
|
+
},
|
21
|
+
:ansi => {
|
22
|
+
:red => %{\e[31m%s\e[0m},
|
23
|
+
:yellow => %{\e[33m%s\e[0m},
|
24
|
+
:green => %{\e[32m%s\e[0m}
|
25
|
+
},
|
26
|
+
:plain => {
|
27
|
+
:red => "%s",
|
28
|
+
:yellow => "%s",
|
29
|
+
:green => "%s"
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
# The evaluation mode, either :doctest or :ruby.
|
34
|
+
#
|
35
|
+
# Modes:
|
36
|
+
# :doctest
|
37
|
+
# - The the Runner expects the file to contain text (e.g. a markdown file).
|
38
|
+
# In addition, it assumes that the text will occasionally be interspersed
|
39
|
+
# with irb lines which it should eval, e.g. '>>' and '=>'.
|
40
|
+
#
|
41
|
+
# :ruby
|
42
|
+
# - The Runner expects the file to be a Ruby source file. The source may contain
|
43
|
+
# comments that are interspersed with irb lines to eval, e.g. '>>' and '=>'.
|
44
|
+
attr_accessor :mode
|
45
|
+
|
46
|
+
# === Tests
|
47
|
+
#
|
48
|
+
# doctest: Runner mode should default to :doctest and :ruby from the filename
|
49
|
+
# >> r = RubyDocTest::Runner.new("", "test.doctest")
|
50
|
+
# >> r.mode
|
51
|
+
# => :doctest
|
52
|
+
#
|
53
|
+
# >> r = RubyDocTest::Runner.new("", "test.rb")
|
54
|
+
# >> r.mode
|
55
|
+
# => :ruby
|
56
|
+
#
|
57
|
+
# doctest: The src_lines should be separated into an array
|
58
|
+
# >> r = RubyDocTest::Runner.new("a\nb\n", "test.doctest")
|
59
|
+
# >> r.instance_variable_get("@src_lines")
|
60
|
+
# => ["a", "b"]
|
61
|
+
def initialize(src, file_name = "test.doctest", initial_mode = nil)
|
62
|
+
@src, @file_name = src, file_name
|
63
|
+
@mode = initial_mode || (File.extname(file_name) == ".rb" ? :ruby : :doctest)
|
64
|
+
|
65
|
+
@src_lines = src.split("\n")
|
66
|
+
@groups, @blocks = [], []
|
67
|
+
$rubydoctest = self
|
68
|
+
end
|
69
|
+
|
70
|
+
# doctest: Using the doctest_require: SpecialDirective should require a file relative to the current one.
|
71
|
+
# >> r = RubyDocTest::Runner.new("# doctest_require: 'doctest_require.rb'", __FILE__)
|
72
|
+
# >> r.prepare_tests
|
73
|
+
# >> is_doctest_require_successful?
|
74
|
+
# => true
|
75
|
+
def prepare_tests
|
76
|
+
@groups = read_groups
|
77
|
+
@blocks = organize_blocks
|
78
|
+
@tests = organize_tests
|
79
|
+
eval(@src, TOPLEVEL_BINDING, @file_name) if @mode == :ruby
|
80
|
+
end
|
81
|
+
|
82
|
+
# === Tests
|
83
|
+
# doctest: Run through a simple inline doctest (rb) file and see if it passes
|
84
|
+
# >> file = File.join(File.dirname(__FILE__), "..", "test", "inline.rb")
|
85
|
+
# >> r = RubyDocTest::Runner.new(IO.read(file), "inline.rb")
|
86
|
+
# >> r.pass?
|
87
|
+
# => true
|
88
|
+
def pass?
|
89
|
+
prepare_tests
|
90
|
+
@tests.all?{ |t| t.pass? }
|
91
|
+
end
|
92
|
+
|
93
|
+
# === Description
|
94
|
+
# Starts an IRB prompt when the "!!!" SpecialDirective is given.
|
95
|
+
def start_irb
|
96
|
+
IRB.init_config(nil)
|
97
|
+
IRB.conf[:PROMPT_MODE] = :SIMPLE
|
98
|
+
irb = IRB::Irb.new(IRB::WorkSpace.new(TOPLEVEL_BINDING))
|
99
|
+
IRB.conf[:MAIN_CONTEXT] = irb.context
|
100
|
+
catch(:IRB_EXIT) do
|
101
|
+
irb.eval_input
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def format_color(text, color)
|
106
|
+
@@color[RubyDocTest.output_format][color] % text.to_s
|
107
|
+
end
|
108
|
+
|
109
|
+
def run
|
110
|
+
prepare_tests
|
111
|
+
newline = "\n "
|
112
|
+
everything_passed = true
|
113
|
+
puts "=== Testing '#{@file_name}'..."
|
114
|
+
ok, fail, err = 0, 0, 0
|
115
|
+
@tests.each do |t|
|
116
|
+
if SpecialDirective === t and t.name == "!!!"
|
117
|
+
start_irb unless RubyDocTest.ignore_interactive
|
118
|
+
else
|
119
|
+
begin
|
120
|
+
if t.pass?
|
121
|
+
ok += 1
|
122
|
+
status = ["OK".center(4), :green]
|
123
|
+
detail = nil
|
124
|
+
else
|
125
|
+
fail += 1
|
126
|
+
everything_passed = false
|
127
|
+
status = ["FAIL".center(4), :red]
|
128
|
+
detail = format_color(
|
129
|
+
"Got: #{t.actual_result}#{newline}Expected: #{t.expected_result}" + newline +
|
130
|
+
" from #{@file_name}:#{t.first_failed.result.line_number}",
|
131
|
+
:red)
|
132
|
+
|
133
|
+
end
|
134
|
+
rescue EvaluationError => e
|
135
|
+
err += 1
|
136
|
+
status = ["ERR".center(4), :yellow]
|
137
|
+
exception_text = e.original_exception.to_s.split("\n").join(newline)
|
138
|
+
if RubyDocTest.output_format == :html
|
139
|
+
exception_text = exception_text.gsub("<", "<").gsub(">", ">")
|
140
|
+
end
|
141
|
+
detail = format_color(
|
142
|
+
"#{e.original_exception.class.to_s}: #{exception_text}" + newline +
|
143
|
+
" from #{@file_name}:#{e.statement.line_number}" + newline +
|
144
|
+
e.statement.source_code,
|
145
|
+
:yellow)
|
146
|
+
end
|
147
|
+
puts \
|
148
|
+
"#{format_color(*status)} | " +
|
149
|
+
"#{t.description.split("\n").join(newline)}" +
|
150
|
+
(detail ? newline + detail : "")
|
151
|
+
end
|
152
|
+
end
|
153
|
+
puts \
|
154
|
+
"#{@blocks.select{ |b| b.is_a? CodeBlock }.size} comparisons, " +
|
155
|
+
"#{@tests.size} doctests, " +
|
156
|
+
"#{fail} failures, " +
|
157
|
+
"#{err} errors"
|
158
|
+
everything_passed
|
159
|
+
end
|
160
|
+
|
161
|
+
# === Tests
|
162
|
+
#
|
163
|
+
# doctest: Non-statement lines get ignored while statement / result lines are included
|
164
|
+
# Default mode is :doctest, so non-irb prompts should be ignored.
|
165
|
+
# >> r = RubyDocTest::Runner.new("a\nb\n >> c = 1\n => 1")
|
166
|
+
# >> groups = r.read_groups
|
167
|
+
# >> groups.size
|
168
|
+
# => 2
|
169
|
+
#
|
170
|
+
# doctest: Group types are correctly created
|
171
|
+
# >> groups.map{ |g| g.class }
|
172
|
+
# => [RubyDocTest::Statement, RubyDocTest::Result]
|
173
|
+
#
|
174
|
+
# doctest: A ruby document can have =begin and =end blocks in it
|
175
|
+
# >> r = RubyDocTest::Runner.new(<<-RUBY, "test.rb")
|
176
|
+
# some_ruby_code = 1
|
177
|
+
# =begin
|
178
|
+
# this is a normal ruby comment
|
179
|
+
# >> z = 10
|
180
|
+
# => 10
|
181
|
+
# =end
|
182
|
+
# more_ruby_code = 2
|
183
|
+
# RUBY
|
184
|
+
# >> groups = r.read_groups
|
185
|
+
# >> groups.size
|
186
|
+
# => 2
|
187
|
+
# >> groups.map{ |g| g.lines.first }
|
188
|
+
# => [" >> z = 10", " => 10"]
|
189
|
+
def read_groups(src_lines = @src_lines, mode = @mode, start_index = 0)
|
190
|
+
groups = []
|
191
|
+
(start_index).upto(src_lines.size) do |index|
|
192
|
+
line = src_lines[index]
|
193
|
+
case mode
|
194
|
+
when :ruby
|
195
|
+
case line
|
196
|
+
|
197
|
+
# Beginning of a multi-line comment section
|
198
|
+
when /^=begin/
|
199
|
+
groups +=
|
200
|
+
# Get statements, results, and directives as if inside a doctest
|
201
|
+
read_groups(src_lines, :doctest_with_end, index)
|
202
|
+
|
203
|
+
else
|
204
|
+
if g = match_group("\\s*#\\s*", src_lines, index)
|
205
|
+
groups << g
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
when :doctest
|
210
|
+
if g = match_group("\\s*", src_lines, index)
|
211
|
+
groups << g
|
212
|
+
end
|
213
|
+
|
214
|
+
when :doctest_with_end
|
215
|
+
break if line =~ /^=end/
|
216
|
+
if g = match_group("\\s*", src_lines, index)
|
217
|
+
groups << g
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
end
|
222
|
+
groups
|
223
|
+
end
|
224
|
+
|
225
|
+
def match_group(prefix, src_lines, index)
|
226
|
+
case src_lines[index]
|
227
|
+
|
228
|
+
# An irb '>>' marker after a '#' indicates an embedded doctest
|
229
|
+
when /^(#{prefix})>>(\s|\s*$)/
|
230
|
+
Statement.new(src_lines, index, @file_name)
|
231
|
+
|
232
|
+
# An irb '=>' marker after a '#' indicates an embedded result
|
233
|
+
when /^(#{prefix})=>\s/
|
234
|
+
Result.new(src_lines, index)
|
235
|
+
|
236
|
+
# Whenever we match a directive (e.g. 'doctest'), add that in as well
|
237
|
+
when /^(#{prefix})(#{SpecialDirective::NAMES_FOR_RX})(.*)$/
|
238
|
+
SpecialDirective.new(src_lines, index)
|
239
|
+
|
240
|
+
else
|
241
|
+
nil
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# === Tests
|
246
|
+
#
|
247
|
+
# doctest: The organize_blocks method should separate Statement, Result and SpecialDirective
|
248
|
+
# objects into CodeBlocks.
|
249
|
+
# >> r = RubyDocTest::Runner.new(">> t = 1\n>> t + 2\n=> 3\n>> u = 1", "test.doctest")
|
250
|
+
# >> r.prepare_tests
|
251
|
+
#
|
252
|
+
# >> r.blocks.first.statements.map{|s| s.lines}
|
253
|
+
# => [[">> t = 1"], [">> t + 2"]]
|
254
|
+
#
|
255
|
+
# >> r.blocks.first.result.lines
|
256
|
+
# => ["=> 3"]
|
257
|
+
#
|
258
|
+
# >> r.blocks.last.statements.map{|s| s.lines}
|
259
|
+
# => [[">> u = 1"]]
|
260
|
+
#
|
261
|
+
# >> r.blocks.last.result
|
262
|
+
# => nil
|
263
|
+
#
|
264
|
+
# doctest: Two doctest directives--each having its own statement--should be separated properly
|
265
|
+
# by organize_blocks.
|
266
|
+
# >> r = RubyDocTest::Runner.new("doctest: one\n>> t = 1\ndoctest: two\n>> t + 2", "test.doctest")
|
267
|
+
# >> r.prepare_tests
|
268
|
+
# >> r.blocks.map{|b| b.class}
|
269
|
+
# => [RubyDocTest::SpecialDirective, RubyDocTest::CodeBlock,
|
270
|
+
# RubyDocTest::SpecialDirective, RubyDocTest::CodeBlock]
|
271
|
+
#
|
272
|
+
# >> r.blocks[0].value
|
273
|
+
# => "one"
|
274
|
+
#
|
275
|
+
# >> r.blocks[1].statements.map{|s| s.lines}
|
276
|
+
# => [[">> t = 1"]]
|
277
|
+
#
|
278
|
+
# >> r.blocks[2].value
|
279
|
+
# => "two"
|
280
|
+
#
|
281
|
+
# >> r.blocks[3].statements.map{|s| s.lines}
|
282
|
+
# => [[">> t + 2"]]
|
283
|
+
def organize_blocks(groups = @groups)
|
284
|
+
blocks = []
|
285
|
+
current_statements = []
|
286
|
+
groups.each do |g|
|
287
|
+
case g
|
288
|
+
when Statement
|
289
|
+
current_statements << g
|
290
|
+
when Result
|
291
|
+
blocks << CodeBlock.new(current_statements, g)
|
292
|
+
current_statements = []
|
293
|
+
when SpecialDirective
|
294
|
+
case g.name
|
295
|
+
when "doctest:"
|
296
|
+
blocks << CodeBlock.new(current_statements) unless current_statements.empty?
|
297
|
+
current_statements = []
|
298
|
+
when "doctest_require:"
|
299
|
+
doctest_require = eval(g.value, TOPLEVEL_BINDING, @file_name, g.line_number)
|
300
|
+
if doctest_require.is_a? String
|
301
|
+
require_relative_to_file_name(doctest_require, @file_name)
|
302
|
+
end
|
303
|
+
when "!!!"
|
304
|
+
# ignore
|
305
|
+
end
|
306
|
+
blocks << g
|
307
|
+
end
|
308
|
+
end
|
309
|
+
blocks << CodeBlock.new(current_statements) unless current_statements.empty?
|
310
|
+
blocks
|
311
|
+
end
|
312
|
+
|
313
|
+
def require_relative_to_file_name(file_name, relative_to)
|
314
|
+
load_path = $:.dup
|
315
|
+
$:.unshift File.expand_path(File.join(File.dirname(relative_to), File.dirname(file_name)))
|
316
|
+
require File.basename(file_name)
|
317
|
+
ensure
|
318
|
+
$:.shift
|
319
|
+
end
|
320
|
+
|
321
|
+
# === Tests
|
322
|
+
#
|
323
|
+
# doctest: Tests should be organized into groups based on the 'doctest' SpecialDirective
|
324
|
+
# >> r = RubyDocTest::Runner.new("doctest: one\n>> t = 1\ndoctest: two\n>> t + 2", "test.doctest")
|
325
|
+
# >> r.prepare_tests
|
326
|
+
# >> r.tests.size
|
327
|
+
# => 2
|
328
|
+
# >> r.tests[0].code_blocks.map{|c| c.statements}.flatten.map{|s| s.lines}
|
329
|
+
# => [[">> t = 1"]]
|
330
|
+
# >> r.tests[1].code_blocks.map{|c| c.statements}.flatten.map{|s| s.lines}
|
331
|
+
# => [[">> t + 2"]]
|
332
|
+
# >> r.tests[0].description
|
333
|
+
# => "one"
|
334
|
+
# >> r.tests[1].description
|
335
|
+
# => "two"
|
336
|
+
#
|
337
|
+
# doctest: Without a 'doctest' SpecialDirective, there is one Test called "Default Test".
|
338
|
+
# >> r = RubyDocTest::Runner.new(">> t = 1\n>> t + 2\n=> 3\n>> u = 1", "test.doctest")
|
339
|
+
# >> r.prepare_tests
|
340
|
+
# >> r.tests.size
|
341
|
+
# => 1
|
342
|
+
#
|
343
|
+
# >> r.tests.first.description
|
344
|
+
# => "Default Test"
|
345
|
+
#
|
346
|
+
# >> r.tests.first.code_blocks.size
|
347
|
+
# => 2
|
348
|
+
def organize_tests(blocks = @blocks)
|
349
|
+
tests = []
|
350
|
+
assigned_blocks = nil
|
351
|
+
unassigned_blocks = []
|
352
|
+
blocks.each do |g|
|
353
|
+
case g
|
354
|
+
when CodeBlock
|
355
|
+
(assigned_blocks || unassigned_blocks) << g
|
356
|
+
when SpecialDirective
|
357
|
+
case g.name
|
358
|
+
when "doctest:"
|
359
|
+
assigned_blocks = []
|
360
|
+
tests << Test.new(g.value, assigned_blocks)
|
361
|
+
when "!!!"
|
362
|
+
tests << g
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
tests << Test.new("Default Test", unassigned_blocks) unless unassigned_blocks.empty?
|
367
|
+
tests
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'lines'
|
5
|
+
|
6
|
+
module RubyDocTest
|
7
|
+
class SpecialDirective < Lines
|
8
|
+
NAMES = ["doctest:", "!!!", "doctest_require:"]
|
9
|
+
NAMES_FOR_RX = NAMES.map{ |n| Regexp.escape(n) }.join("|")
|
10
|
+
|
11
|
+
# === Test
|
12
|
+
#
|
13
|
+
# doctest: The name of the directive should be detected in the first line
|
14
|
+
# >> s = RubyDocTest::SpecialDirective.new(["doctest: Testing Stuff", "Other Stuff"])
|
15
|
+
# >> s.name
|
16
|
+
# => "doctest:"
|
17
|
+
def name
|
18
|
+
if m = lines.first.match(/^#{Regexp.escape(indentation)}(#{NAMES_FOR_RX})/)
|
19
|
+
m[1]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# === Test
|
24
|
+
#
|
25
|
+
# doctest: The value of the directive should be detected in the first line
|
26
|
+
# >> s = RubyDocTest::SpecialDirective.new(["doctest: Testing Stuff", "Other Stuff"])
|
27
|
+
# >> s.value
|
28
|
+
# => "Testing Stuff"
|
29
|
+
#
|
30
|
+
# >> s = RubyDocTest::SpecialDirective.new([" # doctest: Testing Stuff", " # Other Stuff"])
|
31
|
+
# >> s.value
|
32
|
+
# => "Testing Stuff"
|
33
|
+
#
|
34
|
+
# doctest: Multiple lines for the directive value should work as well
|
35
|
+
# >> s = RubyDocTest::SpecialDirective.new(["doctest: Testing Stuff", " On Two Lines"])
|
36
|
+
# >> s.value
|
37
|
+
# => "Testing Stuff\nOn Two Lines"
|
38
|
+
def value
|
39
|
+
if m = lines.join("\n").match(/^#{Regexp.escape(indentation)}(#{NAMES_FOR_RX})(.*)/m)
|
40
|
+
m[2].strip
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/statement.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'rubydoctest'
|
5
|
+
require 'lines'
|
6
|
+
|
7
|
+
module RubyDocTest
|
8
|
+
class EvaluationError < Exception
|
9
|
+
attr_reader :statement, :original_exception
|
10
|
+
def initialize(statement, original_exception)
|
11
|
+
@statement, @original_exception = statement, original_exception
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Statement < Lines
|
16
|
+
|
17
|
+
attr_reader :actual_result
|
18
|
+
|
19
|
+
# === Tests
|
20
|
+
#
|
21
|
+
# doctest: The FILENAME ruby constant should be replaced by the name of the file
|
22
|
+
# >> __FILE__[/statement\.rb$/]
|
23
|
+
# => "statement.rb"
|
24
|
+
def initialize(doc_lines, line_index = 0, file_name = nil)
|
25
|
+
@file_name = file_name
|
26
|
+
super(doc_lines, line_index)
|
27
|
+
end
|
28
|
+
|
29
|
+
# === Tests
|
30
|
+
#
|
31
|
+
# doctest: A statement should parse out a '>>' irb prompt
|
32
|
+
# >> s = RubyDocTest::Statement.new([">> a = 1"])
|
33
|
+
# >> s.source_code
|
34
|
+
# => "a = 1"
|
35
|
+
#
|
36
|
+
# doctest: More than one line should get included, if indentation so indicates
|
37
|
+
# >> s = RubyDocTest::Statement.new([">> b = 1 +", " 1", "not part of the statement"])
|
38
|
+
# >> s.source_code
|
39
|
+
# => "b = 1 +\n1"
|
40
|
+
#
|
41
|
+
# doctest: Lines indented by ?> should have the ?> removed.
|
42
|
+
# >> s = RubyDocTest::Statement.new([">> b = 1 +", "?> 1"])
|
43
|
+
# >> s.source_code
|
44
|
+
# => "b = 1 +\n1"
|
45
|
+
def source_code
|
46
|
+
lines.first =~ /^#{Regexp.escape(indentation)}>>\s(.*)$/
|
47
|
+
first = [$1]
|
48
|
+
remaining = (lines[1..-1] || [])
|
49
|
+
(first + remaining).join("\n")
|
50
|
+
end
|
51
|
+
|
52
|
+
# === Test
|
53
|
+
#
|
54
|
+
# doctest: Evaluating a multi-line statement should be ok
|
55
|
+
# >> s = RubyDocTest::Statement.new([">> b = 1 +", " 1", "not part of the statement"])
|
56
|
+
# >> s.evaluate
|
57
|
+
# => 2
|
58
|
+
#
|
59
|
+
# doctest: Evaluating a syntax error should raise an EvaluationError
|
60
|
+
# >> s = RubyDocTest::Statement.new([">> b = 1 +"])
|
61
|
+
# >> begin s.evaluate; :fail; rescue RubyDocTest::EvaluationError; :ok end
|
62
|
+
# => :ok
|
63
|
+
def evaluate
|
64
|
+
sc = source_code.gsub("__FILE__", @file_name.inspect)
|
65
|
+
# puts "EVAL: #{sc}"
|
66
|
+
@actual_result = eval(sc, TOPLEVEL_BINDING, __FILE__, __LINE__)
|
67
|
+
rescue Exception => e
|
68
|
+
if RubyDocTest.trace
|
69
|
+
raise e.class, e.to_s + "\n" + e.backtrace.first
|
70
|
+
else
|
71
|
+
raise EvaluationError.new(self, e)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|