### ----------------------------------------------------
### -- rak
### -- Licenses: Artistic-2.0
### -- Authors: Elizabeth Mattijsen
### -- File: xt/02-simple.rakutest
### ----------------------------------------------------
BEGIN %*ENV<RAKU_TEST_DIE_ON_FAIL> = 1;
use Test;
use rak;

plan 58;

my $dir := $*TMPDIR.add("rak");
my @targets  := <zero one two three four five six seven eight nine>;
my %name2path = @targets.map: { $_ => $dir.add($_).absolute }
my %path2name = %name2path.kv.reverse;
my $append      := $dir.add("append");
my $append-path := $append.absolute;

$dir.mkdir;
for @targets.kv -> $n, $name {
    %name2path{$name}.IO.spurt: @targets[0..$n].join("\n") ~ "\n";
    is %name2path{$name}.IO.slurp, @targets[0..$n].join("\n") ~ "\n",
      "is the file for $name ok?";
}
LEAVE {
    .IO.unlink for %name2path.values;
    $append.unlink;
    $dir.rmdir
}

# Line locations and their contents for matches
my \l0 = PairMatched.new:  1, "zero";
my \L0 = PairMatched.new:  1, "ZERO";
my \C0 = PairChanged.new:  1, "ZERO";
my \l1 = PairMatched.new:  2, "one";
my \l2 = PairMatched.new:  3, "two";
my \o2 = PairChanged.new:  3, "two" => "tvo";
my \L2 = PairMatched.new:  3, "TWO";
my \C2 = PairChanged.new:  3, "TWO";
my \l3 = PairMatched.new:  4, "three";
my \l4 = PairMatched.new:  5, "four";
my \l5 = PairMatched.new:  6, "five";
my \l6 = PairMatched.new:  7, "six";
my \l7 = PairMatched.new:  8, "seven";
my \l8 = PairMatched.new:  9, "eight";
my \l9 = PairMatched.new: 10, "nine";
my \L9 = PairMatched.new: 10, "NINE";

# Line locations and their contents for contexts
my \c0 = PairContext.new:  1, "zero";
my \c1 = PairContext.new:  2, "one";
my \c2 = PairContext.new:  3, "two";
my \c3 = PairContext.new:  4, "three";
my \c4 = PairContext.new:  5, "four";
my \c5 = PairContext.new:  6, "five";
my \c6 = PairContext.new:  7, "six";
my \c7 = PairContext.new:  8, "seven";
my \c8 = PairContext.new:  9, "eight";
my \c9 = PairContext.new: 10, "nine";

my sub lookup(
  $pattern is copy, *@expected, *%additional
) is test-assertion {
    my $code;
    if Callable.ACCEPTS($pattern) {
        $code    = $pattern;
        $pattern = %additional<name>:delete;
    }
    else {
        $code = try $pattern.EVAL;
    }
    my &needle := $code // *.contains($pattern);
    my %expected = @expected.map: { %name2path{.key} => .value }

    subtest "testing '$pattern'" => {
        plan 5;
        my %nameds = :degree(1), :dont-catch, |%additional;
        my $rak := rak &needle, %nameds;
        isa-ok $rak, Rak;
        is-deeply $rak.stats, Map.new, 'did we get an empty Map for stats';

        my %got = $rak.result.map: {
            .key => .value.List if .value.elems
        }

        is %got.elems, %expected.elems, "did we get %expected.elems() results";
        is-deeply %got, %expected, 'is the search result ok';
        dd %nameds unless
          is %nameds.elems, 0, 'did all named arguments get removed';
    }
}

lookup "nine", :paths($dir),
  "nine" => (l9,)
;

lookup "eight", :paths(($dir,)),
  "eight" => (l8,),
  "nine"  => (l8,),
;

lookup "/ zero /", :paths($dir),
  "zero"  => (l0,),
  "one"   => (l0,),
  "two"   => (l0,),
  "three" => (l0,),
  "four"  => (l0,),
  "five"  => (l0,),
  "six"   => (l0,),
  "seven" => (l0,),
  "eight" => (l0,),
  "nine"  => (l0,),
;

lookup '*.ends-with("o")', :paths($dir), :is-text,
  "zero"  => (l0,),
  "one"   => (l0,),
  "two"   => (l0, l2),
  "three" => (l0, l2),
  "four"  => (l0, l2),
  "five"  => (l0, l2),
  "six"   => (l0, l2),
  "seven" => (l0, l2),
  "eight" => (l0, l2),
  "nine"  => (l0, l2),
;

lookup '-> $_ { .ends-with("o") && .uc }', :paths($dir),
  "zero"  => (C0,),
  "one"   => (C0,),
  "two"   => (C0, C2),
  "three" => (C0, C2),
  "four"  => (C0, C2),
  "five"  => (C0, C2),
  "six"   => (C0, C2),
  "seven" => (C0, C2),
  "eight" => (C0, C2),
  "nine"  => (C0, C2),
;

lookup "/ er /", :paths(%name2path<nine>),
  "nine"  => (l0,),
;
lookup "/ zer /", :paths(%name2path<eight nine>),
  "eight" => (l0,),
  "nine"  => (l0,),
;

lookup "four", :paths(%name2path<nine>), :context(2),
  "nine"  => (c2, c3, l4, c5, c6),
;

lookup "/ four /", :paths(%name2path<nine>), :before-context(2),
  "nine"  => (c2, c3, l4),
;

lookup "/ fou /", :paths(%name2path<nine>), :after-context(2),
  "nine"  => (l4, c5, c6),
;

lookup "/ our /", :paths(%name2path<nine>), :paragraph-context,
  "nine"  => (c0, c1, c2, c3, l4, c5, c6, c7, c8, c9),
;

lookup '*.ends-with("nine")', :paths($dir), :passthru-context,
  "nine"  => (c0, c1, c2, c3, c4, c5, c6, c7, c8, l9),
;

lookup 'zippo', :paths($dir), :passthru-context,
  (),
;

lookup 'zippo', :paths(%name2path<nine>), :passthru,
  "nine"  => (c0, c1, c2, c3, c4, c5, c6, c7, c8, c9),
;

lookup 'o', :paths($dir), :filesize(*>45),
  "nine"  => (l0,l1,l2,l4),
;

lookup 'ine', :paths($dir), :is-writable, :is-readable,
  "nine"  => (l9,),
;

lookup 'in', :paths($dir), :is-owned-by-user, :!is-symbolic-link,
  "nine"  => (l9,),
;

lookup 'zero', :paths($dir), :!is-text,
  ()
;

lookup 'zero', :paths($dir), :!is-owned-by-user,
  ()
;

lookup '-> $_ { $_ eq "zero" }', :paths($dir), :with-line-endings,
  ()
;

lookup 'one', :paths($dir), :is-empty,
  ()
;

lookup 'NINE', :paths($dir), :produce-many(*.IO.lines.map: *.uc),
  "nine"  => (L9,),
;

lookup '/ i /', :paths($dir), :max-matches-per-source(3), :ioify(*.IO),
  "five"  => (l5,),
  "six"   => (l5,l6),
  "seven" => (l5,l6),
  "eight" => (l5,l6,l8),
  "nine"  => (l5,l6,l8),
;

lookup '/ t /', :paths($dir), :max-matches-per-source(2), :context(1),
  "two"   => (c1,l2),
  "three" => (c1,l2,l3),
  "four"  => (c1,l2,l3,c4),
  "five"  => (c1,l2,l3,c4),
  "six"   => (c1,l2,l3,c4),
  "seven" => (c1,l2,l3,c4),
  "eight" => (c1,l2,l3,c4),
  "nine"  => (c1,l2,l3,c4),
;

lookup '*.subst("w","v",:g)', :paths($dir), :old-new,
  "two"   => (o2,),
  "three" => (o2,),
  "four"  => (o2,),
  "five"  => (o2,),
  "six"   => (o2,),
  "seven" => (o2,),
  "eight" => (o2,),
  "nine"  => (o2,),
;

lookup "eight", :accept(*.slurp.contains("nine")), :paths($dir),
  "nine" => (l8,)
;

lookup "eight", :deny(*.slurp.contains("nine")), :paths($dir),
  "eight" => (l8,)
;

my %b is BagHash;
my $FIRST-matcher-fired;
my $LAST-matcher-fired;
my $FIRST-mapper-fired;
my $LAST-mapper-fired;

my int $NEXT-matcher-count;
my int $NEXT-mapper-count;

lookup -> $ {
    FIRST $FIRST-matcher-fired = True;
    NEXT  ++$NEXT-matcher-count;
    LAST  $LAST-matcher-fired  = True;
    True  # accept all lines
}, :name<bagtest>, :paths($dir), :mapper(-> $source, @lines {
    FIRST $FIRST-mapper-fired = True;
    NEXT  ++$NEXT-mapper-count;
    LAST  $LAST-mapper-fired  = True;
    %b.add(@lines.map(*.value));
    Empty
}), 
  ()
;
is-deeply %b, (:10zero, :9one, :8two,   :7three, :6four,
                :5five, :4six, :3seven, :2eight, :1nine).BagHash,
  'did the mapper create the correct bag';
ok $FIRST-matcher-fired, "FIRST phaser fired in matcher";
ok $LAST-matcher-fired,  "LAST phaser fired in matcher";
ok $FIRST-mapper-fired,  "FIRST phaser fired in mapper";
ok $LAST-mapper-fired,   "LAST phaser fired in mapper";

is $NEXT-matcher-count, 10, 'was NEXT phaser in matcher fired ok';
is $NEXT-mapper-count,  10, 'was NEXT phaser in mapper fired ok';

my $rak := rak -> $ --> True { }, :paths($dir), :unique;
is-deeply $rak.result.sort, @targets.sort, 'did we get all unique strings';

sub weird($_) {
    if $_ eq "five" {
        "seven"
    }
    elsif $_ eq "seven" {
        Empty
    }
    else {
        True
    }
}
$rak := rak(&weird, :paths($dir), :stats);
.Str for $rak.result;  # :stats is lazy, so need to process
is-deeply
  $rak.stats,
  Map.new((
    :nr-changes(5), :nr-items(55), :nr-matches(52),
    :nr-passthrus(3), :nr-sources(10)
  )),
  "did stats work out?";

$rak := rak(&weird, :paths($dir), :stats-only);
is-deeply $rak.stats, 
   Map.new((
    :nr-changes(5), :nr-items(55), :nr-matches(52),
    :nr-passthrus(3), :nr-sources(10)
  )),
  "did stats-only work out for stats?";
is-deeply $rak.result, Empty,
  "did stats-only work out for result?";

$rak := rak &defined, :paths($dir), :find, :omit-item-number, :dont-catch;
ok $rak.result.head.value>>.absolute (==) %path2name.keys,
  'did a :find with &defined pattern work out';

$rak := rak &defined, :paths($dir), :find, :sort-sources, :omit-item-number;
is-deeply $rak.result.head.value>>.absolute, %path2name.keys.sort,
  'did a :find with &defined pattern work out sorted';

$rak := rak &defined, :paths($dir), :find, :omit-item-number,
  :sort-sources({ $^b cmp $^a});
is-deeply $rak.result.head.value>>.absolute, %path2name.keys.sort.reverse,
  'did a :find with &defined pattern work out sorted reversed';

$rak := rak *.contains("seven"), :paths($dir), :sources-only, :dont-catch;
ok $rak.result>>.absolute (==) %name2path<seven eight nine>,
  'did a :sources-only only produce paths';

$rak := rak *.defined, :paths($dir), :frequencies, :dont-catch;
is-deeply $rak.result,
  (:10zero, :9one, :8two, :7three, :6four,
   :5five, :4six, :3seven, :2eight, :1nine),
  'did frequencies produce ok';

$rak := rak &defined, :paths($dir), :shell("echo \$_ >> $append-path"),
  :find, :omit-item-number, :dont-catch;
ok $rak.result.head.value>>.absolute (==) %path2name.keys,
  'did a :shell with :find  with &defined pattern work out';
ok $append.lines (==) %path2name.keys, 'did we get all paths';

$rak := rak &defined, :paths(%name2path<eight nine>), :classify(*.substr(0,1));
is-deeply Map.new($rak.result), Map.new((
  :e<eight eight>,
  :f<four five four five>,
  :s<six seven six seven>,
  :t<two three two three>,
  :n(("nine",))
  :o<one one>,
  :z<zero zero>,
)), 'classify on the first letter';

$rak := rak &defined,
  :paths(%name2path<eight nine>), :categorize(*.substr(0,2).comb);
is-deeply Map.new($rak.result), Map.new((
  :e<zero seven eight zero seven eight>,
  :f<four five four five>,
  :h<three three>,
  :i<five six eight five six eight nine>,
  :n<one one nine>,
  :o<one four one four>,
  :s<six seven six seven>,
  :t<two three two three>,
  :w<two two>,
  :z<zero zero>,
)), 'categorize on the first two letters';

# vim: expandtab shiftwidth=3


