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.
- data/README.markdown +114 -25
- data/lib/predicated/Gemfile +15 -0
- data/lib/predicated/LICENSE +20 -0
- data/lib/predicated/README.markdown +191 -0
- data/lib/predicated/Rakefile +51 -0
- data/lib/predicated/lib/predicated.rb +4 -0
- data/lib/predicated/lib/predicated/autogen_call.rb +37 -0
- data/lib/predicated/lib/predicated/constrain.rb +66 -0
- data/lib/predicated/lib/predicated/evaluate.rb +94 -0
- data/lib/predicated/lib/predicated/from/callable_object.rb +108 -0
- data/lib/predicated/lib/predicated/from/json.rb +59 -0
- data/lib/predicated/lib/predicated/from/ruby_code_string.rb +73 -0
- data/lib/predicated/lib/predicated/from/url_part.rb +104 -0
- data/lib/predicated/lib/predicated/from/xml.rb +61 -0
- data/lib/predicated/lib/predicated/gem_check.rb +34 -0
- data/lib/predicated/lib/predicated/predicate.rb +111 -0
- data/lib/predicated/lib/predicated/print.rb +62 -0
- data/lib/predicated/lib/predicated/selectable.rb +102 -0
- data/lib/predicated/lib/predicated/simple_templated_predicate.rb +79 -0
- data/lib/predicated/lib/predicated/string_utils.rb +20 -0
- data/lib/predicated/lib/predicated/to/arel.rb +41 -0
- data/lib/predicated/lib/predicated/to/json.rb +48 -0
- data/lib/predicated/lib/predicated/to/sentence.rb +94 -0
- data/lib/predicated/lib/predicated/to/solr.rb +15 -0
- data/lib/predicated/lib/predicated/to/xml.rb +67 -0
- data/lib/predicated/lib/predicated/version.rb +3 -0
- data/lib/predicated/predicated.gemspec +22 -0
- data/lib/predicated/test/autogen_call_test.rb +40 -0
- data/lib/predicated/test/canonical_transform_cases.rb +63 -0
- data/lib/predicated/test/constrain_test.rb +86 -0
- data/lib/predicated/test/enumerable_test.rb +32 -0
- data/lib/predicated/test/equality_test.rb +32 -0
- data/lib/predicated/test/evaluate_test.rb +149 -0
- data/lib/predicated/test/from/callable_object_canonical_test.rb +43 -0
- data/lib/predicated/test/from/callable_object_test.rb +78 -0
- data/lib/predicated/test/from/json_test.rb +83 -0
- data/lib/predicated/test/from/ruby_code_string_canonical_test.rb +37 -0
- data/lib/predicated/test/from/ruby_code_string_test.rb +103 -0
- data/lib/predicated/test/from/url_part_parser_test.rb +123 -0
- data/lib/predicated/test/from/url_part_test.rb +48 -0
- data/lib/predicated/test/from/xml_test.rb +57 -0
- data/lib/predicated/test/json_conversion_test.rb +33 -0
- data/lib/predicated/test/print_test.rb +66 -0
- data/lib/predicated/test/selectable_test.rb +123 -0
- data/lib/predicated/test/simple_templated_predicate_test.rb +39 -0
- data/lib/predicated/test/suite.rb +2 -0
- data/lib/predicated/test/test_helper.rb +64 -0
- data/lib/predicated/test/test_helper_with_wrong.rb +6 -0
- data/lib/predicated/test/to/arel_test.rb +85 -0
- data/lib/predicated/test/to/json_test.rb +74 -0
- data/lib/predicated/test/to/sentence_test.rb +90 -0
- data/lib/predicated/test/to/solr_test.rb +39 -0
- data/lib/predicated/test/to/xml_test.rb +72 -0
- data/lib/predicated/test/xml_conversion_test.rb +34 -0
- data/lib/predicated/test_integration/arel_integration_test.rb +52 -0
- data/lib/predicated/test_integration/canonical_integration_cases.rb +66 -0
- data/lib/predicated/test_integration/schema.xml +83 -0
- data/lib/predicated/test_integration/solr_integration_test.rb +71 -0
- data/lib/predicated/test_integration/sqlite_db +0 -0
- data/lib/predicated/test_integration/suite.rb +2 -0
- data/lib/predicated/test_integration/usage_test.rb +252 -0
- data/lib/wrong.rb +3 -1
- data/lib/wrong/adapters/test_unit.rb +1 -3
- data/lib/wrong/assert.rb +81 -24
- data/lib/wrong/chunk.rb +145 -0
- data/lib/wrong/message/string_diff.rb +2 -4
- data/lib/wrong/message/test_context.rb +2 -2
- data/lib/wrong/version.rb +2 -2
- data/test/adapters/minitest_test.rb +16 -9
- data/test/adapters/test_unit_test.rb +1 -1
- data/test/assert_test.rb +90 -0
- data/test/catch_raise_test.rb +2 -2
- data/test/chunk_test.rb +236 -0
- data/test/failures_test.rb +109 -74
- data/test/message/array_diff_test.rb +35 -19
- data/test/message/string_diff_test.rb +39 -15
- data/test/message/test_context_text.rb +2 -2
- data/test/test_helper.rb +25 -7
- metadata +86 -33
- data/test/basic_assert_test.rb +0 -38
data/lib/wrong.rb
CHANGED
@@ -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
|
data/lib/wrong/assert.rb
CHANGED
@@ -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;
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
123
|
+
|
124
|
+
end
|
data/lib/wrong/chunk.rb
ADDED
@@ -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
|
data/lib/wrong/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module Wrong
|
2
|
-
VERSION = "0.
|
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
|
-
|
9
|
+
regarding "basic assert features" do
|
10
10
|
|
11
|
-
|
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{
|
15
|
-
|
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{
|
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"
|
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"
|
55
|
+
assert{ msg.include?("1 is equal to 1") }
|
49
56
|
end
|
50
57
|
|
51
58
|
end
|
52
59
|
|
53
|
-
end
|
60
|
+
end
|
data/test/assert_test.rb
ADDED
@@ -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
|