The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/env perl
# -*- mode: perl; coding: utf-8 -*-
#----------------------------------------
use strict;
use warnings FATAL => qw(all);
use FindBin; BEGIN { do "$FindBin::Bin/t_lib.pl" }
#----------------------------------------

use Test::More qw(no_plan);
use YATT::Lite::Test::TestUtil;

use YATT::Lite::Constants;
require_ok('YATT::Lite');

use YATT::Lite::Util qw(appname);
sub myapp {join _ => MyTest => appname($0), @_}

our @res;
sub run_list {
  my ($theme, $list, $obj, $method, @args) = @_;
  open my $fh, ">", \ (my $buf = "") or die $!;
  @res = ();
  eval {$obj->$method($fh, @args)};
  if ($@) {
    fail "$theme $@";
  } else {
    is scalar @res, scalar @$list, "$theme res.len vs list.len";
    my $i = 0;
    foreach my $res (@res) {
      ok($res->[0], "$theme.$i $res->[2] == __LINE__ (runtime ==)");
      is($res->[1], $res->[2], "$theme.$i __LINE__ is $res->[2] (external 'is')");
    } continue {$i++}
  }
}

# 行番号が一致したら BYE, を差し込む。
sub inject {
  my ($text, $listvar) = @_;
  $text =~ s{(?=<!--(\d+)-->)}{
    push @$listvar, $1;
    sprintf q{<?perl push @main::res, [%1$s == __LINE__, __LINE__, %1$s]?>}, $1;
  }eg;
  $text;
}

sub is_debugging {
  my $glob = $main::{'DB::'} or return;
  my $stash = *{$glob}{HASH} or return;
  defined $stash->{'cmd'};
}

my @OPT = (no_lineinfo => $ENV{NO_LINEINFO} // is_debugging()
	   , debug_cgen => $ENV{DEBUG});

my $test_widget_lineno = sub {
  my ($yatt, $THEME, @test) = @_;
  foreach my $test (@test) {
    my ($name, $region, %call) = @$test;
    my $part = $yatt->find_part('index', $name);
    is $part->{cf_startln}, $region->[0]
      , "$THEME widget '$name' startln == $region->[0]";
    is $part->{cf_endln}, $region->[1]
      , "$THEME widget '$name' endln == $region->[1]";
    foreach my $tok (@{$part->{tree}}) {
      next unless ref $tok and $tok->[NODE_TYPE] == TYPE_ELEMENT;
      my $callpath = join ":", @{$tok->[NODE_PATH]};
      is $tok->[NODE_LNO], $call{$callpath}
	, "$THEME widget call $callpath lineno = $call{$callpath}";
    }
  }
  
};

my $i = 1;
{
  my $THEME = "basic";
  my $yatt = new YATT::Lite(app_ns => myapp($i), vfs => [data => {}], @OPT);
  my $SUB = 'index';
  ok(my $tmpl = $yatt->add_to($SUB => inject <<'END', \ my @list)
<!yatt:args x y>
<!--2-->
<yatt:foo />
<!--4-->
<yatt:baz />
<!yatt:widget foo >
<!--7-->
bar

<!yatt:widget baz >
<!--11-->qux


END
     , "$THEME - add_to $SUB");

  my $pkg = $yatt->find_product(perl => $tmpl);
  $test_widget_lineno->($yatt, $THEME
			, ['' => [1, 6], 'yatt:foo' => 3, 'yatt:baz' => 5]
			, [foo => [6, 10]], [baz => [10, 14]]);
  run_list($THEME, \@list, $pkg, render_ => ());
}
# "\n";

$i = 2;
# コメント
{
  my $THEME = "comment";
  my $yatt = new YATT::Lite(app_ns => myapp($i)
			    , vfs => [data => {}], @OPT);
  my $SUB = 'index';
  ok(my $tmpl = $yatt->add_to($SUB => inject <<'END', \ my @list)
<!yatt:args  x y>

<!--#yatt
<!yatt:widget foo >
-->
<yatt:foo /><!--6-->
<!--#yatt
<!yatt:widget bar >
--><!--9-->

<!yatt:widget foo >
<!--12-->bar
END
     , "$THEME - add_to $SUB");

  my $pkg = $yatt->find_product(perl => $tmpl);
  $test_widget_lineno->($yatt, $THEME
			, ['' => [1, 11], 'yatt:foo' => 6]
			, [foo => [11, 13]]);
  run_list($THEME, \@list, $pkg, render_ => ());
}
# 引数リスト
$i = 3;
{
  my $THEME = "newline in arg decls";
  my $yatt = new YATT::Lite(app_ns => myapp($i), vfs => [data => {}], @OPT);
  my $SUB = 'index';
  ok(my $tmpl = $yatt->add_to($SUB => inject <<'END', \ my @list)
<!yatt:args
    x=text
    y=value
>
<!--5-->
<yatt:foo  x y/>

<!yatt:widget
  foo
  x y
   =
  "html?
foo
bar
"
z>
<!--17-->bar
END
     , "$THEME - add_to $SUB");

  my $pkg = $yatt->find_product(perl => $tmpl);
  foreach my $test (['' => [x => 2], [y => 3]]) {
    my ($name, @args) = @$test;
    my $part = $yatt->find_part('index', $name);
    foreach my $arginfo (@args) {
      my ($arg, $lineno) = @$arginfo;
      is $part->{arg_dict}{$arg}->lineno, $lineno, "$THEME arg $arg";
    }
  }
  run_list($THEME, \@list, $pkg, render_ => 'myx', 'myY');
}

$i = 4;
{
  my $THEME = "newline in call args (bodyless)";
  my $yatt = new YATT::Lite(app_ns => myapp($i), vfs => [data => {}], @OPT);
  my $SUB = 'index';
  ok(my $tmpl = $yatt->add_to($SUB => inject <<'END', \ my @list)
<!yatt:args  x y>

<yatt:foo
  x
  y
/>
<!--7-->

<!yatt:widget foo  x y>
<!--10-->bar
END
     , "$THEME - add_to $SUB");

  my $pkg = $yatt->find_product(perl => $tmpl);
 TODO: {
    local our $TODO = "Perl bug?" unless $] >= 5.012;
    run_list($THEME, \@list, $pkg, render_ => 'myx', 'myY');
  }
}

# body

$i = 5;
{
  my $THEME = " body ";
  my $yatt = new YATT::Lite(app_ns => myapp($i), vfs => [data => {}], @OPT);
  my $SUB = 'index';
  ok(my $tmpl = $yatt->add_to($SUB => inject <<'END', \ my @list), "$THEME - add_to $SUB");
<!yatt:args  x y>
foo
<!--3-->
<yatt:bar  x y>
hoehoe
<!--6-->
</yatt:bar>
<!--8-->

<!yatt:widget bar  x y>
<div>
<!--12-->
<yatt:body />
<!--14-->
</div>
END

  is $yatt->find_part($SUB, 'bar')->{cf_startln}, 10, "$THEME-$SUB. bar lineno";

  my $pkg = $yatt->find_product(perl => $tmpl);
  run_list($THEME, \@list, $pkg, render_ => 'foo', 'bar');
}

$i = 6;
TODO: {
  local our $TODO = "Not yet addressed";
  my $THEME = "<?yatt?> before args";
  my $yatt = new YATT::Lite(app_ns => myapp($i), vfs => [data => {}], @OPT);
  my $SUB = 'index';
  ok(my $tmpl = $yatt->add_to($SUB => inject <<'END', \ my @list), "$THEME - add_to $SUB");
<?yatt
?>
<!yatt:args
   x
   y>
<!--5-->
END

  eval {
    my $pkg = $yatt->find_product(perl => $tmpl);
    run_list($THEME, \@list, $pkg, render_ => 'foo', 'bar');
  };
  is $@, undef, "$THEME no error";
}

$i = 7;
TODO: {
  local our $TODO = "Perl bug?";
  my $THEME = "elematt";
  my $yatt = new YATT::Lite(app_ns => myapp($i), vfs => [data => {}], @OPT);
  my $SUB = 'index';
  ok(my $tmpl = $yatt->add_to($SUB => inject <<'END', \ my @list), "$THEME - add_to $SUB");
<yatt:elematt>
<:yatt:title>TITLE</:yatt:title>
BODY<!--3-->
<:yatt:header/>
<!--5-->HEADER
<:yatt:footer/>
<!--7-->FOOTER
</yatt:elematt>
<!--9-->

<!yatt:widget elematt title=code header=code footer=code>
<head>
&yatt:header();
<title>&yatt:title();</title>
</head>
<body>
<h2>&yatt:title();</h2>
<div id=main>
<yatt:body/>
</div>
&yatt:footer();
</body>
END

  is $yatt->find_part($SUB, $THEME)->{cf_startln}, 11
     , "$THEME-$SUB. lineno";

  $yatt->ensure_parsed($yatt->find_part($SUB, ''));

  my $pkg = $yatt->find_product(perl => $tmpl);
  run_list($THEME, \@list, $pkg, render_ => ());
}