rdoctest 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +155 -0
- data/Rakefile +5 -0
- data/bin/rdoctest +31 -0
- data/lib/rdoctest/runner.rb +152 -0
- data/lib/rdoctest/task.rb +79 -0
- data/lib/rdoctest/version.rb +9 -0
- data/lib/rdoctest.rb +2 -0
- metadata +71 -0
data/README.rdoc
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
= Rdoctest
|
2
|
+
|
3
|
+
http://github.com/stephencelis/rdoctest
|
4
|
+
|
5
|
+
A {doctest}[http://docs.python.org/py3k/library/doctest.html] for Ruby.
|
6
|
+
|
7
|
+
|
8
|
+
== Why?
|
9
|
+
|
10
|
+
You'll improve your test coverage and documentation in one fell swoop.
|
11
|
+
|
12
|
+
|
13
|
+
== How?
|
14
|
+
|
15
|
+
Install the gem:
|
16
|
+
|
17
|
+
% [sudo] gem install rdoctest
|
18
|
+
|
19
|
+
|
20
|
+
Use the CLI:
|
21
|
+
|
22
|
+
% rdoctest lib/**/*.rb
|
23
|
+
Loaded suite /.../bin/rdoctest
|
24
|
+
Started
|
25
|
+
.
|
26
|
+
Finished in ... seconds.
|
27
|
+
|
28
|
+
1 tests, 7 assertions, 0 failures, 0 errors, 0 skips
|
29
|
+
|
30
|
+
|
31
|
+
== Examples
|
32
|
+
|
33
|
+
=== Formatting
|
34
|
+
|
35
|
+
Rdoctest expects well-formatted RDoc. This means:
|
36
|
+
|
37
|
+
* Indent examples by 2 spaces.
|
38
|
+
|
39
|
+
# For example:
|
40
|
+
#
|
41
|
+
# 1 + 1 # This code is indented by 2 spaces.
|
42
|
+
|
43
|
+
|
44
|
+
* If your example is a block of code, use <tt># =></tt> to designate the
|
45
|
+
expected result.
|
46
|
+
|
47
|
+
# Short blocks can have the result on the same line:
|
48
|
+
#
|
49
|
+
# 1 + 1 # => 2
|
50
|
+
#
|
51
|
+
# Longer blocks can have the result on the line following:
|
52
|
+
#
|
53
|
+
# def hello_world
|
54
|
+
# puts "Hello, world!"
|
55
|
+
# end
|
56
|
+
# # => nil
|
57
|
+
|
58
|
+
|
59
|
+
* If your example is an IRB session, use <tt>>></tt> and <tt>=></tt>
|
60
|
+
accordingly.
|
61
|
+
|
62
|
+
# Simple:
|
63
|
+
#
|
64
|
+
# >> 1 + 1
|
65
|
+
# => 2
|
66
|
+
#
|
67
|
+
# More complex:
|
68
|
+
#
|
69
|
+
# >> def hello_world
|
70
|
+
# >> puts "Hello, world!"
|
71
|
+
# >> end
|
72
|
+
# => nil
|
73
|
+
# >> hello_world
|
74
|
+
# Hello, world!
|
75
|
+
# => nil
|
76
|
+
#
|
77
|
+
# Use ellipses for partial matches:
|
78
|
+
#
|
79
|
+
# >> goodbye_world
|
80
|
+
# NameError: undefined local variable or method `goodbye_world'...
|
81
|
+
|
82
|
+
|
83
|
+
=== CLI
|
84
|
+
|
85
|
+
Rdoctest's CLI works a lot like Ruby's.
|
86
|
+
|
87
|
+
% rdoctest -h
|
88
|
+
Usage: rdoctest [options] [file ...]
|
89
|
+
-Idirectory
|
90
|
+
-rlibrary
|
91
|
+
...
|
92
|
+
|
93
|
+
If you're testing a complex project, make sure you prepare necessary libraries
|
94
|
+
and load paths:
|
95
|
+
|
96
|
+
% rdoctest -Ilib -rmylibrary lib/**/*.rb
|
97
|
+
|
98
|
+
|
99
|
+
=== Rake
|
100
|
+
|
101
|
+
Rdoctest comes with a Rake task that you can load in your Rakefile.
|
102
|
+
|
103
|
+
require 'rdoctest/task'
|
104
|
+
Rdoctest::Task.new
|
105
|
+
|
106
|
+
|
107
|
+
And run in your project:
|
108
|
+
|
109
|
+
% rake doctest
|
110
|
+
|
111
|
+
|
112
|
+
It comes with a similar configuration to Rake's TestTask.
|
113
|
+
|
114
|
+
Rdoctest::Task.new :test do |t|
|
115
|
+
t.libs << 'lib' # The 'lib' directory is loaded by default,
|
116
|
+
t.ruby_opts << '-rrdoctest' # but require needed files with +ruby_opts+.
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
== Roadmap
|
121
|
+
|
122
|
+
* Better detection of code blocks (the 3-space indent is too restrictive).
|
123
|
+
* Test plain text files (READMEs, for example).
|
124
|
+
* Autotest integration?
|
125
|
+
* Test shell snippets (beginning with <tt>$</tt> and <tt>%</tt>)?
|
126
|
+
|
127
|
+
|
128
|
+
== Prior Art
|
129
|
+
|
130
|
+
{rubydoctest}[http://github.com/tablatom/rubydoctest].
|
131
|
+
|
132
|
+
|
133
|
+
== License
|
134
|
+
|
135
|
+
(The MIT License)
|
136
|
+
|
137
|
+
(c) 2010 Stephen Celis <stephen@stephencelis.com>
|
138
|
+
|
139
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
140
|
+
of this software and associated documentation files (the "Software"), to deal
|
141
|
+
in the Software without restriction, including without limitation the rights
|
142
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
143
|
+
copies of the Software, and to permit persons to whom the Software is
|
144
|
+
furnished to do so, subject to the following conditions:
|
145
|
+
|
146
|
+
The above copyright notice and this permission notice shall be included in all
|
147
|
+
copies or substantial portions of the Software.
|
148
|
+
|
149
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
150
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
151
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
152
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
153
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
154
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
155
|
+
SOFTWARE.
|
data/Rakefile
ADDED
data/bin/rdoctest
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
lib = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
+
$LOAD_PATH.unshift lib
|
5
|
+
|
6
|
+
options = OptionParser.new do |option|
|
7
|
+
option.banner = "Usage: #{basename = File.basename $0} [options] [file ...]"
|
8
|
+
option.on('-Idirectory') { |v|
|
9
|
+
$LOAD_PATH.concat v.split File::PATH_SEPARATOR
|
10
|
+
}
|
11
|
+
option.on('-rlibrary') { |v|
|
12
|
+
require v
|
13
|
+
}
|
14
|
+
option.on('--version') {
|
15
|
+
require 'rdoctest/version'
|
16
|
+
puts "#{basename} #{Rdoctest::Version::VERSION}"
|
17
|
+
exit
|
18
|
+
}
|
19
|
+
option.on('-h', '--help') {
|
20
|
+
puts option
|
21
|
+
exit
|
22
|
+
}
|
23
|
+
end
|
24
|
+
options.parse!
|
25
|
+
|
26
|
+
if ARGF.argv.empty?
|
27
|
+
puts options
|
28
|
+
exit
|
29
|
+
end
|
30
|
+
|
31
|
+
load 'rdoctest/runner.rb'
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'strscan'
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
module Rdoctest
|
6
|
+
# The Runner takes RDoc example text and creates tests for them.
|
7
|
+
#
|
8
|
+
# ==== Example
|
9
|
+
#
|
10
|
+
# Writing inline code that ends with <tt># =></tt> and the result will create
|
11
|
+
# an assertion that the code evaluation is equal to that result.
|
12
|
+
#
|
13
|
+
# 1 + 1
|
14
|
+
# # => 2
|
15
|
+
#
|
16
|
+
# You can also write an assertion on a single line.
|
17
|
+
#
|
18
|
+
# 2 + 2 # => 4
|
19
|
+
#
|
20
|
+
# If you want to assert errors or output, mimic an IRB session.
|
21
|
+
#
|
22
|
+
# >> puts 'hello world'
|
23
|
+
# hello world
|
24
|
+
# => nil
|
25
|
+
# >> def ok
|
26
|
+
# >> a
|
27
|
+
# >> end
|
28
|
+
# => nil
|
29
|
+
#
|
30
|
+
# Use ellipses for partial matches.
|
31
|
+
#
|
32
|
+
# >> a
|
33
|
+
# NameError: undefined local variable or method `a'...
|
34
|
+
class Runner
|
35
|
+
@@ruby = /(.+?)# =>([^\n]+\n)/m
|
36
|
+
@@irb = /((?:^>> [^\n]+\n)+)((?:(?!^(?:>>|=>))[^\n]+\n)*)(^=> [^\n]+)?/m
|
37
|
+
|
38
|
+
attr_reader :files
|
39
|
+
|
40
|
+
def initialize
|
41
|
+
@files = {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def run
|
45
|
+
parse
|
46
|
+
execute
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def parse
|
52
|
+
filename = lineno = in_comment = in_test = nil
|
53
|
+
|
54
|
+
ARGF.each do |line|
|
55
|
+
lineno, filename = 0, ARGF.filename if filename != ARGF.filename
|
56
|
+
lineno += 1
|
57
|
+
|
58
|
+
next in_comment = nil unless line.sub! /^ *# ?/, ''
|
59
|
+
in_comment = lineno if line =~ /^={1,6} \S/
|
60
|
+
in_comment ||= lineno
|
61
|
+
|
62
|
+
if in_test
|
63
|
+
in_test = false if line !~ /^(?: {2,}|$)/
|
64
|
+
else
|
65
|
+
in_test = true if line =~ /^ {2}\S/
|
66
|
+
end
|
67
|
+
|
68
|
+
line = "\n" unless in_test
|
69
|
+
((files[filename] ||= {})[in_comment] ||= '') << line if in_comment
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def execute
|
74
|
+
files.each_pair do |filename, lineno_and_lines|
|
75
|
+
class_name = File.basename(filename, '.rb')
|
76
|
+
class_name.gsub!(/(?:^|_)(\w+:{,2})/) { |r| r.capitalize }
|
77
|
+
class_name.gsub! /\W+/, ''
|
78
|
+
class_name = 'Rdoctest' if class_name.empty?
|
79
|
+
require_filename filename
|
80
|
+
|
81
|
+
test_class = Class.new Test::Unit::TestCase do
|
82
|
+
lineno_and_lines.each_pair do |lineno, lines|
|
83
|
+
next unless lines =~ /\S/ # /(?:^|# )=>/
|
84
|
+
lines.gsub! /^ /, ''
|
85
|
+
|
86
|
+
define_method "assert_eval" do |expected, result, filename, lineno|
|
87
|
+
if expected.gsub!(/\.{3,}/, '.*')
|
88
|
+
assertion, expected = 'match', /#{expected}/
|
89
|
+
else
|
90
|
+
assertion = 'equal'
|
91
|
+
end
|
92
|
+
|
93
|
+
instance_eval <<ASSERTION, filename, lineno
|
94
|
+
assert_#{assertion} #{expected.inspect}, #{result.inspect}
|
95
|
+
ASSERTION
|
96
|
+
end
|
97
|
+
|
98
|
+
define_method "test_line_#{lineno}" do
|
99
|
+
assertions = []
|
100
|
+
scanner = StringScanner.new lines
|
101
|
+
|
102
|
+
binding = send :binding
|
103
|
+
while scanner.skip_until(@@ruby)
|
104
|
+
code_lineno = lineno + scanner.pre_match.count("\n")
|
105
|
+
expected_lineno = code_lineno + scanner[1].count("\n")
|
106
|
+
|
107
|
+
result = eval scanner[1], binding #, filename, code_lineno
|
108
|
+
assert_eval scanner[2].strip, result.inspect, filename,
|
109
|
+
expected_lineno
|
110
|
+
end
|
111
|
+
|
112
|
+
scanner.pos = 0
|
113
|
+
binding = send :binding
|
114
|
+
while scanner.skip_until(@@irb)
|
115
|
+
code_lineno = lineno + scanner.pre_match.count("\n")
|
116
|
+
output_lineno = code_lineno + scanner[2].to_s.count("\n")
|
117
|
+
expected_lineno = output_lineno + scanner[3].to_s.count("\n")
|
118
|
+
|
119
|
+
begin
|
120
|
+
stdout, $stdout = $stdout, StringIO.new
|
121
|
+
result = eval scanner[1].gsub(/^>> /, ''), binding
|
122
|
+
rescue => e
|
123
|
+
puts "#{e.class}: #{e}"
|
124
|
+
ensure
|
125
|
+
$stdout, stdout = stdout, $stdout
|
126
|
+
end
|
127
|
+
|
128
|
+
stdout.rewind
|
129
|
+
output = stdout.read
|
130
|
+
assert_eval scanner[2], output, filename, output_lineno
|
131
|
+
|
132
|
+
if scanner[3]
|
133
|
+
expected = scanner[3].sub(/^=> /, '').strip
|
134
|
+
assert_eval expected, result.inspect, filename,
|
135
|
+
expected_lineno
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
Object.const_set "#{class_name}Test", test_class
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def require_filename filename
|
147
|
+
require filename.gsub(%r{^(?:lib)/|.rb$}, '') unless filename == '-'
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
Rdoctest::Runner.new.run
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/tasklib'
|
3
|
+
|
4
|
+
module Rdoctest
|
5
|
+
# See the README for more information.
|
6
|
+
class Task < Rake::TaskLib
|
7
|
+
# :enddoc:
|
8
|
+
attr_accessor :libs
|
9
|
+
attr_accessor :name
|
10
|
+
attr_accessor :options
|
11
|
+
attr_accessor :pattern
|
12
|
+
attr_accessor :ruby_opts
|
13
|
+
attr_accessor :test_files
|
14
|
+
attr_accessor :verbose
|
15
|
+
attr_accessor :warning
|
16
|
+
|
17
|
+
def initialize name = :doctest
|
18
|
+
@libs = %w(lib)
|
19
|
+
@name = name
|
20
|
+
@options = nil
|
21
|
+
@pattern = nil
|
22
|
+
@ruby_opts = []
|
23
|
+
@test_files = nil
|
24
|
+
@verbose = false
|
25
|
+
@warning = false
|
26
|
+
yield self if block_given?
|
27
|
+
@pattern = 'lib/**/*.rb' if @pattern.nil? && @test_files.nil?
|
28
|
+
define
|
29
|
+
end
|
30
|
+
|
31
|
+
def define
|
32
|
+
desc name ? "Run doctests for #{name}" : 'Run doctests'
|
33
|
+
task name do
|
34
|
+
ruby "#{run_code} #{ruby_opts_string} #{file_list_string}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def ruby_opts_string
|
41
|
+
opts = []
|
42
|
+
opts << %(-I"#{lib_path}") unless libs.empty?
|
43
|
+
opts << %(-w) if warning
|
44
|
+
opts.concat ruby_opts
|
45
|
+
opts.join ' '
|
46
|
+
end
|
47
|
+
|
48
|
+
def lib_path
|
49
|
+
libs.join File::PATH_SEPARATOR
|
50
|
+
end
|
51
|
+
|
52
|
+
def run_code
|
53
|
+
$LOAD_PATH.each do |path|
|
54
|
+
file = File.join path, 'rdoctest'
|
55
|
+
return file if File.executable?(file)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def file_list_string
|
60
|
+
file_list.join ' '
|
61
|
+
# Dir[*file_list].join ' '
|
62
|
+
end
|
63
|
+
|
64
|
+
def file_list
|
65
|
+
if ENV['TEST']
|
66
|
+
Dir[ENV['TEST']]
|
67
|
+
else
|
68
|
+
result = []
|
69
|
+
result += test_files.to_a if test_files
|
70
|
+
result << pattern if pattern
|
71
|
+
result
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def options_list
|
76
|
+
ENV['TESTOPTS'] || @options
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/rdoctest.rb
ADDED
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rdoctest
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Stephen Celis
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-12-05 00:00:00 -06:00
|
18
|
+
default_executable: rdoctest
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Scans RDoc text for examples and tests them.
|
22
|
+
email: stephen@stephencelis.com
|
23
|
+
executables:
|
24
|
+
- rdoctest
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- README.rdoc
|
29
|
+
files:
|
30
|
+
- README.rdoc
|
31
|
+
- Rakefile
|
32
|
+
- lib/rdoctest/runner.rb
|
33
|
+
- lib/rdoctest/task.rb
|
34
|
+
- lib/rdoctest/version.rb
|
35
|
+
- lib/rdoctest.rb
|
36
|
+
- bin/rdoctest
|
37
|
+
has_rdoc: true
|
38
|
+
homepage: http://github.com/stephencelis/rdoctest
|
39
|
+
licenses: []
|
40
|
+
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options:
|
43
|
+
- --main
|
44
|
+
- README.rdoc
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
segments:
|
53
|
+
- 0
|
54
|
+
version: "0"
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.7
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: A doctest for Ruby.
|
70
|
+
test_files: []
|
71
|
+
|