wrong 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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