EV-WebKit
view release on metacpan or search on metacpan
t/61-cookies.t view on Meta::CPAN
$T->load_cookies($jar, sub { ($res, $err) = @_; $fired = 1; EV::break });
EV::run; undef $wd;
ok($fired, 'load_cookies: callback fired under a zero timeout');
is($err, 'timeout', 'load_cookies: a cancelled load reports timeout (not a fake 0-loaded success)')
or diag("res=" . (defined $res ? $res : '(undef)') . " err=" . ($err // '(undef)'));
$T->quit;
}
# The module promises ONE timeout string across the whole API, so a caller can
# test $err eq 'timeout' uniformly. Only some ops had that pinned by a test --
# mutation testing changed cookies'/clear_cookies'/screenshot's string
# independently and the whole suite stayed green each time. Pin them directly.
{
for my $case (
['cookies' => sub { $_[0]->cookies('http://example.com/', $_[1]) }],
['clear_cookies' => sub { $_[0]->clear_cookies($_[1]) }],
['set_cookie' => sub { $_[0]->set_cookie({name=>'t',value=>'1',domain=>'example.com'}, $_[1]) }],
['screenshot' => sub { $_[0]->screenshot({bytes=>1}, $_[1]) }],
) {
my ($name, $call) = @$case;
# timeout => 0 arms the watchdog for the very next tick -- but it is
# still a RACE: on a loaded machine the op can complete before the
# watchdog cancels it, and then there is no error to check (seen live).
# So retry until the watchdog actually wins, and assert only on a run
# where it did. Retrying is not a fudge: the ONE thing under test is the
# error STRING, and a run that completed successfully simply did not
# exercise it.
my ($err, $fired, $timed_out, $tries, $other) = (undef, 0, 0, 0, undef);
while ($tries++ < 10) {
my $T = EV::WebKit->new(window => [200,150], ephemeral => 1, timeout => 0);
($err, $fired) = (undef, 0);
my $wd = EV::timer(15, 0, sub { EV::break });
$call->($T, sub { $fired++; $err = $_[1]; EV::break });
EV::run; undef $wd;
$T->quit;
# An error that is NOT a cancellation means the op failed for its
# own reasons -- seen on a headless CI runner, where get_snapshot
# can fail under llvmpipe before the watchdog lands. That run did
# not exercise the timeout path either, so keep trying rather than
# asserting on it. The block below pins the literal deterministically,
# so degrading to a skip here does not reopen the mutation this
# block was written for.
last if $timed_out = ($fired && defined $err && $err eq 'timeout');
$other = $err if $fired && defined $err;
}
ok($fired, "$name: callback fired under a zero timeout");
SKIP: {
skip "$name: the watchdog never won in $tries tries (machine too fast/"
. 'loaded, or the op failed on its own: '
. ($other // 'no error at all') . ')', 1 unless $timed_out;
is($err, 'timeout', "$name: uses the module's uniform 'timeout' error")
or diag("err=" . ($err // '(undef)'));
}
}
}
# save_cookies($file, \@uris, $cb): a middle argument that is not an arrayref
# was never looked at again, so save_cookies($file, 'https://one.example/', $cb)
# reported SUCCESS having snapshotted every uri the instance had visited --
# the opposite scope from the one asked for, into a file that holds
# credentials. cookies($uri, $cb), the sibling, takes a bare string, which is
# exactly where that spelling comes from.
{
require File::Temp;
my $sc = EV::WebKit->new(window => [200,150], ephemeral => 1, timeout => 10);
$sc->mock_scheme('sc', sub { ('<p>x</p>', 'text/html') });
my $ready = 0;
$sc->go('sc://a', sub { $ready = 1; EV::break });
TWK::run_with_timeout(20);
ok($ready, 'premise: the instance has navigated somewhere');
my $sdir = File::Temp::tempdir(CLEANUP => 1);
my $f = "$sdir/scope.json";
ok(!eval { $sc->save_cookies($f, 'http://only-this.example/', sub {}); 1 },
'save_cookies croaks on a bare uri where a list belongs');
like($@, qr/must be an array reference/, '...saying so');
like($@, qr/cookies\(\) takes a bare uri/, '...and naming the sibling it was confused with');
# Settle first: the write is deferred, so checking with zero loop turns in
# between cannot fail in any world -- measured, the file appears one tick
# later.
{ my $settle = EV::timer(1.0, 0, sub { EV::break }); EV::run }
ok(!-e $f, '...having written nothing');
# the documented spellings still work -- and SUCCEED, not merely answer
my ($e1, $d1) = (undef, 0);
$sc->save_cookies($f, ['sc://a'], sub { $e1 = $_[1]; $d1 = 1; EV::break });
TWK::run_with_timeout(20);
ok($d1, 'save_cookies with an explicit list still answers') or diag 'no callback';
is($e1, undef, '...without an error') or diag $e1;
my ($e2, $d2) = (undef, 0);
$sc->save_cookies("$sdir/scope2.json", sub { $e2 = $_[1]; $d2 = 1; EV::break });
TWK::run_with_timeout(20);
ok($d2, 'save_cookies with the list omitted still answers') or diag 'no callback';
is($e2, undef, '...without an error either') or diag $e2;
$sc->quit;
}
# The live checks above degrade to a SKIP on a machine that never lets the
# watchdog win, so a changed literal could slip through there. Pin it at the
# source too: every cancellation branch in the module must resolve to exactly
# 'timeout'. Deterministic, and needs no browser.
{
# %INC, not a guessed path: the file that is actually LOADED is the one
# under test. Preferring blib/ read the wrong copy under an -I mutation
# run, so the check passed while the literal really had changed.
my $mod = $INC{'EV/WebKit.pm'} or die 'EV::WebKit is not loaded';
open my $mh, '<', $mod or die "cannot read $mod: $!";
my $code = do { local $/; <$mh> };
close $mh;
my @branches = $code =~ /is_cancelled \? (\S+)/g;
cmp_ok(scalar @branches, '>=', 4, 'found the cancellation branches to check');
my @wrong = grep { $_ ne "'timeout'" } @branches;
is(scalar @wrong, 0, "every cancellation branch resolves the uniform 'timeout' string")
or diag('these do not: ' . join(' | ', @wrong));
}
done_testing;
( run in 1.991 second using v1.01-cache-2.11-cpan-007c89162af )