Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Artificial System Clock

by JohnBoy (Initiate)
on Nov 11, 2008 at 13:25 UTC ( [id://722846]=perlquestion: print w/replies, xml ) Need Help??

JohnBoy has asked for the wisdom of the Perl Monks concerning the following question:

Hi - brand new here, please be gentle! I sure don't know if this is the right place to start, but after reading the FAQs, it seemed appropriate.

Anyway, first I'd like to introduce the concept of an Artificial System Clock (ASC). For an introduction to the ASC concept, please visit http://www.timechannels.com.

In a language like Java, you implement this by making a class that has a method on it, perhaps called getSystemDate(). This method looks at some kind of global, static variable for the current "state" of the ASC. By default, it's Off, and in the Off state the getSystemDate() method returns the current, real date-time. But if you set the global state variable to On, and provide a specific date-time value, then the getSystemDate() method returns that specific date-time.

You then go through your code base, and everywhere that you are obtaining the date-time directly, you replace it with a call to this getSystemDate() method.

Once you do this, you have an ASC. You have effectively tricked your whole application into thinking that it is a different date-time, without having to alter the server's clock, and this is enormously valuable for testing purposes.

I would like to solicit professional opinions on how I went about implementing the ASC/TimeChannels client software for Perl.

Here is the implementing class I wrote for Perl:

#!/usr/bin/perl # This is the TimeChannels Artificial System Clock implementation clas +s for Perl. # Everywhere in your application codebase that obtains the date/time d +irectly, replace with a call # to the getSystemDate() sub in this file. # # For simple unit-testing, you can call setToStaticTestState() and pas +s it a specific date-time value. # # For system, integration, acpt testing, call setToTestState() and pas +s it a specific channel id you have setup in # the TimeChannels.com control panel. # # Return to normal time by calling setToProductionState() # # NOTE: Requires LWP bundle in order to work. # Use this line: # # perl -MCPAN -e 'install Bundle::LWP' # # to install it. package TimeChannels; require LWP::UserAgent; use strict; use Time::Local; my $tcURL = "http://www.timechannels.com/GetChannel"; my $tcURLms = "http://www.timechannels.com/GetChannelMS"; my $tcAscState = "TimeChannels.state.Production"; my $tcChannel = "0"; my $tcAscDateTime = 0; my $tcServiceURL = "http://www.timechannels.com/GetChannel"; my $tcMillisecondMode = "false"; sub getState { return $tcAscState; } sub setToProductionState { $tcAscState = "TimeChannels.state.Production"; } sub setToStaticTestState { my ($parm1, $parm2) = @_; $tcAscState = "TimeChannels.state.TestDate"; $tcAscDateTime = $parm2; } sub setToTestState { my ($parm1, $parm2) = @_; $tcAscState = "TimeChannels.state.TestChannel"; $tcChannel = $parm2; } sub setToMillisecondMode { $tcMillisecondMode = "true"; $tcServiceURL = $tcURLms; } sub setToStandardMode { $tcMillisecondMode = "false"; $tcServiceURL = $tcURL; } sub getSystemDate { if ($tcAscState eq "TimeChannels.state.Production") { return time; } if ($tcAscState eq "TimeChannels.state.TestDate") { return $tcAscDateTime; } if ($tcAscState eq "TimeChannels.state.TestChannel") { return TimeChannels->getChannelFromTimeChannelsService(); } return time; } sub getChannelFromTimeChannelsService { my $url = $tcServiceURL . '?channel=' . $tcChannel; use LWP::Simple; my $channelValue = get $url; die "Could not get date-time channel from $url" unless defined $chan +nelValue; my $retVal = time; if ($tcMillisecondMode eq "true") { $retVal = (1 * $channelValue) / 1000; } else { my $year = substr($channelValue, 0, 4); my $month = (1 * substr($channelValue, 5, 2)) - 1; my $day = 1* substr($channelValue, 8, 2); my $hour = 1 * substr($channelValue, 11, 2); my $minute = 1 * substr($channelValue, 14, 2); my $ampm = substr($channelValue, 17, 2); if ($ampm eq "PM") { $hour += 12; } $retVal = timelocal(0, $minute, $hour, $day, $month, $year); } return $retVal; }

How does this look to you? It tests out fine, and I will post the testing/sample usage code soon. But I know next to nothing about Perl, and I would be grateful if anyone with more Perl experience than me would take a look and let me know if you see any potential issues.

Many thanks to all who reply.

John

Replies are listed 'Best First'.
Re: Artificial System Clock
by blokhead (Monsignor) on Nov 11, 2008 at 14:09 UTC
    You might want to check whether Time::Fake (simple overriding of time and related functions) or Time::Warp (more extensive XS magic) are sufficient for your purposes.

    blokhead

Re: Artificial System Clock
by JavaFan (Canon) on Nov 11, 2008 at 13:41 UTC
    Instead of requiring code changes to replace time/localtime/gmtime/sleep/alarm with calls to your new function, I would have just replaced the globals.

    And I think it has already been done. Don't Time::Mock and Time::MockTime do something similar?

      Thanks for your reply. I took a look at these two modules:

      Time:Mock

      I read both:

      http://search.cpan.org/dist/Time-Mock/lib/Time/Mock.pm

      and:

      http://search.cpan.org/~ewilhelm/Time-Mock-v0.0.1/lib/Time/Mock.pm#Class_Methods

      about this, and although I certainly don't understand most of what is described there, it sounds like it does something different. Namely that it allows you to speed-up or slow-down the system clock, and there appears to be something about alarms in there. It looks like there are issues with child processes and forking too, although they may have corrected that by now (?)

      Time::MockTime

      I was unable to find anything about this with a quick Google search. Can you please point me to something that gives an overview of it? (Sorry for my new-ness!)

      * * *

      An Artificial System Clock does something different: it provides you the ability to trick your system into thinking it is a different date-time. Once implemented, you can instantly move the application forward or backward to a date, run some tests to verify that the system does what it's supposed to in that date-time context, and then instantly move the application to a different date-time, verify more application behavior, etc.

      The value the TimeChannels service provides is that it keeps the date-time setting in one, shared ("published") location, where multiple parties can subscribe to it. Especially in building a system that interfaces other systems, if all the interfacing systems have implemented the ASC, and are all subscribing to the same ASC channel, then the testers can coordinate date-time jumps by simply changing the ASC value in one location (i.e., TimeChannels.com), and all the interfacing systems suddenly reflect the new date-time.

      * * *

      I am very interested in your first statement, though, about replacing the globals. Can you please elaborate on that? Do you mean to replace the contents of existing perl library functions like time, localtime, etc.?

      Thanks,

      John

        Have you read http://search.cpan.org/~ddick/Test-MockTime-0.09/lib/Test/MockTime.pod?

        From its description:

        This module was created to enable test suites to test code at specific points in time. Specifically it overrides localtime, gmtime and time at compile time and then relies on the user supplying a mock time via set_relative_time, set_absolute_time or set_fixed_time to alter future calls to gmtime,time or localtime.
        You also asked:
        I am very interested in your first statement, though, about replacing the globals. Can you please elaborate on that? Do you mean to replace the contents of existing perl library functions like time, localtime, etc.?
        Yes, except time, localtime, etc aren't library functions, but core functions. The source to Time::MockTime gives an example of how to do it:
        BEGIN { *CORE::GLOBAL::time = \&Test::MockTime::time; *CORE::GLOBAL::localtime = \&Test::MockTime::localtime; *CORE::GLOBAL::gmtime = \&Test::MockTime::gmtime; }

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://722846]
Approved by Hue-Bond
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others browsing the Monastery: (3)
As of 2024-04-20 04:29 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found