2016-01-18 09:32:32 +00:00
|
|
|
#!/usr/bin/perl
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
use threads;
|
2016-01-18 19:20:57 +00:00
|
|
|
use threads::shared;
|
2016-01-18 09:32:32 +00:00
|
|
|
|
|
|
|
use Getopt::Long;
|
|
|
|
use File::Find;
|
2016-01-18 17:18:39 +00:00
|
|
|
use File::Spec;
|
|
|
|
use Cwd;
|
|
|
|
use Cwd 'abs_path';
|
2016-01-18 17:54:43 +00:00
|
|
|
use Term::ANSIColor;
|
2016-01-18 09:32:32 +00:00
|
|
|
|
|
|
|
sub help {
|
|
|
|
# always useful
|
|
|
|
exit
|
|
|
|
}
|
|
|
|
|
2016-01-18 19:20:57 +00:00
|
|
|
my $mt_timeout :shared;
|
2016-01-18 19:26:36 +00:00
|
|
|
my ($o_suite, $o_help, $o_exe, $o_cfg, $o_max_cpu, $o_timeout, $o_show_diff);
|
2016-01-18 19:20:57 +00:00
|
|
|
|
2016-01-18 17:18:39 +00:00
|
|
|
$o_max_cpu = 1;
|
2016-01-18 19:20:57 +00:00
|
|
|
$o_timeout = 20;
|
|
|
|
|
2016-01-18 09:32:32 +00:00
|
|
|
my $status = Getopt::Long::GetOptions(
|
2016-01-18 19:20:57 +00:00
|
|
|
'cfg=s' => \$o_cfg,
|
|
|
|
'cpu=i' => \$o_max_cpu,
|
|
|
|
'exe=s' => \$o_exe,
|
|
|
|
'help' => \$o_help,
|
|
|
|
'timeout=i' => \$o_timeout,
|
2016-01-18 19:26:36 +00:00
|
|
|
'show_diff' => \$o_show_diff,
|
2016-01-18 19:20:57 +00:00
|
|
|
'suite=s' => \$o_suite,
|
2016-01-18 09:32:32 +00:00
|
|
|
);
|
|
|
|
|
2016-01-18 19:20:57 +00:00
|
|
|
#####################################################
|
|
|
|
# Check option
|
|
|
|
#####################################################
|
2016-01-18 09:32:32 +00:00
|
|
|
unless ($status) {
|
|
|
|
help();
|
|
|
|
}
|
|
|
|
|
|
|
|
unless (defined $o_suite) {
|
|
|
|
print "Error: require a test suite directory\n";
|
|
|
|
help();
|
|
|
|
}
|
|
|
|
|
|
|
|
unless (defined $o_exe) {
|
|
|
|
print "Error: require a PCSX2 exe\n";
|
|
|
|
help();
|
|
|
|
}
|
|
|
|
|
2016-01-18 17:18:39 +00:00
|
|
|
unless (defined $o_cfg) {
|
|
|
|
print "Error: require a deafult cfg directory\n";
|
|
|
|
help();
|
|
|
|
}
|
2016-01-18 19:20:57 +00:00
|
|
|
|
2016-01-18 17:18:39 +00:00
|
|
|
$o_cfg = abs_path($o_cfg);
|
2016-01-18 19:20:57 +00:00
|
|
|
$mt_timeout = $o_timeout;
|
|
|
|
|
2016-01-18 17:18:39 +00:00
|
|
|
|
2016-01-18 19:20:57 +00:00
|
|
|
#####################################################
|
|
|
|
# Run
|
|
|
|
#####################################################
|
2016-01-18 09:32:32 +00:00
|
|
|
|
|
|
|
# Round 1: Collect the tests
|
|
|
|
my $g_test_db;
|
2016-01-18 17:54:43 +00:00
|
|
|
print "INFO: search tests in $o_suite and run them in $o_max_cpu CPU)\n";
|
2016-01-18 17:18:39 +00:00
|
|
|
find({ wanted => \&add_test_cmd_for_elf, no_chdir => 1 }, $o_suite);
|
2016-01-18 09:32:32 +00:00
|
|
|
|
|
|
|
# Round 2: Run the tests (later in thread)
|
2016-01-18 17:54:43 +00:00
|
|
|
foreach my $test (keys(%$g_test_db)) {
|
2016-01-18 17:18:39 +00:00
|
|
|
# wait free CPU slot
|
|
|
|
while( scalar(threads->list() >= $o_max_cpu) ) {
|
|
|
|
if (close_joinnable_threads() == 0) {
|
2016-01-18 17:54:43 +00:00
|
|
|
sleep(1); # test are often fast so 1s is more than enough
|
2016-01-18 19:20:57 +00:00
|
|
|
$mt_timeout--;
|
2016-01-18 17:18:39 +00:00
|
|
|
}
|
2016-01-18 19:20:57 +00:00
|
|
|
kill_thread_if_timeout()
|
2016-01-18 17:18:39 +00:00
|
|
|
}
|
|
|
|
|
2016-01-18 17:54:43 +00:00
|
|
|
create_thread($test);
|
2016-01-18 09:32:32 +00:00
|
|
|
}
|
2016-01-18 17:18:39 +00:00
|
|
|
wait_all_threads();
|
2016-01-18 09:32:32 +00:00
|
|
|
|
2016-01-18 17:54:43 +00:00
|
|
|
# Round 3: Collect the results (not thread safe)
|
|
|
|
collect_result();
|
2016-01-18 09:32:32 +00:00
|
|
|
|
2016-01-18 17:54:43 +00:00
|
|
|
# Pretty print
|
|
|
|
print "\n\n Status | ================= Test ======================\n";
|
|
|
|
foreach my $test (sort(keys(%$g_test_db))) {
|
|
|
|
my $info = $g_test_db->{$test};
|
2016-01-18 19:26:36 +00:00
|
|
|
my $out = $info->{"OUT"};
|
|
|
|
my $exp = $info->{"EXPECTED"};
|
|
|
|
|
2016-01-18 19:20:57 +00:00
|
|
|
if ($info->{"STATUS"} == 0) {
|
2016-01-18 17:54:43 +00:00
|
|
|
print color('bold green');
|
|
|
|
print " OK | $test\n";
|
2016-01-18 19:20:57 +00:00
|
|
|
} elsif ($info->{"STATUS"} == 0xBADBEEF) {
|
|
|
|
print color('bold blue');
|
|
|
|
print " Tout | $test\n";
|
|
|
|
} else {
|
|
|
|
print color('bold red');
|
|
|
|
print " KO | $test\n";
|
2016-01-18 19:26:36 +00:00
|
|
|
if ($o_show_diff) {
|
|
|
|
print color('bold magenta'); print "-----------------------------------------------------------------------\n"; print color('reset');
|
|
|
|
system("diff -u $out $exp");
|
|
|
|
print color('bold magenta'); print "-----------------------------------------------------------------------\n"; print color('reset');
|
|
|
|
}
|
2016-01-18 17:54:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
print color('reset');
|
|
|
|
print "\n";
|
2016-01-18 09:32:32 +00:00
|
|
|
|
2016-01-18 17:54:43 +00:00
|
|
|
#####################################################
|
2016-01-18 19:20:57 +00:00
|
|
|
# Sub helper
|
|
|
|
#####################################################
|
2016-01-18 09:32:32 +00:00
|
|
|
sub collect_result {
|
2016-01-18 17:54:43 +00:00
|
|
|
foreach my $test (keys(%$g_test_db)) {
|
|
|
|
my $info = $g_test_db->{$test};
|
|
|
|
my $out = $info->{"OUT"};
|
|
|
|
my $exp = $info->{"EXPECTED"};
|
|
|
|
|
2016-01-18 19:20:57 +00:00
|
|
|
return unless (-s $out);
|
|
|
|
|
|
|
|
my $end = `tail -1 $out`;
|
|
|
|
if ($end =~ /-- TEST END/) {
|
|
|
|
system("diff $out $exp -q");
|
|
|
|
$info->{"STATUS"} = $?; # potentially not thread safe
|
|
|
|
}
|
2016-01-18 17:54:43 +00:00
|
|
|
}
|
2016-01-18 09:32:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sub add_test_cmd_for_elf {
|
|
|
|
my $file = $_;
|
|
|
|
return 0 unless ($file =~ /\.elf/);
|
2016-01-18 17:18:39 +00:00
|
|
|
# Fast test
|
|
|
|
#return 0 unless ($file =~ /branchdelay/);
|
2016-01-18 09:32:32 +00:00
|
|
|
|
|
|
|
my $dir = $File::Find::dir;
|
|
|
|
print "INFO: found $file in $dir\n";
|
|
|
|
|
2016-01-18 17:18:39 +00:00
|
|
|
$g_test_db->{$File::Find::name}->{"CFG_DIR"} = $File::Find::name =~ s/\.elf/_cfg/r;
|
2016-01-18 09:32:32 +00:00
|
|
|
$g_test_db->{$File::Find::name}->{"EXPECTED"} = $File::Find::name =~ s/\.elf/.expected/r;
|
|
|
|
$g_test_db->{$File::Find::name}->{"OUT"} = $File::Find::name =~ s/\.elf/.PCSX2.out/r;
|
2016-01-18 19:20:57 +00:00
|
|
|
$g_test_db->{$File::Find::name}->{"STATUS"} = 0xBADBEEF;
|
2016-01-18 09:32:32 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-01-18 17:18:39 +00:00
|
|
|
sub run_thread {
|
|
|
|
my $test = shift;
|
|
|
|
my $info = $g_test_db->{$test};
|
|
|
|
|
|
|
|
generate_cfg($info->{"CFG_DIR"});
|
|
|
|
|
|
|
|
run_elf($test, $info->{"CFG_DIR"}, $info->{"OUT"});
|
|
|
|
}
|
|
|
|
|
|
|
|
sub generate_cfg {
|
|
|
|
my $out_dir = shift;
|
|
|
|
|
|
|
|
#system("rm -fr $out_dir");
|
|
|
|
system("cp -a --remove-destination --no-target-directory $o_cfg $out_dir");
|
|
|
|
# Enable the logging to get the trace log
|
|
|
|
my $ui_ini = File::Spec->catfile($out_dir, "PCSX2_ui.ini");
|
|
|
|
my $vm_ini = File::Spec->catfile($out_dir, "PCSX2_vm.ini");
|
|
|
|
|
|
|
|
my %sed;
|
|
|
|
# Enable logging for test
|
|
|
|
$sed{".EEout"} = "enabled";
|
|
|
|
$sed{".IOPout"} = "enabled";
|
|
|
|
$sed{"ConsoleToStdio"} = "enabled";
|
|
|
|
# FIXME add interpreter vs recompiler
|
|
|
|
# FIXME add clamping / rounding option
|
|
|
|
# FIXME need separate cfg dir !
|
|
|
|
|
|
|
|
foreach my $option (keys(%sed)) {
|
|
|
|
my $v = $sed{$option};
|
|
|
|
system("sed -i -e 's/$option=.*/$option=$v/' $ui_ini");
|
|
|
|
system("sed -i -e 's/$option=.*/$option=$v/' $vm_ini");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-18 09:32:32 +00:00
|
|
|
sub run_elf {
|
|
|
|
my $elf = shift;
|
|
|
|
my $cfg = shift;
|
|
|
|
my $out = shift;
|
|
|
|
|
|
|
|
my $line;
|
|
|
|
my $dump = 0;
|
|
|
|
open(my $run, ">$out") or die "Impossible to open $!";
|
|
|
|
|
2016-01-18 17:18:39 +00:00
|
|
|
# FIXME timeout
|
|
|
|
my $pid = open(my $log, "$o_exe --elf $elf --cfgpath=$cfg |") or die "Impossible to pipe $!";
|
2016-01-18 17:54:43 +00:00
|
|
|
print "INFO: Execute $elf (PID=$pid) with cfg ($cfg)\n";
|
2016-01-18 09:32:32 +00:00
|
|
|
|
2016-01-18 19:20:57 +00:00
|
|
|
# Kill me
|
|
|
|
$SIG{'KILL'} = sub {
|
|
|
|
# FIXME doesn't work (no print, neither kill)
|
|
|
|
print "ERROR: timeout detected on pid $pid.\n";
|
|
|
|
kill 'KILL', $pid;
|
|
|
|
threads->exit();
|
|
|
|
};
|
|
|
|
|
2016-01-18 09:32:32 +00:00
|
|
|
while ($line = <$log>) {
|
2016-01-18 19:20:57 +00:00
|
|
|
$mt_timeout = $o_timeout; # Keep me alive
|
|
|
|
|
2016-01-18 09:32:32 +00:00
|
|
|
$line =~ s/\e\[\d+(?>(;\d+)*)m//g;
|
|
|
|
if ($line =~ /-- TEST BEGIN/) {
|
|
|
|
$dump = 1;
|
|
|
|
}
|
|
|
|
if ($dump == 1) {
|
|
|
|
print $run $line;
|
|
|
|
}
|
|
|
|
if ($line =~ /-- TEST END/) {
|
|
|
|
$dump = 0;
|
|
|
|
print "Info kill process $pid\n";
|
|
|
|
kill 'KILL', $pid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-18 17:18:39 +00:00
|
|
|
|
|
|
|
#####################################################
|
|
|
|
# Thread management
|
|
|
|
#####################################################
|
|
|
|
my $g_counter = 0;
|
|
|
|
sub create_thread {
|
|
|
|
my $cmd = shift;
|
|
|
|
|
|
|
|
my $thr = threads->create(\&run_thread, $cmd );
|
|
|
|
|
|
|
|
$g_counter++;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub close_joinnable_threads {
|
|
|
|
my $closed = 0;
|
|
|
|
foreach my $thr (threads->list(threads::joinable)) {
|
|
|
|
$thr->join();
|
|
|
|
$closed = 1;
|
|
|
|
$g_counter--;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $closed;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub wait_all_threads {
|
2016-01-18 19:20:57 +00:00
|
|
|
# wait free CPU slot
|
|
|
|
while( scalar(threads->list() > 0) and $mt_timeout > 0) {
|
|
|
|
if (close_joinnable_threads() == 0) {
|
|
|
|
sleep(1); # test are often fast so 1s is more than enough
|
|
|
|
$mt_timeout--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
kill_thread_if_timeout()
|
|
|
|
}
|
|
|
|
|
|
|
|
sub kill_thread_if_timeout {
|
|
|
|
if ($mt_timeout <= 0) {
|
|
|
|
foreach my $thr (threads->list()) {
|
|
|
|
# Farewell my friend
|
|
|
|
print "ERROR: send kill on timeout process\n";
|
|
|
|
$thr->kill('KILL')->detach();
|
|
|
|
}
|
|
|
|
$mt_timeout = 100;
|
2016-01-18 17:18:39 +00:00
|
|
|
}
|
|
|
|
}
|