#!/usr/bin/perl -l # $Id: test.pl,v 1.22 2004/09/08 02:25:59 trammell Exp $ # vim: set ai expandtab ts=4 : use strict; use warnings; use Data::Dumper; use File::Basename 'basename'; use Getopt::Long; use Test::More; use Time::HiRes qw/gettimeofday tv_interval/; sub usage { die < \$MAX_N, "r|reps=i" => \$REPS) or usage(); # generate cache of Catalan numbers my %cat = map { $_, catalan($_) } 0 .. $MAX_N; # run test(s) based on command-line options my $option = shift; my %map = ( correct => \&test_correct, 'time' => \&test_time, memory => \&test_memory, ); usage() unless $option && exists $map{ $option }; $map{ $option }->($_) for @ARGV; # routine to test correctness of output sub test_correct { my $script = shift; my $incorrect = 0; warn "# ===== testing script $script, 0 .. $MAX_N =====\n"; foreach my $i ( 0 .. $MAX_N ) { my @lines = `perl $script $i`; for (@lines) { y/()//cd; } unless (@lines == $cat{$i}) { warn "#! $script, n=$i: got @{[ scalar @lines ]} lines, should be $cat{$i}\n" if $incorrect < 20; ++$incorrect; } if (my $d = dupes_exist(@lines)) { warn "#! $script, n=$i: found duplicate: '$d'\n" if $incorrect < 20; ++$incorrect; } unless (lines_are_sorted(@lines)) { warn "#! $script, n=$i: output isn't in lexicographic order\n" if $incorrect < 20; ++$incorrect; } for (@lines) { next if line_is_balanced($_); warn "#! $script, n=$i: got unbalanced line: '$_'\n" if $incorrect < 20; ++$incorrect; } } if ($incorrect) { warn "# script $script had $incorrect problems.\n"; } else { warn "# script $script generated the correct output.\n"; } } # routine to generate timing data sub test_time { my $script = shift; warn "# testing script $script\n"; # step 1: time the creation code; save data for verification my $teststarttime = [gettimeofday]; my @timing; foreach my $i ( 0 .. $MAX_N ) { for (1 .. $REPS) { my $repstarttime = [gettimeofday]; my $foo = `perl $script $i > /dev/null`; push @{$timing[$i]}, tv_interval($repstarttime); } } my $totalelapsed = tv_interval($teststarttime); # report timing to STDOUT in CSV format $script = basename $script; foreach my $i ( 0 .. $MAX_N ) { my $avg = sprintf "%.6f", average(@{$timing[$i]}); print "$script, $i, $avg"; } print "$script, total, $totalelapsed"; } sub test_memory { my $script = shift; warn "# calculatng maximum memory for script $script\n"; # step 1: time the creation code; save data for verification my @mem; foreach my $i ( 0 .. $MAX_N ) { my $foo = `time - perl $script $i > /dev/null`; $mem[$i] = 'foo'; } # report memory usage to STDOUT in CSV format $script = basename $script; foreach my $i ( 0 .. $MAX_N ) { print "$script, $i, $mem[$i]"; } } sub line_is_balanced { (my $x = $_[0]) =~ y/()//cd; 1 while $x =~ s/\(\)//; return length($x) == 0; } # thanks to Daniel Martin for the binom() and catalan() subs sub binom { my ($n, $k) = @_; if ($n < 1 or $k < 1 or $k >= $n) { return 1; } binom($n-1,$k-1) * $n / $k; } sub catalan { binom(2*$_[0], $_[0]) / ($_[0]+1); } sub dupes_exist { my %seen; for (@_) { return $_ if $seen{ $_ }++; } return 0; } sub lines_are_sorted { my @lines = @_; my @sorted = sort @lines; return Test::More::eq_array(\@lines, \@sorted); } sub average { return unless @_; my $sum; $sum += $_ for @_; return $sum / @_; }