use Test2::V0;

use Clean::Eval qw/clean_eval clean_string_eval last_error/;

subtest block_success => sub {
    my $ret = clean_eval { 42 };
    ok($ret, "true on success");
    is($ret, 1, "returns 1 on success");
};

subtest block_success_false_value => sub {
    my $ret = clean_eval { 0 };
    ok($ret, "still true even though block returned false");
    is($ret, 1, "returns 1, not the block's false return");

    $ret = clean_eval { undef };
    ok($ret, "still true even though block returned undef");
    is($ret, 1, "returns 1, not the block's undef return");
};

subtest block_failure => sub {
    my $ret = clean_eval { die "boom\n" };
    ok(!$ret, "false on failure (boolean overload)");
    is("$ret", "boom\n", "stringifies to error message");
    is($ret->to_string, "boom\n", "to_string method matches");
    isa_ok($ret, ['Clean::Eval'], "blessed into Clean::Eval");
    is($ret->{error}, "boom\n", "error key set");
    ok(defined $ret->{package}, "package key set");
    ok(defined $ret->{file},    "file key set");
    like($ret->{line}, qr/^\d+$/, "line key is a number");
};

subtest string_success => sub {
    my $ret = clean_string_eval 'my $x = 1 + 1';
    ok($ret, "true on success");
    is($ret, 1, "returns 1");
};

subtest string_failure => sub {
    my $ret = clean_string_eval 'die "kapow\n"';
    ok(!$ret, "false on failure");
    is("$ret", "kapow\n", "stringifies to error message");
    isa_ok($ret, ['Clean::Eval'], "blessed into Clean::Eval");
};

subtest string_eval_line_directive => sub {
    # Compile-time error in the string should report this file
    my $line = __LINE__ + 1;
    my $ret = clean_string_eval 'this is not valid perl ][[';
    ok(!$ret, "compile error caught");
    like("$ret", qr/\Q@{[__FILE__]}\E/, "error mentions our file (via #line)");
    like("$ret", qr/line $line\b/, "error mentions our line");
};

subtest last_error_tracking => sub {
    my $before = clean_eval { die "first\n" };
    is(last_error(), $before, "last_error matches most recent failure");

    my $after = clean_eval { die "second\n" };
    is(last_error(), $after, "last_error updates");
    isnt(last_error(), $before, "previous error replaced");

    # success does not clear last_error
    my $ok = clean_eval { 1 };
    is($ok, 1, "success returned 1");
    is(last_error(), $after, "last_error unchanged after a success");
};

subtest caller_dollar_at_preserved => sub {
    local $@ = "caller-set\n";
    my $ret = clean_eval { die "trapped\n" };
    ok(!$ret, "failure returned");
    is($@, "caller-set\n", "outer \$@ untouched");

    $ret = clean_eval { 1 };
    is($ret, 1, "success returned");
    is($@, "caller-set\n", "outer \$@ still untouched");

    $ret = clean_string_eval 'die "trapped\n"';
    ok(!$ret, "string failure returned");
    is($@, "caller-set\n", "outer \$@ untouched by string form");
};

subtest object_error_preserved => sub {
    my $obj = bless { msg => "oops" }, "Some::Err";
    my $ret = clean_eval { die $obj };
    ok(!$ret, "failure");
    is($ret->{error}, $obj, "object error stored without stringification");
};

subtest exports => sub {
    # Importer-based: nothing exported by default
    package Clean::Eval::Test::NoImport;
    use Clean::Eval;
    ::ok(!__PACKAGE__->can('clean_eval'),        "clean_eval not imported by default");
    ::ok(!__PACKAGE__->can('clean_string_eval'), "clean_string_eval not imported by default");
    ::ok(!__PACKAGE__->can('last_error'),        "last_error not imported by default");
};

done_testing;
