wrong 0.1.0 → 0.2.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 (80) hide show
  1. data/README.markdown +114 -25
  2. data/lib/predicated/Gemfile +15 -0
  3. data/lib/predicated/LICENSE +20 -0
  4. data/lib/predicated/README.markdown +191 -0
  5. data/lib/predicated/Rakefile +51 -0
  6. data/lib/predicated/lib/predicated.rb +4 -0
  7. data/lib/predicated/lib/predicated/autogen_call.rb +37 -0
  8. data/lib/predicated/lib/predicated/constrain.rb +66 -0
  9. data/lib/predicated/lib/predicated/evaluate.rb +94 -0
  10. data/lib/predicated/lib/predicated/from/callable_object.rb +108 -0
  11. data/lib/predicated/lib/predicated/from/json.rb +59 -0
  12. data/lib/predicated/lib/predicated/from/ruby_code_string.rb +73 -0
  13. data/lib/predicated/lib/predicated/from/url_part.rb +104 -0
  14. data/lib/predicated/lib/predicated/from/xml.rb +61 -0
  15. data/lib/predicated/lib/predicated/gem_check.rb +34 -0
  16. data/lib/predicated/lib/predicated/predicate.rb +111 -0
  17. data/lib/predicated/lib/predicated/print.rb +62 -0
  18. data/lib/predicated/lib/predicated/selectable.rb +102 -0
  19. data/lib/predicated/lib/predicated/simple_templated_predicate.rb +79 -0
  20. data/lib/predicated/lib/predicated/string_utils.rb +20 -0
  21. data/lib/predicated/lib/predicated/to/arel.rb +41 -0
  22. data/lib/predicated/lib/predicated/to/json.rb +48 -0
  23. data/lib/predicated/lib/predicated/to/sentence.rb +94 -0
  24. data/lib/predicated/lib/predicated/to/solr.rb +15 -0
  25. data/lib/predicated/lib/predicated/to/xml.rb +67 -0
  26. data/lib/predicated/lib/predicated/version.rb +3 -0
  27. data/lib/predicated/predicated.gemspec +22 -0
  28. data/lib/predicated/test/autogen_call_test.rb +40 -0
  29. data/lib/predicated/test/canonical_transform_cases.rb +63 -0
  30. data/lib/predicated/test/constrain_test.rb +86 -0
  31. data/lib/predicated/test/enumerable_test.rb +32 -0
  32. data/lib/predicated/test/equality_test.rb +32 -0
  33. data/lib/predicated/test/evaluate_test.rb +149 -0
  34. data/lib/predicated/test/from/callable_object_canonical_test.rb +43 -0
  35. data/lib/predicated/test/from/callable_object_test.rb +78 -0
  36. data/lib/predicated/test/from/json_test.rb +83 -0
  37. data/lib/predicated/test/from/ruby_code_string_canonical_test.rb +37 -0
  38. data/lib/predicated/test/from/ruby_code_string_test.rb +103 -0
  39. data/lib/predicated/test/from/url_part_parser_test.rb +123 -0
  40. data/lib/predicated/test/from/url_part_test.rb +48 -0
  41. data/lib/predicated/test/from/xml_test.rb +57 -0
  42. data/lib/predicated/test/json_conversion_test.rb +33 -0
  43. data/lib/predicated/test/print_test.rb +66 -0
  44. data/lib/predicated/test/selectable_test.rb +123 -0
  45. data/lib/predicated/test/simple_templated_predicate_test.rb +39 -0
  46. data/lib/predicated/test/suite.rb +2 -0
  47. data/lib/predicated/test/test_helper.rb +64 -0
  48. data/lib/predicated/test/test_helper_with_wrong.rb +6 -0
  49. data/lib/predicated/test/to/arel_test.rb +85 -0
  50. data/lib/predicated/test/to/json_test.rb +74 -0
  51. data/lib/predicated/test/to/sentence_test.rb +90 -0
  52. data/lib/predicated/test/to/solr_test.rb +39 -0
  53. data/lib/predicated/test/to/xml_test.rb +72 -0
  54. data/lib/predicated/test/xml_conversion_test.rb +34 -0
  55. data/lib/predicated/test_integration/arel_integration_test.rb +52 -0
  56. data/lib/predicated/test_integration/canonical_integration_cases.rb +66 -0
  57. data/lib/predicated/test_integration/schema.xml +83 -0
  58. data/lib/predicated/test_integration/solr_integration_test.rb +71 -0
  59. data/lib/predicated/test_integration/sqlite_db +0 -0
  60. data/lib/predicated/test_integration/suite.rb +2 -0
  61. data/lib/predicated/test_integration/usage_test.rb +252 -0
  62. data/lib/wrong.rb +3 -1
  63. data/lib/wrong/adapters/test_unit.rb +1 -3
  64. data/lib/wrong/assert.rb +81 -24
  65. data/lib/wrong/chunk.rb +145 -0
  66. data/lib/wrong/message/string_diff.rb +2 -4
  67. data/lib/wrong/message/test_context.rb +2 -2
  68. data/lib/wrong/version.rb +2 -2
  69. data/test/adapters/minitest_test.rb +16 -9
  70. data/test/adapters/test_unit_test.rb +1 -1
  71. data/test/assert_test.rb +90 -0
  72. data/test/catch_raise_test.rb +2 -2
  73. data/test/chunk_test.rb +236 -0
  74. data/test/failures_test.rb +109 -74
  75. data/test/message/array_diff_test.rb +35 -19
  76. data/test/message/string_diff_test.rb +39 -15
  77. data/test/message/test_context_text.rb +2 -2
  78. data/test/test_helper.rb +25 -7
  79. metadata +86 -33
  80. data/test/basic_assert_test.rb +0 -38
@@ -1,3 +1,5 @@
1
1
  $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ require "predicated/lib/predicated"
2
3
 
3
- require "wrong/assert"
4
+ require "wrong/assert"
5
+ require "wrong/chunk"
@@ -1,8 +1,6 @@
1
1
  require "wrong/assert"
2
2
 
3
3
  class Test::Unit::TestCase
4
-
5
-
6
4
  include Wrong::Assert
7
5
 
8
6
  Wrong::Assert.disable_existing_assert_methods(self)
@@ -10,4 +8,4 @@ class Test::Unit::TestCase
10
8
  def failure_class
11
9
  Test::Unit::AssertionFailedError
12
10
  end
13
- end
11
+ end
@@ -1,6 +1,7 @@
1
1
  require "predicated/predicate"
2
2
  require "predicated/from/callable_object"
3
3
  require "predicated/to/sentence"
4
+ require "wrong/chunk"
4
5
 
5
6
  #see http://yehudakatz.com/2009/01/18/other-ways-to-wrap-a-method/
6
7
  class Module
@@ -12,28 +13,20 @@ end
12
13
 
13
14
  module Wrong
14
15
  module Assert
15
-
16
- class AssertionFailedError < RuntimeError; end
17
-
16
+
17
+ class AssertionFailedError < RuntimeError;
18
+ end
19
+
18
20
  def failure_class
19
21
  AssertionFailedError
20
22
  end
21
-
22
- def assert(&block)
23
- unless block.call
24
- raise failure_class.new(
25
- failure_message(:assert, block, Predicated::Predicate.from_callable_object(block))
26
- )
27
- end
28
- end
29
23
 
24
+ def assert(explanation = nil, depth = 0, &block)
25
+ aver(:assert, explanation, depth, &block)
26
+ end
30
27
 
31
- def deny(&block)
32
- if block.call
33
- raise failure_class.new(
34
- failure_message(:deny, block, Predicated::Predicate.from_callable_object(block))
35
- )
36
- end
28
+ def deny(explanation = nil, depth = 0, &block)
29
+ aver(:deny, explanation, depth, &block)
37
30
  end
38
31
 
39
32
  def catch_raise
@@ -43,25 +36,89 @@ module Wrong
43
36
  rescue Exception, RuntimeError => e
44
37
  error = e
45
38
  end
46
- e
39
+ error
47
40
  end
48
-
41
+
49
42
  overridable do
50
43
  def failure_message(method_sym, block, predicate)
51
44
  method_sym == :deny ? predicate.to_sentence : predicate.to_negative_sentence
52
45
  end
53
46
  end
54
-
47
+
55
48
  def self.disable_existing_assert_methods(the_class)
56
49
  (the_class.public_instance_methods.
57
- select{|m|m =~ /^assert/} - ["assert"]).each do |old_assert_method|
50
+ map { |m| m.to_s }.
51
+ select { |m| m =~ /^assert/ } - ["assert"]).each do |old_assert_method|
58
52
  the_class.class_eval(%{
59
53
  def #{old_assert_method}(*args)
60
54
  raise "#{old_assert_method} has been disabled. When you use Wrong, it overrides 'assert', which most test frameworks have defined, and use internally."
61
55
  end
62
56
  })
63
57
  end
64
- end
65
-
58
+ end
59
+
60
+ private
61
+
62
+ def indent(indent)
63
+ (" " * indent)
64
+ end
65
+
66
+ def newline(indent)
67
+ "\n" + self.indent(indent)
68
+ end
69
+
70
+ # todo: test
71
+ def details(block, chunk)
72
+ # p chunk.claim
73
+ details = ""
74
+ parts = chunk.parts
75
+ parts.shift # remove the first part, since it's the same as the code
76
+ if parts.size > 0
77
+ details = "\n"
78
+ parts.each do |part|
79
+ begin
80
+ value = eval(part, block.binding)
81
+ unless part == value.inspect
82
+ if part =~ /\n/m
83
+ part.gsub!(/\n/, newline(2))
84
+ part += newline(3)
85
+ end
86
+ value = value.inspect.gsub("\n", "\n#{indent(3)}")
87
+ details << indent(2) << "#{part} is #{value}\n"
88
+ end
89
+ rescue Exception => e
90
+ details << indent(2) << "#{part} raises #{e.class}: #{e.message.gsub("\n", "\n#{indent(3)}")}\n"
91
+ if false
92
+ puts "#{e.class}: #{e.message} evaluating #{part.inspect}"
93
+ puts "\t" + e.backtrace.join("\n\t")
94
+ end
95
+ end
96
+ end
97
+ end
98
+ details
99
+ end
100
+
101
+ def aver(valence, explanation = nil, depth = 0, &block)
102
+ value = block.call
103
+ value = !value if valence == :deny
104
+ unless value
105
+ chunk = Wrong::Chunk.from_block(block, depth + 2)
106
+ code = chunk.code
107
+ predicate = begin
108
+ Predicated::Predicate.from_ruby_code_string(code, block.binding)
109
+ rescue Predicated::Predicate::DontKnowWhatToDoWithThisSexpError
110
+ nil
111
+ rescue Exception
112
+ nil
113
+ end
114
+ message = ""
115
+ message << "#{explanation}: " if explanation
116
+ message << "#{valence == :deny ? "Didn't expect" : "Expected"} #{code}, but"
117
+ message << " #{failure_message(valence, block, predicate)}" if predicate
118
+ message << details(block, chunk)
119
+ raise failure_class.new(message)
120
+ end
121
+ end
66
122
  end
67
- end
123
+
124
+ end
@@ -0,0 +1,145 @@
1
+ require 'ruby_parser'
2
+ require 'ruby2ruby'
3
+
4
+ module Wrong
5
+ class Chunk
6
+ def self.from_block(block, depth = 0)
7
+ file, line = if block.to_proc.respond_to? :source_location
8
+ # in Ruby 1.9, it reads the source location from the block
9
+ block.to_proc.source_location
10
+ else
11
+ # in Ruby 1.8, it reads the source location from the call stack
12
+ caller[depth].split(":")
13
+ end
14
+ new(file, line)
15
+ end
16
+
17
+ attr_reader :file, :line_number
18
+
19
+ # line parameter is 1-based
20
+ def initialize(file, line_number)
21
+ @file = file
22
+ @line_number = line_number.to_i
23
+ end
24
+
25
+ def line_index
26
+ @line_number - 1
27
+ end
28
+
29
+ def location
30
+ "#{@file}:#{@line_number}"
31
+ end
32
+
33
+ # Algorithm:
34
+ # try to parse the starting line
35
+ # if it parses OK, then we're done!
36
+ # if not, then glom the next line and try again
37
+ # repeat until it parses or we're out of lines
38
+ def parse
39
+ lines = File.read(@file).split("\n")
40
+ @parser ||= RubyParser.new
41
+ @chunk = nil
42
+ c = 0
43
+ @sexp = nil
44
+ while @sexp.nil? && line_index + c < lines.size
45
+ begin
46
+ @chunk = lines[line_index..line_index+c].join("\n")
47
+ @sexp = @parser.parse(@chunk)
48
+ rescue Racc::ParseError => e
49
+ # loop and try again
50
+ c += 1
51
+ end
52
+ end
53
+ @sexp
54
+ end
55
+
56
+ # The claim is the part of the assertion inside the curly braces.
57
+ # E.g. for "assert { x == 5 }" the claim is "x == 5"
58
+ def claim
59
+ parse()
60
+
61
+ if @sexp.nil?
62
+ raise "Could not parse #{location}"
63
+ else
64
+ assertion = @sexp.assertion
65
+ statement = assertion && assertion[3]
66
+ if statement.nil?
67
+ @sexp
68
+ # raise "Could not find assertion in #{location}\n\t#{@chunk.strip}\n\t#{@sexp}"
69
+ else
70
+ statement
71
+ end
72
+ end
73
+ end
74
+
75
+ def code
76
+ self.claim.to_ruby
77
+ end
78
+
79
+ def parts(sexp = nil)
80
+ if sexp.nil?
81
+ parts(self.claim).compact.uniq
82
+ else
83
+ # todo: extract into Sexp, once I have more unit tests
84
+ parts_list = []
85
+ begin
86
+ unless sexp.first == :arglist
87
+ code = sexp.to_ruby.strip
88
+ parts_list << code unless code == "" || parts_list.include?(code)
89
+ end
90
+ rescue => e
91
+ puts "#{e.class}: #{e.message}"
92
+ puts e.backtrace.join("\n")
93
+ end
94
+ sexp.each do |sub|
95
+ if sub.is_a?(Sexp)
96
+ parts_list += parts(sub)
97
+ end
98
+ end
99
+ parts_list
100
+ end
101
+ end
102
+
103
+ end
104
+
105
+ end
106
+
107
+ # todo: move to separate monkey patch file
108
+ class Sexp < Array
109
+ def doop
110
+ Marshal.load(Marshal.dump(self))
111
+ end
112
+
113
+ def to_ruby
114
+ d = self.doop
115
+ x = Ruby2Ruby.new.process(d)
116
+ x
117
+ end
118
+
119
+ def assertion?
120
+ self.is_a? Sexp and
121
+ self[0] == :iter and
122
+ self[1].is_a? Sexp and
123
+ self[1][0] == :call and
124
+ [:assert, :deny].include? self[1][2] # todo: allow aliases for assert (e.g. "is")
125
+ end
126
+
127
+ def assertion
128
+ sexp = self
129
+ assertion = if sexp.assertion?
130
+ sexp
131
+ else
132
+ # todo: extract into sexp
133
+ nested_assertions.first
134
+ end
135
+ assertion
136
+ end
137
+
138
+ private
139
+ def nested_assertions
140
+ assertions = []
141
+ self.each_of_type(:iter) { |sexp| assertions << sexp if sexp.assertion? }
142
+ assertions
143
+ end
144
+
145
+ end
@@ -37,8 +37,6 @@ module Wrong
37
37
  carrot_string
38
38
  end
39
39
  end
40
-
41
-
42
-
40
+
43
41
  end
44
- end
42
+ end
@@ -1,6 +1,6 @@
1
1
  module Wrong
2
2
  module Assert
3
-
3
+ # todo: integrate with / use Chunk somehow?
4
4
  def failure_message(method_sym, block, predicate)
5
5
  upper_portion = super
6
6
 
@@ -23,4 +23,4 @@ module Wrong
23
23
  end
24
24
 
25
25
  end
26
- end
26
+ end
@@ -1,3 +1,3 @@
1
1
  module Wrong
2
- VERSION = "0.1.0" unless defined?(Wrong::VERSION)
3
- end
2
+ VERSION = "0.2.0" unless defined?(Wrong::VERSION)
3
+ end
@@ -1,4 +1,4 @@
1
- require "test/test_helper"
1
+ require "./test/test_helper"
2
2
 
3
3
  require "minitest/spec"
4
4
  require "minitest/unit"
@@ -6,18 +6,25 @@ require "minitest/unit"
6
6
  require "wrong/assert"
7
7
  require "wrong/adapters/minitest"
8
8
 
9
- apropos "basic assert features" do
9
+ regarding "basic assert features" do
10
10
 
11
- apropos "pass/fail basics" do
11
+ regarding "pass/fail basics" do
12
12
  test "disables other assert methods" do
13
13
  test_case_instance = Class.new(MiniTest::Unit::TestCase).new("x")
14
- assert{ catch_raise{test_case_instance.assert_equal(1,1)}.
15
- message.include?("has been disabled") }
14
+ assert{
15
+ catch_raise{
16
+ test_case_instance.assert_equal(1,1)
17
+ }.message.include?("has been disabled")
18
+ }
16
19
  end
17
20
 
18
21
  test "raises minitest assertion failures" do
19
22
  test_case_instance = Class.new(MiniTest::Unit::TestCase).new("x")
20
- assert{ catch_raise{test_case_instance.assert{1==2}}.is_a?(MiniTest::Assertion)}
23
+ assert{
24
+ catch_raise{
25
+ test_case_instance.assert{1==2}
26
+ }.is_a?(MiniTest::Assertion)
27
+ }
21
28
  end
22
29
 
23
30
  test "assert and deny are available to minitest tests" do
@@ -42,12 +49,12 @@ apropos "basic assert features" do
42
49
  end
43
50
 
44
51
  msg = catch_raise{MyFailingAssertTest.new.test_fail}.message
45
- assert{ "1 is not equal to 2" == msg }
52
+ assert{ msg.include?("1 is not equal to 2") }
46
53
 
47
54
  msg = catch_raise{MyFailingDenyTest.new.test_fail}.message
48
- assert{ "1 is equal to 1" == msg }
55
+ assert{ msg.include?("1 is equal to 1") }
49
56
  end
50
57
 
51
58
  end
52
59
 
53
- end
60
+ end
@@ -1,4 +1,4 @@
1
- require "test/test_helper"
1
+ require "./test/test_helper"
2
2
 
3
3
  require "test/unit"
4
4
 
@@ -0,0 +1,90 @@
1
+ require "./test/test_helper"
2
+ require "wrong/assert"
3
+
4
+ regarding "basic assert features" do
5
+
6
+ before do
7
+ @m = Module.new do
8
+ extend Wrong::Assert
9
+ end
10
+ end
11
+
12
+ regarding "pass/fail basics" do
13
+ test "passes when the result is true. deny does the reverse" do
14
+ @m.assert { true }
15
+ @m.assert { 1==1 }
16
+
17
+ @m.deny { false }
18
+ @m.deny { 1==2 }
19
+ end
20
+
21
+ test "fails when result is false. deny does the reverse" do
22
+ get_error {
23
+ @m.assert { false }
24
+ } || fail
25
+ get_error {
26
+ @m.assert { 1==2 }
27
+ } || fail
28
+
29
+ get_error {
30
+ @m.deny { true }
31
+ } || fail
32
+ get_error {
33
+ @m.deny { 1==1 }
34
+ } || fail
35
+ end
36
+
37
+ class MyError < StandardError;
38
+ end
39
+
40
+ test "both deny and assert fail when an error is thrown. bubbles up the error." do
41
+ assert_raises(MyError) { @m.assert { raise MyError.new } }
42
+ assert_raises(MyError) { @m.deny { raise MyError.new } }
43
+ end
44
+
45
+ test "assert takes an optional explanation" do
46
+ e = get_error {
47
+ sky = "green"
48
+ @m.assert("the sky should be blue") { sky == "blue" }
49
+ }
50
+ assert e.message =~ /^the sky should be blue: /
51
+ end
52
+
53
+ test "deny takes an optional explanation" do
54
+ e = get_error {
55
+ sky = "blue"
56
+ @m.deny("the sky should not be blue") { sky == "blue" }
57
+ }
58
+ assert e.message =~ /^the sky should not be blue: /
59
+ end
60
+ end
61
+ end
62
+
63
+
64
+ regarding "advanced assert features" do
65
+ include Wrong::Assert
66
+
67
+ def assert_many(*procs)
68
+ failures = []
69
+ procs.each do |proc|
70
+ begin
71
+ assert(nil, 3, &proc)
72
+ rescue => e
73
+ failures << e
74
+ end
75
+ end
76
+ assert { failures.empty? }
77
+ end
78
+
79
+ test "it's possible (but not advisable) to define procs in different places from the assert call" do
80
+ x = 10
81
+ e = catch_raise do
82
+ assert_many(lambda { x == 10 })
83
+ assert_many(lambda { x > 10 })
84
+ end
85
+
86
+ assert { e.message =~ /^Expected failures.empty\?/ }
87
+ assert { e.message =~ /x is 10/ }
88
+ end
89
+
90
+ end