wrong 0.4.0-java

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