assert2 0.3.2 → 0.3.3

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