wrong 0.3.3 → 0.4.0
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.
- 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
|