Use Time::Fake to avoid sleeping during tests

This commit is contained in:
Maxime Besson 2019-11-04 14:54:41 +01:00
parent 5b2e6f7d9f
commit 983a4e6485
28 changed files with 570 additions and 100 deletions

View File

@ -8,19 +8,19 @@ init(
'Lemonldap::NG::Handler::Server',
{
logLevel => 'error',
handlerServiceTokenTTL => 2,
handlerServiceTokenTTL => 120,
vhostOptions => {
'test1.example.com' => {
vhostHttps => 0,
vhostPort => 80,
vhostMaintenance => 0,
vhostServiceTokenTTL => 3,
vhostServiceTokenTTL => 180,
},
'test2.example.com' => {
vhostHttps => 0,
vhostPort => 80,
vhostMaintenance => 0,
vhostServiceTokenTTL => 5,
vhostServiceTokenTTL => 300,
}
},
exportedHeaders => {
@ -48,7 +48,7 @@ ok(
VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token,
),
'Query with token'
'Query with token 1'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2);
@ -61,8 +61,8 @@ ok( @values == 2, 'Found 2 service header values' )
or print STDERR Data::Dumper::Dumper( $res->[1] );
count(2);
diag 'Waiting';
sleep 1;
# Waiting
Time::Fake->offset("+90s");
ok(
$res = $client->_get(
@ -70,7 +70,7 @@ ok(
VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token,
),
'Query with token'
'Query with token 2'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2);
@ -83,8 +83,8 @@ ok( @values == 2, 'Found 2 service header values' )
or print STDERR Data::Dumper::Dumper( $res->[1] );
count(2);
diag 'Waiting';
sleep 2;
# Waiting
Time::Fake->offset("+210s");
ok(
$res = $client->_get(
@ -92,7 +92,7 @@ ok(
VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token,
),
'Query with token'
'Query with token 3'
);
ok( $res->[0] == 302, 'Code is 200' ) or explain( $res->[0], 302 );
count(2);
@ -102,8 +102,8 @@ ok( @headers == 0, 'NONE service header found' )
or print STDERR Data::Dumper::Dumper( $res->[1] );
count(1);
diag 'Waiting';
sleep 1;
# Waiting
Time::Fake->offset("+270s");
ok(
$res = $client->_get(
@ -111,7 +111,7 @@ ok(
VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token,
),
'Query with token'
'Query with token 4'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2);
@ -131,8 +131,8 @@ ok( @values == 2, 'Found 2 service header values' )
or print STDERR Data::Dumper::Dumper( $res->[1] );
count(2);
diag 'Waiting';
sleep 1;
# Waiting
Time::Fake->offset("+330s");
ok(
$res = $client->_get(
@ -140,7 +140,7 @@ ok(
VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token,
),
'Query with token'
'Query with token 5'
);
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
count(2);
@ -156,7 +156,7 @@ ok(
VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token,
),
'Query with token'
'Query with token 6'
);
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
count(2);
@ -173,7 +173,7 @@ ok(
VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token,
),
'Query with token'
'Query with token 7'
);
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
count(2);

View File

@ -0,0 +1,235 @@
package Time::Fake;
use Carp;
use strict;
use vars '$VERSION';
$VERSION = "0.11";
#####################
my $OFFSET = 0;
*CORE::GLOBAL::time = sub() { CORE::time() + $OFFSET };
*CORE::GLOBAL::localtime = sub(;$) {
@_ ? CORE::localtime($_[0])
: CORE::localtime(CORE::time() + $OFFSET);
};
*CORE::GLOBAL::gmtime = sub(;$) {
@_ ? CORE::gmtime($_[0])
: CORE::gmtime(CORE::time() + $OFFSET);
};
sub import {
my $pkg = shift;
$pkg->offset(shift);
}
sub offset {
my $pkg = shift;
return $OFFSET if !@_;
my $old_offset = $OFFSET;
$OFFSET = _to_offset(shift);
return $old_offset;
}
sub reset {
shift->offset(0);
}
my %mult = (
s => 1,
m => 60,
h => 60*60,
d => 60*60*24,
M => 60*60*24*30,
y => 60*60*24*365,
);
sub _to_offset {
my $t = shift || return 0;
if ($t =~ m/^([+-]\d+)([smhdMy]?)$/) {
$t = $1 * $mult{ $2 || "s" };
} elsif ($t !~ m/\D/) {
$t = $t - CORE::time;
} else {
croak "Invalid time offset: `$t'";
}
return $t;
}
1;
__END__
=head1 NAME
Time::Fake - Simulate different times without changing your system clock
=head1 SYNOPSIS
Pretend we are running 1 day in the future:
use Time::Fake '+1d';
Pretend we are running 1 year in the past:
use Time::Fake '-1y';
Pretend the script started at epoch time 1234567:
use Time::Fake 1234567;
See what an existing script would do if run 20 years in the future:
% perl -MTime::Fake="+20y" test.pl
Run a section of code in a time warp:
use Time::Fake;
# do some setup
Time::Fake->offset("+1y");
run_tests(); # thinks it's a year ahead
Time::Fake->reset; # back to the present
=head1 DESCRIPTION
Use this module to achieve the effect of changing your system clock, but
without actually changing your system clock. It overrides the Perl builtin
subs C<time>, C<localtime>, and C<gmtime>, causing them to return a
"faked" time of your choice. From the script's point of view, time still
flows at the normal rate, but it is just offset as if it were executing
in the past or present.
You may find this module useful in writing test scripts for code that has
time-sensitive logic.
=head1 USAGE
=head2 Using and importing:
use Time::Fake $t;
Is equivalent to:
use Time::Fake;
Time::Fake->offset($t);
See below for arguments to C<offset>. This usage makes it easy to
fake the time for existing scripts, as in:
% perl -MTime::Fake=+1y script.pl
=head2 offset
Time::Fake->offset( [$t] );
C<$t> is either an epoch time, or a relative offset of the following
form:
+3 # 3 seconds in the future
-3s # 3 seconds in the past
+1h # 1 hour in the future
etc..
Relative offsets must begin with a plus or minus symbol. The supported
units are:
s second
m minute
h hour
d day (24 hours)
M month (30 days)
y year (365 days)
If C<$t> is an epoch time, then C<time>, C<localtime>, and C<gmtime>
will act as though the the current time (when C<offset> was called) was
actually at C<$t> epoch seconds.
Otherwise, the offset C<$t> will be added to the times returned by these
builtin subs.
When C<$t> is false, C<time>, C<localtime>, C<gmtime>
remain overridden, but their behavior resets to reflect the actual
system time.
When C<$t> is omitted, nothing is changed, but C<offset> returns the
current additive offset (in seconds). Otherwise, its return value is
the I<previous> offset.
C<offset> may be called several times. However, I<The effect of multiple
calls is NOT CUMULATIVE.> That is:
Time::Fake->offset("+1h");
Time::Fake->offset("+1h");
## same as
# Time::Fake->offset("+1h");
## NOT the same as
# Time::Fake->offset("+2h");
Each call to C<offset> completely cancels out the effect of any
previous calls. To make the effect cumulative, use the return value
of calling C<offset> with no arguments:
Time::Fake->offset("+1h");
...
Time::Fake->offset( Time::Fake->offset + 3600 ); # add another hour
=head2 reset
Time::Fake->reset;
Is the same as:
Time::Fake->offset(0);
That is, it returns all the affected builtin subs to their
default behavior -- reporing the actual system time.
=head1 KNOWN CAVEATS
Time::Fake must be loaded at C<BEGIN>-time (e.g., with a standard
C<use> statement). It must be loaded before perl I<compiles> any code
that uses C<time>, C<localtime>, or C<gmtime>. Due to inherent
limitations in overriding builtin subs, any code that was compiled
before loading Time::Fake will not be affected.
Because the system clock is not being changed, only Perl code that
uses C<time>, C<localtime>, or C<gmtime> will be fooled about the date.
In particular, the operating system is not fooled,
nor are other programs. If your Perl code modifies a file for example,
the file's modification time will reflect the B<actual> (not faked) time.
Along the same lines, if your Perl script obtains the time from somewhere
other than the affected builtins subs (e.g., C<qx/date/>), the actual
(not faked) time will be reflected.
Time::Fake doesn't affect -M, -A, -C filetest operators in the way you'd
probably want. These still report the B<actual> (not faked) script start
time minus file access time.
Time::Fake has not been tested with other modules that override the time
builtins, e.g., Time::HiRes.
=head1 SEE ALSO
Time::Warp, which uses XS to fool more of Perl.
=head1 AUTHOR
Time::Fake is written by Mike Rosulek E<lt>mike@mikero.comE<gt>. Feel
free to contact me with comments, questions, patches, or whatever.
=head1 COPYRIGHT
Copyright (c) 2008 Mike Rosulek. All rights reserved. This module is free
software; you can redistribute it and/or modify it under the same terms as Perl
itself.

View File

@ -9,6 +9,10 @@ use_ok('Lemonldap::NG::Common::PSGI::Cli::Lib');
our $client;
our $count = 1;
BEGIN {
require 't/Time-Fake.pm';
}
no warnings 'redefine';
my $module;

View File

@ -13,7 +13,7 @@ BEGIN {
my $maintests = 16;
my $debug = 'error';
my $timeout = 6;
my $timeout = 72000;
my ( $issuer, $sp, $res );
my %handlerOR = ( issuer => [], sp => [] );
@ -164,8 +164,8 @@ SKIP: {
expectAutoPost( $res, 'auth.idp.com', '/saml/singleLogout',
'SAMLRequest' );
diag 'Waiting';
sleep $timeout + 1;
# Jump ahead in time
Time::Fake->offset("+".($timeout*1.5)."s");
# Push SAML logout request to IdP
switch ('issuer');

View File

@ -65,7 +65,7 @@ SKIP: {
expectOK($res);
my $idpId = expectCookie($res);
pass('Waiting timeout');
sleep 3;
Time::Fake->offset("+30s");
# Simple SP access
my $res;
@ -179,7 +179,7 @@ sub issuer {
portal => 'http://auth.idp.com',
authentication => 'Choice',
userDB => 'Same',
portalForceAuthnInterval => 2,
portalForceAuthnInterval => 5,
authChoiceParam => 'test',
authChoiceModules => {
demo => 'Demo;Demo;Demo',

View File

@ -11,7 +11,7 @@ BEGIN {
require 't/saml-lib.pm';
}
my $maintests = 12;
my $maintests = 11;
my $debug = 'error';
my ( $issuer, $sp, $res );
my %handlerOR = ( issuer => [], sp => [] );
@ -52,8 +52,9 @@ SKIP: {
);
expectOK($res);
my $idpId = expectCookie($res);
pass('Waiting timeout');
sleep 3;
# Skipping time
Time::Fake->offset("+30s");
# Simple SP access
my $res;
@ -168,7 +169,7 @@ sub issuer {
portal => 'http://auth.idp.com',
authentication => 'Demo',
userDB => 'Same',
portalForceAuthnInterval => 2,
portalForceAuthnInterval => 5,
issuerDBSAMLActivation => 1,
samlSPMetaDataOptions => {
'sp.com' => {

View File

@ -50,7 +50,7 @@ my $op = LLNG::Manager::Test->new( {
oidcRPMetaDataOptionsIDTokenSignAlg => "HS512",
oidcRPMetaDataOptionsClientSecret => "rpsecret",
oidcRPMetaDataOptionsUserIDAttr => "",
oidcRPMetaDataOptionsAccessTokenExpiration => 1,
oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
oidcRPMetaDataOptionsBypassConsent => 1,
oidcRPMetaDataOptionsRefreshToken => 1,
}
@ -166,7 +166,8 @@ $json = expectJSON($res);
ok( $json->{'name'} eq "Frédéric Accents", 'Got User Info' );
sleep(2);
# Skip ahead in time
Time::Fake->offset("+2h");
# Access token should have expired
$res = $op->_post(

View File

@ -55,7 +55,7 @@ my $op = LLNG::Manager::Test->new( {
oidcRPMetaDataOptionsIDTokenSignAlg => "HS512",
oidcRPMetaDataOptionsClientSecret => "rpsecret",
oidcRPMetaDataOptionsUserIDAttr => "",
oidcRPMetaDataOptionsAccessTokenExpiration => 1,
oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
oidcRPMetaDataOptionsBypassConsent => 1,
},
oauth => {
@ -187,7 +187,7 @@ ok( $json->{active}, "Token is valid" );
is( $json->{sub}, "french", "Response contains the correct sub" );
# Check status after expiration
sleep(2);
Time::Fake->offset("+2h");
$query = "token=$token";
ok(

View File

@ -55,7 +55,7 @@ my $op = LLNG::Manager::Test->new( {
oidcRPMetaDataOptionsIDTokenSignAlg => "HS512",
oidcRPMetaDataOptionsClientSecret => "rpsecret",
oidcRPMetaDataOptionsUserIDAttr => "",
oidcRPMetaDataOptionsAccessTokenExpiration => 1,
oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
oidcRPMetaDataOptionsBypassConsent => 1,
},
rp2 => {
@ -65,7 +65,7 @@ my $op = LLNG::Manager::Test->new( {
oidcRPMetaDataOptionsIDTokenSignAlg => "HS512",
oidcRPMetaDataOptionsClientSecret => "rp2secret",
oidcRPMetaDataOptionsUserIDAttr => "",
oidcRPMetaDataOptionsAccessTokenExpiration => 1,
oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
oidcRPMetaDataOptionsBypassConsent => 1,
oidcRPMetaDataOptionsRule => '$uid eq "dwho"',
}
@ -213,7 +213,8 @@ $res = expectJSON($res);
my $token = $res->{access_token};
ok( $token, 'Access token present' );
count(1);
sleep(2);
Time::Fake->offset("+2h");
ok(
$res = $op->_post(

View File

@ -20,7 +20,7 @@ SKIP: {
totp2fSelfRegistration => 1,
totp2fActivation => 1,
requireToken => 1,
formTimeout => 2,
formTimeout => 120,
loginHistoryEnabled => 0,
authentication => 'Combination',
userDB => 'Same',
@ -153,9 +153,8 @@ SKIP: {
'Code' );
$query =~ s/code=/code=$code/;
# Expired token
diag 'Waiting';
sleep 3;
# Skip ahead in time until the form token has expired
Time::Fake->offset("+5m");
ok(
$res = $client->_post(

View File

@ -122,8 +122,8 @@ ok( $attributes{'_updateTime'} =~ /^\d{14}$/, 'Timestamp found' )
or print STDERR Dumper( \%attributes );
count(3);
diag 'Waiting';
sleep 3;
# Waiting
Time::Fake->offset("+3s");
# Refresh rights
# ------------------------

View File

@ -168,8 +168,8 @@ ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/,
'Rejected -> Protection enabled' );
count(1);
diag 'Waiting';
sleep 1;
# Waiting
Time::Fake->offset("+1s");
## Sixth failed connection -> Rejected
ok(
@ -187,8 +187,8 @@ ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/,
'Rejected -> Protection enabled' );
count(1);
diag 'Waiting';
sleep 2;
# Waiting
Time::Fake->offset("+3s");
## Sixth successful connection -> Rejected
ok(
@ -206,8 +206,8 @@ ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/,
'Rejected -> Protection enabled' );
count(1);
diag 'Waiting';
sleep 3;
# Waiting
Time::Fake->offset("+6s");
## Seventh successful connection -> Accepted
ok(

View File

@ -14,7 +14,7 @@ my $client = LLNG::Manager::Test->new( {
authentication => 'Demo',
userdb => 'Same',
portalForceAuthn => 1,
portalForceAuthnInterval => 2,
portalForceAuthnInterval => 5,
}
}
);
@ -32,8 +32,8 @@ expectOK($res);
my $id1 = expectCookie($res);
count(1);
diag 'Waiting';
sleep 3;
# Skip ahead in time
Time::Fake->offset("+30s");
ok(
$res = $client->_get(

View File

@ -14,8 +14,8 @@ my $client = LLNG::Manager::Test->new( {
logLevel => 'error',
authentication => 'Demo',
userdb => 'Same',
timeoutActivity => 4,
timeoutActivityInterval => 2,
timeoutActivity => 7200,
timeoutActivityInterval => 60,
handlerInternalCache => 1,
}
}
@ -33,8 +33,8 @@ expectOK($res);
my $id1 = expectCookie($res);
count(1);
diag 'Waiting';
sleep 3;
# Skip ahead in time before activity timeout
Time::Fake->offset("+20m");
ok(
$res = $client->_get(
@ -49,8 +49,8 @@ ok( $res->[2]->[0] =~ qr%<span trspan="yourApps">Your applications</span>%,
or print STDERR Dumper( $res->[2]->[0] );
count(2);
diag 'Waiting';
sleep 5;
# Skip ahead in time after activity timeout
Time::Fake->offset("+3h");
ok(
$res = $client->_get(

View File

@ -14,7 +14,7 @@ my $client = LLNG::Manager::Test->new( {
logLevel => 'error',
authentication => 'Demo',
userdb => 'Same',
timeout => 10,
timeout => 72000,
handlerInternalCache => 1,
}
}
@ -32,8 +32,8 @@ expectOK($res);
my $id1 = expectCookie($res);
count(1);
diag 'Waiting';
sleep 9;
# Skip ahead in time before session timeout
Time::Fake->offset("+5h");
ok(
$res = $client->_get(
@ -48,8 +48,8 @@ ok( $res->[2]->[0] =~ qr%<span trspan="yourApps">Your applications</span>%,
or print STDERR Dumper( $res->[2]->[0] );
count(2);
diag 'Waiting';
sleep 2;
# Skip ahead in time after session timeout
Time::Fake->offset("+25h");
ok(
$res = $client->_get(

View File

@ -18,7 +18,7 @@ my $client = LLNG::Manager::Test->new( {
checkUser => 1,
requireToken => 1,
tokenUseGlobalStorage => 1,
formTimeout => 2,
formTimeout => 120,
checkUserDisplayPersistentInfo => 1,
checkUserDisplayEmptyValues => 1,
}
@ -64,9 +64,9 @@ ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
count(1);
# Expired token
diag 'Waiting';
sleep 3;
# Skipping time until the form token has expired
Time::Fake->offset("+5m");
$query =~ s/user=/user=rtyler/;
$query =~ s/url=/url=http%3A%2F%2Ftest1.example.com/;

View File

@ -18,7 +18,7 @@ my $client = LLNG::Manager::Test->new( {
checkUser => 1,
requireToken => 1,
tokenUseGlobalStorage => 0,
formTimeout => 2,
formTimeout => 120,
checkUserDisplayPersistentInfo => 1,
checkUserDisplayEmptyValues => 1,
}
@ -64,9 +64,9 @@ ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
count(1);
# Expired token
diag 'Waiting';
sleep 3;
# Skipping time ahead until the form token has expired
Time::Fake->offset("+5m");
$query =~ s/user=/user=rtyler/;
$query =~ s/url=/url=http%3A%2F%2Ftest1.example.com/;

View File

@ -36,9 +36,6 @@ ok(
count(1);
expectReject($res);
diag 'Waiting';
sleep 1;
## Try to authenticate
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
count(1);
@ -74,9 +71,6 @@ expectOK($res);
expectAuthenticatedAs( $res, 'rtyler' );
$client->logout($id);
diag 'Waiting';
sleep 1;
## Try to Impersonate
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
count(1);

View File

@ -23,7 +23,7 @@ SKIP: {
totp2fActivation => 1,
totp2fDigits => 8,
totp2fTTL => -1,
formTimeout => 2,
formTimeout => 120,
requireToken => 1,
}
}
@ -174,8 +174,8 @@ SKIP: {
'LLNG Code' );
$query =~ s/code=/code=$code/;
diag 'Waiting';
sleep 3;
# Skipping time until form token expiration
Time::Fake->offset("+5m");
ok(
$res = $client->_post(

View File

@ -18,7 +18,7 @@ SKIP: {
logLevel => 'error',
totp2fSelfRegistration => 1,
totp2fActivation => 1,
totp2fTTL => 2,
totp2fTTL => 120,
sfRemovedMsgRule => '$uid eq "dwho"',
sfRemovedUseNotif => 1,
portalMainLogo => 'common/logos/logo_llng_old.png',
@ -130,8 +130,8 @@ SKIP: {
$id = expectCookie($res);
$client->logout($id);
diag 'Waiting';
sleep 3;
# Skipping time until TOTP token expiration
Time::Fake->offset("+5m");
# Try to sign-in
ok(

View File

@ -22,7 +22,7 @@ SKIP: {
logLevel => 'error',
totp2fSelfRegistration => 1,
totp2fActivation => 1,
totp2fTTL => 2,
totp2fTTL => 120,
sfRemovedMsgRule => '$uid eq "dwho"',
sfRemovedUseNotif => 1,
portalMainLogo => 'common/logos/logo_llng_old.png',
@ -134,8 +134,8 @@ SKIP: {
$id = expectCookie($res);
$client->logout($id);
diag 'Waiting';
sleep 3;
# Skipping time until TOTP expiration
Time::Fake->offset("+5m");
# Try to sign-in
ok(

View File

@ -17,7 +17,7 @@ SKIP: {
logLevel => 'error',
totp2fSelfRegistration => 1,
totp2fActivation => 1,
totp2fTTL => 2,
totp2fTTL => 120,
portalMainLogo => 'common/logos/logo_llng_old.png',
}
}
@ -123,8 +123,8 @@ SKIP: {
$id = expectCookie($res);
$client->logout($id);
diag 'Waiting';
sleep 3;
# Skipping time until TOTP expiration
Time::Fake->offset("+5m");
# Try to sign-in
ok(

View File

@ -18,8 +18,8 @@ SKIP: {
u2fSelfRegistration => 1,
u2fActivation => 1,
portalMainLogo => 'common/logos/logo_llng_old.png',
totp2fTTL => 2,
u2fTTL => 2,
totp2fTTL => 120,
u2fTTL => 120,
sfRemovedMsgRule => 1,
}
}
@ -199,8 +199,8 @@ JjTJecOOS+88fK8qL1TrYv5rapIdqUI7aQ==
);
}
diag 'Waiting';
sleep 3;
# Skipping time until second factor TTL expiration
Time::Fake->offset("+5m");
# Try to sign-in
ok(

View File

@ -18,8 +18,8 @@ SKIP: {
u2fSelfRegistration => 1,
u2fActivation => 1,
portalMainLogo => 'common/logos/logo_llng_old.png',
totp2fTTL => 2,
u2fTTL => 2,
totp2fTTL => 120,
u2fTTL => 120,
}
}
);
@ -198,8 +198,8 @@ JjTJecOOS+88fK8qL1TrYv5rapIdqUI7aQ==
);
}
diag 'Waiting';
sleep 3;
# Skipping time until second factor registration has expired
Time::Fake->offset("+5m");
# Try to sign-in
ok(

View File

@ -172,8 +172,7 @@ SKIP: {
) or print STDERR Dumper( $res->[2]->[0] );
# Wait to have two different epoch values
diag 'Waiting';
sleep 1;
Time::Fake->offset("+1m");
# Ajax registration request
ok(

View File

@ -76,8 +76,8 @@ count(1);
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/, 'Protection enabled' );
count(1);
diag 'Waiting';
sleep 2;
# Cool down
Time::Fake->offset("+2s");
# Try to authenticate
# -------------------
@ -117,8 +117,8 @@ count(1);
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/, 'Protection enabled' );
count(1);
diag 'Waiting';
sleep 4;
# Cool down
Time::Fake->offset("+6s");
# Try to authenticate again
# -------------------------

View File

@ -0,0 +1,235 @@
package Time::Fake;
use Carp;
use strict;
use vars '$VERSION';
$VERSION = "0.11";
#####################
my $OFFSET = 0;
*CORE::GLOBAL::time = sub() { CORE::time() + $OFFSET };
*CORE::GLOBAL::localtime = sub(;$) {
@_ ? CORE::localtime($_[0])
: CORE::localtime(CORE::time() + $OFFSET);
};
*CORE::GLOBAL::gmtime = sub(;$) {
@_ ? CORE::gmtime($_[0])
: CORE::gmtime(CORE::time() + $OFFSET);
};
sub import {
my $pkg = shift;
$pkg->offset(shift);
}
sub offset {
my $pkg = shift;
return $OFFSET if !@_;
my $old_offset = $OFFSET;
$OFFSET = _to_offset(shift);
return $old_offset;
}
sub reset {
shift->offset(0);
}
my %mult = (
s => 1,
m => 60,
h => 60*60,
d => 60*60*24,
M => 60*60*24*30,
y => 60*60*24*365,
);
sub _to_offset {
my $t = shift || return 0;
if ($t =~ m/^([+-]\d+)([smhdMy]?)$/) {
$t = $1 * $mult{ $2 || "s" };
} elsif ($t !~ m/\D/) {
$t = $t - CORE::time;
} else {
croak "Invalid time offset: `$t'";
}
return $t;
}
1;
__END__
=head1 NAME
Time::Fake - Simulate different times without changing your system clock
=head1 SYNOPSIS
Pretend we are running 1 day in the future:
use Time::Fake '+1d';
Pretend we are running 1 year in the past:
use Time::Fake '-1y';
Pretend the script started at epoch time 1234567:
use Time::Fake 1234567;
See what an existing script would do if run 20 years in the future:
% perl -MTime::Fake="+20y" test.pl
Run a section of code in a time warp:
use Time::Fake;
# do some setup
Time::Fake->offset("+1y");
run_tests(); # thinks it's a year ahead
Time::Fake->reset; # back to the present
=head1 DESCRIPTION
Use this module to achieve the effect of changing your system clock, but
without actually changing your system clock. It overrides the Perl builtin
subs C<time>, C<localtime>, and C<gmtime>, causing them to return a
"faked" time of your choice. From the script's point of view, time still
flows at the normal rate, but it is just offset as if it were executing
in the past or present.
You may find this module useful in writing test scripts for code that has
time-sensitive logic.
=head1 USAGE
=head2 Using and importing:
use Time::Fake $t;
Is equivalent to:
use Time::Fake;
Time::Fake->offset($t);
See below for arguments to C<offset>. This usage makes it easy to
fake the time for existing scripts, as in:
% perl -MTime::Fake=+1y script.pl
=head2 offset
Time::Fake->offset( [$t] );
C<$t> is either an epoch time, or a relative offset of the following
form:
+3 # 3 seconds in the future
-3s # 3 seconds in the past
+1h # 1 hour in the future
etc..
Relative offsets must begin with a plus or minus symbol. The supported
units are:
s second
m minute
h hour
d day (24 hours)
M month (30 days)
y year (365 days)
If C<$t> is an epoch time, then C<time>, C<localtime>, and C<gmtime>
will act as though the the current time (when C<offset> was called) was
actually at C<$t> epoch seconds.
Otherwise, the offset C<$t> will be added to the times returned by these
builtin subs.
When C<$t> is false, C<time>, C<localtime>, C<gmtime>
remain overridden, but their behavior resets to reflect the actual
system time.
When C<$t> is omitted, nothing is changed, but C<offset> returns the
current additive offset (in seconds). Otherwise, its return value is
the I<previous> offset.
C<offset> may be called several times. However, I<The effect of multiple
calls is NOT CUMULATIVE.> That is:
Time::Fake->offset("+1h");
Time::Fake->offset("+1h");
## same as
# Time::Fake->offset("+1h");
## NOT the same as
# Time::Fake->offset("+2h");
Each call to C<offset> completely cancels out the effect of any
previous calls. To make the effect cumulative, use the return value
of calling C<offset> with no arguments:
Time::Fake->offset("+1h");
...
Time::Fake->offset( Time::Fake->offset + 3600 ); # add another hour
=head2 reset
Time::Fake->reset;
Is the same as:
Time::Fake->offset(0);
That is, it returns all the affected builtin subs to their
default behavior -- reporing the actual system time.
=head1 KNOWN CAVEATS
Time::Fake must be loaded at C<BEGIN>-time (e.g., with a standard
C<use> statement). It must be loaded before perl I<compiles> any code
that uses C<time>, C<localtime>, or C<gmtime>. Due to inherent
limitations in overriding builtin subs, any code that was compiled
before loading Time::Fake will not be affected.
Because the system clock is not being changed, only Perl code that
uses C<time>, C<localtime>, or C<gmtime> will be fooled about the date.
In particular, the operating system is not fooled,
nor are other programs. If your Perl code modifies a file for example,
the file's modification time will reflect the B<actual> (not faked) time.
Along the same lines, if your Perl script obtains the time from somewhere
other than the affected builtins subs (e.g., C<qx/date/>), the actual
(not faked) time will be reflected.
Time::Fake doesn't affect -M, -A, -C filetest operators in the way you'd
probably want. These still report the B<actual> (not faked) script start
time minus file access time.
Time::Fake has not been tested with other modules that override the time
builtins, e.g., Time::HiRes.
=head1 SEE ALSO
Time::Warp, which uses XS to fool more of Perl.
=head1 AUTHOR
Time::Fake is written by Mike Rosulek E<lt>mike@mikero.comE<gt>. Feel
free to contact me with comments, questions, patches, or whatever.
=head1 COPYRIGHT
Copyright (c) 2008 Mike Rosulek. All rights reserved. This module is free
software; you can redistribute it and/or modify it under the same terms as Perl
itself.

View File

@ -65,6 +65,7 @@ use Lemonldap::NG::Common::FormEncode;
no warnings 'redefine';
BEGIN {
require 't/Time-Fake.pm';
use_ok('Lemonldap::NG::Portal::Main');
}