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 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
+