Class: Minitest::Minitap

Inherits:
StatisticsReporter
  • Object
show all
Defined in:
lib/minitap/minitest5.rb

Overview

Base class for TapY and TapJ runners.

Direct Known Subclasses

TapJ, TapY

Constant Summary collapse

REVISION =

TAP-Y/J Revision

4
IGNORE_CALLERS =

Backtrace patterns to be omitted.

$RUBY_IGNORE_CALLERS

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Minitap

Initialize new Minitap Minitest reporter.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/minitap/minitest5.rb', line 39

def initialize(options = {})
  io = options.delete(:io) || $stdout

  super(io, options)

  # since tapout uses a unix pipe, we don't want any buffering
  io.sync = true

  #@_stdout = StringIO.new
  #@_stderr = StringIO.new

  #@test_results = {}
  #self.assertion_count = 0

  @_source_cache = {}
end

Instance Attribute Details

#case_start_timeObject

Returns the value of attribute case_start_time.



35
36
37
# File 'lib/minitap/minitest5.rb', line 35

def case_start_time
  @case_start_time
end

#suite_start_timeObject

Returns the value of attribute suite_start_time.



34
35
36
# File 'lib/minitap/minitest5.rb', line 34

def suite_start_time
  @suite_start_time
end

#test_casesObject (readonly)

Test results. attr_reader :test_results



31
32
33
# File 'lib/minitap/minitest5.rb', line 31

def test_cases
  @test_cases
end

#test_countObject (readonly)

Returns the value of attribute test_count.



32
33
34
# File 'lib/minitap/minitest5.rb', line 32

def test_count
  @test_count
end

#test_start_timeObject

Returns the value of attribute test_start_time.



36
37
38
# File 'lib/minitap/minitest5.rb', line 36

def test_start_time
  @test_start_time
end

Instance Method Details

#after_case(test_case) ⇒ Object



147
148
149
# File 'lib/minitap/minitest5.rb', line 147

def after_case(test_case)
  tapout_after_case(test_case)
end

#after_suiteObject

(test_cases)



138
139
140
# File 'lib/minitap/minitest5.rb', line 138

def after_suite() #(test_cases)
  tapout_after_suite()
end

#after_test(result) ⇒ Object



156
157
158
# File 'lib/minitap/minitest5.rb', line 156

def after_test(result)
  tapout_after_test(result)
end

#before_case(test_case) ⇒ Object



142
143
144
145
# File 'lib/minitap/minitest5.rb', line 142

def before_case(test_case)
  @case_start_time = Time.now
  tapout_before_case(test_case)
end

#before_suite(test_cases) ⇒ Object



132
133
134
135
136
# File 'lib/minitap/minitest5.rb', line 132

def before_suite(test_cases)
  @suite_start_time = Time.now
  count_tests!(test_cases)
  tapout_before_suite()
end

#before_test(result) ⇒ Object



151
152
153
154
# File 'lib/minitap/minitest5.rb', line 151

def before_test(result)
  @test_start_time = Time.now
  tapout_before_test(result)
end

#capture_ioObject (private)



624
625
626
627
# File 'lib/minitap/minitest5.rb', line 624

def capture_io
  @_stdout, @_stderr = $stdout, $stderr
  $stdout, $stderr = StringIO.new, StringIO.new
end

#clean_message(message) ⇒ Object (private)



605
606
607
# File 'lib/minitap/minitest5.rb', line 605

def clean_message(message)
  message.strip #.gsub(/\s*\n\s*/, "\n")
end

#code_snippet(file, line) ⇒ Object (private)

Returns a String of source code.



555
556
557
558
559
560
561
562
563
564
565
566
567
568
# File 'lib/minitap/minitest5.rb', line 555

def code_snippet(file, line)
  s = []
  if File.file?(file)
    source = source(file)
    radius = 2 # TODO: make customizable (number of surrounding lines to show)
    region = [line - radius, 1].max ..
             [line + radius, source.length].min

    s = region.map do |n|
      {n => source[n-1].chomp}
    end
  end
  return s
end

#count_tests!(test_cases) ⇒ Object (private)

#

def add_test_result(suite, test, test_runner)
  self.report[suite] ||= {}
  self.report[suite][test.to_sym] = test_runner

  self.assertion_count += test_runner.assertions

  case test_runner.result
  when :skip then self.skips += 1
  when :failure then self.failures += 1
  when :error then self.errors += 1
  end
end


270
271
272
273
274
275
276
277
# File 'lib/minitap/minitest5.rb', line 270

def count_tests!(test_cases)
  filter = options[:filter] || '/./'
  filter = Regexp.new $1 if filter =~ /\/(.*)\//

  @test_count = test_cases.inject(0) do |acc, test_case|
    acc + test_case.runnable_methods.grep(filter).length
  end
end

#filter_backtrace(backtrace) ⇒ Object (private)

Clean the backtrace of any reference to test framework itself.



537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
# File 'lib/minitap/minitest5.rb', line 537

def filter_backtrace(backtrace)
  ## remove backtraces that match any pattern in IGNORE_CALLERS
  trace = backtrace.reject{|b| IGNORE_CALLERS.any?{|i| i=~b}}
  ## remove `:in ...` portion of backtraces
  trace = trace.map do |bt| 
    i = bt.index(':in')
    i ? bt[0...i] :  bt
  end
  ## now apply MiniTest's own filter (note: doesn't work if done first, why?)
  trace = Minitest::filter_backtrace(trace)
  ## if the backtrace is empty now then revert to the original
  trace = backtrace if trace.empty?
  ## simplify paths to be relative to current workding diectory
  trace = trace.map{ |bt| bt.sub(Dir.pwd+File::SEPARATOR,'') }
  return trace
end

#filtered_tests(test_case) ⇒ Object (private)



248
249
250
251
252
# File 'lib/minitap/minitest5.rb', line 248

def filtered_tests(test_case) #suite, type
  filter = options[:filter] || '/./'
  filter = Regexp.new($1) if filter =~ /\/(.*)\//
  test_case.runnable_methods.grep(filter)
end

#location(e) ⇒ Object (private)

Get location of exception.



593
594
595
596
597
598
599
600
601
602
# File 'lib/minitap/minitest5.rb', line 593

def location e # :nodoc:
  last_before_assertion = ""
  e.backtrace.reverse_each do |s|
    break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
    last_before_assertion = s
  end
  file, line = last_before_assertion.sub(/:in .*$/, '').split(':')
  line = line.to_i if line
  return file, line
end

#p(*args) ⇒ Object

Stub out the three IO methods used by the built-in reporter.



231
232
233
# File 'lib/minitap/minitest5.rb', line 231

def p(*args)
  args.each{ |a| io.print(a.inspect); puts }
end

#parse_source_location(caller) ⇒ Object (private)

Parse source location from caller, caller or an Exception object.



579
580
581
582
583
584
585
586
587
588
589
590
# File 'lib/minitap/minitest5.rb', line 579

def parse_source_location(caller)
  case caller
  when Exception
    trace  = caller.backtrace.reject{ |bt| bt =~ INTERNALS }
    caller = trace.first
  when Array
    caller = caller.first
  end
  caller =~ /(.+?):(\d+(?=:|\z))/ or return ""
  source_file, source_line = $1, $2.to_i
  return source_file, source_line
end


239
240
241
# File 'lib/minitap/minitest5.rb', line 239

def print(*args)
  io.print(*args)
end

#puts(*args) ⇒ Object



235
236
237
# File 'lib/minitap/minitest5.rb', line 235

def puts(*args)
  io.puts(*args)
end

#record(minitest_result) ⇒ Object

Process a test result.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/minitap/minitest5.rb', line 76

def record(minitest_result)
  super(minitest_result)

  result = TestResult.new(minitest_result)

  #if exception #&& ENV['minitap_debug']
  #  STDERR.puts exception
  #  STDERR.puts exception.backtrace.join("\n")
  #end

  #@test_results[suite] ||= {}
  #@test_results[suite][test.to_sym] = record

  case_change = false

  if @previous_case != result.test_case
    case_change = true  
    before_case(result.test_case)
  end

  #before_test(result)
  case result.type
  when :skip
    test_skip(result)
  when :pass
    test_pass(result)
  when :fail
    test_fail(result)
  when :err
    test_err(result)
  end
  after_test(result)

  if case_change
    after_case(result.test_case)
  end

  @previous_case = result.test_case
end

#record_stdcom(doc) ⇒ Object (private)



515
516
517
518
519
520
521
522
523
524
525
526
# File 'lib/minitap/minitest5.rb', line 515

def record_stdcom(doc)
  begin
    doc['stdout'] = $stdout.string unless $stdout.length == 0 #empty?
    doc['stderr'] = $stderr.string unless $stderr.length == 0 #empty?
    $stdout.close; $stderr.close
    $stdout, $stderr = StringIO.new, StringIO.new
  #end
  rescue => err
    uncapture_io
    raise err
  end
end

#reportObject

Minitest’s finalization hook.



119
120
121
122
123
124
125
# File 'lib/minitap/minitest5.rb', line 119

def report
  super

  uncapture_io

  after_suite()
end

#source(file) ⇒ Object (private)

Cache source file text. This is only used if the TAP-Y stream doesn not provide a snippet and the test file is locatable.



572
573
574
575
576
# File 'lib/minitap/minitest5.rb', line 572

def source(file)
  @_source_cache[file] ||= (
    File.readlines(file)
  )
end

#startObject

Minitest’s initial hook ran just before testing begins.



59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/minitap/minitest5.rb', line 59

def start
  super

  @test_cases = Runnable.runnables

  capture_io

  #@_stdout, @_stderr = capture_io do
  #  super_result = super(suite, type)
  #end

  before_suite(@test_cases)
end

#tapout_after_case(test_case) ⇒ Object (private)



327
328
# File 'lib/minitap/minitest5.rb', line 327

def tapout_after_case(test_case)
end

#tapout_after_suiteObject (private)



296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/minitap/minitest5.rb', line 296

def tapout_after_suite()
  doc = {
    'type' => 'final',
    'time' => Time.now - self.suite_start_time,
    'counts' => {
      'total' => self.test_count,
      'pass'  => self.test_count - self.failures - self.errors - self.skips,
      'fail'  => self.failures,
      'error' => self.errors,
      'omit'  => self.skips,
      'todo'  => 0  # TODO: does minitest support pending tests?
    }
  }
  return doc
end

#tapout_after_test(result) ⇒ Object (private)



335
336
# File 'lib/minitap/minitest5.rb', line 335

def tapout_after_test(result)
end

#tapout_before_case(test_case) ⇒ Object (private)



313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/minitap/minitest5.rb', line 313

def tapout_before_case(test_case)
  doc = {
    'type'    => 'case',
    'subtype' => '',
    'label'   => "#{test_case}",
    'level'   => 0
  }

  record_stdcom(doc)

  return doc
end

#tapout_before_suiteObject (private)



284
285
286
287
288
289
290
291
292
293
# File 'lib/minitap/minitest5.rb', line 284

def tapout_before_suite()
  doc = {
    'type'  => 'suite',
    'start' => self.suite_start_time.strftime('%Y-%m-%d %H:%M:%S'),
    'count' => self.test_count,
    'seed'  => self.options[:seed],
    'rev'   => REVISION
  }
  return doc
end

#tapout_before_test(result) ⇒ Object (private)



331
332
# File 'lib/minitap/minitest5.rb', line 331

def tapout_before_test(result)
end

#tapout_test_error(result) ⇒ Object (private)

TAP record for error.

Returns [Hash].



465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
# File 'lib/minitap/minitest5.rb', line 465

def tapout_test_error(result)
  e = result.exception
  e_file, e_line = location(e)
  r_file = e_file.sub(Dir.pwd+'/', '')

  doc = {
    'type'        => 'test',
    'subtype'     => '',
    'status'      => 'error',
    'label'       => "#{result.label}",
    #'setup' => "foo instance",
    #'expected' => 2,
    #'returned' => 1,
    #'file' => test/test_foo.rb
    #'line' => 45
    #'source' => ok 1, 2
    #'snippet' =>
    #  - 44: ok 0,0
    #  - 45: ok 1,2
    #  - 46: ok 2,4
    #'coverage' =>
    #  'file' => lib/foo.rb
    #  'line' => 11..13
    #  'code' => Foo#*
    'exception' => {
      'message'   => clean_message(e.error.message),
      'class'     => e.error.class.name,
      'file'      => r_file,
      'line'      => e_line,
      'source'    => source(e_file)[e_line-1].strip,
      'snippet'   => code_snippet(e_file, e_line),
      'backtrace' => filter_backtrace(e.backtrace)
    },
    'time' => Time.now - self.suite_start_time  # result.time
  }

  record_stdcom(doc)

  #stdout, stderr = @_stdout, @_stderr
  #doc['stdout'] = stdout unless stdout.empty?
  #doc['stderr'] = stderr unless stderr.empty?

  return doc
end

#tapout_test_failure(result) ⇒ Object (private)

TAP record for failure.

Returns [Hash].



417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
# File 'lib/minitap/minitest5.rb', line 417

def tapout_test_failure(result)
  e = result.exception
  e_file, e_line = location(result.exception)
  r_file = e_file.sub(Dir.pwd+'/', '')

  doc = {
    'type'        => 'test',
    'subtype'     => '',
    'status'      => 'fail',
    'label'       => "#{result.label}",
    #'setup' => "foo instance",
    #'expected' => 2,
    #'returned' => 1,
    #'file' => test/test_foo.rb
    #'line' => 45
    #'source' => ok 1, 2
    #'snippet' =>
    #  - 44: ok 0,0
    #  - 45: ok 1,2
    #  - 46: ok 2,4
    #'coverage' =>
    #  'file' => lib/foo.rb
    #  'line' => 11..13
    #  'code' => Foo#*
    'exception' => {
      'message'   => clean_message(e.message),  
      'class'     => e.class.name,
      'file'      => r_file,
      'line'      => e_line,
      'source'    => source(e_file)[e_line-1].strip,
      'snippet'   => code_snippet(e_file, e_line),
      'backtrace' => filter_backtrace(e.backtrace)
    },
    'time' => Time.now - self.suite_start_time   # result.time
  }

  record_stdcom(doc)

  #stdout, stderr = @_stdout, @_stderr
  #doc['stdout'] = stdout unless stdout.empty?
  #doc['stderr'] = stderr unless stderr.empty?

  return doc
end

#tapout_test_pass(result) ⇒ Object (private)

TAP record for pass.

Returns [Hash].



341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/minitap/minitest5.rb', line 341

def tapout_test_pass(result) #suite, test, test_runner
  doc = {
    'type'        => 'test',
    'subtype'     => '',
    'status'      => 'pass',
    #'setup': foo instance
    'label'       => "#{result.label}",
    #'expected' => 2
    #'returned' => 2
    #'file' => 'test/test_foo.rb'
    #'line': 45
    #'source': ok 1, 2
    #'snippet':
    #  - 44: ok 0,0
    #  - 45: ok 1,2
    #  - 46: ok 2,4
    #'coverage':
    #  file: lib/foo.rb
    #  line: 11..13
    #  code: Foo#*
    'time' => Time.now - self.suite_start_time
  }

  record_stdcom(doc)

  #stdout, stderr = @_stdout, @_stderr
  #doc['stdout'] = stdout unless stdout.empty?
  #doc['stderr'] = stderr unless stderr.empty?

  return doc
end

#tapout_test_skip(result) ⇒ Object (private)

TAP record for skip.

Returns [Hash].



376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/minitap/minitest5.rb', line 376

def tapout_test_skip(result) #suite, test, test_runner
  e = result.exception
  e_file, e_line = location(result.exception)
  r_file = e_file.sub(Dir.pwd+'/', '')

  doc = {
    'type'        => 'test',
    'subtype'     => '',
    'status'      => 'skip',
    'label'       => "#{result.label}",
    #'setup' => "foo instance",
    #'expected' => 2,
    #'returned' => 1,
    #'file' => test/test_foo.rb
    #'line' => 45
    #'source' => ok 1, 2
    #'snippet' =>
    #  - 44: ok 0,0
    #  - 45: ok 1,2
    #  - 46: ok 2,4
    #'coverage' =>
    #  'file' => lib/foo.rb
    #  'line' => 11..13
    #  'code' => Foo#*
    'exception' => {
      'message'   => clean_message(e.message),
      'class'     => e.class.name,
      'file'      => r_file,
      'line'      => e_line,
      'source'    => source(e_file)[e_line-1].strip,
      'snippet'   => code_snippet(e_file, e_line),
      'backtrace' => filter_backtrace(e.backtrace)
    },
    'time' => Time.now - self.suite_start_time
  }
  return doc
end

#test_err(result) ⇒ Object



172
173
174
# File 'lib/minitap/minitest5.rb', line 172

def test_err(result)
  tapout_test_error(result)
end

#test_fail(result) ⇒ Object



168
169
170
# File 'lib/minitap/minitest5.rb', line 168

def test_fail(result)
  tapout_test_failure(result)
end

#test_pass(result) ⇒ Object



164
165
166
# File 'lib/minitap/minitest5.rb', line 164

def test_pass(result)
  tapout_test_pass(result)
end

#test_skip(result) ⇒ Object



160
161
162
# File 'lib/minitap/minitest5.rb', line 160

def test_skip(result)
  tapout_test_skip(result)
end

#uncapture_ioObject (private)



630
631
632
# File 'lib/minitap/minitest5.rb', line 630

def uncapture_io
  $stdout, $stderr = @_stdout, @_stderr
end