assert2 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
data/lib/assert2.rb CHANGED
@@ -1,216 +1,323 @@
1
- require 'test/unit'
2
- require File.dirname(__FILE__) + '/ruby_reflector'
3
-
4
- # FIXME evaluate parts[3]
5
- # ERGO if the block is a block, decorate with do-end
6
- # ERGO decorate assert_latest's block at fault time
7
-
8
- module Assert_2_0
9
- include RubyNodeReflector
10
- include Coulor #:nodoc:
11
-
12
- # This assertion calls a block, and faults if this returns
13
- # +false+ or +nil+. The fault diagnostic will reflect the
14
- # intermediate value of every variable and expression in the
15
- # block.
16
- #
17
- # The first argument can be a diagnostic string:
18
- #
19
- # assert("foo failed"){ foo() }
20
- #
21
- # The fault diagnostic will print that line.
22
- #
23
- # The next time you think to write any of these assertions...
24
- #
25
- # - +assert+
26
- # - +assert_equal+
27
- # - +assert_instance_of+
28
- # - +assert_kind_of+
29
- # - +assert_operator+
30
- # - +assert_match+
31
- # - +assert_not_nil+
32
- #
33
- # use <code>assert{ 2.0 }</code> instead.
34
- #
35
- # If no block is provided, the assertion calls +assert_classic+,
36
- # which simulates RubyUnit's standard <code>assert()</code>.
37
- #
38
- # Note: This only works for Ruby 1.8.6 so far...
39
- #
40
- def assert(*args, &block)
41
- if block
42
- assert_ args.first, '', &block
43
- else
44
- assert_classic *args
45
- end
46
- end
47
-
48
- # The new <code>assert()</code> calls this to interpret
49
- # blocks of assertive statements.
50
- #
51
- def assert_(diagnostic = nil, twizzler = '_', &block)
52
- # puts reflect(&block) # activate this line and test to see all your successes!
53
- result = nil
54
-
55
- begin
56
- result = block.call
57
- rescue => e
58
- diagnostic = [diagnostic, e.inspect, *e.backtrace].compact.join("\n\t")
59
- _flunk_2_0("\nassert#{ twizzler }{ ", diagnostic, block, result)
60
- end
61
-
62
- return if result
63
- _flunk_2_0("assert#{ twizzler }{ ", diagnostic, block, result)
64
- end
65
-
66
- # This assertion replaces:
67
- #
68
- # - +assert_nil+
69
- # - +assert_no_match+
70
- # - +assert_not_equal+
71
- #
72
- # It faults, and prints its block's contents and values,
73
- # if its block returns non-+false+ and non-+nil+.
74
- #
75
- def deny(diagnostic = nil, &block)
76
- # "None shall pass!" --the Black Knight
77
- # puts reflect(&block) # activate this line and test to see all your denials!
78
- result = nil
79
-
80
- begin
81
- result = block.call
82
- rescue => e
83
- diagnostic = [diagnostic, e.inspect, *e.backtrace].compact.join("\n\t")
84
- _flunk_2_0("\ndeny{ ", diagnostic, block, result)
85
- end
86
-
87
- return unless result
88
- _flunk_2_0('deny{ ', diagnostic, block, result)
89
- end # "You're a looney!" -- King Arthur
90
-
91
- # Assert that a block raises a given Exception type matching
92
- # a given message
93
- #
94
- # * +types+ - single exception class or array of classes
95
- # * +matcher+ - Regular Expression to match the inner_text of XML nodes
96
- # * +diagnostic+ - optional string to add to failure message
97
- # * +block+ - Ruby statements that should raise an exception
98
- #
99
- # Examples:
100
- # %transclude AssertXPathSuite#test_assert_raise_message_detects_assertion_failure
101
- #
102
- # %transclude AssertXPathSuite#test_assert_raise_message_raises_message
103
- #
104
- # See: {assert_raise - Don't Just Say "No"}[http://www.oreillynet.com/onlamp/blog/2007/07/assert_raise_on_ruby_dont_just.html]
105
- #
106
- def assert_raise_message(types, matcher, diagnostic = nil, &block)
107
- args = [types].flatten + [diagnostic]
108
- exception = assert_raise(*args, &block)
109
-
110
- assert_match matcher,
111
- exception.message,
112
- [ diagnostic,
113
- "incorrect #{ exception.class.name
114
- } message raised from block:",
115
- "\t"+reflect_source(&block).split("\n").join("\n\t")
116
- ].compact.join("\n")
117
-
118
- return exception
119
- end
120
-
121
- def deny_raise_message(types, matcher, diagnostic = nil, &block) #:nodoc:
122
- exception = assert_raise_message(types, //, diagnostic, &block)
123
-
124
- assert_no_match matcher,
125
- exception.message,
126
- [ diagnostic,
127
- "exception #{ exception.class.name
128
- } with this message should not raise from block:",
129
- "\t"+reflect_source(&block).split("\n").join("\n\t")
130
- ].compact.join("\n")
131
-
132
- return exception.message
133
- end
134
-
135
- def assert_flunked(gripe, diagnostic = nil, &block) #:nodoc:
136
- assert_raise_message Test::Unit::AssertionFailedError,
137
- gripe,
138
- diagnostic,
139
- &block
140
- end
141
-
142
- def deny_flunked(gripe, diagnostic = nil, &block) #:nodoc:
143
- deny_raise_message Test::Unit::AssertionFailedError,
144
- gripe,
145
- diagnostic,
146
- &block
147
- end # ERGO move to assert{ 2.0 }, reflect, and leave there!
148
-
149
- # This is a copy of the classic assert, so your pre�xisting
150
- # +assert+ calls will not change their behavior
151
- #
152
- def assert_classic(boolean, message=nil)
153
- _wrap_assertion do
154
- assert_block("assert<classic> should not be called with a block.") { !block_given? }
155
- assert_block(build_message(message, "<?> is not true.", boolean)) { boolean }
156
- end
157
- end
158
-
159
- # wrap this common idiom:
160
- # foo = assemble()
161
- # deny{ foo.bar() }
162
- # foo.activate()
163
- # assert{ foo.bar() }
164
- #
165
- # that becomes:
166
- # foo = assemble()
167
- #
168
- # assert_yin_yang proc{ foo.bar() } do
169
- # foo.activate()
170
- # end
171
- #
172
- def assert_yin_yang(*args, &block)
173
- # prock, diagnostic = nil, &block)
174
- procks, diagnostic = args.partition{|p| p.respond_to? :call }
175
- block ||= procks.shift
176
- source = reflect_source(&block)
177
- fuss = [diagnostic, "fault before calling:", source].compact.join("\n")
178
- procks.each do |prock| deny(fuss, &prock); end
179
- block.call
180
- fuss = [diagnostic, "fault after calling:", source].compact.join("\n")
181
- procks.each do |prock| assert(fuss, &prock); end
182
- end
183
-
184
- # the prock assertion must pass on both sides of the called block
185
- #
186
- def deny_yin_yang(prock, diagnostic = nil, &block)
187
- source = reflect_source(&block)
188
- fuss = [diagnostic, "fault before calling:", source].compact.join("\n")
189
- assert fuss, &prock
190
- block.call
191
- fuss = [diagnostic, "fault after calling:", source].compact.join("\n")
192
- assert fuss, &prock
193
- end
194
-
195
- private
196
- def build_message_(diagnostic, reflection)
197
- diagnostic = nil if diagnostic == ''
198
- return [diagnostic, reflection].compact.join("\n")
199
- end
200
-
201
- def _flunk_2_0(polarity, diagnostic, block, result)
202
- rf = RubyReflector.new(block)
203
- effect = " - should #{ 'not ' if polarity =~ /deny/ }pass\n"
204
-
205
- report = magenta(polarity) + bold(rf.result) + magenta(" }") +
206
- red(arrow_result(result) + effect) +
207
- rf.format_evaluations
208
-
209
- flunk build_message_(diagnostic, report)
210
- end
211
-
212
- end # "Eagle-eyes it!"
213
-
214
- class Test::Unit::TestCase #:nodoc:
215
- include Assert_2_0
216
- end
1
+ require 'test/unit'
2
+
3
+ # FIXME the first failing assertion of a batch should suggest you get with Ruby1.9...
4
+ # TODO install Coulor
5
+ # TODO add :verbose => option to assert{}
6
+ # TODO pay for Staff Benda Bilili ALBUM: Tr�s Tr�s Fort (Promo Sampler) !
7
+ # TODO evaluate parts[3]
8
+ # ERGO if the block is a block, decorate with do-end
9
+ # ERGO decorate assert_latest's block at fault time
10
+
11
+ #~ if RUBY_VERSION > '1.8.6'
12
+ #~ puts "\nWarning: This version of assert{ 2.0 } requires\n" +
13
+ #~ "RubyNode, which only works on Ruby versions < 1.8.7.\n" +
14
+ #~ "Upgrade to Ruby1.9, and try 'gem install assert21'\n\n"
15
+ #~ end
16
+
17
+ #~ def colorize(whatever)
18
+ #~ # FIXME stop ignoring this and start colorizing v2.1!
19
+ #~ end
20
+
21
+ if RUBY_VERSION < '1.9.0'
22
+ require 'assert2/rubynode_reflector' # FIXME default to null_reflector if rubynode not available
23
+ else
24
+ require 'assert2/ripper_reflector'
25
+ end
26
+
27
+ # CONSIDER fix if an assertion contains more than one command - reflect it all!
28
+
29
+ module Test; module Unit; module Assertions
30
+
31
+ FlunkError = if defined? Test::Unit::AssertionFailedError
32
+ Test::Unit::AssertionFailedError
33
+ else
34
+ MiniTest::Assertion
35
+ end
36
+
37
+ def add_diagnostic(whatever = nil, &block)
38
+ @__additional_diagnostics ||= []
39
+
40
+ if whatever == :clear
41
+ @__additional_diagnostics = []
42
+ whatever = nil
43
+ end
44
+
45
+ @__additional_diagnostics += [whatever, block] # note .compact will take care of them if they don't exist
46
+ end
47
+
48
+ def assert(*args, &block)
49
+ # This assertion calls a block, and faults if it returns
50
+ # +false+ or +nil+. The fault diagnostic will reflect the
51
+ # assertion's complete source - with comments - and will
52
+ # reevaluate the every variable and expression in the
53
+ # block.
54
+ #
55
+ # The first argument can be a diagnostic string:
56
+ #
57
+ # assert("foo failed"){ foo() }
58
+ #
59
+ # The fault diagnostic will print that line.
60
+ #
61
+ # The next time you think to write any of these assertions...
62
+ #
63
+ # - +assert+
64
+ # - +assert_equal+
65
+ # - +assert_instance_of+
66
+ # - +assert_kind_of+
67
+ # - +assert_operator+
68
+ # - +assert_match+
69
+ # - +assert_not_nil+
70
+ #
71
+ # use <code>assert{ 2.1 }</code> instead.
72
+ #
73
+ # If no block is provided, the assertion calls +assert_classic+,
74
+ # which simulates RubyUnit's standard <code>assert()</code>.
75
+ if block
76
+ assert_ *args, &block
77
+ else
78
+ assert_classic *args
79
+ end
80
+ return true # or die trying ;-)
81
+ end
82
+
83
+ module Coulor #:nodoc:
84
+ def colorize(we_color)
85
+ @@we_color = we_color
86
+ end
87
+ unless defined? BOLD
88
+ BOLD = "\e[1m"
89
+ CLEAR = "\e[0m"
90
+ end # ERGO modularize these; anneal with Win32
91
+ def colour(text, colour_code)
92
+ return colour_code + text + CLEAR if colorize?
93
+ return text
94
+ end
95
+ def colorize? # ERGO how other libraries set these options transparent??
96
+ we_color = (@@we_color rescue true) # ERGO parens needed?
97
+ return false if ENV['EMACS'] == 't'
98
+ return (we_color == :always or we_color && $stdout.tty?)
99
+ end
100
+ def bold(text)
101
+ return BOLD + text + CLEAR if colorize?
102
+ return text
103
+ end
104
+ def green(text); colour(text, "\e[32m"); end
105
+ def red(text); colour(text, "\e[31m"); end
106
+ def magenta(text); colour(text, "\e[35m"); end
107
+ def blue(text); colour(text, "\e[34m"); end
108
+ def orange(text); colour(text, "\e[3Bm"); end
109
+ end
110
+
111
+ class RubyReflector
112
+ attr_accessor :captured_block_vars,
113
+ :args
114
+
115
+ include Coulor
116
+
117
+ def split_and_read(called)
118
+ if called =~ /([^:]+):(\d+):/
119
+ file, line = $1, $2.to_i
120
+ return File.readlines(file)[line - 1 .. -1]
121
+ end
122
+
123
+ return nil
124
+ end
125
+
126
+ def __evaluate_diagnostics
127
+ @__additional_diagnostics.each_with_index do |d, x|
128
+ @__additional_diagnostics[x] = d.call if d.respond_to? :call
129
+ end
130
+ end # CONSIDER pass the same args as blocks take?
131
+
132
+ def __build_message(reflection)
133
+ __evaluate_diagnostics
134
+ return (@__additional_diagnostics.uniq + [reflection]).compact.join("\n")
135
+ end # TODO move this fluff to the ruby_reflector!
136
+
137
+ def format_inspection(inspection, spaces)
138
+ spaces = ' ' * spaces
139
+ inspection = inspection.gsub('\n'){ "\\n\" +\n \"" } if inspection =~ /^".*"$/
140
+ inspection = inspection.gsub("\n"){ "\n" + spaces }
141
+ return inspection.lstrip
142
+ end
143
+
144
+ def format_assertion_result(assertion_source, inspection)
145
+ spaces = " --> ".length
146
+ inspection = format_inspection(inspection, spaces)
147
+ return assertion_source.rstrip + "\n --> #{inspection.lstrip}\n"
148
+ end
149
+
150
+ def format_capture(width, snip, value)
151
+ return "#{ format_snip(width, snip) } --> #{ format_value(width, value) }"
152
+ end
153
+
154
+ def format_value(width, value) # TODO width is a de-facto instance variable
155
+ width += 4
156
+ source = value.pretty_inspect.rstrip
157
+ return format_inspection(source, width)
158
+ end
159
+
160
+ def measure_capture(kap)
161
+ return kap.split("\n").inject(0){|x, v| v.strip.length > x ? v.strip.length : x } if kap.match("\n")
162
+ kap.length
163
+ # TODO need the if?
164
+ end
165
+
166
+ end
167
+
168
+ def colorize(to_color)
169
+ RubyReflector.new.colorize(to_color)
170
+ end
171
+
172
+ # TODO work with raw MiniTest
173
+
174
+ # This is a copy of the classic assert, so your pre-existing
175
+ # +assert+ calls will not change their behavior
176
+ #
177
+ if defined? MiniTest::Assertion
178
+ def assert_classic(test, msg=nil)
179
+ msg ||= "Failed assertion, no message given."
180
+ self._assertions += 1
181
+ unless test then
182
+ msg = msg.call if Proc === msg
183
+ raise MiniTest::Assertion, msg
184
+ end
185
+ true
186
+ end
187
+
188
+ def add_assertion
189
+ self._assertions += 1
190
+ end
191
+ else
192
+ def assert_classic(boolean, message=nil)
193
+ #_wrap_assertion do
194
+ assert_block("assert<classic> should not be called with a block.") { !block_given? }
195
+ assert_block(build_message(message, "<?> is not true.", boolean)) { boolean }
196
+ #end
197
+ end
198
+ end
199
+
200
+ # The new <code>assert()</code> calls this to interpret
201
+ # blocks of assertive statements.
202
+ #
203
+ def assert_(diagnostic = nil, options = {}, &block)
204
+ options[:keep_diagnostics] or add_diagnostic :clear
205
+
206
+ begin
207
+ got = block.call(*options[:args]) and add_assertion and return got
208
+ rescue FlunkError
209
+ raise # asserts inside assertions that fail do not decorate the outer assertion
210
+ rescue => got
211
+ add_exception got
212
+ end
213
+
214
+ flunk diagnose(diagnostic, got, caller[1], options, block)
215
+ end
216
+
217
+ def add_exception(ex)
218
+ ex.backtrace[0..10].each do |line|
219
+ add_diagnostic ' ' + line
220
+ end
221
+ end
222
+
223
+ # This assertion replaces:
224
+ #
225
+ # - +assert_nil+
226
+ # - +assert_no_match+
227
+ # - +assert_not_equal+
228
+ #
229
+ # It faults, and prints its block's contents and values,
230
+ # if its block returns non-+false+ and non-+nil+.
231
+ #
232
+ def deny(diagnostic = nil, options = {}, &block)
233
+ # "None shall pass!" --the Black Knight
234
+
235
+ options[:keep_diagnostics] or add_diagnostic :clear
236
+
237
+ begin
238
+ got = block.call(*options[:args]) or (add_assertion and return true)
239
+ rescue FlunkError
240
+ raise
241
+ rescue => got
242
+ add_exception got
243
+ end
244
+
245
+ flunk diagnose(diagnostic, got, caller[0], options, block)
246
+ end # "You're a looney!" -- King Arthur
247
+
248
+ alias denigh deny # to line assert{ ... } and
249
+ # denigh{ ... } statements up neatly!
250
+
251
+ #~ def __reflect_assertion(called, options, block, got)
252
+ #~ effect = RubyReflector.new(called)
253
+ #~ effect.args = *options[:args]
254
+ #~ return effect.reflect_assertion(block, got)
255
+ #~ end
256
+
257
+ #~ def __reflect_assertion(called, options, block, got)
258
+ #~ effect = RubyReflector.new(called)
259
+ #~ effect.args = *options[:args]
260
+ #~ effect.block = block
261
+ #~ return effect.reflect_assertion(block, got) # TODO merge this and its copies into assert2_utilities
262
+ #~ end
263
+
264
+ #!doc!
265
+ def diagnose(diagnostic = nil, got = nil, called = caller[0],
266
+ options = {}, block = nil) # TODO make this directly callable
267
+ rf = RubyReflector.new
268
+ rf.diagnose(diagnostic, got, called, options, block, @__additional_diagnostics)
269
+ #~ options = { :args => [] }.merge(options)
270
+ #~ # CONSIDER only capture the block_vars if there be args?
271
+ #~ @__additional_diagnostics.unshift diagnostic
272
+ #~ return __build_message(__reflect_assertion(called, options, block, got))
273
+ end
274
+
275
+ if RubyReflector::HAS_RUBYNODE
276
+ # wrap this common idiom:
277
+ # foo = assemble()
278
+ # deny{ foo.bar() }
279
+ # foo.activate()
280
+ # assert{ foo.bar() }
281
+ #
282
+ # that becomes:
283
+ # foo = assemble()
284
+ #
285
+ # assert_yin_yang proc{ foo.bar() } do
286
+ # foo.activate()
287
+ # end
288
+ #
289
+ def assert_yin_yang(*args, &block)
290
+ # prock(s), diagnostic = nil, &block)
291
+ procks, diagnostic = args.partition{|p| p.respond_to? :call }
292
+ block ||= procks.shift
293
+ source = reflect_source(&block)
294
+ fuss = [diagnostic, "fault before calling:", source].compact.join("\n")
295
+ procks.each do |prock| deny(fuss, &prock); end
296
+ block.call
297
+ fuss = [diagnostic, "fault after calling:", source].compact.join("\n")
298
+ procks.each do |prock| assert(fuss, &prock); end
299
+ end
300
+
301
+ # the prock assertion must pass on both sides of the called block
302
+ #
303
+ def deny_yin_yang(*args, &block)
304
+ # prock(s), diagnostic = nil, &block)
305
+ procks, diagnostic = args.partition{|p| p.respond_to? :call }
306
+ block ||= procks.shift
307
+ source = reflect_source(&block)
308
+ fuss = [diagnostic, "fault before calling:", source].compact.join("\n")
309
+ procks.each do |prock| assert(fuss, &prock); end
310
+ block.call
311
+ fuss = [diagnostic, "fault after calling:", source].compact.join("\n")
312
+ procks.each do |prock| assert(fuss, &prock); end
313
+ end
314
+
315
+ end
316
+
317
+ end ; end ; end
318
+
319
+ class File
320
+ def self.write(filename, contents)
321
+ open(filename, 'w'){|f| f.write(contents) }
322
+ end
323
+ end