use Test::More; use Getopt::Std; use Modern::Perl; use Data::Printer; use Test::Exception; use AdvancedShop; use MaleCustomer; use FemaleCustomer; use vars qw(%opts); getopts('p:', \%opts); # probability my $p = $opts{p} || 0.03; { # male customer, tolerance in range: my $male = MaleCustomer->new( tolerance => 45 ); # limits 30 .. 90 is ($male->wait, 45, 'male customer tolerance value between limits'); is ($male->getPayment, 12, 'returned expected male customer cost value'); is ($male->getRequiredTime, 15, 'returned expected male customer requiredTime value'); } { # female customer, requiredTime in range: my $female = FemaleCustomer->new( requiredTime => 25 ); # limits 20 .. 50 is ($female->getRequiredTime, 25, 'female customer requiredTime value between limits'); is ($female->getPayment, 25, 'returned expected female customer cost value'); is ($female->wait, 120, 'returned expected female customer tolerance value'); } { # male customer, tolerance out of range: throws_ok { MaleCustomer->new( tolerance => 95 ) } # limits 30 .. 90 qr/isa check for "tolerance" failed/, 'male customer tolerance value out of limits'; } { # female customer, requiredTime out of range: throws_ok { FemaleCustomer->new( requiredTime => 55 ) } # limits 20 .. 50 qr/isa check for "requiredTime" failed/, 'female customer requiredTime value out of limits'; } { # male customer, tolerance not a number: throws_ok { MaleCustomer->new( tolerance => 'potato' ) } qr/potato is not a number/, 'male customer tolerance value failed isa check'; } { # female customer, requiredTime out of range: throws_ok { FemaleCustomer->new( requiredTime => 'tomato' ) } qr/isa check for "requiredTime" failed/, 'female customer requiredTime value failed isa check'; } { # test inheritance - no method parentMethodOnly() in child class: my $male = MaleCustomer->new( tolerance => 45 ); is ( $male->parentMethodOnly, 'inheritance check OK', 'inheritance test passed' ); } { # AdvancedShop constructor, no seats param: throws_ok { AdvancedShop->new() } qr/Missing required arguments\: seats/, 'AdvancedShop constructor missing required arguments'; } { # AdvancedShop constructor, 4 seats: my $o = AdvancedShop->new( seats => 4 ); my $que = $o->getQueue; # p $que; is ( ref $que, 'ARRAY', 'got expected data structure for getQueue' ); is ( @$que, 4, 'got expected queue size' ); } { # test AdvancedShop->arrivingCustomer my $o = AdvancedShop->new( seats => 4 ); my $new_customer = $o->arrivingCustomer(); # p $new_customer; like ( ref $new_customer, qr/(fe)?malecustomer/i, 'have new customer object'); } { # test AdvancedShop->addToQueue my $o = AdvancedShop->new( seats => 4 ); # p $o->getQueue; is ( ref $o->getQueue->[0], '', '1st element of queue is empty' ); my $new_customer = $o->arrivingCustomer(); # p $new_customer; my $success = $o->addToQueue($new_customer); # p $o->getQueue; is ( $success, 1, 'successfully added new customer to queue'); like ( ref $o->getQueue->[0], qr/(fe)?malecustomer/i, '1st element of queue is now a customer object' ); } { # test AdvancedShop->removeFromQueue my $o = AdvancedShop->new( seats => 4 ); # p $o->getQueue; my $new_customer = $o->arrivingCustomer(); # p $new_customer; $o->addToQueue($new_customer); # p $o->getQueue; my $success = $o->removeFromQueue(); # p $o->getQueue; like ( $success, qr/(fe)?malecustomer/i, # returns Customer object 'successfully removed new customer from queue'); is ( ref $o->getQueue->[0], '', '1st element of queue is empty' ); } { # test AdvancedShop->expire # expire() will remove 1st element if it's an object and its wait() value is <= 0 # after subtraction of shifted customers' $duration, or if it's a MaleCustomer # & $duration > 35 my $o = AdvancedShop->new( seats => 4 ); # p $o->getQueue; my $new_customer = $o->arrivingCustomer(); # say 'new customer:'.ref $new_customer; # need at least 1 customer object in queue: $o->addToQueue($new_customer); # p $o->getQueue; # expire function accepts shifted 1st element so bypass shift stage: my $simulated_removed_customer = $o->arrivingCustomer(); # say 'served customer:'.ref $simulated_removed_customer; my $duration = $simulated_removed_customer->getRequiredTime; my $wait = $new_customer->wait; # say 'wait:'. $wait; say 'duration:'.$duration; say 'calc:'. ($wait - $duration); $o->expire($simulated_removed_customer); my $queue = $o->getQueue; # p $queue; if ( ref $new_customer eq 'MaleCustomer' && $duration > 35 ) { is ( $queue->[0], 0, 'male customer gone away due to excess wait' ); } elsif ( $wait - $duration <= 0 ) { is ( $queue->[0], 0, 'customer removed from queue' ); } else { # $wait - $duration greater than zero, custumer still in queue: like ( ref $o->getQueue->[0], qr/(fe)?malecustomer/i, 'customer still in queue'); } # unlikely for new customer to expire on 1st cycle other than male & excess # wait, due to duration value either 15 mins (male) or 20 - 50 mins (Female) # need new customer to be male with low wait, and served (shifted) customer # to be Female with duration > new customer wait, but < 35: { my $o = AdvancedShop->new( seats => 4 ); # p $o->getQueue; my $new_customer = MaleCustomer->new(tolerance => 30); # lowest permitted $o->addToQueue($new_customer); # p $o->getQueue; # simulate shifted (served) customer to be female with time for haircut between 31 & 34: my $simulated_removed_customer = FemaleCustomer->new(requiredTime => 31); # get shifted (served) customers duration (time for hair cut) & remove from my $duration = $simulated_removed_customer->getRequiredTime; # 31 # get waiting tolerance for next customer in line - is LESS than time # for customer being served, so will expire from queue: my $wait = $new_customer->wait; # 30 # say 'wait:'. $wait; say 'duration:'.$duration; say 'calc:'. ($wait - $duration); is ( $wait - $duration, -1, 'duration exceeds wait for expire' ); $o->expire($simulated_removed_customer); # p $o->getQueue; # expect this customer to have expired: is ( $o->getQueue->[0], 0, 'customer removed from queue' ); } } { # run AdvancedShop->serveOneCustomer with probablility 0.03: my $o = AdvancedShop->new( seats => 4 ); my $revenue = $o->serveOneCustomer($p); # p $revenue; # 1st return always zero (as no customer in queue: is ( $revenue, 0, 'got expected revenue for 1st call to serveOneCustomer'); for ( 1 .. 10 ) { my $revenue = $o->serveOneCustomer($p); # p $revenue; like ( $revenue, qr/0|12|25/, sprintf 'receieved £%s for customer %s', $revenue, $_); } } { # run 1 day (480 minutes, p = 0.03): my $o = AdvancedShop->new( seats => 4 ); $o->runDay(480, $p); my $revenue = $o->getTotalRevenue; # p $revenue; like ( $revenue, qr/\d+/, sprintf 'revenue for 1 day = £%s', $revenue ); # p $o->customers; } { # run for several days: my $days = 365; my $o = AdvancedShop->new( seats => 4 ); my $revenue = $o->averageRevenue(480, $days, $p); # p $revenue; like ( $revenue, qr/\d+/, sprintf 'average revenue for %s days = £%s', $days, $revenue ); my $total_customers = $o->customers; my $female = $total_customers->{FemaleCustomer}; my $male = $total_customers->{MaleCustomer}; # caclculate customer frequency (just tests value begins with number; for info purposes): like ( $female->{joined_queue}, qr{\d+}, 'total female customers joined queue: ' . $female->{joined_queue} ); like ( $male->{joined_queue}, qr{\d+}, 'total male customers joined queue: ' . $male->{joined_queue} ); like ( $female->{joined_queue} / $male->{joined_queue}, qr{\d+}, sprintf 'ratio female : male = %0.2f', $female->{joined_queue} / ( $male->{joined_queue} + $female->{joined_queue} ) ); like ( $female->{served}, qr{\d+}, 'total female customers served: ' . $female->{served} ); like ( $female->{served} / $male->{joined_queue}, qr{\d+}, sprintf 'ratio female customers served = %0.2f', $female->{served} / $female->{joined_queue} ); like ( $male->{served}, qr{\d+}, 'total male customers served: ' . $female->{served} ); like ( $female->{served} / $male->{joined_queue}, qr{\d+}, sprintf 'ratio of male customers served = %0.2f', $male->{served} / $male->{joined_queue} ); } done_testing();