The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use v6-alpha;

use Test;

# various tests derived from L<S10/Autoloading>

plan 38;

if $?PUGS_BACKEND ne "BACKEND_PUGS" {
  skip_rest "PIL2JS and PIL-Run do not support eval() yet.";
  exit;
}

package OughtaLoad {
    sub AUTOLOAD(*@args) {
        "\&{$_}({ @args.map:{"'$_'"}.join(", ") })"
    };
}

# currently, $_ is passed in as a global as per spec!
$_ = "test";
my $x = OughtaLoad::AUTOLOAD(1,2,3);
is($x, q[&test('1', '2', '3')], "sanity");

lives_ok { $x = OughtaLoad::test(2,3,4) },
    "AUTOLOAD", :todo<feature>;

package OughtaWork {
    our $s = 0;
    sub AUTOSCALAR($_) { my $v = "\$$_ number {++$s}";
                     eval "our \$$_ := \$v";
                     \$v }
    our $a = 0;
    sub AUTOARRAY($_)  { my @v = ( "auto", $_, ++$a );
                     eval "our @$_ := @v";
                     \@v }
    our $h = 0;
    sub AUTOHASH($_)   { my %v = ( auto => $_, num => ++$h );
                     eval "our %$_ := %v";
                     \%v }
    our $u = 0;
    sub AUTOSUB($_)    { my $x = "\&{$_} number {++$u}";
                     my $sub = sub { $x };
                     eval "our &$_ := \$sub";
                     return $sub;
                   }
    our $m = 0;
    method AUTOMETH($_) { my $x = "{self} {$_}.number {++$m}";
                     my $method = sub($self:) { "{$self}.$x" };
                     eval "our &$_ := \$method";
                     return $method;
                   }
}

# however, the use of $_ in the above is because the first argument is
# bound to $_ via "default invocants" (which seems to not be
# working... hmm)
$_ = "poison";

# scalar
$x = OughtaWork::AUTOSCALAR("test");
is($x, q[$test number 1], "AUTOSCALAR sanity test");

lives_ok { $x = $OughtaWork::foo }, "AUTOSCALAR - first";
is($x, q[$foo number 2], "Returns correct var", :todo<feature>);

$x="";
lives_ok { $x = $OughtaWork::foo }, "AUTOSCALAR - repeat";
is($x, q[$foo number 2], "AUTOSCALAR only called once", :todo<feature>);

lives_ok { $x = $OughtaWork::bar }, "AUTOSCALAR - second";
is($x, q[$bar number 3], "Returns correct var", :todo<feature>);


# array
my $a = OughtaWork::AUTOARRAY("test");
is($a.join(","), q[auto,test,1], "AUTOARRAY sanity test");

my @a;
lives_ok { @a = @OughtaWork::foo }, "AUTOARRAY - first";
is(@a.join(","), q[auto,foo,2], "Returns correct var", :todo<feature>);

@a=();
lives_ok { @a = @OughtaWork::foo }, "AUTOARRAY - repeat";
is(@a.join(","), q[auto,foo,2], "AUTOARRAY only called once", :todo<feature>);

lives_ok { @a = @OughtaWork::bar }, "AUTOARRAY - second";
is(@a.join(","), q[auto,bar,3], "Returns correct var", :todo<feature>);


# hash
my %h = OughtaWork::AUTOHASH("test");
is(%h.kv.sort.join(","), q[1,auto,num,test], "AUTOHASH sanity test");

lives_ok { %h = %OughtaWork::foo }, "AUTOHASH - first";
is(%h.kv.sort.join(","), q[2,auto,foo,num], "Returns correct var", :todo<feature>);

%h=();
lives_ok { %h = %OughtaWork::foo }, "AUTOHASH - repeat";
is(%h.kv.sort.join(","), q[2,auto,foo,num], "AUTOHASH only called once", :todo<feature>);

lives_ok { %h = %OughtaWork::bar }, "AUTOHASH - second";
is(%h.kv.sort.join(","), q[3,auto,bar,num], "Returns correct var", :todo<feature>);


# sub
my $s = OughtaWork::AUTOSUB("test");
my $v = $s();
is($v, q[&test number 1], "AUTOSUB sanity test");

$v="";
ok eval(q{ $s = &OughtaWork::foo; $v = $s(); }),
        "AUTOSUB - first", :todo<feature>;
is($v, q[&foo number 2], "Returns correct var", :todo<feature>);

$v="";
ok eval(q{ $s = &OughtaWork::foo; $v = $s();  }),
        "AUTOSUB - repeat", :todo<feature>;
is($v, q[&foo number 2], "AUTOSUB only called once", :todo<feature>);

$v="";
ok eval(q{ $s = &OughtaWork::bar; $v = $s();  }),
        "AUTOSUB - second", :todo<feature>;
is($v, q[&bar number 3], "Returns correct var", :todo<feature>);


# presumably AUTOMETH on packages only has any meaning to "package
# methods"; they have to be classes or roles for AUTOMETH to be method
# lookups.
my $inv = ::OughtaWork;
ok eval(q{ $s = OughtaWork.AUTOMETH("test"); $v = $s($inv:) }),
        "AUTOMETH - sanity", :todo<bug>;
is($v, q[OughtaWork.test number 1], "AUTOMETH sanity test", :todo<bug>);

$v = "";
ok eval(q{ $s = OughtaWork.foo; $v = $s($inv:) }),
        "AUTOMETH - first", :todo<feature>;
is($x, q[OughtaWork.foo number 2], "Returns correct var", :todo<feature>);

$s = sub { };
ok eval(q{ $s = OughtaWork.foo; $v = $s($inv:)  }),
        "AUTOMETH - repeat", :todo<feature>;
is($x, q[OughtaWork.foo number 2], "AUTOMETH only called once", :todo<feature>);
ok eval(q{ $s = OughtaWork.bar; $v = $s($inv:)  }),
        "AUTOMETH - second", :todo<feature>;
is($x, q[OughtaWork.bar number 3], "Returns correct var", :todo<feature>);

# discovered bugs (TODO: write tests to track down:)

# 1. changing the is($v, ...) to is($s(), ...) causes:

#  pugs: cannot cast from VInt 1 to Pugs.AST.Internals.VCode

# seems to work interactively though;

# pugs> package OughtaWork { our $u = 0;  sub AUTOSUB    { my $x = "\&{$_} number {++$u}"; sub { $x } } }
# undef
# pugs> my $s = OughtaWork::AUTOSUB
# sub {...}
# pugs> $s
# sub {...}
# pugs> $s()
# '& number 1'

# further investigation required...

# 2. named subs don't seem to get implicit arguments via $_ (actually
#    as per S04 this seems to be design behaviour)

# 3. Various nonsense with using a Package as an invocant in a method
#    call

# 4. Couldn't take ref of a method via &Package.foo