RSS Git Download  Clone
Raw Blame History
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();