wrong 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +77 -11
- data/lib/wrong.rb +20 -0
- data/lib/wrong/adapters/minitest.rb +6 -18
- data/lib/wrong/adapters/rspec.rb +18 -7
- data/lib/wrong/adapters/test_unit.rb +2 -2
- data/lib/wrong/assert.rb +45 -90
- data/lib/wrong/chunk.rb +89 -27
- data/lib/wrong/close_to.rb +7 -11
- data/lib/wrong/d.rb +42 -0
- data/lib/wrong/failure_message.rb +43 -0
- data/lib/wrong/helpers.rb +66 -0
- data/lib/wrong/irb.rb +1 -1
- data/lib/wrong/message/array_diff.rb +57 -75
- data/lib/wrong/message/string_comparison.rb +88 -0
- data/lib/wrong/message/test_context.rb +2 -0
- data/lib/{predicated/lib/predicated/sexp_patch.rb → wrong/ruby2ruby_patch.rb} +3 -5
- data/lib/wrong/sexp_ext.rb +12 -4
- data/lib/wrong/version.rb +1 -1
- data/test/adapters/rspec1/failing_spec.rb +23 -0
- data/test/adapters/rspec2/failing_spec.rb +26 -0
- data/test/adapters/rspec_test.rb +65 -4
- data/test/adapters/test_unit_test.rb +6 -1
- data/test/assert_advanced_test.rb +51 -0
- data/test/assert_test.rb +4 -48
- data/test/capturing_test.rb +4 -2
- data/test/chunk_test.rb +36 -11
- data/test/close_to_test.rb +2 -2
- data/test/config_test.rb +5 -5
- data/test/d_test.rb +64 -0
- data/test/failure_message_test.rb +40 -0
- data/test/failures_test.rb +6 -7
- data/test/message/array_diff_test.rb +18 -14
- data/test/message/{test_context_text.rb → test_context_test.rb} +2 -1
- data/test/rescuing_test.rb +5 -4
- data/test/separate.rb +4 -0
- data/test/sexp_ext_test.rb +2 -2
- data/test/string_comparison_test.rb +159 -0
- data/test/suite.rb +7 -4
- data/test/test_helper.rb +2 -0
- data/test/wrong_test.rb +60 -0
- metadata +92 -46
- data/lib/wrong/message/string_diff.rb +0 -42
- data/test/message/string_diff_test.rb +0 -89
data/lib/wrong/close_to.rb
CHANGED
@@ -1,13 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
class Fixnum
|
10
|
-
def close_to?(*args)
|
11
|
-
self.to_f.close_to?(*args)
|
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
|
12
6
|
end
|
7
|
+
Float.send :include, CloseTo
|
8
|
+
Fixnum.send :include, CloseTo
|
13
9
|
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
CHANGED
@@ -1,87 +1,69 @@
|
|
1
1
|
require "diff/lcs"
|
2
|
+
require "wrong/failure_message"
|
2
3
|
|
3
4
|
module Wrong
|
4
|
-
|
5
|
-
|
6
|
-
def failure_message(method_sym, block, predicate)
|
7
|
-
message = super
|
8
|
-
|
9
|
-
if predicate.is_a?(Predicated::Equal) &&
|
10
|
-
predicate.left.is_a?(Enumerable) &&
|
11
|
-
predicate.right.is_a?(Enumerable)
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
diffs = Diff::LCS.sdiff(predicate.left, predicate.right)
|
16
|
-
# left_offset = 0
|
17
|
-
left_arr = []
|
18
|
-
right_arr = []
|
19
|
-
diff_arr = []
|
20
|
-
|
21
|
-
diffs.each do |diff|
|
22
|
-
left_elem = diff.old_element.nil? ? "nil" : diff.old_element.inspect
|
23
|
-
right_elem = diff.new_element.nil? ? "nil" : diff.new_element.inspect
|
24
|
-
|
25
|
-
max_length = [left_elem.length, right_elem.length].max
|
26
|
-
left_arr << left_elem.ljust(max_length) unless diff.action == "+"
|
27
|
-
right_arr << right_elem.ljust(max_length) unless diff.action == "-"
|
28
|
-
diff_arr << (diff.action == "=" ? " ".ljust(max_length) : "^".ljust(max_length))
|
29
|
-
end
|
30
|
-
|
31
|
-
|
32
|
-
left_str, right_str, diff_str = ArrayDiff.compute_and_format(predicate.left, predicate.right)
|
5
|
+
class ArrayDiff < FailureMessage::Formatter
|
6
|
+
register # tell FailureMessage::Formatter about us
|
33
7
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
8
|
+
def match?
|
9
|
+
predicate.is_a?(Predicated::Equal) &&
|
10
|
+
arrayish?(predicate.left) &&
|
11
|
+
arrayish?(predicate.right)
|
12
|
+
end
|
13
|
+
|
14
|
+
def arrayish?(object)
|
15
|
+
# in some Rubies, String is Enumerable
|
16
|
+
object.is_a?(Enumerable) && !object.is_a?(String)
|
17
|
+
end
|
18
|
+
|
19
|
+
def describe
|
20
|
+
left_str, right_str, diff_str = compute_and_format(predicate.left, predicate.right)
|
21
|
+
|
22
|
+
message = "\n"
|
23
|
+
message << left_str + "\n"
|
24
|
+
message << right_str + "\n"
|
25
|
+
message << diff_str + "\n"
|
40
26
|
message
|
27
|
+
|
41
28
|
end
|
42
|
-
|
43
|
-
module ArrayDiff
|
44
|
-
def self.compute_and_format(left, right)
|
45
|
-
diffs = Diff::LCS.sdiff(left, right)
|
46
|
-
|
47
|
-
left_arr = []
|
48
|
-
right_arr = []
|
49
|
-
diff_arr = []
|
50
|
-
|
51
|
-
diffs.each do |diff|
|
52
|
-
left_elem = diff.old_element.nil? ? "nil" : diff.old_element.inspect
|
53
|
-
right_elem = diff.new_element.nil? ? "nil" : diff.new_element.inspect
|
54
|
-
|
55
|
-
max_length = [left_elem.length, right_elem.length].max
|
56
|
-
left_arr << left_elem.ljust(max_length) unless diff.action == "+"
|
57
|
-
right_arr << right_elem.ljust(max_length) unless diff.action == "-"
|
58
|
-
diff_arr << (diff.action == "=" ? " ".ljust(max_length) : "^".ljust(max_length))
|
59
|
-
end
|
60
|
-
|
61
29
|
|
62
|
-
|
63
|
-
|
64
|
-
|
30
|
+
def compute_and_format(left, right)
|
31
|
+
diffs = Diff::LCS.sdiff(left, right)
|
32
|
+
|
33
|
+
left_arr = []
|
34
|
+
right_arr = []
|
35
|
+
diff_arr = []
|
36
|
+
|
37
|
+
diffs.each do |diff|
|
38
|
+
left_elem = diff.old_element.nil? ? "nil" : diff.old_element.inspect
|
39
|
+
right_elem = diff.new_element.nil? ? "nil" : diff.new_element.inspect
|
40
|
+
|
41
|
+
max_length = [left_elem.length, right_elem.length].max
|
42
|
+
left_arr << left_elem.ljust(max_length) unless diff.action == "+"
|
43
|
+
right_arr << right_elem.ljust(max_length) unless diff.action == "-"
|
44
|
+
diff_arr << (diff.action == "=" ? " ".ljust(max_length) : "^".ljust(max_length))
|
65
45
|
end
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
46
|
+
|
47
|
+
|
48
|
+
diff_str = " " + diff_arr.join(" ") + " "
|
49
|
+
|
50
|
+
[format(left_arr), format(right_arr), diff_str]
|
51
|
+
end
|
52
|
+
|
53
|
+
def format(thing)
|
54
|
+
str = ""
|
55
|
+
if thing.is_a?(Array)
|
56
|
+
str << "["
|
57
|
+
thing.each_with_index do |item, i|
|
58
|
+
str << format(item)
|
59
|
+
str << ", " unless i == thing.length-1
|
78
60
|
end
|
79
|
-
str
|
61
|
+
str << "]"
|
62
|
+
else
|
63
|
+
str << thing
|
80
64
|
end
|
81
|
-
|
65
|
+
str
|
82
66
|
end
|
83
|
-
|
84
|
-
|
85
|
-
|
67
|
+
|
86
68
|
end
|
87
|
-
end
|
69
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require "wrong/failure_message"
|
2
|
+
|
3
|
+
module Wrong
|
4
|
+
class StringComparison
|
5
|
+
@@window = 64
|
6
|
+
@@prelude = 12
|
7
|
+
|
8
|
+
def self.window
|
9
|
+
@@window
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.window=(val)
|
13
|
+
@@window = val
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.prelude
|
17
|
+
@@prelude
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.prelude=(val)
|
21
|
+
@@prelude = val
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(first, second)
|
25
|
+
@first = first
|
26
|
+
@second = second
|
27
|
+
end
|
28
|
+
|
29
|
+
def same?
|
30
|
+
@first == @second
|
31
|
+
end
|
32
|
+
|
33
|
+
def different_at
|
34
|
+
if (@first.nil? || @second.nil?)
|
35
|
+
0
|
36
|
+
else
|
37
|
+
i = 0
|
38
|
+
while (i < @first.size && i < @second.size)
|
39
|
+
if @first[i] != @second[i]
|
40
|
+
break
|
41
|
+
end
|
42
|
+
i += 1
|
43
|
+
end
|
44
|
+
return i
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def message
|
49
|
+
"Strings differ at position #{different_at}:\n" +
|
50
|
+
" first: #{chunk(@first)}\n" +
|
51
|
+
"second: #{chunk(@second)}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def chunk(s)
|
55
|
+
prefix, middle, suffix = "...", "", "..."
|
56
|
+
|
57
|
+
start = different_at - @@prelude
|
58
|
+
if start < 0
|
59
|
+
prefix = ""
|
60
|
+
start = 0
|
61
|
+
end
|
62
|
+
|
63
|
+
stop = start + @@window
|
64
|
+
if stop >= s.size
|
65
|
+
suffix = ""
|
66
|
+
stop = s.size
|
67
|
+
end
|
68
|
+
|
69
|
+
[prefix, s[start...stop].inspect, suffix].join
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class StringComparisonFormatter < FailureMessage::Formatter
|
74
|
+
register # tell FailureMessage::Formatter about us
|
75
|
+
|
76
|
+
def match?
|
77
|
+
predicate.is_a?(Predicated::Equal) &&
|
78
|
+
predicate.left.is_a?(String) &&
|
79
|
+
predicate.right.is_a?(String)
|
80
|
+
end
|
81
|
+
|
82
|
+
def describe
|
83
|
+
comparison = Wrong::StringComparison.new(predicate.left, predicate.right)
|
84
|
+
"\n" + comparison.message
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -1,10 +1,12 @@
|
|
1
1
|
module Wrong
|
2
2
|
module Assert
|
3
3
|
# todo: integrate with / use Chunk somehow?
|
4
|
+
#
|
4
5
|
def failure_message(method_sym, block, predicate)
|
5
6
|
upper_portion = super
|
6
7
|
|
7
8
|
first_test_line = caller.find{|line|line =~ /(_test.rb|_spec.rb)/}
|
9
|
+
raise "Can't find test or spec in call chain: #{caller.join('|')}" if first_test_line.nil?
|
8
10
|
file, failure_line_number = first_test_line.split(":",2)
|
9
11
|
|
10
12
|
lines = File.readlines(file)
|
@@ -1,9 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
#see http://gist.github.com/321038
|
4
|
-
# # Monkey-patch to have Ruby2Ruby#translate with r2r >= 1.2.3, from
|
5
|
-
# # http://seattlerb.rubyforge.org/svn/ruby2ruby/1.2.2/lib/ruby2ruby.rb
|
6
1
|
class ::Ruby2Ruby < ::SexpProcessor
|
2
|
+
# see http://gist.github.com/321038
|
3
|
+
# Monkey-patch to have Ruby2Ruby#translate with r2r >= 1.2.3, from
|
4
|
+
# http://seattlerb.rubyforge.org/svn/ruby2ruby/1.2.2/lib/ruby2ruby.rb
|
7
5
|
def self.translate(klass_or_str, method = nil)
|
8
6
|
sexp = ParseTree.translate(klass_or_str, method)
|
9
7
|
unifier = Unifier.new
|
data/lib/wrong/sexp_ext.rb
CHANGED
@@ -3,16 +3,24 @@ require 'ruby2ruby'
|
|
3
3
|
require 'wrong/config'
|
4
4
|
|
5
5
|
class Sexp < Array
|
6
|
-
def doop
|
7
|
-
Marshal.load(Marshal.dump(self))
|
8
|
-
end
|
9
6
|
|
10
7
|
def to_ruby
|
11
|
-
d = self.
|
8
|
+
d = self.deep_clone
|
12
9
|
ruby = Ruby2Ruby.new.process(d)
|
13
10
|
ruby
|
14
11
|
end
|
15
12
|
|
13
|
+
# visit every node in the tree, including the root, that is an Sexp
|
14
|
+
# todo: test
|
15
|
+
def each_subexp(&block)
|
16
|
+
yield self
|
17
|
+
each do |child|
|
18
|
+
if child.is_a?(Sexp)
|
19
|
+
child.each_subexp(&block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
16
24
|
def assertion?
|
17
25
|
self.is_a? Sexp and
|
18
26
|
self[0] == :iter and
|