rubydoctest 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ == 0.2.1 2008-05-26
2
+
3
+ * Adding self-doctesting doctests
4
+
5
+ == 0.2.0 2008-05-25
6
+
7
+ * Initial changes by Dr Nic
8
+ * __FILE__ is changed to the explicit file path (since it was eval'ing to the code, not the test file)
9
+ * Starting to write doctests for the project itself
10
+
11
+ == 0.1.0 2008-05-25
12
+
13
+ * 1 major enhancement:
14
+ * Initial release from Tom's personal stash of code
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 FIXME full name
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,31 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ PostInstall.txt
5
+ README.txt
6
+ Rakefile
7
+ bin/rubydoctest
8
+ config/hoe.rb
9
+ config/requirements.rb
10
+ lib/rubydoctest.rb
11
+ lib/rubydoctest/version.rb
12
+ rubydoctest.gemspec
13
+ script/console
14
+ script/destroy
15
+ script/generate
16
+ script/txt2html
17
+ setup.rb
18
+ tasks/deployment.rake
19
+ tasks/doctests.rake
20
+ tasks/environment.rake
21
+ tasks/website.rake
22
+ test/doctest/file_relative.doctest
23
+ test/doctest/runner.doctest
24
+ test/doctest/simple.doctest
25
+ test/test_helper.rb
26
+ test/test_rubydoctest.rb
27
+ website/index.html
28
+ website/index.txt
29
+ website/javascripts/rounded_corners_lite.inc.js
30
+ website/stylesheets/screen.css
31
+ website/template.html.erb
@@ -0,0 +1,8 @@
1
+
2
+ rubydoctest comes as an executable that takes a file or directory:
3
+
4
+ rubydoctest .
5
+ rubydoctest some/path/to/tests
6
+ rubydoctest simple.doctest
7
+
8
+
@@ -0,0 +1,109 @@
1
+ = rubydoctest
2
+
3
+ * http://github.com/tablatom/rubydoctest
4
+
5
+ == DESCRIPTION:
6
+
7
+ Ruby version of Python's doctest tool, but a bit different.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * FIX (list of features or problems)
12
+
13
+ == SYNOPSIS:
14
+
15
+ rubydoctest comes as an executable that takes a file or directory:
16
+
17
+ rubydoctest .
18
+ rubydoctest simple.doctest
19
+
20
+ == EXAMPLE:
21
+
22
+ Here is an example doctest file (say called simple.doctest):
23
+
24
+ # Simple test of RubyDocTest
25
+
26
+ This is an example test
27
+
28
+ >> 1 + 2
29
+ => 3
30
+
31
+ And here's a test that will fail
32
+
33
+ >> 1 + 2
34
+ => 4
35
+
36
+ Test a some multiline statements
37
+
38
+ >>
39
+ class Person
40
+ attr_accessor :name
41
+ end
42
+
43
+ >> Person
44
+ => Person
45
+ >> p = Person.new
46
+ >> p.name = "Tom"
47
+ >> p.name
48
+ => "Tom"
49
+
50
+
51
+ >> "a
52
+ b"
53
+ => "a\nb"
54
+
55
+ >> 1 +
56
+ ?> 2
57
+ => 3
58
+
59
+ == INSTALL:
60
+
61
+ Major releases:
62
+
63
+ sudo gem install rubydoctest
64
+
65
+ Build from source:
66
+
67
+ git clone git://github.com/tablatom/rubydoctest.git
68
+ cd rubydoctest
69
+ rake manifest && rake install
70
+
71
+ == SELF-DOCTESTING:
72
+
73
+ Ruby DocTest uses itself to test and document itself.
74
+
75
+ rake test:doctest
76
+
77
+ In development of Ruby DocTest, there is an autotest system in-built
78
+ using script/rstakeout
79
+
80
+ rake test:doctest:auto
81
+
82
+ == TEXTMATE BUNDLE:
83
+
84
+ See http://github.com/drnic/ruby-doctest-tmbundle
85
+
86
+ == LICENSE:
87
+
88
+ (The MIT License)
89
+
90
+ Copyright (c) 2008 FIX
91
+
92
+ Permission is hereby granted, free of charge, to any person obtaining
93
+ a copy of this software and associated documentation files (the
94
+ 'Software'), to deal in the Software without restriction, including
95
+ without limitation the rights to use, copy, modify, merge, publish,
96
+ distribute, sublicense, and/or sell copies of the Software, and to
97
+ permit persons to whom the Software is furnished to do so, subject to
98
+ the following conditions:
99
+
100
+ The above copyright notice and this permission notice shall be
101
+ included in all copies or substantial portions of the Software.
102
+
103
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
104
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
105
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
106
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
107
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
108
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
109
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ USAGE = "USAGE: rdoctest [ --html ] [ --trace ] [ --debugger ] <path>"
4
+
5
+ SUFFIX = "{r,}doctest"
6
+
7
+ file = ARGV.pop or (puts USAGE; exit 1)
8
+
9
+ requires = ['rubygems', File.dirname(__FILE__) + "/../lib/rubydoctest"]
10
+ requires << 'ruby-debug' if ARGV.include?("--debugger")
11
+
12
+ command = if ARGV.include?("--rcov")
13
+ args = (ARGV - ["--rcov"]).map { |a| %("#{a}") }.join(' ')
14
+ "rcov -x /gems/,rdoctest"
15
+ else
16
+ "ruby"
17
+ end
18
+ trace = "RubyDocTest.trace = true;" if ARGV.include?("--trace")
19
+
20
+ requires = requires.map {|lib| "require '#{lib}'; "}.join
21
+
22
+ puts "<html><body><pre>" if ARGV.include?("--html")
23
+
24
+ files = if File.directory?(file)
25
+ Dir["#{file}/**/*.#{SUFFIX}"]
26
+ else
27
+ [file]
28
+ end
29
+
30
+ files.reverse_each do |f|
31
+ puts "*** " + f.sub("#{file}/", "").sub(".#{SUFFIX}", ""), "" if files.length > 1
32
+ system %(#{command} -e "#{requires} #{trace} RubyDocTest.new(File.read('#{f}'), '#{f}').run")
33
+ end
34
+
35
+ puts "</pre></body></html>" if ARGV.include?("--html")
@@ -0,0 +1,74 @@
1
+ require 'rubydoctest/version'
2
+
3
+ AUTHOR = ['Tom Locke', 'Dr Nic Williams'] # can also be an array of Authors
4
+ EMAIL = "drnicwilliams@gmail.com"
5
+ DESCRIPTION = "Ruby version of Python's doctest tool, but a bit different."
6
+ GEM_NAME = 'rubydoctest' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'rubydoctest' # The unix name for your project
8
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
+ EXTRA_DEPENDENCIES = [
11
+ # ['activesupport', '>= 1.3.1']
12
+ ] # An array of rubygem dependencies [name, version]
13
+
14
+ @config_file = "~/.rubyforge/user-config.yml"
15
+ @config = nil
16
+ RUBYFORGE_USERNAME = "unknown"
17
+ def rubyforge_username
18
+ unless @config
19
+ begin
20
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
21
+ rescue
22
+ puts <<-EOS
23
+ ERROR: No rubyforge config file found: #{@config_file}
24
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
25
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
26
+ EOS
27
+ exit
28
+ end
29
+ end
30
+ RUBYFORGE_USERNAME.replace @config["username"]
31
+ end
32
+
33
+
34
+ REV = nil
35
+ # UNCOMMENT IF REQUIRED:
36
+ # REV = YAML.load(`svn info`)['Revision']
37
+ VERS = Rubydoctest::VERSION::STRING + (REV ? ".#{REV}" : "")
38
+ RDOC_OPTS = ['--quiet', '--title', 'rubydoctest documentation',
39
+ "--opname", "index.html",
40
+ "--line-numbers",
41
+ "--main", "README",
42
+ "--inline-source"]
43
+
44
+ class Hoe
45
+ def extra_deps
46
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
47
+ @extra_deps
48
+ end
49
+ end
50
+
51
+ # Generate all the Rake tasks
52
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
53
+ $hoe = Hoe.new(GEM_NAME, VERS) do |p|
54
+ p.author = AUTHOR
55
+ p.email = EMAIL
56
+ p.description = DESCRIPTION
57
+ p.summary = DESCRIPTION
58
+ p.url = HOMEPATH
59
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
60
+ p.test_globs = ["test/**/test_*.rb"]
61
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
62
+
63
+ # == Optional
64
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
65
+ #p.extra_deps = EXTRA_DEPENDENCIES
66
+
67
+ #p.spec_extras = {} # A hash of extra values to set in the gemspec.
68
+ end
69
+
70
+ CHANGES = $hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
71
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
72
+ $hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
73
+ $hoe.rsync_args = '-av --delete --ignore-errors'
74
+ $hoe.spec.post_install_message = File.open(File.dirname(__FILE__) + "/../PostInstall.txt").read rescue ""
@@ -0,0 +1,15 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
@@ -0,0 +1,271 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'irb'
5
+
6
+ class RubyDocTest
7
+
8
+ class << self
9
+ attr_accessor :trace
10
+ end
11
+
12
+ PROMPT_RX = /^[>?]>( |\s*$)/
13
+
14
+ RESULT_RX = /^=> /
15
+
16
+ CODE_LINE_RX = /^( |\t)/
17
+
18
+ def initialize(src, file_name)
19
+ @passed = 0
20
+ @block_count = 0
21
+ @failures = []
22
+ @src_lines = src.split("\n")
23
+ @line_num = 0
24
+ @file_name = file_name
25
+
26
+ # next_line # get first line
27
+ end
28
+
29
+ def run
30
+ run_file
31
+ print_report
32
+ end
33
+
34
+ attr_accessor :passed, :failures, :current_line, :src_lines, :line_num, :block_count
35
+
36
+ def environment
37
+ TOPLEVEL_BINDING
38
+ end
39
+
40
+ def next_line
41
+ @line_num += 1
42
+ @current_line = src_lines.shift
43
+ end
44
+
45
+ def next_line?
46
+ src_lines.any?
47
+ end
48
+
49
+ def strip_prompt(s)
50
+ s.sub(PROMPT_RX, "")
51
+ end
52
+
53
+ def result_start?(s=current_line)
54
+ s =~ RESULT_RX
55
+ end
56
+
57
+ def string_result_start?(s=current_line)
58
+ s =~ /^=>""/
59
+ end
60
+
61
+ def statement_start?(s=current_line)
62
+ s =~ PROMPT_RX
63
+ end
64
+
65
+ def strip_result_marker(s=current_line)
66
+ s.sub(RESULT_RX, "")
67
+ end
68
+
69
+ def normalize_result(s)
70
+ s.gsub(/:0x[a-f0-9]{8}>/, ':0xXXXXXXXX>').strip
71
+ end
72
+
73
+ def code_line?(s=current_line)
74
+ s =~ CODE_LINE_RX
75
+ end
76
+
77
+ def code_block_start?(s=current_line)
78
+ l = unindent_code_line
79
+ code_line? && (statement_start?(l) || irb_interrupt?(l))
80
+ end
81
+
82
+ def blank_line?(s=current_line)
83
+ s =~ /^\s*$/
84
+ end
85
+
86
+ def irb_interrupt?(line)
87
+ line =~ /!!!/
88
+ end
89
+
90
+ def run_file
91
+ # run_code_block if code_block_start?
92
+ while next_line
93
+ run_code_block if code_block_start?
94
+ end
95
+ failures.length == 0
96
+ end
97
+
98
+ def unindent_code_line(s=current_line)
99
+ s.sub(CODE_LINE_RX, "")
100
+ end
101
+
102
+
103
+ def get_code_lines
104
+ lines = []
105
+ while blank_line? || code_line?
106
+ lines << [unindent_code_line, line_num]
107
+ next_line
108
+ end
109
+ lines.pop while blank_line?(lines.last)
110
+ lines
111
+ end
112
+
113
+
114
+ def run_code_block
115
+ self.block_count += 1
116
+
117
+ lines = get_code_lines
118
+
119
+ reading = :statement
120
+
121
+ result = nil
122
+ statement = ""
123
+ statement_line = lines.first.last
124
+ lines.each do |line, line_num|
125
+
126
+ if irb_interrupt?(line)
127
+ evaluate(statement, statement_line) unless statement == ""
128
+
129
+ puts statement unless statement.blank?
130
+ puts "=> #{result}" unless result.blank?
131
+ puts
132
+
133
+ start_irb
134
+ line = nil
135
+
136
+ elsif string_result_start?(line)
137
+ reading = :string_result
138
+ line = nil
139
+ result = ""
140
+
141
+ elsif result_start?(line)
142
+ reading = :ruby_result
143
+ line = strip_result_marker(line)
144
+ result = ""
145
+
146
+ elsif statement_start?(line)
147
+ if result
148
+ # start of a new statement
149
+ if reading == :string_result
150
+ # end of a string result statement
151
+ result.chomp!
152
+ run_statement(statement, result, statement_line, true)
153
+ else
154
+ run_statement(statement, result, statement_line)
155
+ end
156
+ statement_line = line_num
157
+ statement = ""
158
+ result = nil
159
+ end
160
+ reading = :statement
161
+ end
162
+
163
+ if reading == :statement
164
+ # The statement has a prompt on every line
165
+ if line
166
+ line = strip_prompt(line)
167
+ statement << line + "\n"
168
+ end
169
+ else
170
+ # There's only one result marker - stripped above
171
+ result << line + "\n" if line
172
+ end
173
+
174
+ end
175
+
176
+ if result.nil?
177
+ # The block ends with a statement - just evaluate it
178
+ evaluate(statement, statement_line)
179
+ else
180
+ run_statement(statement, result, statement_line)
181
+ end
182
+ end
183
+
184
+
185
+ def start_irb
186
+ IRB.init_config(nil)
187
+ IRB.conf[:PROMPT_MODE] = :SIMPLE
188
+ irb = IRB::Irb.new(IRB::WorkSpace.new(environment))
189
+ IRB.conf[:MAIN_CONTEXT] = irb.context
190
+ irb.eval_input
191
+ end
192
+
193
+
194
+ def run_statement(statement, expected_result, statement_line, string_comparison=false)
195
+ actual_result = evaluate(statement, statement_line)
196
+
197
+ if result_matches?(expected_result, actual_result, string_comparison)
198
+ self.passed += 1
199
+ else
200
+ actual_result = actual_result.inspect unless string_comparison
201
+ failures << [statement, actual_result, expected_result, statement_line]
202
+ end
203
+ end
204
+
205
+
206
+ def result_matches?(expected_result, actual_result, string_comparison)
207
+ if string_comparison
208
+ actual_result == expected_result
209
+ else
210
+ actual_result = actual_result.inspect
211
+ normalize_result(expected_result) == normalize_result(actual_result) or
212
+ # If the expected result looks like a literal, see if they eval to equal objects - this will often fail
213
+ if expected_result =~ /^[:\[{A-Z'"%\/]/
214
+ begin
215
+ eval(expected_result) == eval(actual_result)
216
+ rescue Exception
217
+ false
218
+ end
219
+ end
220
+ end
221
+ end
222
+
223
+
224
+ def evaluate(statement, line_num)
225
+ statement.gsub!("__FILE__", @file_name.inspect)
226
+ eval(statement, environment, __FILE__, __LINE__)
227
+ rescue SyntaxError => e
228
+ puts "Syntax error in statement on line #{line_num}:"
229
+ puts indent(statement)
230
+ puts e.to_s
231
+ puts
232
+ exit 1
233
+ rescue Exception => e
234
+ puts "Exception in statement on line #{line_num}:"
235
+ puts indent(statement)
236
+ puts e.backtrace
237
+
238
+ if RubyDocTest.trace
239
+ raise
240
+ else
241
+ puts e.to_s
242
+ puts
243
+ exit 1
244
+ end
245
+ end
246
+
247
+
248
+ def number_run
249
+ passed + failures.length
250
+ end
251
+
252
+ def indent(s, level=4)
253
+ spaces = " " * level
254
+ spaces + s.split("\n").join("\n#{spaces}")
255
+ end
256
+
257
+ def print_report
258
+ statements_per_block = number_run.to_f / block_count
259
+ puts("%d blocks, %d tests (avg. %.1f/block), %d failures\n\n" %
260
+ [block_count, number_run, statements_per_block, failures.length])
261
+
262
+ failures.each do |statement, actual, expected, lnum|
263
+ puts "Failure line #{lnum}"
264
+ puts " Statement:", indent(statement)
265
+ puts " Expected:", indent(expected)
266
+ puts " Got:\n", indent(actual)
267
+ puts
268
+ end
269
+ end
270
+
271
+ end