
| Current Path : /var/www/web-klick.de/dsh/50_dev2017/1300__perllib/DivBasicF/ |
Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64 |
| Current File : /var/www/web-klick.de/dsh/50_dev2017/1300__perllib/DivBasicF/AutoTest.pm |
package DivBasicF::AutoTest;
use strict;
use Data::Dumper;
use Digest::SHA1;
use File::Copy;
use File::Path;
use Cwd;
use DivBasicF::AutoSettings;
sub DELIVER { 2 }
sub new {
my $class = shift;
my $self = {};
bless($self,$class);
return($self);
}
#***********************************************
sub collect_data {
my $self = shift;
my $dir = shift || "."; # Test-Item
my $depth = shift || 100000; # if < 0, all requs will be composed to one Requirement ALLREQU
# and all architectures will be mapped to ALLARCH
my $archs = shift; # Comma-separated list of architectures
# If started with a dot, short output (for frontend)
# If started with :, masks a leading dot!
my $requs = shift; # Comma-separated list of requirements
#print STDERR "ARCHS1:$archs\n";
my $short_output = 0;
$short_output = 1 if ($archs =~ s/^\.//);
$archs =~ s/^\://;
my $depth_offset = $dir;
$depth_offset =~ s/[^\\\/]//gs;
$depth_offset = length($depth_offset);
#print STDERR "ARCHS2:$archs\n";
$archs = '[a-zA-Z_0-9]+' if (!$archs);
$requs = '[a-zA-Z_0-9]+' if (!$requs);
$archs =~ s/\,/\|/g;
$requs =~ s/\,/\|/g;
my $allrequ = 0;
if ($depth < 0) {
$depth = - $depth;
$allrequ = 1;
}
$depth = 0 if ($depth < 1);
my $zeile; my $result; my $remark; my $user; my $sleep; my $o;
my $entry; my $entry0; my $item; my $item0;
my $val; my $val0; my $orig_val; my $dumptime; my $waketime;
# Hier wird die Originaleingabe $dir untersucht.
# Wenn es sich um eine Datei handelt, wird diese durchsucht,
# sowie das dazugehoerige Unterverzeichnis.
# Wenn es sich um ein Directory handelt, dann nur dieses
$dir =~ s/[\\\/]/\//gs;
$dir =~ s/[\\\/]+$//gs;
my $testitem = "";
if (!$dir) {
$dir = ".";
}
elsif (-f $dir) {
$testitem = $dir;
$dir = "";
if ($testitem =~ /^(.*)\.(py|pm)$/) {
$dir = $1 . "_";
}
}
elsif (-d $dir) {
1;
}
elsif (-d "$dir\_") {
$dir = $dir . "_";
}
#print STDERR "WWW: $dir - $archs - $requs - $depth\n";
my $sample_data = "";
my $backend_dir = DivBasicF::AutoSettings::settings()->{'C'};
if ($testitem) {
$o = ".";
if ($testitem =~ s/^(.*)\/(.*)$/$2/gs) { $o = $1; }
if ($^O=~/Win/){
$sample_data = `$backend_dir/grep -r -P \"^\:((RESULT|REMARK|USER) *.*|ARCH: +($archs)|($requs): +[0-9\.]+ *\)\" --include=$testitem $o`;
} else {
$sample_data = `grep -r -P \"^\:((RESULT|REMARK|USER) *.*|ARCH: +($archs)|($requs): +[0-9\.]+ *\)\" --include=$testitem $o`;
}
}
$sample_data =~ s/(^|\n)\.[\\\/]/$1/gs;
if ($^O=~/Win/){
$sample_data = $sample_data .
`$backend_dir/grep -r -P \"^\:((RESULT|REMARK|USER) *.*|ARCH: +($archs)|($requs): +[0-9\.]+ *\)\" --include=*.py $dir`;
} else {
$sample_data = $sample_data . `grep -r -P \"^\:((RESULT|REMARK|USER) *.*|ARCH: +($archs)|($requs): +[0-9\.]+ *\)\" --include=*.py $dir`;
}
$sample_data =~ s/\:(ARCH|RESULT|REMARK|USER|DUMPTIME|WAKETIME)\:/\:AAAAAAAA$1\:/g;
# print STDERR $sample_data;
$sample_data =~ s/
//gs;
$sample_data =~ s/([^\_])[\\\/]item\.(py|pm)\:\:/$1\:\:/g;
$sample_data =~ s/\.(py|pm)\:\:/\:\:/gs;
$sample_data =~ s/\_([\\\/])/$1/gs;
if ($sample_data =~ /^\./) { # im Falle, dass das Directory . ist, muss der Directory-Name hinzugefuegt werden
$o = Cwd::cwd();
$o =~ s/^(.*)[\\\/](.*)$/$2/;
$sample_data =~ s/(^|\n)\./$1$o/gs;
}
# print $sample_data; # exit;
open(FFILE,">/test_projects/out.txt");
print FFILE $testitem . "\n";
print FFILE $dir . "\n";
print FFILE $sample_data;
close(FFILE);
my $itemreport = {};
my $arch = "";
my $requ = "";
my $multiply = 1;
$dir =~ s/\_$//;
my $requ0 = "";
my @ee = split(/\n/,$sample_data);
@ee = sort @ee;
foreach $zeile (@ee) {
next if ($zeile !~ /^(.*)\:\:(.*?)\: *(.*?) *$/);
# print STDERR "ZZZ $zeile\n";
$item = $1;
$requ = $2;
$val = $3;
#print "XX: $item . $requ . $val\n";
# print $zeile . " - $requ\n";
if ($item =~ /^(.*)[\\\/](.*)$/) {
$item0 = $1;
}
if (substr($requ0,0,8) ne "AAAAAAAA" and substr($requ,0,8) eq "AAAAAAAA") { # new dataset
$arch = "NOARCH";
$remark = "";
$result = [];
$user = "";
$dumptime = "";
}
$requ0 = $requ;
# print " YY: $arch $requ $item\n";
if ($requ eq "AAAAAAAAARCH") { $arch = $val; }
elsif ($requ eq "AAAAAAAARESULT") { $result = [split(/\//,$val)]; }
elsif ($requ eq "AAAAAAAAREMARK") { $remark = $val; }
elsif ($requ eq "AAAAAAAAUSER") { $user = $val; }
elsif ($requ eq "AAAAAAAADUMPTIME") { $dumptime = $val; }
elsif ($requ eq "AAAAAAAAWAKETIME") { $waketime = $val; }
elsif ($arch and @$result) {
# print STDERR " X: $arch $requ $item\n";
# print Dumper($result);
$val0 = $val;
$requ = "ALLREQU" if ($allrequ);
$arch = "ALLARCH" if ($allrequ);
$orig_val = $val0;
if ($val < 0) {
$val = exp($val) if ($multiply); # Change exponential to normal
# $orig_val = 1 - $val;
} else {
$val = 1/$val if ($val > 1);
$val = 1 - $val;
$val0 = log($val) if (!$multiply)
}
if ($multiply) {
$result->[4] = $val;
} else {
$result->[4] = $val0;
}
$item0 = $item;
while (0 == 0) {
if (!(exists($itemreport->{"$arch:$requ:$item0"}))) {
$itemreport->{"$arch:$requ:$item0"} = [@$result];
} else {
foreach $o (0,1,2,3,4) {
$entry = $itemreport->{"$arch:$requ:$item0"};
if ($multiply and $o == 4) {
$entry->[$o] = $entry->[$o] * $result->[$o]; # summierte Werte
} else {
$entry->[$o] = $entry->[$o] + $result->[$o];
}
if ($item0 eq $item) { # Original-Werte
$entry->[$o+5] = $result->[$o];
}
}
}
if ($item eq $item0) {
push(@{$itemreport->{"$arch:$requ:$item0"}},@$result,$orig_val,$remark,$user,$dumptime);
}
#last if ($allrequ); # keine Aufsummierung bei reinem Testtree
last if ($item0 !~ s/^(.*)[\\\/](.*)$/$1/); # wenn es kein uebergeordnetes Item mehr gibt
# $entry = $itemreport->{"$arch:$requ:$item"};
# if (!$entry) {
# $entry = [@$result];
# $itemreport->{"$arch:$requ:$item"} = $entry;
# } else {
# foreach $o (0,1,2,3,4) {
# if ($multiply and $o == 4) {
# $entry->[$o] = $entry->[$o] * $result->[$o];
# } else {
# $entry->[$o] = $entry->[$o] + $result->[$o];
# }
# }
# }
#
# $item0 = $item;
# while ($item0 =~ s/^(.*)[\\\/](.*)$/$1/) { # wenn es ein uebergeordnetes Item gibt,
# $entry0 = $itemreport->{"$arch:$requ:$item0"};
# print "123 $item0\n" . Dumper($entry0);
# if (!$entry0) {
# $entry0 = [@$entry];
# $itemreport->{"$arch:$requ:$item0"} = $entry0;
# } else {
# foreach $o (0,1,2,3,4) {
# if ($multiply and $o == 4) {
# $entry0->[$o] = $entry0->[$o] * $entry->[$o];
# } else {
# $entry0->[$o] = $entry0->[$o] + $entry->[$o];
# }
# }
# }
# last;
# }
# push(@$entry,@$result,$orig_val,$remark,$user,$dumptime);
}
}
}
# print STDERR Dumper($itemreport); # exit;
my $erg = [];
foreach $o (sort keys %$itemreport) {
# $o =~ s/[\\\/\.]/\//gs;
$o =~ s/[\\]/\//gs;
next if ($o !~ /^(.*)\:(.*)\:(.*)$/);
$item = $itemreport->{$o};
# next if (length($item0) > $depth);
# print "WW: $o " . join("---",@$item) . "\n";
$val = $item->[4];
$val = exp($val) if (!$multiply);
if ($short_output) {
$item = [$1,$2,$3,join("/",@$item[0..3]),1-$val,$item->[11],0];
} else {
$item = [$1,$2,$3,join("/",@$item[0..3]),1-$val,$item->[11],0,$item->[12],
join("/",@$item[5..8]),$item->[10],$item->[13]];
}
$item0 = $item->[2];
$item0 =~ s/[^\\\/]//gs;
next if (length($item0) > $depth);
next if (length($item0) < $depth_offset);
$item->[6] = substr(Digest::SHA1::sha1_hex(join("-",@$item[0..5])),0,8);
push(@$erg,$item);
# print "$o: " . join(",",@{$itemreport->{$o}}) . "\n";
}
print STDERR Dumper($erg); # exit;
#open(FFILE,">>/test_projects/out.txt");
#print FFILE " arch = " . $arch . " requ = ". $requ . "\n";
#print FFILE Dumper($erg);
#close(FFILE);
return($erg);
}
#***********************************************
sub server {
my $self = shift;
my $repeats = shift; # Anzahl der Wiederholungen des Serverlaufs
print "Background Server process started with $repeats repeats\n" if ($repeats);
print "Background Server process started in an endless loop\n" if (!$repeats);
my $zaehler = 0;
my $zaehler1 = 0;
my $wakelist = ""; # List of all found testitems that have to be started in the future
my $time0; my $zeile; my $wakeitem; my $wakeitem0; my $wakepath; my $waketime; my $o; my $o1;
my $max_run; my $late_item; my $dumptime; my $running_item; my $mode; my $params; my $text;
my $count_run = 0; # Anzahl der laufenden Items
my $check_change = 3; # bei jeder x-ten Runde die Aenderungen in Files ueberpruefen
while (0 == 0) {
$zaehler = $zaehler + 1;
$zaehler1 = $zaehler % $check_change;
last if ($repeats and $zaehler > $repeats);
print
"Round: " . sprintf("%7u",$zaehler) . ", " .
"Max-Run: " . sprintf("%4u",$max_run) . ", " .
"Running Items: " . sprintf("%4u",$count_run) . ", " .
"Late Items: " . sprintf("%4u",$late_item) . ", " .
"\n";
# print "grep -r -P \"\^\\:(DUMPTIME\\: *[0-9\\.]+|REMARK\\: *SLEEP\\d+)" --include=*.py .\n";
$wakelist = "";
move("wakelist.run","wakelist.r1");
if (open(FFILE,"<wakelist.r1")) {
$wakelist = join("",<FFILE>);
close(FFILE);
}
unlink("wakelist.r1");
$wakelist = $wakelist . `grep -r -P \"\^\\:(DUMPTIME\\: *[0-9\\.]+|REMARK\\: *SLEEP\\d+)\" --include=*.py .`;
# print $wakelist; exit;
$time0 = time();
$wakeitem0 = "";
$waketime = 0;
while (0 == 0) {
$o = wait();
last if ($o == -1);
$count_run = $count_run - 1;
}
if ($late_item > 200) { $max_run = $max_run + 10; }
elsif ($late_item > 50) { $max_run = $max_run + 5; }
elsif ($late_item > 0) { $max_run = $max_run + 2; }
else { $max_run = $max_run - 1; }
if ($max_run < 10) { $max_run = 10; }
elsif ($max_run > 50) { $max_run = 50; }
$late_item = 0; # Anzahl der Items, die wegen max_run nicht gestartet werden koennen
foreach $zeile (split(/\n/,$wakelist)) {
$zeile =~ s/\r//gs;
$mode = "";
if ($zeile =~ /^(.*?) *___RUN___ *(.*?) *$/) {
$wakeitem = $1;
$params = $2;
$waketime = 1;
$mode = "r";
print "RUN\n";
}
elsif ($zeile =~ /^(.*?)\:\:(DUMPTIME\: *|REMARK\: *SLEEP)([0-9\.]+)/) {
$wakeitem = $1;
$o1 = $2;
$o = $3;
$mode = "c";
$dumptime = $o if ($o1 =~ /DUMPTIME/);
if ($wakeitem0 ne $wakeitem) {
$waketime = 0;
$wakeitem0 = $wakeitem;
}
if ($waketime < 0) {
$waketime = - $waketime + $o;
} else {
$waketime = - $o; # to indicate that the computation is not yet done
$waketime = -0.000001 if (!$waketime); # Workaround for SLEEP0
}
}
next if (!$mode);
next if ($waketime < 0);
#print "---> $wakeitem $waketime $mode $params $dumptime\n";
#print "WW: " . [stat($wakeitem)]->[9] . "\n";
# if (!$zaehler1 and [stat($wakeitem)]->[9] > $dumptime+1) { # start at next cycle
# open(FFILE,"<".$wakeitem);
# $text = join("",<FFILE>);
# close(FFILE);
# $text =~ s/(\:REMARK\: +)(SLEEP\d+)/$1SLEEP0/;
# open(FFILE,">".$wakeitem);
# print FFILE $text;
# close(FFILE);
# }
next if ($waketime > $time0);
#print Dumper($running_item);
#print "DDD: $dumptime $wakeitem\n";
next if ($dumptime and (exists ($running_item->{$wakeitem})) and
($running_item->{$wakeitem} eq $dumptime)); # Item laeuft schon
next if (!(-f $wakeitem));
if ($count_run < $max_run) {
if ($mode eq "c") {
$running_item->{$wakeitem} = $dumptime;
print "$wakeitem restarted...\n";
} else {
print "$wakeitem run started...\n";
}
$wakeitem =~ s/[\\\/]item.py$//;
$wakeitem =~ s/^\.[\\\/]//;
$o1 = fork();
print "o1 = ".$o1."\n";
if ($o1) {
select(undef, undef, undef, 0.005); # parent process sleeps 5 ms
# use Time::HiRes qw( usleep);
# usleep (5000);
} else {
system("ot $mode $params $wakeitem\n");
exit;
}
$count_run = $count_run + 1;
} else {
$late_item = $late_item + 1;
if ($mode eq "r") {
open(FFILE,">>wakelist.run");
print FFILE $zeile . "\n";
close(FFILE);
}
}
# $o = [ sort keys (%$running_item) ];
# $start_interval = (time0 - $o->[0]) / $#$o; # Durchschnitts-Startabstand der laufenden Items
# $max_run = 10;
# $max_run = 15 if ($start_interval > 5);
# last if ($#$o > $max_run);
# $o1 = fork();
# if (!$o1) {
# system("ot c $wakeitem");
# exit;
# }
# print "$wakeitem to restarted...\n";
# $running_item->{$o1} = $time0;
}
sleep 1 if ($count_run == 0);
}
return("Server process ended\n");
}
#***********************************************
sub xxserver {
my $self = shift;
my $repeats = shift; # Anzahl der Wiederholungen des Serverlaufs
my $mode = shift;
my $backend_url = "";
my $ua = "";
my $testprojdir = "";
my $backend_path;
if ($mode == -1) { # wird vom Apache ausgefuehrt
eval("use DivBasicF::AutoSettings");
$testprojdir = DivBasicF::AutoSettings::settings();
$testprojdir = 'http://localhost/' . $testprojdir->{'R'};
$testprojdir =~ s/([^\:])\/\//$1\//g;
}
elsif ($repeats < 0) { # Aufrufe werden ueber den Apache gepuffert
$mode = - $repeats; # wird dem URL-Aufruf als Repeat-Anzahl uebergeben
$repeats = 0; # ox selbst laeuft jetzt unendlich lange
eval("use DivBasicF::AutoSettings");
$backend_url = DivBasicF::AutoSettings::settings();
$backend_url = 'http://localhost/' . $backend_url->{'E'};
$backend_url =~ s/([^\:])\/\//$1\//g;
eval("use LWP::UserAgent");
$ua = LWP::UserAgent->new();
$ua->timeout(0.0001);
}
else {
$mode = 0;
}
print STDERR "Background Server process started with $repeats repeats\n" if ($repeats);
print STDERR "Background Server process started in an endless loop\n" if (!$repeats);
my $zaehler = 0;
my $wakelist = ""; # List of all found testitems that have to be started in the future
my $time0; my $zeile; my $wakeitem; my $wakeitem0; my $wakepath; my $waketime; my $o;
my $testprojects_dir = ".";
if ($mode == -1) {
eval("use DivBasicF::AutoSettings");
$testprojects_dir = DivBasicF::AutoSettings::settings();
$backend_path = $testprojects_dir->{'C'};
$testprojects_dir = $testprojects_dir->{'R'};
}
# print "TT $testprojects_dir\n";
while (0 == 0) {
$zaehler = $zaehler + 1;
last if ($repeats and $zaehler > $repeats);
print STDERR "$$ $zaehler\n" if ($mode != -1);
# print "grep -r -P \"\^\\:(DUMPTIME\\: *[0-9\\.]+|REMARK\\: *SLEEP\\d+) *\$\" --include=*.py $testprojects_dir\n";
$wakelist = `grep -r -P \"\^\\:(DUMPTIME\\: *[0-9\\.]+|REMARK\\: *SLEEP\\d+) *\$\" --include=*.py $testprojects_dir`;
# print $wakelist;
$time0 = time();
$wakeitem0 = "";
$waketime = 0;
foreach $zeile (split(/\n/,$wakelist)) {
next if ($zeile !~ /^(.*?)\:\:(DUMPTIME\: *|REMARK\: *SLEEP)([0-9\.]+) *$/);
$wakeitem = $1;
$o = $3;
if ($wakeitem0 ne $wakeitem) {
$waketime = 0;
$wakeitem0 = $wakeitem;
}
if ($waketime < 0) {
$waketime = - $waketime + $o;
} else {
$waketime = - $o;
}
next if ($waketime <= 0);
next if ($waketime > $time0);
next if (!(-f $wakeitem));
print STDERR "$wakeitem to be restarted...\n";
$ua->get($backend_url.'?srvrepeats='.$mode) if ($mode > 0);
last if ($mode > 0);
$wakeitem =~ s/[\\\/][\\\/]*/\//g;
$wakepath = "";
if ($wakeitem =~ s/^(.*)\/(.*)$/$2/) {
$wakepath = $1;
}
$wakeitem =~ s/^item\.py$//;
$ua = Cwd::cwd();
chdir($wakepath);
print STDERR `pwd`;
print STDERR "/usr/lib/cgi-bin/autotest_0_91/ot c $wakeitem\n";
system($backend_path . "/ot c $wakeitem");
chdir($ua);
}
sleep 1;
}
exit 1 if ($mode == -1);
return("Server process ended\n");
}
#***************************************************************************
sub xxcontinue_item {
my $self = shift;
my $wakeitem = shift;
my $mode = shift;
my $wakepath = "";
if ($wakeitem =~ s/^(.*)[\\\/](.*)$/$2/) {
$wakepath = $1;
$wakepath =~ s/\\/\//gs;
chdir($wakepath);
}
$wakeitem =~ s/^item\.py$//;
system("ot c $wakeitem");
}
#***************************************************************************
sub xxdispatcher {
my $self = shift;
my $task = shift;
my $repeats = shift;
my $time0; my $time1; my $dbh; my $o; my $o1; my $o2; my $conn; my $cursor;
$main::XCONSOLE = 1;
eval("use DivBasicF::AutoSettings");
my $backend_url = DivBasicF::AutoSettings::settings();
$backend_url = 'http://localhost/' . $backend_url->{'E'};
$backend_url =~ s/([^\:])\/\//$1\//g;
eval("use LWP::UserAgent");
my $ua = LWP::UserAgent->new();
$ua->timeout(0.0001);
while (0 == 0) { # alle 10 Sekunden wird das gesamte Verzeichnis auf existierende
$dbh = {}; # testtree.db's durchgescannt
$conn = {};
$self->dispatcher1(".",$dbh,$task);
$time0 = time();
print STDERR "reconnect test projects ... " . localtime($time0) . "\n";
while (0 == 0) {
$time1 = time();
last if ($time1 > $time0 + 10);
$o2 = 0;
foreach $o (keys %$dbh) {
$cursor = $dbh->{$o}->prepare("select msgid from " . $task->tblconn() .
" where msgtime < $time1 limit 1");
next if (!(ref($cursor)));
$cursor->execute();
$o1 = $cursor->fetchrow();
# $cursor->finish();
if ($o1 and $o1 !~ /^0E0$/) {
if (!(exists($conn->{$o1}))) {
print STDERR "found triggering task $o1 in test project $o, starting background process for $repeats times\n";
$ua->get($backend_url.'?srvproc='.$o.'&srvrepeats='.$repeats);
$o2 = 1;
$conn->{$o1} = 1;
}
}
}
next if ($o2);
# print STDERR "... waiting for tasks to start ... " . localtime($time1) . "\n";
sleep 1 if (!$o2);
}
foreach $o (keys %$dbh) {
$dbh->{$o}->disconnect();
}
}
}
#*************************************************************************
sub xxdispatcher1 {
my $self = shift;
my $dir = shift;
my $dbh = shift;
my $task = shift;
my $o;
if (-f "$dir/testtree.db") { # Eintragen in die Gesamtliste der testtree.db's, vorher ueberpruefen
# ob es eine gueltige testtree.db ist
$dbh->{$dir} = DBI->connect("dbi:SQLite:$dir/testtree.db",{PrintError => 0});
delete($dbh->{$dir}) if (!(ref($dbh->{$dir})));
$o = $dbh->{$dir}->prepare("select msgid from " . $task->tblconn() . " limit 1");
delete($dbh->{$dir}) if (!(ref($o)));
unlink("$dir/testtree.db") if (!(exists($dbh->{$dir})) and ( (stat("$dir/testtree.db"))[7] < 5 ) );
# print STDERR "--> reconnect test project $dir\n";
return(1);
}
my @ee = ();
opendir(DDIR,$dir);
while (0 == 0) {
$o = readdir(DDIR);
last if (!$o);
next if ($o =~ /^\./);
if (-d "$dir/$o") {
push(@ee,$o);
}
}
closedir(DDIR);
map { $self->dispatcher1("$dir/$_",$dbh,$task) } @ee;
}
#*****************************************************************
sub mm { # profiling
my $self = shift;
my $mark = shift;
if (!$main::XCONSOLE) {
return(1);
}
eval("use Time::HiRes");
my $newdate = sprintf("%16.5f",Time::HiRes::time());
my $diff = $newdate - $main::lasttime;
if ($diff > 10000000) {
$main::mmgesamt = 0;
$diff = 0;
}
$diff = sprintf("%10.2f",$diff*1000);
$main::mmgesamt = $main::mmgesamt + $diff;
$main::lasttime = $newdate;
my $erg = $diff . " ms fuer: " . ($self->{'MARK'}||"START") . "\n";
$self->{'MARK'} = $mark;
if ($main::XCONSOLE > 1) {
if (!($main::cgp_opened)) {
open(GFILE,">".$mark);
$main::cgp_opened = 1;
$mark = $mark . " opened";
}
print GFILE $erg;
}
if ($main::XCONSOLE != 2) {
print STDERR $erg;
}
}
#**************************************************************************
sub autoitem_version {
my $self = shift;
return(<<'CODE_ENDE')
AUTOTEST_VERSION
CODE_ENDE
}
#**************************************************************************
sub autoitem_py { # Programmcode der autoitem.py
return(<<'CODE_ENDE');
AUTOTEST_PY
CODE_ENDE
}
#**************************************************************************
sub autoitem_pm { # Programmcode der autoitem.pm
return(<<'CODE_ENDE');
AUTOTEST_PM
CODE_ENDE
}
1;