wrong 0.4.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/README.markdown +300 -0
  2. data/lib/wrong.rb +27 -0
  3. data/lib/wrong/adapters/minitest.rb +14 -0
  4. data/lib/wrong/adapters/rspec.rb +21 -0
  5. data/lib/wrong/adapters/test_unit.rb +9 -0
  6. data/lib/wrong/assert.rb +105 -0
  7. data/lib/wrong/chunk.rb +233 -0
  8. data/lib/wrong/close_to.rb +9 -0
  9. data/lib/wrong/config.rb +29 -0
  10. data/lib/wrong/d.rb +42 -0
  11. data/lib/wrong/failure_message.rb +43 -0
  12. data/lib/wrong/helpers.rb +66 -0
  13. data/lib/wrong/irb.rb +16 -0
  14. data/lib/wrong/message/array_diff.rb +69 -0
  15. data/lib/wrong/message/string_comparison.rb +88 -0
  16. data/lib/wrong/message/test_context.rb +28 -0
  17. data/lib/wrong/rainbow.rb +127 -0
  18. data/lib/wrong/ruby2ruby_patch.rb +37 -0
  19. data/lib/wrong/sexp_ext.rb +49 -0
  20. data/lib/wrong/version.rb +3 -0
  21. data/test/adapters/minitest_test.rb +97 -0
  22. data/test/adapters/rspec1/failing_spec.rb +23 -0
  23. data/test/adapters/rspec2/failing_spec.rb +26 -0
  24. data/test/adapters/rspec_test.rb +104 -0
  25. data/test/adapters/test_unit_test.rb +59 -0
  26. data/test/assert_advanced_test.rb +51 -0
  27. data/test/assert_test.rb +76 -0
  28. data/test/capturing_test.rb +59 -0
  29. data/test/chunk_test.rb +264 -0
  30. data/test/close_to_test.rb +39 -0
  31. data/test/config_test.rb +89 -0
  32. data/test/d_test.rb +64 -0
  33. data/test/failure_message_test.rb +40 -0
  34. data/test/failures_test.rb +157 -0
  35. data/test/message/array_diff_test.rb +79 -0
  36. data/test/message/test_context_test.rb +69 -0
  37. data/test/rescuing_test.rb +17 -0
  38. data/test/separate.rb +4 -0
  39. data/test/sexp_ext_test.rb +80 -0
  40. data/test/string_comparison_test.rb +159 -0
  41. data/test/suite.rb +7 -0
  42. data/test/test_helper.rb +64 -0
  43. data/test/wrong_test.rb +60 -0
  44. metadata +215 -0
@@ -0,0 +1,233 @@
1
+ require 'ruby_parser'
2
+ require 'ruby2ruby'
3
+
4
+ begin
5
+ require "ParseTree"
6
+ rescue LoadError => e
7
+ raise e unless e.message == "no such file to load -- ParseTree"
8
+ end
9
+
10
+ begin
11
+ require "sourcify"
12
+ rescue LoadError => e
13
+ raise e unless e.message == "no such file to load -- sourcify"
14
+ end
15
+
16
+ require "wrong/config"
17
+ require "wrong/sexp_ext"
18
+
19
+ module Wrong
20
+ class Chunk
21
+ def self.from_block(block, depth = 0)
22
+
23
+ as_proc = block.to_proc
24
+ file, line =
25
+ if as_proc.respond_to? :source_location
26
+ # in Ruby 1.9, or with Sourcify, it reads the source location from the block
27
+ as_proc.source_location
28
+ else
29
+ # in Ruby 1.8, it reads the source location from the call stack
30
+ caller[depth].split(":")
31
+ end
32
+
33
+ new(file, line, block)
34
+ end
35
+
36
+ attr_reader :file, :line_number, :block
37
+
38
+ # line parameter is 1-based
39
+ def initialize(file, line_number, block = nil)
40
+ @file = file
41
+ @line_number = line_number.to_i
42
+ @block = block
43
+ end
44
+
45
+ def line_index
46
+ @line_number - 1
47
+ end
48
+
49
+ def location
50
+ "#{@file}:#{@line_number}"
51
+ end
52
+
53
+ def sexp
54
+ @sexp ||= build_sexp
55
+ end
56
+
57
+ def build_sexp
58
+ sexp = begin
59
+ unless @block.nil? or @block.is_a?(String) or !Object.const_defined?(:Sourcify)
60
+ # first try sourcify
61
+ @block.to_sexp[3] # the [3] is to strip out the "proc {" sourcify adds to everything
62
+ end
63
+ rescue ::Sourcify::MultipleMatchingProcsPerLineError, Racc::ParseError, Errno::ENOENT => e
64
+ # fall through
65
+ end
66
+
67
+ # next try glomming
68
+ sexp ||= glom(if @file == "(irb)"
69
+ IRB.CurrentContext.all_lines
70
+ else
71
+ read_source_file(@file)
72
+ end)
73
+ end
74
+
75
+ def read_source_file(file, dir = ".")
76
+ File.read "#{dir}/#{file}"
77
+
78
+ rescue Errno::ENOENT, Errno::EACCES => e
79
+ # we may be in a chdir underneath where the file is, so move up one level and try again
80
+ parent = "#{dir}/..".gsub(/^(\.\/)*/, '')
81
+ if File.expand_path(dir) == File.expand_path(parent)
82
+ raise Errno::ENOENT, "couldn't find #{file}"
83
+ end
84
+ read_source_file(file, parent)
85
+
86
+ end
87
+
88
+ # Algorithm:
89
+ # * try to parse the starting line
90
+ # * if it parses OK, then we're done!
91
+ # * if not, then glom the next line and try again
92
+ # * repeat until it parses or we're out of lines
93
+ def glom(source)
94
+ lines = source.split("\n")
95
+ @parser ||= RubyParser.new
96
+ @chunk = nil
97
+ c = 0
98
+ sexp = nil
99
+ while sexp.nil? && line_index + c < lines.size
100
+ begin
101
+ @chunk = lines[line_index..line_index+c].join("\n")
102
+ sexp = @parser.parse(@chunk)
103
+ rescue Racc::ParseError => e
104
+ # loop and try again
105
+ c += 1
106
+ end
107
+ end
108
+ sexp
109
+ end
110
+
111
+ # The claim is the part of the assertion inside the curly braces.
112
+ # E.g. for "assert { x == 5 }" the claim is "x == 5"
113
+ def claim
114
+ sexp()
115
+
116
+ if @sexp.nil?
117
+ raise "Could not parse #{location}"
118
+ else
119
+ assertion = @sexp.assertion
120
+ statement = assertion && assertion[3]
121
+ if statement.nil?
122
+ @sexp
123
+ # raise "Could not find assertion in #{location}\n\t#{@chunk.strip}\n\t#{@sexp}"
124
+ else
125
+ statement
126
+ end
127
+ end
128
+ end
129
+
130
+ def code
131
+ self.claim.to_ruby
132
+ rescue => e
133
+ # note: this is untested; it's to recover from when we can't locate the code
134
+ message = "Failed assertion at #{caller[depth + 2]} [couldn't retrieve source code due to #{e.inspect}]"
135
+ raise failure_class.new(message)
136
+ end
137
+
138
+ def parts(sexp = nil)
139
+ if sexp.nil?
140
+ parts(self.claim).compact.uniq
141
+ else
142
+ # todo: extract some of this into Sexp
143
+ parts_list = []
144
+ begin
145
+ unless sexp.first == :arglist
146
+ code = sexp.to_ruby.strip
147
+ parts_list << code unless code == "" || parts_list.include?(code)
148
+ end
149
+ rescue => e
150
+ puts "#{e.class}: #{e.message}"
151
+ puts e.backtrace.join("\n")
152
+ end
153
+
154
+ if sexp.first == :iter
155
+ sexp.delete_at(1) # remove the method-call-sans-block subnode
156
+ end
157
+
158
+ sexp.each do |sub|
159
+ if sub.is_a?(Sexp)
160
+ parts_list += parts(sub)
161
+ end
162
+ end
163
+
164
+ parts_list
165
+ end
166
+ end
167
+
168
+ def details
169
+ require "wrong/rainbow" if Wrong.config[:color]
170
+ s = ""
171
+ parts = self.parts
172
+ parts.shift # remove the first part, since it's the same as the code
173
+
174
+ details = []
175
+
176
+ if parts.size > 0
177
+ parts.each do |part|
178
+ begin
179
+ value = eval(part, block.binding)
180
+ unless part == value.inspect # this skips literals or tautologies
181
+ if part =~ /\n/m
182
+ part.gsub!(/\n/, newline(2))
183
+ part += newline(3)
184
+ end
185
+ value = indent_all(3, value.inspect)
186
+ if Wrong.config[:color]
187
+ part = part.color(:blue)
188
+ value = value.color(:magenta)
189
+ end
190
+ details << indent(2, part, " is ", value)
191
+ end
192
+ rescue Exception => e
193
+ raises = "raises #{e.class}"
194
+ if Wrong.config[:color]
195
+ part = part.color(:blue)
196
+ raises = raises.bold.color(:red)
197
+ end
198
+ formatted_exeption = if e.message and e.message != e.class.to_s
199
+ indent(2, part, " ", raises, ": ", indent_all(3, e.message))
200
+ else
201
+ indent(2, part, " ", raises)
202
+ end
203
+ details << formatted_exeption
204
+ end
205
+ end
206
+ end
207
+
208
+ details.uniq!
209
+ if details.empty?
210
+ ""
211
+ else
212
+ "\n" + details.join("\n") + "\n"
213
+ end
214
+
215
+ end
216
+
217
+ private
218
+
219
+ def indent(indent, *s)
220
+ "#{" " * indent}#{s.join('')}"
221
+ end
222
+
223
+ def newline(indent)
224
+ "\n" + self.indent(indent)
225
+ end
226
+
227
+ def indent_all(amount, s)
228
+ s.gsub("\n", "\n#{indent(amount)}")
229
+ end
230
+
231
+ end
232
+
233
+ end
@@ -0,0 +1,9 @@
1
+ module Wrong
2
+ module CloseTo
3
+ def close_to?(other, tolerance = 0.001)
4
+ (self.to_f - other.to_f).abs < tolerance
5
+ end
6
+ end
7
+ Float.send :include, CloseTo
8
+ Fixnum.send :include, CloseTo
9
+ end
@@ -0,0 +1,29 @@
1
+ module Wrong
2
+ def self.config
3
+ @config ||= Config.new
4
+ end
5
+
6
+ class Config < Hash
7
+ def alias_assert(method_name)
8
+ Wrong::Assert.send(:alias_method, method_name, :assert)
9
+ self.assert_method_names << method_name.to_sym unless self.assert_method_names.include?(method_name)
10
+ end
11
+
12
+ def alias_deny(method_name)
13
+ Wrong::Assert.send(:alias_method, method_name, :deny)
14
+ self.deny_method_names << method_name.to_sym unless self.deny_method_names.include?(method_name)
15
+ end
16
+
17
+ def assert_method_names
18
+ (self[:assert_method] ||= [:assert])
19
+ end
20
+
21
+ def deny_method_names
22
+ (self[:deny_method] ||= [:deny])
23
+ end
24
+
25
+ def assert_methods
26
+ assert_method_names + deny_method_names
27
+ end
28
+ end
29
+ end
data/lib/wrong/d.rb ADDED
@@ -0,0 +1,42 @@
1
+ require "sexp"
2
+ require "wrong/chunk"
3
+
4
+ class ::Sexp < ::Array
5
+ def d?
6
+ is_a?(Sexp) &&
7
+ (self[0] == :iter) &&
8
+ (self[1][0] == :call) &&
9
+ (self[1][2] == :d)
10
+ end
11
+
12
+ end
13
+
14
+ module Wrong
15
+ module D
16
+ def d(*args, &block)
17
+ called_from = caller.first.split(':')
18
+ chunk = Chunk.from_block(block, 1)
19
+ sexp = chunk.sexp
20
+
21
+ # look for a "d" inside the block
22
+ sexp.each_subexp do |subexp|
23
+ if subexp.d?
24
+ sexp = subexp[3] # swap in the block part of the nested d call
25
+ end
26
+ end
27
+
28
+ code = sexp.to_ruby
29
+ value = eval(code, block.binding, called_from[0], called_from[1].to_i).inspect
30
+
31
+ if Wrong.config[:color]
32
+ require "wrong/rainbow"
33
+ code = code.color(:blue)
34
+ value = value.color(:magenta)
35
+ end
36
+
37
+ puts [code, "is", value].join(" ")
38
+ end
39
+
40
+ extend D # this allows you to call Wrong::D.d if you like
41
+ end
42
+ end
@@ -0,0 +1,43 @@
1
+ module Wrong
2
+ class FailureMessage
3
+ @@formatters = []
4
+
5
+ def self.register_formatter(formatter)
6
+ @@formatters << formatter
7
+ end
8
+
9
+ def self.formatters
10
+ @@formatters
11
+ end
12
+
13
+ def self.formatter_for(predicate)
14
+ @@formatters.each do |formatter_class|
15
+ formatter = formatter_class.new(predicate)
16
+ if formatter.match?
17
+ return formatter
18
+ end
19
+ end
20
+ nil
21
+ end
22
+
23
+ class Formatter
24
+ def self.register
25
+ Wrong::FailureMessage.register_formatter(self)
26
+ end
27
+
28
+ attr_reader :predicate
29
+
30
+ def initialize(predicate)
31
+ @predicate = predicate
32
+ end
33
+
34
+ def describe(valence)
35
+
36
+ end
37
+
38
+ def match?
39
+ false
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,66 @@
1
+ module Wrong
2
+ module Helpers
3
+
4
+ # Executes a block that is expected to raise an exception. Returns that exception, or nil if none was raised.
5
+ def rescuing
6
+ error = nil
7
+ begin
8
+ yield
9
+ rescue Exception, RuntimeError => e
10
+ error = e
11
+ end
12
+ error
13
+ end
14
+
15
+ # Usage:
16
+ # capturing { puts "hi" } => "hi\n"
17
+ # capturing(:stderr) { $stderr.puts "hi" } => "hi\n"
18
+ # out, err = capturing(:stdout, :stderr) { ... }
19
+ #
20
+ # see http://www.justskins.com/forums/closing-stderr-105096.html for more explanation
21
+ def capturing(*streams)
22
+ streams = [:stdout] if streams.empty?
23
+ original = {}
24
+ captured = {}
25
+
26
+ # reassign the $ variable (which is used by well-behaved code e.g. puts)
27
+ streams.each do |stream|
28
+ original[stream] = (stream == :stdout ? $stdout : $stderr)
29
+ captured[stream] = StringIO.new
30
+ case stream
31
+ when :stdout
32
+ $stdout = captured[stream]
33
+ when :stderr
34
+ $stderr = captured[stream]
35
+ end
36
+ end
37
+
38
+ yield
39
+
40
+ # return either one string, or an array of two strings
41
+ if streams.size == 1
42
+ captured[streams.first].string
43
+ else
44
+ [captured[streams[0]].string, captured[streams[1]].string]
45
+ end
46
+
47
+ ensure
48
+
49
+ streams.each do |stream|
50
+ # bail if stream was reassigned inside the block
51
+ if (stream == :stdout ? $stdout : $stderr) != captured[stream]
52
+ raise "#{stream} was reassigned while being captured"
53
+ end
54
+ # support nested calls to capturing
55
+ original[stream] << captured[stream].string if original[stream].is_a? StringIO
56
+ case stream
57
+ when :stdout
58
+ $stdout = original[stream]
59
+ when :stderr
60
+ $stderr = original[stream]
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ end
data/lib/wrong/irb.rb ADDED
@@ -0,0 +1,16 @@
1
+ if defined? IRB
2
+ module IRB
3
+ class Context
4
+ alias :original_evaluate :evaluate
5
+ attr_reader :all_lines
6
+
7
+ def evaluate(line, line_no)
8
+ (@all_lines ||= "") <<line
9
+ original_evaluate line, line_no
10
+ end
11
+ end
12
+ end
13
+
14
+ # include it in the top level too, since if you're using Wrong inside IRB that's probably what you want
15
+ include Wrong
16
+ end