← Back to index

lib/BDD/Behave/Configuration.rakumod

116 / 118 lines covered (98.3%)

1unit module BDD::Behave::Configuration;23# The execution models `--parallel-mode` accepts. `isolated` is the default: it4# gives every spec file its own process, so top-level symbols one file declares5# cannot collide with another file's, and a crashed worker takes one file down6# rather than the run. `lpt` and `queue` share a fixed pool of processes across7# files, trading that isolation for fewer process startups.8our constant PARALLEL-MODES = <isolated lpt queue>;9our constant DEFAULT-PARALLEL-MODE = 'isolated';1011our sub resolve-parallel-mode($requested --> Str) {12  ($requested // DEFAULT-PARALLEL-MODE).lc;13}1415our sub is-parallel-mode(Str $mode --> Bool) {16  so $mode ∈ PARALLEL-MODES;17}1819our class ConfigInclude {20  has Mu  $.class is required;21  has Str $.as;22  has Str $.tag;23  has %.meta;2425  method key(--> Str) {26    $!as.defined && $!as.chars ?? $!as !! $!class.^name.split('::')[*-1];27  }28}2930our class ConfigHook {31  has Str      $.phase   is required;32  has Callable $.block   is required;33  has Str      $.tag;34  has Str      $.exclude-tag;35  has %.meta;3637  method matches-example($example --> Bool) {38    return True unless $!tag.defined39                    || $!exclude-tag.defined40                    || %!meta.elems;41    my @tags = $example.effective-tags.list;42    if $!tag.defined {43      return False unless @tags.first(* eq $!tag).defined;44    }45    if $!exclude-tag.defined {46      return False if @tags.first(* eq $!exclude-tag).defined;47    }48    for %!meta.kv -> $k, $v {49      my $actual = $example.effective-metadata-value($k);50      return False unless $actual.defined;51      if $v ~~ Bool {52        return False unless ?$actual == $v;53      } else {54        return False unless $actual eq $v;55      }56    }57    True;58  }59}6061our class Configuration {62  has Str      $.format               is rw;63  has Str      $.order                is rw;64  has Int      $.seed                 is rw;65  has Str      $.seed-mode            is rw;66  has Bool     $.show-seed            is rw;67  has Str      $.parallel-mode        is rw;68  has Int      $.parallel              is rw;69  has Int      $.parallel-retry       is rw;70  has Bool     $.progress-total       is rw;71  has Int      $.fail-fast            is rw;72  has Int      $.retry                is rw;73  has Bool     $.only-failures        is rw;74  has IO::Path $.failures-path        is rw;75  has Bool     $.verbose              is rw;76  has          $.aggregate-failures   is rw;77  has Int      $.profile-limit        is rw;78  has Real     $.slow-threshold       is rw;79  has Bool     $.benchmark-mode       is rw;80  has Int      $.benchmark-iterations is rw;81  has IO::Path $.benchmark-baseline   is rw;82  has IO::Path $.benchmark-save       is rw;83  has Real     $.benchmark-threshold  is rw;84  has Str      $.benchmark-format     is rw;85  has IO::Path $.benchmark-output     is rw;86  has Bool     $.coverage             is rw;87  has Real     $.coverage-minimum     is rw;88  has Str      $.coverage-format      is rw;89  has IO::Path $.coverage-output      is rw;90  has IO::Path $.coverage-baseline    is rw;91  has Bool     $.coverage-branch      is rw;92  has Bool     $.coverage-counts      is rw;9394  has Str @.include-tags;95  has Str @.exclude-tags;96  has Str @.example-patterns;97  has Str @.only-locations;98  has Str @.spec-paths;99  has Str @.coverage-include;100  has Str @.coverage-exclude;101102  has ConfigInclude @.includes;103  has ConfigHook    @.hooks;104  has %.metadata-filters;105  has %.metadata-exclude-filters;106  has @.match-filters;107108  method include-tag(*@tags) {109    @!include-tags.append: @tags.map(*.Str);110    self;111  }112113  method exclude-tag(*@tags) {114    @!exclude-tags.append: @tags.map(*.Str);115    self;116  }117118  method example-pattern(*@patterns) {119    @!example-patterns.append: @patterns.map(*.Str);120    self;121  }122123  method only-location(*@locations) {124    @!only-locations.append: @locations.map(*.Str);125    self;126  }127128  method include-spec(*@paths) {129    @!spec-paths.append: @paths.map(*.Str);130    self;131  }132133  method coverage-include-path(*@paths) {134    @!coverage-include.append: @paths.map(*.Str);135    self;136  }137138  method coverage-exclude-path(*@paths) {139    @!coverage-exclude.append: @paths.map(*.Str);140    self;141  }142143  method include(Mu $class, Str :$as, Str :$tag, *%meta) {144    @!includes.push: ConfigInclude.new(:$class, :$as, :$tag, :%meta);145    self;146  }147148  method before-all(&block, Str :$tag, Str :$exclude-tag, *%meta) {149    @!hooks.push: ConfigHook.new(150      :phase<before-all>, :&block, :$tag, :$exclude-tag, :%meta,151    );152    self;153  }154155  method after-all(&block, Str :$tag, Str :$exclude-tag, *%meta) {156    @!hooks.push: ConfigHook.new(157      :phase<after-all>, :&block, :$tag, :$exclude-tag, :%meta,158    );159    self;160  }161162  method before-each(&block, Str :$tag, Str :$exclude-tag, *%meta) {163    @!hooks.push: ConfigHook.new(164      :phase<before-each>, :&block, :$tag, :$exclude-tag, :%meta,165    );166    self;167  }168169  method after-each(&block, Str :$tag, Str :$exclude-tag, *%meta) {170    @!hooks.push: ConfigHook.new(171      :phase<after-each>, :&block, :$tag, :$exclude-tag, :%meta,172    );173    self;174  }175176  method around-each(&block, Str :$tag, Str :$exclude-tag, *%meta) {177    @!hooks.push: ConfigHook.new(178      :phase<around-each>, :&block, :$tag, :$exclude-tag, :%meta,179    );180    self;181  }182183  method hooks-for(Str $phase --> List) {184    @!hooks.grep(*.phase eq $phase).List;185  }186187  method filter(*%pairs) {188    for %pairs.kv -> $k, $v { %!metadata-filters{$k} = $v }189    self;190  }191192  method exclude-filter(*%pairs) {193    for %pairs.kv -> $k, $v { %!metadata-exclude-filters{$k} = $v }194    self;195  }196197  method filter-run-when-matching(*@keys, *%pairs) {198    for @keys -> $k { @!match-filters.push: $k.Str => True }199    for %pairs.kv -> $k, $v { @!match-filters.push: $k.Str => $v }200    self;201  }202203  method merge(Configuration $other --> Configuration) {204    my $result = Configuration.new;205    for <format order seed seed-mode show-seed parallel-mode parallel parallel-retry progress-total fail-fast retry only-failures failures-path verbose aggregate-failures206         profile-limit slow-threshold207         benchmark-mode benchmark-iterations benchmark-baseline benchmark-save208         benchmark-threshold benchmark-format benchmark-output209         coverage coverage-minimum coverage-format coverage-output210         coverage-baseline coverage-branch coverage-counts> -> $attr {211      my $self-val  = self."$attr"();212      my $other-val = $other."$attr"();213      my $picked = $other-val.defined ?? $other-val !! $self-val;214      $result."$attr"() = $picked if $picked.defined;215    }216217    $result.include-tags.append:      |self.include-tags,      |$other.include-tags;218    $result.exclude-tags.append:      |self.exclude-tags,      |$other.exclude-tags;219    $result.example-patterns.append:  |self.example-patterns,  |$other.example-patterns;220    $result.only-locations.append:    |self.only-locations,    |$other.only-locations;221    $result.spec-paths.append:        |self.spec-paths,        |$other.spec-paths;222    $result.coverage-include.append:  |self.coverage-include,  |$other.coverage-include;223    $result.coverage-exclude.append:  |self.coverage-exclude,  |$other.coverage-exclude;224    $result.includes.append:          |self.includes,          |$other.includes;225    $result.hooks.append:             |self.hooks,             |$other.hooks;226    $result.match-filters.append:     |self.match-filters,     |$other.match-filters;227228    for self.metadata-filters.kv -> $k, $v {229      $result.metadata-filters{$k} = $v;230    }231    for $other.metadata-filters.kv -> $k, $v {232      $result.metadata-filters{$k} = $v;233    }234    for self.metadata-exclude-filters.kv -> $k, $v {235      $result.metadata-exclude-filters{$k} = $v;236    }237    for $other.metadata-exclude-filters.kv -> $k, $v {238      $result.metadata-exclude-filters{$k} = $v;239    }240241    $result;242  }243244}245246our sub defaults(--> Configuration) {247  my $c = Configuration.new;248  $c.format               = 'progress';249  $c.order                = 'random';250  $c.seed-mode            = 'xor';251  $c.show-seed            = False;252  $c.parallel-mode        = DEFAULT-PARALLEL-MODE;253  $c.fail-fast            = 0;254  $c.retry                = 0;255  $c.only-failures        = False;256  $c.verbose              = False;257  $c.aggregate-failures   = False;258  $c.profile-limit        = 0;259  $c.slow-threshold       = 0.Real;260  $c.benchmark-mode       = False;261  $c.benchmark-iterations = 1;262  $c.benchmark-threshold  = 0.10;263  $c.benchmark-format     = 'text';264  $c.coverage             = False;265  $c.coverage-minimum     = 0.Real;266  $c.coverage-format      = 'html';267  $c.coverage-branch      = False;268  $c.coverage-counts      = False;269  $c;270}271272our sub user-config-path(--> IO::Path) {273  my $home = %*ENV<HOME> // '';274  return IO::Path unless $home.chars;275  $home.IO.add('.behave');276}277278our sub project-config-path(IO::Path :$base = $*CWD --> IO::Path) {279  $base.IO.add('.behave');280}281282our sub load-file(IO::Path $path --> Configuration) {283  my $config = Configuration.new;284  return $config unless $path.defined && $path.e;285  use MONKEY-SEE-NO-EVAL;286  {287    my $*BEHAVE-CONFIG = $config;288    try {289      EVALFILE $path;290      CATCH {291        default {292          die "Failed to load config file '{$path}': {.message}";293        }294      }295    }296  }297  $config;298}299300our sub configure-behave(&block --> Nil) is export {301  my $config = $*BEHAVE-CONFIG;302  die "configure-behave called outside of a .behave config file"303    unless $config.defined;304  block($config);305}