#! /usr/bin/perl
+
+# Copyright (c) 2012-2014. The SimGrid Team.
+# All rights reserved.
+
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the license (GNU LGPL) which comes with this package.
+
eval 'exec perl -S $0 ${1+"$@"}'
if $running_under_some_shell;
my $path = $0;
my $OS;
my $enable_coverage=0;
+my $sort_prefix = 19;
my $tesh_file;
my $tesh_name;
my $error=0;
my $exitcode=0;
+my @bg_cmds;
$path =~ s|[^/]*$||;
push @INC,$path;
use strict;
use Term::ANSIColor;
use IPC::Open3;
+use IO::File;
if($^O eq "linux"){
$OS = "UNIX";
$ENV{"PRINTF_EXPONENT_DIGITS"} = "2";
}
-
-sub trim($)
-{
- my $string = shift;
- $string =~ s/^\s+//;
- $string =~ s/\s+$//;
- return $string;
-}
-
-# make sure we received a tesh file
-scalar @ARGV > 0 || die "Usage:\n tesh [*options*] *tesh_file*\n";
-
#Add current directory to path
$ENV{PATH} = "$ENV{PATH}:.";
sub exit_status {
my $status = shift;
if (POSIX::WIFEXITED($status)) {
+ $exitcode=POSIX::WEXITSTATUS($status)+40;
return "returned code ".POSIX::WEXITSTATUS($status);
} elsif (POSIX::WIFSIGNALED($status)) {
- my $code;
- if (POSIX::WTERMSIG($status) == SIGINT){$code="SIGINT"; }
- elsif (POSIX::WTERMSIG($status) == SIGTERM) {$code="SIGTERM"; }
- elsif (POSIX::WTERMSIG($status) == SIGKILL) {$code= "SIGKILL"; }
- elsif (POSIX::WTERMSIG($status) == SIGABRT) {$code="SIGABRT"; }
- elsif (POSIX::WTERMSIG($status) == SIGSEGV) {$code="SIGSEGV" ;}
- $exitcode=POSIX::WTERMSIG($status)+4;
- return "got signal $code";
+ my $code;
+ if (POSIX::WTERMSIG($status) == SIGINT){$code="SIGINT"; }
+ elsif (POSIX::WTERMSIG($status) == SIGTERM) {$code="SIGTERM"; }
+ elsif (POSIX::WTERMSIG($status) == SIGKILL) {$code= "SIGKILL"; }
+ elsif (POSIX::WTERMSIG($status) == SIGABRT) {$code="SIGABRT"; }
+ elsif (POSIX::WTERMSIG($status) == SIGSEGV) {$code="SIGSEGV" ;}
+ $exitcode=POSIX::WTERMSIG($status)+4;
+ return "got signal $code";
}
return "Unparsable status. Is the process stopped?";
}
###
# exec the command line
### $line =~ s/\r//g;
- $pid = open3(\*CHILD_IN, \*OUT, \*OUT, $cmd{'cmd'} );
+
+ $cmd{'got'} = IO::File->new_tmpfile;
+ $cmd{'got'}->autoflush(1);
+ local *E = $cmd{'got'};
+ $cmd{'pid'} = open3(\*CHILD_IN, ">&E", ">&E", $cmd{'cmd'} );
# push all provided input to executing child
- map { print CHILD_IN "$_\n" } @{$cmd{'in'}};
+ map { print CHILD_IN "$_\n"; } @{$cmd{'in'}};
close CHILD_IN;
# if timeout specified, fork and kill executing child at the end of timeout
die "fork() failed: $!" unless defined $forked;
if ( $forked == 0 ) { # child
sleep $time_to_wait;
- kill(SIGKILL, $pid);
+ kill(SIGKILL, $cmd{'pid'});
exit $time_to_wait;
}
}
+
+ # Cleanup the executing child, and kill the timeouter brother on need
+ $cmd{'return'} = 0 unless defined($cmd{'return'});
+ if($cmd{'background'} != 1){
+ waitpid ($cmd{'pid'}, 0);
+ $cmd{'gotret'} = exit_status($?);
+ parse_out(\%cmd);
+ }else{
+ # & commands, which will be handled at the end
+ push @bg_cmds, \%cmd;
+ # no timeout for background commands
+ if($forked){
+ kill(SIGKILL, $forked);
+ $timeout=0;
+ $forked=0;
+ }
+ }
+}
+
+
+sub parse_out {
+ my %cmd = %{$_[0]};
+ my $gotret=$cmd{'gotret'};
+
+ my $wantret;
+
+ if(defined($cmd{'expect'}) and ($cmd{'expect'} ne "")){
+ $wantret = "got signal $cmd{'expect'}";
+ }else{
+ $wantret = "returned code ".(defined($cmd{'return'})? $cmd{'return'} : 0);
+ }
+ local *got = $cmd{'got'};
+ seek(got,0,0);
# pop all output from executing child
my @got;
- while(defined(my $got=<OUT>)) {
+ while(defined(my $got=<got>)) {
$got =~ s/\r//g;
- $got =~ s/^( )*//g;
chomp $got;
- $got=trim($got);
- if( $got ne ""){
- if (!($enable_coverage and $got=~ /^profiling:/)){
- push @got, "$got";
- }
- }
+ if (!($enable_coverage and $got=~ /^profiling:/)){
+ push @got, $got;
+ }
}
- close OUT;
-
+
if ($cmd{'sort'}){
sub mysort{
- $a cmp $b
+ substr($a, 0, $sort_prefix) cmp substr($b, 0, $sort_prefix)
}
- use sort qw(defaults _quicksort); # force quicksort
+ use sort 'stable';
@got = sort mysort @got;
+ while (@got and $got[0] eq "") {
+ shift @got;
+ }
+
#also resort the other one, as perl sort is not the same as the C one used to generate teshes
if(defined($cmd{'out'})){
@{$cmd{'out'}}=sort mysort @{$cmd{'out'}};
+ while (@{$cmd{'out'}} and ${$cmd{'out'}}[0] eq "") {
+ shift @{$cmd{'out'}};
+ }
}
}
-
- # Cleanup the executing child, and kill the timeouter brother on need
- $cmd{'return'} = 0 unless defined($cmd{'return'});
- my $wantret;
- if(defined($cmd{'expect'}) and ($cmd{'expect'} ne "")){
- $wantret = "got signal $cmd{'expect'}";
- }else{
- $wantret = "returned code ".(defined($cmd{'return'})? $cmd{'return'} : 0);
- $exitcode= 41;
- }
- my $gotret;
- waitpid ($pid, 0);
- $gotret = exit_status($?);
+
#Did we timeout ? If yes, handle it. If not, kill the forked process.
if($timeout==-1 and $gotret eq "got signal SIGKILL"){
my $file = $cmd{'arg'};
print "[Tesh/INFO] mkfile $file\n";
- die "[TESH/CRITICAL] no input provided to mkfile\n" unless defined($cmd{'in'}) && scalar @{$cmd{'in'}};
unlink($file);
open(FILE,">$file") or die "[Tesh/CRITICAL] Unable to create file $file: $!\n";
print FILE join("\n", @{$cmd{'in'}});
}
# Push delayed commands on empty lines
- unless ($line =~ m/^(.).(.*)$/) {
- if (defined($cmd{'cmd'})) {
+ unless ($line =~ m/^(.)(.*)$/) {
+ if (defined($cmd{'cmd'})) {
exec_cmd(\%cmd);
%cmd = ();
}
}
my ($cmd,$arg) = ($1,$2);
+ $arg =~ s/^ //g;
$arg =~ s/\r//g;
$arg =~ s/\\\\/\\/g;
# handle the commands
if ($cmd =~ /^#/) { #comment
} elsif ($cmd eq '>'){ #expected result line
print "[TESH/debug] push expected result\n" if $opts{'debug'};
- $arg=trim($arg);
- if($arg ne ""){
push @{$cmd{'out'}}, $arg;
- }
} elsif ($cmd eq '<') { # provided input
print "[TESH/debug] push provided input\n" if $opts{'debug'};
%cmd = ();
}
$cmd{'sort'} = 1;
+ if ($line =~ /^!\s*output sort\s+(\d+)/) {
+ $sort_prefix = $1;
+ }
}
elsif($line =~ /^!\s*output ignore/){ #output ignore
if (defined($cmd{'cmd'})) {
exec_cmd(\%cmd);
%cmd = ();
}
+print "hey\n";
$cmd{'expect'} = "$1";
}
elsif($line =~ /^!\s*expect return/){ #expect return
$line =~ s/\r//g;
setenv_cmd($line);
}
- elsif($line =~ /^!\s*include/){ #output sort
+ elsif($line =~ /^!\s*include/){ #include
if (defined($cmd{'cmd'})) {
exec_cmd(\%cmd);
%cmd = ();
$timeout=0;
}
+foreach(@bg_cmds){
+ my %test=%{$_};
+ waitpid ($test{'pid'}, 0);
+ $test{'gotret'} = exit_status($?);
+ parse_out(\%test);
+}
+
+@bg_cmds=();
+
+
if($error !=0){
exit $exitcode;
}elsif($tesh_file eq "(stdin)"){