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.
@@ -0,0 +1,726 @@
1
+ require 'pp'
2
+ require 'ripper' # note we only work with Ruby >= 1.9 !
3
+
4
+ module Test; module Unit; module Assertions
5
+
6
+ class RubyReflector # this class turns hamburger back into live cattle
7
+ HAS_RUBYNODE = false
8
+ HAS_RIPPER = true
9
+
10
+ attr_reader :assertion_source,
11
+ :captures,
12
+ :reflect
13
+ attr_accessor :ripped
14
+ attr_writer :block
15
+
16
+ def initialize(called = '')
17
+ reset(called)
18
+ @reflect = ''
19
+ @captures = []
20
+ @line_number = nil
21
+ end
22
+
23
+ def reset(called)
24
+ source = split_and_read(called)
25
+ @ripped = rip(source) if source
26
+ end
27
+
28
+ def rip(lines)
29
+ lines = [lines].flatten
30
+ x = 0
31
+
32
+ until exp = Ripper.sexp(@assertion_source = lines[0..x].join)
33
+ (x += 1) >= lines.length and
34
+ raise 'your assertion failed, but your source is ' +
35
+ 'incorrectly formatted and resists reflection!' +
36
+ "\nSee: http://assert2.rubyforge.org/assert21.html#Warning_Put_Assertions_on_Separate_Lines\n"
37
+ lines.inspect
38
+ end
39
+
40
+ return exp.last
41
+ end
42
+
43
+ class Nada; end
44
+
45
+ def detect(ident)
46
+ if @args and @captured_block_vars
47
+ ident = "#{@captured_block_vars} = $__args.kind_of?(Array) && $__args.length == 1 ? $__args.first : $__args\n" +
48
+ ident
49
+ $__args = @args
50
+ end
51
+ return eval(ident, @block.binding)
52
+ rescue ArgumentError => e
53
+ return Nada if e.message =~ /wrong number of arguments \(0 for /
54
+ return e.inspect
55
+ rescue Exception => e
56
+ return e.inspect
57
+ end
58
+
59
+ def capture_source
60
+ longness = @reflect.length
61
+ yield
62
+ return @reflect[longness..-1].strip
63
+ end
64
+
65
+ def capture(&block)
66
+ snip = capture_source(&block)
67
+ return if @captures.select{|k,v| k == snip }.length > 0 # TODO there's probably a shorter method...
68
+
69
+ if @block
70
+ value = detect(snip)
71
+ capture_snip(snip, value) unless (value == Nada rescue false)
72
+ end
73
+ end
74
+
75
+ def capture_snip(snip, value)
76
+ return if snip =~ /^"(.*)"$/ and $1 == value
77
+ return if snip =~ /^\/(.*)\/$/ and $1.match(value) # an unmashed string or regexp!
78
+ @captures << [snip, value]
79
+ end
80
+
81
+ def extract_block(rippage = @ripped)
82
+ brace_block = rippage.first
83
+ # CONSIDER assert brace_block.first == method_add_block
84
+ # and brace_block.second includes assert
85
+ brace_block = brace_block.last
86
+
87
+ if block_var = brace_block[1]
88
+ ripper = RubyReflector.new
89
+ ripper.sender block_var
90
+ @captured_block_vars = ripper.reflect.sub(/^\|/, '').sub(/\| $/, '')
91
+ end
92
+ return brace_block[2]
93
+ end
94
+
95
+ # CONSIDER extract_block must not skip block-vars - intercept
96
+ # them here not down there
97
+
98
+ def format_snip(width, snip)
99
+ snips = snip.split("\n")
100
+
101
+ if snips.length > 1 and
102
+ snips.inject(0){|x, v| v.strip.length > x ? v.strip.length : x } <= width # TODO we have seen that inject before
103
+ snips.last.replace("%*s" % [width, snips.last.strip])
104
+ return snips.join("\n")
105
+ end
106
+
107
+ return "%*s" % [width, snip] if snip.length <= width
108
+ chop = snip.scan(/(\w+[[:punct:]]?)/).flatten
109
+ snip = ''
110
+ length = 0 # TODO we probly don't need this stuff!
111
+
112
+ chop.each do |snippet|
113
+ (snip << "\n"; length = 0) if length + snippet.length > width
114
+ snip << snippet
115
+ length += snippet.length
116
+ end
117
+
118
+ return snip.split("\n").map{|snippet| format_snip(width, snippet) }.join("\n")
119
+ end
120
+
121
+ def format_captures
122
+ width = @captures.inject(0){|x, (k, v)|
123
+ e = measure_capture(k)
124
+ x < e ? e : x
125
+ }
126
+ return @captures.map{|snip, capture|
127
+ format_capture width, snip, capture
128
+ }.join("\n")
129
+ end
130
+
131
+ def cycle(args, tween = nil)
132
+ waz = false
133
+ args.each do |arg|
134
+ if arg and arg != []
135
+ sink tween if tween and waz
136
+ if arg.class == Array and arg.first.class == Array
137
+ cycle arg, tween
138
+ else
139
+ sender arg
140
+ end
141
+ waz = true
142
+ end
143
+ end
144
+ end
145
+
146
+ def sink(text)
147
+ @reflect << text
148
+ end
149
+
150
+ def sender(args)
151
+ if args.inspect =~ /, \[(\d+), \d+\]\]$/
152
+ lineno = $1.to_i
153
+ @line_number ||= lineno
154
+
155
+ if @line_number < lineno
156
+ @reflect << "\n "
157
+ @line_number = lineno
158
+ end
159
+ end
160
+
161
+ send :"_#{args[0]}", *args[1..-1] if args
162
+ end
163
+
164
+ # TODO google for or claim 'motion potion'
165
+
166
+ def wrap(ldelim, rdelim = ldelim)
167
+ sink ldelim
168
+ yield
169
+ sink rdelim
170
+ end
171
+
172
+ %w( tstring_content ).each do |thang| # TODO shrimplify
173
+ define_method '_@' + thang do |arg, at|
174
+ wrap @strung ? '' : '"' do
175
+ arg.gsub!(/(^|[^\\])"/, '\1\"')
176
+ sink arg
177
+ end
178
+ end
179
+ end
180
+
181
+ %w( backref CHAR const ivar kw ident gvar int
182
+ op period regexp_end ).each do |thang|
183
+ define_method '_@' + thang do |arg, at|
184
+ sink arg.to_s
185
+ end
186
+ end
187
+
188
+ %w( label ).each do |thang|
189
+ define_method '_@' + thang do |arg, at|
190
+ sink ':' + arg.sub(/:$/, '')
191
+ end
192
+ end
193
+
194
+ %w( heredoc_end ).each do |thang|
195
+ define_method '_@' + thang do |arg, at|
196
+ raise 'the ripper library cannot see "heredoc" notation. take it out of your block'
197
+ end
198
+ end
199
+
200
+ def _command_call(*args)
201
+ sender args.shift
202
+ sink args.shift.to_s
203
+ sender args.shift
204
+
205
+ if args.first.compact != [:params]
206
+ sink ' '
207
+ end
208
+ cycle args
209
+ end
210
+
211
+ def _super(*args)
212
+ sink 'super'
213
+ cycle args
214
+ end
215
+
216
+ def _zsuper(*args)
217
+ sink 'super'
218
+ end
219
+
220
+ def _mrhs_add_star(*args)
221
+ sink '*'
222
+ sender args.last[1]
223
+
224
+ wrap '[', ']' do
225
+ cycle args.last[2..-1]
226
+ end
227
+ # cycle args.last
228
+ # CONSIDER what's the [] do?
229
+ #~ [[],
230
+ #~ [:aref,
231
+ #~ [:var_ref, [:@ident, "options", [727, 19]]],
232
+ #~ [:args_add_block,
233
+ #~ [[:symbol_literal, [:symbol, [:@ident, "args", [727, 28]]]]],
234
+ #~ false]]]
235
+ end
236
+
237
+ def _command(*args)
238
+ capture do
239
+ args.each do |item|
240
+ if item.first.class == Symbol
241
+ sender item
242
+ sink ' ' if item == args.first
243
+ else
244
+ cycle item
245
+ end
246
+ end
247
+ end
248
+ end
249
+
250
+ def _bare_assoc_hash(*args); cycle args, ', '; end
251
+
252
+ def _string_literal(*args)
253
+ capture do
254
+ waz_strung = @strung
255
+ @strung = true
256
+ wrap('"'){ cycle args }
257
+ @strung = waz_strung
258
+ end
259
+ end
260
+
261
+ def _string_content(*args)
262
+ cycle args # CONSIDER what be between them?
263
+ end
264
+
265
+ def _string_concat(*args)
266
+ cycle args, ' '
267
+ end
268
+
269
+ def _dyna_symbol(*args)
270
+ @strung = true
271
+ wrap ':"', '"' do cycle args end
272
+ @strung = false
273
+ end
274
+
275
+ def _string_embexpr(*args)
276
+ waz_strung = @strung
277
+ @strung = false
278
+ wrap '#{ ', ' }' do cycle args end
279
+ @strung = waz_strung
280
+ end
281
+
282
+ def _field(*args)
283
+ sender args.shift
284
+ sink args.shift.to_s
285
+ sender args.shift
286
+ end
287
+
288
+ def _array(*args)
289
+ wrap('[', ']'){ cycle args, ', ' }
290
+ end
291
+
292
+ def _regexp_literal(*args)
293
+ capture do
294
+ regexp_end = args.pop
295
+ sender regexp_end
296
+ @strung = true
297
+ cycle args
298
+ @strung = false
299
+ sender regexp_end
300
+ end
301
+ end
302
+
303
+ def delimit(what = ', ')
304
+ longness = @reflect.length
305
+ yield
306
+ sink what if longness < @reflect.length
307
+ end
308
+
309
+ def _args_add_star(*args)
310
+ delimit do
311
+ cycle args.shift, ', '
312
+ end
313
+
314
+ sink '*'
315
+ sender args.shift
316
+ end
317
+
318
+ def _class(*args)
319
+ sink 'class '
320
+
321
+ args.compact.each do |arg|
322
+ sink ' < ' if arg == args[1]
323
+ sender arg
324
+ end
325
+
326
+ sink "\nend"
327
+ end
328
+
329
+ def _void_stmt(*args)
330
+ end
331
+
332
+ def _const_ref(*args)
333
+ capture{ sender args.last }
334
+ end
335
+
336
+ def _aref_field(*args)
337
+ sender args.shift
338
+ wrap('[', ']'){ sender args.shift }
339
+ end
340
+
341
+ def _def(*args) # the irony _is_ lost on us...
342
+ sink 'def '
343
+ sender args.shift
344
+
345
+ if args.first.first == :paren
346
+ wrap '(', ')' do
347
+ sender args.shift.last
348
+ end
349
+ elsif args.first.compact != [:params]
350
+ sink ' '
351
+ end
352
+
353
+ cycle args
354
+ sink "end"
355
+ end
356
+
357
+ def _defs(*args)
358
+ sink 'def '
359
+ cycle args
360
+ sink "end\n"
361
+ end
362
+
363
+ def _for(*args)
364
+ sink 'for '
365
+ sender args.shift
366
+ sink ' in '
367
+ sender args.shift
368
+ sink "\n"
369
+ cycle args
370
+ sink "\nend"
371
+ end
372
+
373
+ def _until(*args)
374
+ sink 'until '
375
+ sender args.shift
376
+ sink "\n"
377
+ cycle args
378
+ sink "\nend"
379
+ end
380
+
381
+ def _while(*args)
382
+ sink 'while '
383
+ sender args.shift
384
+ sink "\n"
385
+ cycle args
386
+ sink "\nend"
387
+ end
388
+
389
+ def _body_stmt(*args)
390
+ wrap "\n" do
391
+ cycle args.shift, "\n"
392
+ end
393
+
394
+ cycle args, "\n"
395
+ end
396
+
397
+ def _break(*args)
398
+ sink "break\n"
399
+ end
400
+
401
+ def _rescue(*args)
402
+ sink "rescue"
403
+ sink ' ' if args[0]
404
+
405
+ if args[0] and args[0].first.class == Array
406
+ cycle args[0]
407
+ else
408
+ sender args[0]
409
+ end
410
+
411
+ sink ' => ' if args[1]
412
+ sender args[1] if args[1]
413
+ cycle args[2], "\n"
414
+ end
415
+
416
+ def _ensure(*args)
417
+ sink "ensure\n"
418
+ cycle args, "\n"
419
+ end
420
+
421
+ def _call(*args)
422
+ capture do
423
+ if args.first.first.class == Array
424
+ wrap('[ ', ' ]'){ cycle args.shift, ', ' }
425
+ else
426
+ sender args.shift
427
+ end
428
+
429
+ sink args.shift.to_s
430
+ sender args.shift
431
+ end
432
+ end
433
+
434
+ def _const_path_ref(*args)
435
+ capture do
436
+ args.each do |arg|
437
+ sink '::' unless arg == args.first
438
+ sender arg
439
+ end
440
+ end
441
+ end
442
+
443
+ def _dot2(*args); cycle args, '..'; end
444
+ def _dot3(*args); cycle args, '...'; end
445
+
446
+ def _aref(*args)
447
+ capture do
448
+ sender args.shift
449
+ wrap('[', ']'){ sender args.shift }
450
+ end
451
+ end
452
+
453
+ def _if(*args)
454
+ sink "if "
455
+ sender args.shift
456
+ sink "\n"
457
+ cycle args
458
+ sink "\nend"
459
+ end
460
+
461
+ def _unless(*args)
462
+ sink "unless "
463
+ sender args.shift
464
+ sink "\n"
465
+ cycle args
466
+ sink "\nend"
467
+ end
468
+
469
+ def _assoclist_from_args(*args)
470
+ cycle args, ', '
471
+ end
472
+
473
+ def _hash(*args)
474
+ wrap('{ ', ' }'){ cycle args, ', ' }
475
+ end
476
+
477
+ def _else(*args)
478
+ sink "\nelse\n"
479
+ cycle args
480
+ end
481
+
482
+ def _elsif(*args)
483
+ sink "\nelsif "
484
+ sender args.shift
485
+ sink "\n"
486
+ cycle args
487
+ end
488
+
489
+ def _fcall(*args); sender *args; end
490
+ def _method_add_arg(*args); capture{ cycle args }; end
491
+ def _method_add_block(*args); capture{ cycle args }; end
492
+ def _var_field(*args); cycle args, ', '; end
493
+
494
+ def _binary(from, op, to)
495
+ capture do
496
+ sender from
497
+ sink " #{op} "
498
+ sender to
499
+ end
500
+ end
501
+
502
+ def _brace_block(*args)
503
+ sink '{ '
504
+ sender args.shift
505
+ cycle args.first, "\n"
506
+ sink ' }'
507
+ end
508
+
509
+ def _do_block(*args)
510
+ sink " do\n"
511
+ sender args.shift
512
+ cycle args.first, "\n"
513
+ sink "\nend"
514
+ end
515
+
516
+ def _assign(*args)
517
+ sender args.shift
518
+ sink ' = '
519
+
520
+ if args.first.first.class == Array
521
+ wrap('[ ', ' ]'){ cycle args, ', ' }
522
+ else
523
+ cycle args, ', '
524
+ end
525
+ end
526
+
527
+ def _arg_paren(*args);
528
+ sink '('
529
+ cycle args, ', '
530
+ sink ')'
531
+ end
532
+
533
+ def _symbol_literal(*args)
534
+ cycle args, ', '
535
+ end
536
+
537
+ def _symbol(*args);
538
+ sink ':'
539
+ cycle args, ', '
540
+ end
541
+
542
+ def _yield0; sink 'yield'; end
543
+ def _opassign(*args); cycle args, ' '; end
544
+
545
+ def _rest_param(*args)
546
+ sink '*'
547
+ cycle args
548
+ end
549
+
550
+ def _block_var(*args)
551
+ params = args.first[1]
552
+ rest_param = args.first[3]
553
+ blockarg = args.first[5]
554
+ sink '|'
555
+
556
+ cycle params, ', ' if params
557
+
558
+ if rest_param
559
+ sink ', ' if params
560
+ sender rest_param
561
+ end
562
+
563
+ if blockarg
564
+ sink ', ' if params or rest_param
565
+ sender blockarg
566
+ end
567
+
568
+ sink '| '
569
+ end # CONSIDER just call cycle already!
570
+
571
+ def _params(*args)
572
+ cycle args, ', '
573
+ end
574
+
575
+ def _yield(*args)
576
+ if args.first.first == :paren
577
+ sink 'yield('
578
+ sender args.first.last
579
+ sink ')'
580
+ end
581
+ end
582
+
583
+ def _paren(*args)
584
+ sink '('
585
+ cycle args.last, "\n"
586
+ sink ')'
587
+ end
588
+
589
+ def _blockarg(*args)
590
+ sink '&'
591
+ sender args.first
592
+ end
593
+
594
+ def _begin(*args)
595
+ sink "begin"
596
+ sender args.shift
597
+ sink "\nend"
598
+ end
599
+
600
+ def _mlhs_paren(*args)
601
+ if args.first.first.class == Array
602
+ sink '('
603
+ cycle args.first, ', '
604
+ sink ')'
605
+ else
606
+ sender args.first
607
+ end
608
+ end
609
+
610
+ def _assoc_new(*args)
611
+ cycle args, ' => '
612
+ end
613
+
614
+ def _unary(*args)
615
+ capture do
616
+ sink arg = args.shift.to_s
617
+ sink ' ' if arg == 'not'
618
+ cycle args
619
+ end
620
+ end
621
+
622
+ def _var_ref(args); capture{ sender args }; end
623
+ def _class_name_error(args); capture{ sender args }; end
624
+
625
+ def _args_add_block(*args)
626
+ baseline = @reflect.length
627
+ if args.first.first.class == Array
628
+ cycle args.shift, ', '
629
+ else
630
+ thing = args.shift
631
+ sender thing if thing.any?
632
+ end
633
+ if args.first
634
+ sink ', ' if @reflect.length > baseline
635
+ sink '&'
636
+ sender args.first
637
+ end
638
+ end
639
+
640
+ def _massign(*args)
641
+ cycle args.shift, ', '
642
+ sink ' = '
643
+ cycle args
644
+ end
645
+
646
+ def _module(*args)
647
+ sink 'module '
648
+ sender args.shift
649
+ sender args.shift
650
+ sink 'end'
651
+ end
652
+
653
+ def _mrhs_new_from_args(*args) cycle args, ', ' end
654
+
655
+ def _if_mod(*args)
656
+ sender args.last
657
+ sink ' if '
658
+ sender args.first
659
+ end
660
+
661
+ def _unless_mod(*args)
662
+ sender args.last
663
+ sink ' unless '
664
+ sender args.first
665
+ end
666
+
667
+ def _ifop(*args)
668
+ sender args.shift
669
+ sink ' ? '
670
+ sender args.shift
671
+ sink ' : '
672
+ sender args.shift
673
+ end
674
+
675
+ def _return0(*args)
676
+ sink 'return'
677
+ end
678
+
679
+ def _return(*args)
680
+ sink 'return '
681
+ cycle args, ', '
682
+ end
683
+
684
+ def _alias(*args)
685
+ cycle args, ' '
686
+ end
687
+
688
+ def _method_missing(*args)
689
+ puts " rippage = #{ args.pretty_inspect } "
690
+ end
691
+
692
+ def reflect_assertion(block, got)
693
+ self.block = block
694
+
695
+ extract_block.each do |statement|
696
+ sender statement
697
+ end
698
+
699
+ inspection = got.pretty_inspect
700
+
701
+ return format_assertion_result(assertion_source, inspection) +
702
+ format_captures
703
+ end
704
+
705
+ def __reflect_assertion(called, options, block, got)
706
+ effect = self
707
+ effect.args = *options[:args]
708
+ effect.block = block
709
+ reset(called)
710
+ return effect.reflect_assertion(block, got) # TODO merge this and its copies
711
+ end
712
+
713
+ def diagnose(diagnostic = nil, got = nil, called = caller[0],
714
+ options = {}, block = nil, additional_diagnostics)
715
+ @__additional_diagnostics = additional_diagnostics
716
+ # rf.diagnose(diagnostic, got, called, options, block, @__additional_diagnostics)
717
+ options = { :args => [] }.merge(options)
718
+ # CONSIDER only capture the block_vars if there be args?
719
+ @__additional_diagnostics.unshift diagnostic
720
+ return __build_message(__reflect_assertion(called, options, block, got))
721
+ end
722
+
723
+ end
724
+
725
+ end; end; end
726
+