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

%*ENV<RAK_CONFIG> := $*TMPDIR.add("rak-test-$*PID.json").absolute;
END unlink %*ENV<RAK_CONFIG>;

my constant BON  = "\e[1m";   # BOLD ON
my constant BOFF = "\e[22m";  # BOLD OFF

my $dir  = $*PROGRAM.parent.sibling("q");
my $dira = $dir.absolute ~ $*SPEC.dir-sep;
my $rel := $dir.relative ~ $*SPEC.dir-sep;
my $dot  = $?FILE.IO.parent.parent;

my $patterns := $dot.add("patterns");
my $rak      := $dot.add("bin").add("rak").relative;

my @filenames := <zero one two three four five six seven eight nine>;
my %name2path  = @filenames.map: { $_ => $dir.add($_).absolute }
my %path2name  = %name2path.kv.reverse;

my $paths = $dir.sibling("rak-paths");
$paths.spurt: %name2path{@filenames}.join("\n");
END $paths.IO.unlink;

my @targets  = @filenames;
@targets[3]  = "";
@targets[6] .= uc;

# using paths from here on out
$dot   .= relative;
$dir   .= absolute;
$paths .= absolute;

my sub query-ok(
  *@query,              # the actual parameters
  :$ok is copy = "",    # the expected result
  :$head = 1,           # whether to do the --only-first test with this number
  :$add-paths  = True,  # add paths specification
  :$add-human  = True,  # add human specification
  :$add-degree = True,  # add degree specification
  :$ERR        = "",    # expected output on STDERR
  :$add-absolute,       # add absolute specification
  :$todo,               # mark tests as todo
) is test-assertion {
    my @args = $*EXECUTABLE.absolute, "-I$dot", $rak, @query.Slip;
    @args.push: "--paths-from=$paths" if $add-paths;
    @args.push: "--human"             if $add-human;
    @args.push: "--degree"            if $add-degree;
    @args.push: "--absolute"          if $add-absolute;
    $ok .= chomp;

    # Logic to run the query
    my sub run-query() {
        my $proc := run @args, :out, :err;
        my $key = "huh?";

        todo($todo) if $todo;
        is $proc.err.slurp(:close), $ERR, "is '@query[]' STDERR correct?";
        $add-absolute
          ?? $proc.out.lines.join("\n")
          !! $proc.out.lines.map(*.subst($rel, :g)).join("\n")
    }

    # Base query
    my $result := run-query;
    todo($todo) if $todo;
    is $result, $ok, "is '@query[]' result ok?";

    # If there was a result, try for only the first result
    if $ok && $head {
        @query.push: '--only-first';
        @args.push:  '--only-first';
        my $result := run-query;
        todo($todo) if $todo;
        is $result, $ok.lines.head($head).join("\n"),
          "is '@query[]' result ok?";
    }
}

# Checks for "ine"
my @ine = (
  <ine>,
  <ine --type=auto>,
  <ine --type=regex>,
  <ine --type=ends-with>,
  <INE --type=ends-with --ignorecase>,
  <ïñę --type=ends-with --ignoremark>,
  <ÍNĒ --type=ends-with --ignorecase --ignoremark>,
  <ine --type=contains>,
  <ine --type=contains --smartcase>,
  <ine --type=contains --smartmark>,
  </ine/>,
  </ine$/>,
  </in.$/>,
  </INE$/ --ignorecase>,
  </ïñę/ --ignoremark>,
  </ÍNĒ/ --ignorecase --ignoremark>,
);

# Check highlighting as if a human was watching
for @ine {
    query-ok .Slip, '--highlight', :head(2), ok => qq:to/OK/;
nine
10:n{BON}ine{BOFF}
OK
}

# Check without highlighting
for |@ine, <nine --type=equal>, '^nine$', '^nine', 'nine$', '§nine' {
    query-ok .Slip,
      <--show-filename --show-item-number --group-matches --/highlight>,
      :head(2), ok => q:to/OK/;
nine
10:nine
OK
}

# Sure to not produce any matches
for (
  <§ine>,
  <^ine>,
  <^ine$>,
  <ine --type=words>,
  <ine --type=starts-with>,
  <INE>,
  <INE --smartcase>,
  <ÍNĒ --smartmark>,
  </^ine/>,
  <42>,
) {
    query-ok .Slip, :ok("\n");
    query-ok .Slip, '--highlight', :ok("\n");
}

for "six", ('.lc eq "six"', '--type=code') {
    query-ok .Slip,
      <--smartcase --/group-matches --/show-item-number --/highlight --/break>,
      ok => q:to/OK/;
six:SIX
seven:SIX
eight:SIX
nine:SIX
OK
}

query-ok <seven --type=words>, :head(2), ok => qq:to/OK/;
seven
8:{BON}seven{BOFF}

eight
8:{BON}seven{BOFF}

nine
8:{BON}seven{BOFF}
OK

query-ok </o$/ --find>, :add-absolute, :!head, ok => qq:to/OK/;
{$dira}tw{BON}o{BOFF}
{$dira}zer{BON}o{BOFF}
OK

query-ok q/{.uc if .contains("u")}/, :head(2), ok => q:to/OK/;
four
5:FOUR

five
5:FOUR

six
5:FOUR

seven
5:FOUR

eight
5:FOUR

nine
5:FOUR
OK

query-ok q/.uc if .contains("v")/, <--type=code --/group-matches --/break>,
  ok => q:to/OK/;
five:6:FIVE
six:6:FIVE
seven:6:FIVE
seven:8:SEVEN
eight:6:FIVE
eight:8:SEVEN
nine:6:FIVE
nine:8:SEVEN
OK

query-ok q/{.uc if .contains("v")}/, '--/group-matches', '--break',
  ok => q:to/OK/;
five:6:FIVE

six:6:FIVE

seven:6:FIVE
seven:8:SEVEN

eight:6:FIVE
eight:8:SEVEN

nine:6:FIVE
nine:8:SEVEN
OK

for </\w+/>, <*.words.Slip> {
    query-ok $_, '--frequencies', ok => q:to/OK/;
10:zero
9:one
8:two
6:four
5:five
4:SIX
3:seven
2:eight
1:nine
OK
}

for <*.defined>, <{$_}> {
    query-ok $_, '--frequencies', ok => q:to/OK/;
10:zero
9:one
8:two
7:
6:four
5:five
4:SIX
3:seven
2:eight
1:nine
OK
}

for </\w+/>, <*.words.Slip> {
    query-ok $_, '--unique', ok => q:to/OK/;
eight
five
four
nine
one
seven
SIX
two
zero
OK
}

for <*.defined>, <{$_}> {
    query-ok $_, '--unique', ok => q:to/OK/;

eight
five
four
nine
one
seven
SIX
two
zero
OK
}

query-ok '*.comb.Slip', '--frequencies', ok => q:to/OK/;
33:e
33:o
16:r
14:n
11:f
10:t
10:z
8:i
8:v
8:w
6:u
4:I
4:S
4:X
3:s
2:g
2:h
OK

query-ok 'zero', '--stats-only', :!head, ERR => qq:to/ERR/;
----------------------
Statistics for 'zero':
----------------------
    Number of files: 10
    Number of lines: 55
  Number of matches: 10
ERR

query-ok 'zero', '--count-only', :!head, ok => qq:to/OK/;
10 matches in 10 files
OK

query-ok 'eight', <--stats --/highlight>, :!head,
  ok => qq:to/OK/, ERR => qq:to/ERR/;
eight
9:eight

nine
9:eight
OK
-----------------------
Statistics for 'eight':
-----------------------
    Number of files: 10
    Number of lines: 55
  Number of matches: 2
ERR

my $bon8boff := BON ~ "eight" ~ BOFF;
query-ok 'eight', '--stats', '--highlight', :!head,
  ok => qq:to/OK/, ERR => qq:to/ERR/;
eight
9:$bon8boff

nine
9:$bon8boff
OK
-----------------------
Statistics for 'eight':
-----------------------
    Number of files: 10
    Number of lines: 55
  Number of matches: 2
ERR

query-ok 'seven', :head(2), ok => qq:to/OK/;
seven
8:{BON}seven{BOFF}

eight
8:{BON}seven{BOFF}

nine
8:{BON}seven{BOFF}
OK

query-ok <even --no-group-matches --/show-item-number>, ok => qq:to/OK/;
seven:s{BON}even{BOFF}

eight:s{BON}even{BOFF}

nine:s{BON}even{BOFF}
OK

query-ok <eve --/show-filename>, ok => qq:to/OK/;
8:s{BON}eve{BOFF}n

8:s{BON}eve{BOFF}n

8:s{BON}eve{BOFF}n
OK

query-ok <ev --/group-matches --/break>, ok => qq:to/OK/;
seven:8:s{BON}ev{BOFF}en
eight:8:s{BON}ev{BOFF}en
nine:8:s{BON}ev{BOFF}en
OK

query-ok <ven --highlight-before=* --/show-filename --/show-item-number>,
  ok => q:to/OK/;
se*ven*

se*ven*

se*ven*
OK

query-ok <eig --highlight-before=* --highlight-after=## --/show-filename>,
  ok => q:to/OK/;
9:*eig##ht

9:*eig##ht
OK

query-ok <seven --context=2>, :head(4), ok => qq:to/OK/;
seven
6:five
7:SIX
8:{BON}seven{BOFF}

eight
6:five
7:SIX
8:{BON}seven{BOFF}
9:eight

nine
6:five
7:SIX
8:{BON}seven{BOFF}
9:eight
10:nine
OK

query-ok <seven --before-context=2 --/highlight>, :head(4), ok => q:to/OK/;
seven
6:five
7:SIX
8:seven

eight
6:five
7:SIX
8:seven

nine
6:five
7:SIX
8:seven
OK

query-ok <seven --after-context=2 --/highlight>, :head(2), ok => q:to/OK/;
seven
8:seven

eight
8:seven
9:eight

nine
8:seven
9:eight
10:nine
OK

query-ok <nine --passthru-context --highlight>, :!head, ok => qq:to/OK/;
nine
1:zero
2:one
3:two
4:
5:four
6:five
7:SIX
8:seven
9:eight
10:{BON}nine{BOFF}
OK

query-ok 'zero', q/--filesize=*>30/, :head(2), ok => qq:to/OK/;
seven
1:{BON}zero{BOFF}

eight
1:{BON}zero{BOFF}

nine
1:{BON}zero{BOFF}
OK

my $user := ~$*USER;
my $uid  := +$*USER;
for "--user=$user", qq/--user=*eq"$user"/, "--user=$user,$user",
    "--uid=$uid", "--uid=*==$uid" {
    query-ok '--find', $_, :!head, ok => qq:to/OK/;
eight
five
four
nine
one
seven
six
three
two
zero
OK
}

for "--user=!$user", qq/--user=*ne"$user"/, "--user=!$user,$user",
    "--uid=*!=$uid" {
    query-ok '--find', $_, ok => "";
}

my $group := ~$*GROUP;
my $gid   := +$*GROUP;
for "--group=$group", qq/--group=*eq"$group"/, "--group=$group,$group",
    "--gid=$gid", "--gid=*==$gid" {
    query-ok '--find', $_, :!head, ok => qq:to/OK/;
eight
five
four
nine
one
seven
six
three
two
zero
OK
}

for "--group=!$group", qq/--group=*ne"$group"/, "--group=!$group,$group",
    "--gid=*!=$gid" {
    query-ok '--find', $_, ok => "";
}

for <zero --files-with-matches>, <null --files-without-matches> {
    query-ok $_, ok => qq:to/OK/;
zero
one
two
three
four
five
six
seven
eight
nine
OK
}

query-ok 'zero', '--files-with-matches', '--count-only', :!head,
  ok => '10 files with matches';
query-ok 'null', '--files-without-matches', '--count-only', :!head,
  ok => '10 files without matches';

for (
  <zero --files-with-matches    --file-separator-null>,
  <null --files-without-matches --file-separator-null>,
) {
    query-ok $_, :!head,
  ok => "zero\0one\0two\0three\0four\0five\0six\0seven\0eight\0nine";
}

for '--per-file', q/--per-file=*.slurp/ {
    query-ok <nine --/group-matches>, $_, :!head, ok => qq:to/OK/;
nine:zero
one
two

four
five
SIX
seven
eight
{BON}nine{BOFF}
OK
}

for '--per-file', q/--per-file=*.slurp/ {
    query-ok '/ \w* v \w* /', $_, '--matches-only', :head(2), ok => q:to/OK/;
five
five

six
five

seven
five
seven

eight
five
seven

nine
five
seven
OK
}

for '--per-file', q/--per-file=*.slurp/ {
    query-ok '/ \w* v \w* /', $_, '--matches-only', '--unique', ok => q:to/OK/;
five
seven
OK
}

for '--per-file', q/--per-file=*.slurp/ {
    query-ok '/ \w* v \w* /', $_, '--matches-only', '--frequencies',
      ok => q:to/OK/;
5:five
3:seven
OK
}

query-ok <o --max-matches-per-file=2>, :head(2), ok => qq:to/OK/;
zero
1:zer{BON}o{BOFF}

one
1:zer{BON}o{BOFF}
2:{BON}o{BOFF}ne

two
1:zer{BON}o{BOFF}
2:{BON}o{BOFF}ne

three
1:zer{BON}o{BOFF}
2:{BON}o{BOFF}ne

four
1:zer{BON}o{BOFF}
2:{BON}o{BOFF}ne

five
1:zer{BON}o{BOFF}
2:{BON}o{BOFF}ne

six
1:zer{BON}o{BOFF}
2:{BON}o{BOFF}ne

seven
1:zer{BON}o{BOFF}
2:{BON}o{BOFF}ne

eight
1:zer{BON}o{BOFF}
2:{BON}o{BOFF}ne

nine
1:zer{BON}o{BOFF}
2:{BON}o{BOFF}ne
OK

query-ok <--unicode banknote>, :!add-paths, ok => qq:to/OK/;
1F4B4 {BON}BANKNOTE{BOFF} WITH YEN SIGN 💴
1F4B5 {BON}BANKNOTE{BOFF} WITH DOLLAR SIGN 💵
1F4B6 {BON}BANKNOTE{BOFF} WITH EURO SIGN 💶
1F4B7 {BON}BANKNOTE{BOFF} WITH POUND SIGN 💷
OK

query-ok '{ .comb(2).Slip if $_ eq "nine" }', :!head, ok => q:to/OK/;
nine
10:ni
10:ne
OK

# This check is a little brittle
query-ok '--eco-meta', q/{ .<name> eq "eigenstates" && .<version> }/,
  :!add-paths, ok => q:to/OK/;
0.0.11
0.0.10
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.4
0.0.3
0.0.2
0.0.1
OK

# This check is a little brittle
query-ok '--eco-meta=fez', q/{ .<name> eq "eigenstates" && .<version> }/,
  :todo<need to fix order, or fix test without order being significant>,
  :!add-paths, ok => q:to/OK/;
0.0.6
0.0.7
0.0.8
0.0.9
0.0.10
0.0.11
OK

# This check is a little brittle
query-ok '--sourcery', 'say "foo"', <--/show-item-number --/show-filename>,
  ok => 'multi ' ~ BON ~ 'sub say' ~ BOFF ~ '(\x) {' ~ "\n"
      ~ 'multi ' ~ BON ~ 'sub say' ~ BOFF ~ '(|) {';

# This check is a little brittle
query-ok '--json-per-elem', q/{ .<version> if .<name> eq 'eigenstates' }/,
  :todo<need to fix order, or fix test without order being significant>,
  <https://360.zef.pm --show-filename>, :!add-paths, :head(2), ok => q:to/OK/;
https://360.zef.pm
0.0.6
0.0.7
0.0.8
0.0.9
0.0.10
0.0.11
OK

query-ok '*.substr(0,1) eq "f"', '--classify=*.substr(0,1)', :head(2),
  ok => q:to/OK/;
f
four
four
five
four
five
four
five
four
five
four
five
OK

query-ok '*.chars == 5', '--categorize={ .substr(0,2).comb if $_ }', :head(2),
  ok => q:to/OK/;
e
seven
seven
eight
seven
eight

s
seven
seven
seven

i
eight
eight
OK

query-ok <--ignorecase --save=i>, :!add-paths, :!add-human, :!add-degree,
  :!head, ok => qq:to/OK/;
Saved '--ignorecase' as: -i
OK

query-ok <--ignoremark --save=m>, :!add-paths, :!add-human, :!add-degree,
  :!head, ok => qq:to/OK/;
Saved '--ignoremark' as: -m
OK

query-ok <-im --list-expanded-options>, :!add-paths, :!add-human, :!add-degree,
  :!head, ok => qq:to/OK/;
--ignorecase --ignoremark
OK

query-ok <--before-context=! --save=B>, :!add-paths, :!add-human, :!add-degree,
  :!head, ok => qq:to/OK/;
Saved '--before-context=!' as: -B
OK

query-ok <--context=[2] --save=C>, :!add-paths, :!add-human, :!add-degree,
  :!head, ok => qq:to/OK/;
Saved '--context=[2]' as: -C
OK

query-ok <--smartcase --save=(default)>, :!add-paths, :!add-human, :!add-degree,
  :!head, ok => qq:to/OK/;
Saved '--smartcase' as: (default)
OK

query-ok <--list-custom-options>, :!add-paths, :!add-human, :!add-degree,
  :!head, ok => qq:to/OK/;
  (default): --smartcase
         -B: --before-context=!
         -C: --context=[2]
         -i: --ignorecase
         -m: --ignoremark
OK

query-ok <--list-expanded-options>, :!add-paths, :!add-human, :!add-degree,
  :!head, ok => qq:to/OK/;
--smartcase
OK

query-ok <--save=(default)>, :!add-paths, :!add-human, :!add-degree,
  :!head, ok => qq:to/OK/;
Removed custom option '(default)'
OK

query-ok <--list-custom-options>, :!add-paths, :!add-human, :!add-degree,
  :!head, ok => qq:to/OK/;
 -B: --before-context=!
 -C: --context=[2]
 -i: --ignorecase
 -m: --ignoremark
OK

query-ok <-/im --list-expanded-options>, :!add-paths, :!add-human, :!add-degree,
  :!head, ok => qq:to/OK/;
No options found
OK

query-ok <*@ --type=starts-with q>, :!add-paths, :!head, ok => qq:to/OK/;
foo
1:{BON}*\@{BOFF}foo
OK

query-ok "--patterns-from=$patterns.relative()", :!head, ok => qq:to/OK/;
eight
9:{BON}eight{BOFF}

nine
9:{BON}eight{BOFF}
10:{BON}nine{BOFF}
OK

query-ok <v --and=n>, :head(2), ok => qq:to/OK/;
seven
8:se{BON}v{BOFF}e{BON}n{BOFF}

eight
8:se{BON}v{BOFF}e{BON}n{BOFF}

nine
8:se{BON}v{BOFF}e{BON}n{BOFF}
OK

query-ok <v --or=x --smartcase>, :head(2), ok => qq:to/OK/;
five
6:fi{BON}v{BOFF}e

six
6:fi{BON}v{BOFF}e
7:SI{BON}X{BOFF}

seven
6:fi{BON}v{BOFF}e
7:SI{BON}X{BOFF}
8:se{BON}v{BOFF}en

eight
6:fi{BON}v{BOFF}e
7:SI{BON}X{BOFF}
8:se{BON}v{BOFF}en

nine
6:fi{BON}v{BOFF}e
7:SI{BON}X{BOFF}
8:se{BON}v{BOFF}en
OK

query-ok <--not=e --andnot=o --andnot=^$>, :head(2), ok => qq:to/OK/;
six
7:SIX

seven
7:SIX

eight
7:SIX

nine
7:SIX
OK

query-ok <--per-paragraph five --and=nine>, :!head, ok => qq:to/OK/;
nine
5:four
{BON}five{BOFF}
SIX
seven
eight
{BON}nine{BOFF}
OK

query-ok </./ --unique --matches-only>, ok => qq:to/OK/;
e
f
g
h
i
I
n
o
r
S
s
t
u
v
w
X
z
OK

query-ok </./ --unique --count-only>, ok => qq:to/OK/;
9
OK

query-ok </./ --unique --count-only --matches-only>, ok => qq:to/OK/;
17
OK

query-ok </./ --unique --count-only --matches-only --verbose>, ok => qq:to/OK/;
17 unique occurrences in 10 files
OK

query-ok <eight --also-first>, :!head, ok => qq:to/OK/;
eight
1:zero
9:{BON}eight{BOFF}

nine
1:zero
9:{BON}eight{BOFF}
OK

query-ok <eight --also-first=3>, :!head, ok => qq:to/OK/;
eight
1:zero
2:one
3:two
9:{BON}eight{BOFF}

nine
1:zero
2:one
3:two
9:{BON}eight{BOFF}
OK

query-ok <eight --always-first>, :!head, ok => qq:to/OK/;
zero
1:zero

one
1:zero

two
1:zero

three
1:zero

four
1:zero

five
1:zero

six
1:zero

seven
1:zero

eight
1:zero
9:{BON}eight{BOFF}

nine
1:zero
9:{BON}eight{BOFF}
OK

query-ok <eight --always-first=2>, :!head, ok => qq:to/OK/;
zero
1:zero

one
1:zero
2:one

two
1:zero
2:one

three
1:zero
2:one

four
1:zero
2:one

five
1:zero
2:one

six
1:zero
2:one

seven
1:zero
2:one

eight
1:zero
2:one
9:{BON}eight{BOFF}

nine
1:zero
2:one
9:{BON}eight{BOFF}
OK

done-testing;

# vim: expandtab shiftwidth=4


