#!/usr/bin/perl # # Copyright (C) 2000 Brian Ewins # XML::Parser::PerlSAX is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. # # $Id: tester.pl,v 1.1 2000/02/20 20:34:42 ba22a Exp $ # use Tk; my $runner=new Test::TkRunner(@ARGV); MainLoop; package Test::TkRunner; use Tk; use Test::SuiteWrapper; use Benchmark; use strict; # The pass,fail, error methods are up front here 'cos they're # the ones that are callbacks for the test suite. The rest of them # are pretty much ignorable. # Not quite right yet. The callback interfaces dont use # the TestResult object so they dont do much interesting, # and anyway I can't get Tk::DialogBox to do the right thing. # Also stop quits instead of cancelling, and theres no visual # indication of whether the tests are still running. # Finally, SuiteWrapper is used directly, instead of being called # by a loader, this needs changed so things like XUnit can be # tacked on. sub plan{ my $self=shift; my ($max)=@_; } sub pass { my $self=shift; my ($msg,$detail)=@_; $self->{'passes'}++; $self->update(); } sub fail { my $self=shift; $self->add_message(@_); $self->{'failures'}++; $self->update(); } sub error { my $self=shift; my ($msg,$detail)=@_; $self->add_message(@_); $self->{'errors'}++; $self->update(); } # Normal methods follow... sub new { my $self=bless {}, shift; # fill in the test name from the command line if possible. $self->{'testname'}=shift; map {$self->{$_}=0} qw(runs errors failures passes cancel start history_size); $self->{'history'}=[]; # Lay the window out.... $self->{'mw'}=MainWindow->new(-title=>'PerlUnit Test Harness'); $self->{'history_list'}=$self->{'mw'}-> BrowseEntry(-label=>"Test name", -width=>30, -variable=>\$self->{'testname'}, -listcmd=> sub {$self->populate_history();}); $self->{'history_list'}->grid(-row=>0,-column=>0,-columnspan=>4); $self->{'mw'}->Button(-text => "Run", -command => sub {$self->run()} )-> grid(-row=>0,-column=>4); $self->{'mw'}->LabEntry(-label=>'Runs:',-text=> \$self->{'runs'}, -state=>'disabled',-width=>5, -relief=>'flat')-> grid(-row=>1,-column=>0); $self->{'mw'}->LabEntry(-label=>'Passed:',-text=>\$self->{'status'}, -state=>'disabled',-width=>10, -relief=>'flat')-> grid(-row=>1,-column=>1); $self->{'mw'}->LabEntry(-label=>'Failures:',-text=>\$self->{'failures'}, -state=>'disabled',-width=>5, -relief=>'flat')-> grid(-row=>2,-column=>0); $self->{'mw'}->LabEntry(-label=>'Errors:',-text=>\$self->{'errors'}, -state=>'disabled',-width=>5, -relief=>'flat')-> grid(-row=>2,-column=>1); $self->{'mw'}->LabEntry(-label=>'Elapsed Time:',-text=>\$self->{'elapsed'}, -state=>'disabled',-width=>35, -relief=>'flat')-> grid(-row=>1,-column=>2,-columnspan=>2); # There is no gauge control. Make do by having a # coloured rectangle on a Tk::Canvas. $self->{'gauge'}=$self->{'mw'}->Canvas(-width=>200,-height=>20, -relief=>"sunken",-borderwidth=>2); $self->{'gauge'}->createRectangle(4,4,200,20,-fill=>'red'); $self->{'gauge'}->createRectangle(4,4,100,20,-fill=>'green',-tag=>'passes'); $self->{'gauge'}-> grid(-row=>2,-column=>2,-columnspan=>2); $self->{'list'}=$self->{'mw'}-> Scrolled('Listbox',-scrollbars=>'e',-width=>50,-height=>10)-> grid(-row=>3,-column=>0,-columnspan=>5); $self->{'mw'}->Button(-text => 'Stop', -command => [$self->{'mw'} => 'destroy'] )-> grid(-row=>4,-column=>0); $self->{'mw'}->Button(-text => 'View Details', -command => sub {$self->view_details()} )-> grid(-row=>4,-column=>2); $self->{'mw'}->Button(-text => 'Quit', -command => [$self->{'mw'} => 'destroy'] )-> grid(-row=>4,-column=>4); return $self; } sub add_message { my $self=shift; my ($msg,$detail)=@_; $self->{'list'}->insert("end",$msg); push @{$self->{'detail'}},$detail; } sub clear_messages { my $self=shift; $self->{'list'}->delete(0,"end"); $self->{'detail'}=[]; } sub update { my $self=shift; my ($ratio,$percent,$total); my $total=0; map { $total+=$self->{$_}} qw(passes failures errors); if ($total==0) { $ratio=0.5; } else { $ratio=$self->{'passes'}/$total; } $self->{'status'}=$self->{'passes'}."/$total, ".(int(100*$ratio))."%"; $self->{'gauge'}->delete('passes'); $self->{'gauge'}->createRectangle(4,4,200*$ratio, 20,-fill=>'green',-tag=>'passes'); $self->{'elapsed'}=timestr(timediff(new Benchmark(),$self->{'start'}),'nop'); #sleep 1; # force entry into the event loop. # this makes it nearly like its threaded... die "Cancelled" if $self->{'cancel'}; $self->{'mw'}->update(); } sub run { my $self=shift; # if the test just run isn't the one at the top of the list, # then add it. $self->{'history'}=[$self->{'testname'}, grep { $_ ne $self->{'testname'}} (@{$self->{'history'}})[0..9]]; $self->{'runs'}++; map {$self->{$_}=0} qw(errors failures passes cancel); $self->clear_messages(); $self->{'start'}=new Benchmark(); $self->update(); $self->{'suite'}=new Test::SuiteWrapper($self->{'testname'}); $self->{'suite'}->add_listener($self); $self->{'suite'}->run(); $self->update(); } sub populate_history { my $self=shift; my $h=$self->{'history_list'}; $h->delete(0,$self->{'history_size'}); foreach (@{$self->{'history'}}) { $h->insert("end",$_); } $self->{'history_size'}=scalar @{$self->{'history'}}; } sub view_details { # pop up a text dialog containing the details. my $self=shift; my $dialog=$self->{'mw'}->Dialog(-title=>'Details',-buttons=>['OK'], -text=>'Tk:DialogBox disnae wurk'); $dialog->Show(); } sub fake_tests { my $self=shift; # fake some tests # this simulates a test suite calling its listener. $self->pass(); $self->pass(); $self->fail("Failed test 1","Detail"); $self->fail("Failed test 2","Detail"); $self->fail("Failed test 3","Detail"); $self->fail("Failed test 4","Detail"); $self->error("Error in test 1","Detail"); $self->error("Error in test 2","Detail"); $self->error("Error in test 3","Detail"); $self->pass(); $self->pass(); $self->fail("Failed test 1","Detail"); $self->fail("Failed test 2","Detail"); $self->fail("Failed test 3","Detail"); $self->fail("Failed test 4","Detail"); $self->error("Error in test 1","Detail"); $self->error("Error in test 2","Detail"); $self->error("Error in test 3","Detail"); $self->pass(); $self->pass(); $self->fail("Failed test 1","Detail"); $self->fail("Failed test 2","Detail"); $self->fail("Failed test 3","Detail"); $self->fail("Failed test 4","Detail"); $self->error("Error in test 1","Detail"); $self->error("Error in test 2","Detail"); $self->error("Error in test 3","Detail"); $self->error("Error in test 4","Detail"); $self->update(); }