Class: DocTest

Inherits:
Object
  • Object
show all
Defined in:
lib/doctest.rb

Constant Summary collapse

CODE_REGEX =
Regexp.new(/^(>>|irb.*?>) (.*)/)
RESULT_REGEX =
Regexp.new(/^=> (.*)/)
EXCEPTION_REGEX =
Regexp.new(/^([A-Z][A-Za-z0-9]*):/)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.runObject



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/doctest.rb', line 175

def DocTest.run
 # parse command line--currently just 'filename' or 'directory name'
 runner = DocTest.new
 if File.directory? ARGV[0] || ''
   ruby_file_names = runner.get_ruby_files(ARGV[0])
 elsif File.exist? ARGV[0] || ''
   ruby_file_names = [ARGV[0]]
 else
   ruby_file_names = runner.get_ruby_files('.')
 end
 
 total_report = "Looking for doctests in a total of #{ruby_file_names.length} possible files\n"
 total_files, total_tests, total_succeeded, total_failed = 0, 0, 0, 0
 ruby_file_names.each do |ruby_file_name|
   tests, succeeded, failed, report = runner.process_ruby_file(ruby_file_name)
   total_files += 1 if tests > 0
   total_tests += tests
   total_succeeded += succeeded
   total_failed += failed
   total_report << report << "\n" unless report.empty?
 end
 total_report << "Total files: #{total_files}, total tests: #{total_tests}, assertions succeeded: #{total_succeeded}, assertions failed: #{total_failed}"
 puts total_report
end

Instance Method Details

#dbgObject

#doctest should match with hashes >> 2=>2, 3=> 3, 4=>4,5=>5

> 1=>1, 2=>2, 3=>3, 4=>4

now test with different ordered hashes >> 2=>2, 3=> 3, 4=>4,5=>5

> 1=>1, 2=>2, 3=>3, 5=>5

>> 2=>2, 3=> 3, 4=>4,5=>5

> 1=>“:0x123456”, 2=>2, 3=>3, 5=>5



93
94
95
# File 'lib/doctest.rb', line 93

def dbg
   require 'rubygems'; require 'ruby-debug'; debugger
end

#failure_report(statement, expected_result, result) ⇒ Object



76
77
78
79
80
81
# File 'lib/doctest.rb', line 76

def failure_report(statement, expected_result, result)
  report = "\n FAILED" #add line number logic here
  report << " Code: " << statement << "\n"
  report << " Expected: " << expected_result << "\n"
  report << " But got: " << result
end

#get_ruby_files(dir_name) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/doctest.rb', line 48

def get_ruby_files(dir_name)
  ruby_file_names = []

  Dir.foreach(dir_name) do |file_name|
    unless file_name == '.' || file_name == '..'
      full_name = File.join(dir_name, file_name)
      if /.*\.rb$/ =~ full_name
        ruby_file_names << full_name
      elsif File.directory? full_name
        sub_files = get_ruby_files(full_name)
        ruby_file_names.concat(sub_files) unless sub_files.empty?
      end
    end
  end

  ruby_file_names
end

#normalize_result(input) ⇒ Object

#doctest normalize substring >> a = DocTest.new

> #<DocTest:0x37012c>

>> a.normalize_result(‘0xtion:0x1876bc0 @@p’)

> “0xtion:0xXXXXXXXX @@p”



72
73
74
# File 'lib/doctest.rb', line 72

def normalize_result(input)
  input.gsub(/:0x([a-f0-9]){5,8}/, ':0xXXXXXXXX')  # makes them all 8 digits long
end

#process_ruby_file(file_name) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/doctest.rb', line 143

def process_ruby_file(file_name)
  tests, succeeded, failed = 0, 0, 0
  file_report = ''
  code = File.read file_name

  startup_code_for_this_file = code.scan(/begin\s#setup_doctest once_per_file(.*?)=end/m)

  if startup_code_for_this_file.length > 0
    raise 'can only do one_time_file_setup declaration once' if startup_code_for_this_file.length > 1 or startup_code_for_this_file[0].length > 1
    startup_code_for_this_file = startup_code_for_this_file[0][0]
    begin
    	eval startup_code_for_this_file, BINDING
    rescue Exception => e
	print "Uh oh unable to execute startup code for #{file_name}...continuing #{e}\n"
    end
  end

  # todo would be nice to have multiple tests in the same comment block
  # so a scan + sub scan for doctests
  code.scan(/=begin\s#doctest([^\n]*)\n(.*?)=end/m) do |doc_test| # could do--replace default named ones with their line number :)
    require file_name # might as well have its functions available to itself :P
    # todo could tear out anything loaded after each file, I suppose, as active support does
    file_report << "\n Testing '#{doc_test[0]}'..."
    passed, wrong, report = run_doc_tests(doc_test[1])
    file_report += (wrong == 0 ? "OK" : report)
    tests += 1
    succeeded += passed
    failed += wrong
  end
  file_report = "Processing '#{file_name}' from current directory " + file_report unless file_report.empty?
  return tests, succeeded, failed, file_report
end

#run_doc_tests(doc_test) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/doctest.rb', line 97

def run_doc_tests(doc_test)
  statement, report = '', ''
  wrong, passed = 0, 0
  doc_test.split("\n").each do |line|
    case line
      when CODE_REGEX
        statement << CODE_REGEX.match(line)[2]
      when RESULT_REGEX, EXCEPTION_REGEX
        if line =~ RESULT_REGEX
expected_result_string = normalize_result(RESULT_REGEX.match(line)[1])
 else
raise unless line =~ EXCEPTION_REGEX
expected_result_string = $1
 end
	
        begin
result_we_got = eval(statement, BINDING)
 rescue Exception => e
result_we_got = e.class
        end
     
        they_match = false 
        if result_we_got.class.ancestors.include? Hash
          # change them to 'kind of real' hashes, so that we cancompare them and have comparison work--hashes sometimes display in different orders when printed
          expected_result = eval(expected_result_string, BINDING)
   if eval(normalize_result(result_we_got.inspect), BINDING) == expected_result # todo some tests for this with whack-o stuff thrown in  :)
# the Hashes matched, string-wise
they_match = true
   end
        end

 they_match = true if expected_result_string =~ /#doctest_fail_ok/
        result_string = normalize_result(result_we_got.inspect)
 they_match = true if result_string == expected_result_string
        unless they_match
          report << failure_report(statement, expected_result_string, result_string)
          wrong += 1
        else
          passed += 1
        end
        statement = '' # reset it for the next round
    end
  end
  return passed, wrong, report
end