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