wrong 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/README.markdown +77 -11
  2. data/lib/wrong.rb +20 -0
  3. data/lib/wrong/adapters/minitest.rb +6 -18
  4. data/lib/wrong/adapters/rspec.rb +18 -7
  5. data/lib/wrong/adapters/test_unit.rb +2 -2
  6. data/lib/wrong/assert.rb +45 -90
  7. data/lib/wrong/chunk.rb +89 -27
  8. data/lib/wrong/close_to.rb +7 -11
  9. data/lib/wrong/d.rb +42 -0
  10. data/lib/wrong/failure_message.rb +43 -0
  11. data/lib/wrong/helpers.rb +66 -0
  12. data/lib/wrong/irb.rb +1 -1
  13. data/lib/wrong/message/array_diff.rb +57 -75
  14. data/lib/wrong/message/string_comparison.rb +88 -0
  15. data/lib/wrong/message/test_context.rb +2 -0
  16. data/lib/{predicated/lib/predicated/sexp_patch.rb → wrong/ruby2ruby_patch.rb} +3 -5
  17. data/lib/wrong/sexp_ext.rb +12 -4
  18. data/lib/wrong/version.rb +1 -1
  19. data/test/adapters/rspec1/failing_spec.rb +23 -0
  20. data/test/adapters/rspec2/failing_spec.rb +26 -0
  21. data/test/adapters/rspec_test.rb +65 -4
  22. data/test/adapters/test_unit_test.rb +6 -1
  23. data/test/assert_advanced_test.rb +51 -0
  24. data/test/assert_test.rb +4 -48
  25. data/test/capturing_test.rb +4 -2
  26. data/test/chunk_test.rb +36 -11
  27. data/test/close_to_test.rb +2 -2
  28. data/test/config_test.rb +5 -5
  29. data/test/d_test.rb +64 -0
  30. data/test/failure_message_test.rb +40 -0
  31. data/test/failures_test.rb +6 -7
  32. data/test/message/array_diff_test.rb +18 -14
  33. data/test/message/{test_context_text.rb → test_context_test.rb} +2 -1
  34. data/test/rescuing_test.rb +5 -4
  35. data/test/separate.rb +4 -0
  36. data/test/sexp_ext_test.rb +2 -2
  37. data/test/string_comparison_test.rb +159 -0
  38. data/test/suite.rb +7 -4
  39. data/test/test_helper.rb +2 -0
  40. data/test/wrong_test.rb +60 -0
  41. metadata +92 -46
  42. data/lib/wrong/message/string_diff.rb +0 -42
  43. data/test/message/string_diff_test.rb +0 -89
@@ -1,13 +1,9 @@
1
- # TODO: make it a module, and optionally include it into Float and Fixnum if asked
2
-
3
- class Float
4
- def close_to?(other, tolerance = 0.001)
5
- (self - other.to_f).abs < tolerance
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
@@ -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
@@ -12,5 +12,5 @@ if defined? IRB
12
12
  end
13
13
 
14
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::Assert
15
+ include Wrong
16
16
  end
@@ -1,87 +1,69 @@
1
1
  require "diff/lcs"
2
+ require "wrong/failure_message"
2
3
 
3
4
  module Wrong
4
- module Assert
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
- message << "\n\narray diff:\n"
35
- message << left_str + "\n"
36
- message << right_str + "\n"
37
- message << diff_str + "\n"
38
- end
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
- [format(left_arr),
63
- format(right_arr),
64
- " " + diff_arr.join(" ") + " "]
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
- def self.format(thing)
68
- str = ""
69
- if thing.is_a?(Array)
70
- str << "["
71
- thing.each_with_index do |item, i|
72
- str << format(item)
73
- str << ", " unless i == thing.length-1
74
- end
75
- str << "]"
76
- else
77
- str << thing
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
@@ -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.doop
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