← Back to index

lib/BDD/Behave/Formatter/TAP.rakumod

101 / 101 lines covered (100.0%)

1use BDD::Behave::Failures;2use BDD::Behave::Formatter;34unit class BDD::Behave::Formatter::TAP does BDD::Behave::Formatter;56class Entry {7  has Str $.status   is required;   # 'ok' or 'not ok'8  has Str $.directive is rw;        # 'TODO' or 'SKIP' (optional)9  has Str $.directive-reason is rw;10  has Str $.description is rw = '';11  has @.diagnostics;                # yaml-ish key/value pairs (Array of Pair)12}1314has @!description-stack;15has @!entries;16has Bool $!multi-file = False;17has Bool $!emitted    = False;18has Int  $!failure-watermark = 0;19has Str  $!pending-auto-description;2021method name(--> Str) { 'tap' }2223method suite-loading(Str :$file) { }2425method suite-start($suite, Bool :$multi-file = False) {26  $!multi-file = $multi-file;27}2829method suite-end($suite) { }3031method group-start($group) {32  @!description-stack.push: $group.description;33}3435method group-end($group) {36  @!description-stack.pop;37}3839method group-around-skipped($group) { }4041method !full-description(Str $description --> Str) {42  my @parts = @!description-stack.clone;43  @parts.push: $description;44  @parts.grep(*.defined).join(' ');45}4647method example-start($example, Bool :$auto = False) {48  $!failure-watermark = Failures.list.elems;49  $!pending-auto-description = Str;50}5152method example-auto-description($example, Str :$description) {53  $!pending-auto-description = $description;54}5556method example-pass($example) {57  my $description = $!pending-auto-description // $example.description;58  @!entries.push: Entry.new(59    :status('ok'),60    :description(self!full-description($description)),61  );62  $!pending-auto-description = Str;63}6465method example-fail($example, :$failure-info) {66  my $description = $!pending-auto-description // $example.description;67  my $entry = Entry.new(68    :status('not ok'),69    :description(self!full-description($description)),70  );71  my @diag;72  if $failure-info.defined && $failure-info<exception>.defined {73    @diag.push: 'severity' => 'error';74    @diag.push: 'message'  => $failure-info<exception>.message;75    @diag.push: 'file'     => $example.file.Str;76    @diag.push: 'line'     => ($failure-info<line> // $example.line);77  } else {78    @diag.push: 'severity' => 'fail';79    @diag.push: 'file'     => $example.file.Str;80    @diag.push: 'line'     => ($failure-info<line> // $example.line);81    my $count = Failures.list.elems;82    if $count > $!failure-watermark {83      my $first = Failures.list[$!failure-watermark];84      if $first.message.defined {85        @diag.push: 'message' => $first.message.lines[0];86      } else {87        my $op = $first.negated ?? 'not to be' !! 'to be';88        @diag.push: 'message' => "Expected " ~ $first.given.gist ~ " $op " ~ $first.expected.gist;89        @diag.push: 'got'      => $first.given.gist;90        @diag.push: 'expected' => $first.expected.gist;91      }92    }93  }94  $entry.diagnostics = @diag;95  @!entries.push: $entry;96  $!pending-auto-description = Str;97}9899method example-pending($example) {100  my $reason = $example.get-metadata('pending-reason') // 'pending';101  my $description = $!pending-auto-description // $example.description;102  @!entries.push: Entry.new(103    :status('ok'),104    :description(self!full-description($description)),105    :directive('TODO'),106    :directive-reason($reason),107  );108}109110method example-skipped($example) {111  my $description = $!pending-auto-description // $example.description;112  @!entries.push: Entry.new(113    :status('ok'),114    :description(self!full-description($description)),115    :directive('SKIP'),116    :directive-reason('skipped'),117  );118}119120method example-around-skipped($example) {121  my $description = $!pending-auto-description // $example.description;122  @!entries.push: Entry.new(123    :status('ok'),124    :description(self!full-description($description)),125    :directive('SKIP'),126    :directive-reason('around-each did not invoke continuation'),127  );128}129130method example-slow($example, Real :$threshold) { }131132method run-summary(133  $result,134  Bool :$aborted   = False,135  Int  :$fail-fast = 0,136  Str  :$order     = 'defined',137  Int  :$seed,138) {139  return if $!multi-file;140  self!emit($aborted);141}142143method profile-summary(@records, Int :$limit)        { }144145method benchmark-summary-section(146  @summaries, @regressions,147  Real     :$threshold,148  Str      :$format,149  IO::Path :$output,150  :$runner,151) { }152153method multi-file-overall($result, Str :$order = 'defined', Int :$seed) {154  self!emit;155}156157method multi-file-profile($runner, @records, Int :$limit)        { }158method multi-file-benchmark(159  $runner,160  @summaries, @regressions,161  Real     :$threshold,162  Str      :$format,163  IO::Path :$output,164) { }165166method load-errors(@errors) { }167168method !escape-description(Str $s --> Str) {169  return '' unless $s.defined;170  $s.subst('#', '\\#', :g);171}172173method !yaml-string(Str $s --> Str) {174  return "''" unless $s.defined;175  my $body = $s.subst('\\', '\\\\', :g).subst("'", "''", :g);176  "'" ~ $body ~ "'";177}178179method !render-diag(@diagnostics --> Str) {180  return '' unless @diagnostics.elems;181  my @lines;182  @lines.push: '  ---';183  for @diagnostics -> $pair {184    my $key = $pair.key;185    my $val = $pair.value;186    my $rendered = do given $val {187      when Int { $val.Str }188      when Real { $val.Str }189      default { self!yaml-string($val.Str) }190    };191    @lines.push: "  $key: $rendered";192  }193  @lines.push: '  ...';194  @lines.join("\n");195}196197method !render-entry(Int $index, Entry $entry --> Str) {198  my $desc = self!escape-description($entry.description);199  my $line = "{$entry.status} $index";200  $line ~= " - $desc" if $desc.chars;201  if $entry.directive.defined {202    my $reason = $entry.directive-reason // '';203    $line ~= " # {$entry.directive} {$reason}";204  }205  if $entry.diagnostics.elems {206    $line ~= "\n" ~ self!render-diag($entry.diagnostics);207  }208  $line;209}210211method !emit(Bool $aborted = False) {212  return if $!emitted;213  $!emitted = True;214215  say 'TAP version 13';216  say "1..{@!entries.elems}";217  for @!entries.kv -> $i, $entry {218    say self!render-entry($i + 1, $entry);219  }220  say "Bail out! Aborted after fail-fast" if $aborted;221}