rdoctest 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,5 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
2
+ $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), 'bin'))
3
+ require 'rdoctest/task'
4
+ Rdoctest::Task.new
5
+ task :default => :doctest
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
@@ -0,0 +1,9 @@
1
+ module Rdoctest
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ PATCH = 1
6
+
7
+ VERSION = [MAJOR, MINOR, PATCH].compact.join('.').freeze
8
+ end
9
+ end
data/lib/rdoctest.rb ADDED
@@ -0,0 +1,2 @@
1
+ module Rdoctest # :nodoc:
2
+ end
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
+