Missions allow the TestBot administrator to specify which tests a given
VM should perform for mailing list patches. For instance this can be
used to restrict a 64 bit Windows VM to 32 bit tests (useful when it
duplicates another VM with just a different locale).
Furthermore missions can get options. Currently only the 'nosubmit'
option is supported and it disables submitting the WineTest results
online.
Signed-off-by: Francois Gouget <fgouget(a)codeweavers.com>
---
Eventually missions will make it possible to specify how much testing a
Wine VM should do, from running only patched tests to running every test
for every patch; against which Wine build; only in the English locale,
or in other locales too.
testbot/bin/CheckForWinetestUpdate.pl | 105 ++++++++++++++------
testbot/bin/WineRunTask.pl | 12 ++-
testbot/bin/WineRunWineTest.pl | 31 +++---
testbot/bin/build/WineTest.pl | 131 +++++++++++++------------
testbot/ddl/update41.sql | 48 +++++++++
testbot/ddl/winetestbot.sql | 2 +
testbot/doc/winetestbot-schema.dia | 46 +++++++++
testbot/lib/WineTestBot/Missions.pm | 99 +++++++++++++++++++
testbot/lib/WineTestBot/PatchUtils.pm | 8 +-
testbot/lib/WineTestBot/Patches.pm | 134 +++++++++++++++-----------
testbot/lib/WineTestBot/StepsTasks.pm | 55 +++++++----
testbot/lib/WineTestBot/Tasks.pm | 1 +
testbot/lib/WineTestBot/VMs.pm | 41 ++++++++
testbot/web/JobDetails.pl | 1 +
testbot/web/Submit.pl | 80 ++++++++-------
testbot/web/admin/VMDetails.pl | 16 +++
16 files changed, 587 insertions(+), 223 deletions(-)
create mode 100644 testbot/ddl/update41.sql
create mode 100644 testbot/lib/WineTestBot/Missions.pm
diff --git a/testbot/bin/CheckForWinetestUpdate.pl b/testbot/bin/CheckForWinetestUpdate.pl
index 29953642ef..95cb13d8ed 100755
--- a/testbot/bin/CheckForWinetestUpdate.pl
+++ b/testbot/bin/CheckForWinetestUpdate.pl
@@ -53,6 +53,7 @@ use WineTestBot::Config;
use WineTestBot::Engine::Notify;
use WineTestBot::Jobs;
use WineTestBot::Log;
+use WineTestBot::Missions;
use WineTestBot::PatchUtils;
use WineTestBot::Users;
use WineTestBot::Utils;
@@ -201,21 +202,38 @@ sub AddJob($$$)
$NewJob->Priority($BaseJob && $Build eq "exe32" ? 8 : 9);
$NewJob->Remarks($Remarks);
- # Add a step to the job
- my $Steps = $NewJob->Steps;
- my $NewStep = $Steps->Add();
- $NewStep->Type("suite");
- $NewStep->FileName($LatestBaseName);
- $NewStep->FileType($Build);
-
# Add a task for each VM
- my $Tasks = $NewStep->Tasks;
+ my $Tasks;
foreach my $VMKey (@{$VMs->SortKeysBySortOrder($VMs->GetKeys())})
{
- Debug(" $VMKey $Build\n");
- my $Task = $Tasks->Add();
- $Task->VM($VMs->GetItem($VMKey));
- $Task->Timeout(GetTestTimeout(undef, { $Build => 1 }));
+ my $VM = $VMs->GetItem($VMKey);
+ my ($ErrMessage, $Missions) = ParseMissionStatement($VM->Missions);
+ if (defined $ErrMessage)
+ {
+ Debug("$VMKey has an invalid mission statement: $!\n");
+ next;
+ }
+
+ foreach my $TaskMissions (@$Missions)
+ {
+ next if (!$TaskMissions->{Builds}->{$Build});
+
+ if (!$Tasks)
+ {
+ # Add a step to the job
+ my $TestStep = $NewJob->Steps->Add();
+ $TestStep->Type("suite");
+ $TestStep->FileName($LatestBaseName);
+ $TestStep->FileType($Build);
+ $Tasks = $TestStep->Tasks;
+ }
+
+ Debug(" $VMKey $Build\n");
+ my $Task = $Tasks->Add();
+ $Task->VM($VM);
+ $Task->Timeout($SuiteTimeout);
+ $Task->Missions($TaskMissions->{Statement});
+ }
}
# Save it all
@@ -246,9 +264,6 @@ sub AddJob($$$)
return 1;
}
-my @ExeBuilds = qw(exe32 exe64);
-my @WineBuilds = qw(win32 wow32 wow64);
-
sub AddReconfigJob($)
{
my ($VMType) = @_;
@@ -287,9 +302,23 @@ sub AddReconfigJob($)
Debug(" $VMKey $VMType reconfig\n");
my $Task = $BuildStep->Tasks->Add();
$Task->VM($VM);
- my $Builds;
- map { $Builds->{$_} = 1 } ($VMType eq "wine" ? @WineBuilds : @ExeBuilds);
- $Task->Timeout(GetBuildTimeout(undef, $Builds));
+
+ # Merge all the tasks into one so we only recreate the base snapshot once
+ my $MissionStatement = $VMType ne "wine" ? "exe32:exe64" :
+ MergeMissionStatementTasks($VM->Missions);
+ my ($ErrMessage, $Missions) = ParseMissionStatement($MissionStatement);
+ if (defined $ErrMessage)
+ {
+ Debug("$VMKey has an invalid mission statement: $!\n");
+ next;
+ }
+ if (@$Missions != 1)
+ {
+ Debug("Found no mission or too many task missions for $VMKey\n");
+ next;
+ }
+ $Task->Timeout(GetBuildTimeout(undef, $Missions->[0]));
+ $Task->Missions($Missions->[0]->{Statement});
}
# Save the build step so the others can reference it.
@@ -303,22 +332,38 @@ sub AddReconfigJob($)
if ($VMType eq "wine")
{
# Add steps to run WineTest on Wine
- foreach my $Build (@WineBuilds)
+ my $Tasks;
+ foreach my $VMKey (@$SortedKeys)
{
- # Add a step to the job
- my $NewStep = $Steps->Add();
- $NewStep->PreviousNo($BuildStep->No);
- $NewStep->Type("suite");
- $NewStep->FileType("none");
+ my $VM = $VMs->GetItem($VMKey);
+ # Move all the missions into separate tasks so we don't have one very
+ # long task hogging the VM forever. Note that this also ok because the
+ # WineTest tasks don't have to recompile Wine.
+ my $MissionStatement = SplitMissionStatementTasks($VM->Missions);
+ my ($ErrMessage, $Missions) = ParseMissionStatement($MissionStatement);
+ if (defined $ErrMessage)
+ {
+ Debug("$VMKey has an invalid mission statement: $!\n");
+ next;
+ }
- foreach my $VMKey (@$SortedKeys)
+ foreach my $TaskMissions (@$Missions)
{
- my $VM = $VMs->GetItem($VMKey);
- Debug(" $VMKey $Build\n");
- my $Task = $NewStep->Tasks->Add();
+ if (!$Tasks)
+ {
+ # Add a step to the job
+ my $TestStep = $Steps->Add();
+ $TestStep->PreviousNo($BuildStep->No);
+ $TestStep->Type("suite");
+ $TestStep->FileType("none");
+ $Tasks = $TestStep->Tasks;
+ }
+
+ Debug(" $VMKey $TaskMissions->{Statement}\n");
+ my $Task = $Tasks->Add();
$Task->VM($VM);
- $Task->CmdLineArg($Build);
- $Task->Timeout(GetTestTimeout(undef, { $Build => 1 }));
+ $Task->Timeout(GetTestTimeout(undef, $TaskMissions));
+ $Task->Missions($TaskMissions->{Statement});
}
}
}
diff --git a/testbot/bin/WineRunTask.pl b/testbot/bin/WineRunTask.pl
index 077426b232..fea8c03f5e 100755
--- a/testbot/bin/WineRunTask.pl
+++ b/testbot/bin/WineRunTask.pl
@@ -45,6 +45,7 @@ use WineTestBot::Engine::Notify;
use WineTestBot::Jobs;
use WineTestBot::Log;
use WineTestBot::LogUtils;
+use WineTestBot::Missions;
use WineTestBot::Utils;
use WineTestBot::VMs;
@@ -391,6 +392,13 @@ if ($Step->FileType ne "exe32" and $Step->FileType ne "exe64")
FatalError("Unexpected file type '". $Step->FileType ."' found for ". $Step->Type ." step\n");
}
+my ($ErrMessage, $Missions) = ParseMissionStatement($Task->Missions);
+FatalError "$ErrMessage\n" if (defined $ErrMessage);
+FatalError "Empty mission statement\n" if (!@$Missions);
+FatalError "Cannot specify missions for multiple tasks\n" if (@$Missions > 1);
+FatalError "Cannot specify multiple missions\n" if (@{$Missions->[0]->{Missions}} > 1);
+my $Mission = $Missions->[0]->{Missions}->[0];
+
#
# Setup the VM
@@ -477,8 +485,8 @@ elsif ($Step->Type eq "suite")
$Info =~ s/"/\\"/g;
$Info =~ s/%/%%/g;
$Info =~ s/%/%%/g;
- $Script .= "-q -o $RptFileName -t $Tag -m \"$EMail\" -i \"$Info\"\r\n".
- "$FileName -q -s $RptFileName\r\n";
+ $Script .= "-q -o $RptFileName -t $Tag -m \"$EMail\" -i \"$Info\"\r\n";
+ $Script .= "$FileName -q -s $RptFileName\r\n" if (!$Mission->{nosubmit});
}
Debug(Elapsed($Start), " Sending the script: [$Script]\n");
if (!$TA->SendFileFromString($Script, "script.bat", $TestAgent::SENDFILE_EXE))
diff --git a/testbot/bin/WineRunWineTest.pl b/testbot/bin/WineRunWineTest.pl
index 0e438f957c..f47a02413a 100755
--- a/testbot/bin/WineRunWineTest.pl
+++ b/testbot/bin/WineRunWineTest.pl
@@ -41,13 +41,14 @@ $Name0 =~ s+^.*/++;
use WineTestBot::Config;
+use WineTestBot::Engine::Notify;
use WineTestBot::Jobs;
+use WineTestBot::Missions;
use WineTestBot::PatchUtils;
-use WineTestBot::VMs;
use WineTestBot::Log;
use WineTestBot::LogUtils;
use WineTestBot::Utils;
-use WineTestBot::Engine::Notify;
+use WineTestBot::VMs;
#
@@ -220,6 +221,8 @@ sub LogTaskError($)
}
}
+my $TaskMissions;
+
sub WrapUpAndExit($;$$$)
{
my ($Status, $TestFailures, $Retry, $TimedOut) = @_;
@@ -287,9 +290,7 @@ sub WrapUpAndExit($;$$$)
if ($Step->Type eq 'suite' and $Status eq 'completed' and !$TimedOut)
{
- my $BuildList = $Task->CmdLineArg;
- $BuildList =~ s/ .*$//;
- foreach my $Build (split /,/, $BuildList)
+ foreach my $Build (keys %{$TaskMissions->{Builds}})
{
# Keep the old report if the new one is missing
my $RptFileName = "$Build.report";
@@ -392,6 +393,12 @@ if (($Step->Type eq "suite" and $Step->FileType ne "none") or
FatalError("Unexpected file type '". $Step->FileType ."' found for ". $Step->Type ." step\n");
}
+my ($ErrMessage, $Missions) = ParseMissionStatement($Task->Missions);
+FatalError "$ErrMessage\n" if (defined $ErrMessage);
+FatalError "Empty mission statement\n" if (!@$Missions);
+FatalError "Cannot specify missions for multiple tasks\n" if (@$Missions > 1);
+$TaskMissions = $Missions->[0];
+
#
# Setup the VM
@@ -431,7 +438,7 @@ $Script .= " ../bin/build/WineTest.pl ";
if ($Step->Type eq "suite")
{
my $BaseTag = BuildTag($VM->Name);
- $Script .= "--winetest ". $Task->CmdLineArg ." $BaseTag ";
+ $Script .= "--winetest ". $Task->Missions ." $BaseTag ";
if (defined $WebHostName)
{
my $StepTask = 100 * $StepNo + $TaskNo;
@@ -447,7 +454,7 @@ if ($Step->Type eq "suite")
}
else
{
- $Script .= "--testpatch ". $Task->CmdLineArg ." patch.diff";
+ $Script .= "--testpatch ". $Task->Missions ." patch.diff";
}
$Script .= "\n) >Task.log 2>&1\n";
Debug(Elapsed($Start), " Sending the script: [$Script]\n");
@@ -475,7 +482,7 @@ if (!$Pid)
#
my $NewStatus = 'completed';
-my ($TaskFailures, $TaskTimedOut, $ErrMessage, $TAError, $PossibleCrash);
+my ($TaskFailures, $TaskTimedOut, $TAError, $PossibleCrash);
Debug(Elapsed($Start), " Waiting for the script (", $Task->Timeout, "s timeout)\n");
if (!defined $TA->Wait($Pid, $Task->Timeout, 60))
{
@@ -542,9 +549,7 @@ elsif (!defined $TAError)
if ($Step->Type ne "build")
{
- my $BuildList = $Task->CmdLineArg;
- $BuildList =~ s/ .*$//;
- foreach my $Build (split /,/, $BuildList)
+ foreach my $Build (keys %{$TaskMissions->{Builds}})
{
my $RptFileName = "$Build.report";
Debug(Elapsed($Start), " Retrieving '$RptFileName'\n");
@@ -602,9 +607,7 @@ if ($NewStatus eq 'completed')
{
my $LatestDir = "$DataDir/latest";
my $StepDir = $Step->GetDir();
- my $BuildList = $Task->CmdLineArg;
- $BuildList =~ s/ .*$//;
- foreach my $Build (split /,/, $BuildList)
+ foreach my $Build (keys %{$TaskMissions->{Builds}})
{
my $RptFileName = "$Build.report";
my $RefReport = $Task->VM->Name ."_$RptFileName";
diff --git a/testbot/bin/build/WineTest.pl b/testbot/bin/build/WineTest.pl
index 712341b339..c5d38df9d4 100755
--- a/testbot/bin/build/WineTest.pl
+++ b/testbot/bin/build/WineTest.pl
@@ -43,6 +43,7 @@ sub BEGIN
use Build::Utils;
use WineTestBot::Config;
+use WineTestBot::Missions;
use WineTestBot::Utils;
@@ -52,9 +53,9 @@ use WineTestBot::Utils;
sub BuildWine($$)
{
- my ($Targets, $Build) = @_;
+ my ($TaskMissions, $Build) = @_;
- return 1 if (!$Targets->{$Build});
+ return 1 if (!$TaskMissions->{Builds}->{$Build});
InfoMsg "\nRebuilding the $Build Wine\n";
my $CPUCount = GetCPUCount();
@@ -74,44 +75,40 @@ sub BuildWine($$)
# Test helpers
#
-
-sub DailyWineTest($$$$$)
+sub DailyWineTest($$$$)
{
- my ($Targets, $Build, $NoSubmit, $BaseTag, $Args) = @_;
-
- return 1 if (!$Targets->{$Build});
+ my ($Mission, $NoSubmit, $BaseTag, $Args) = @_;
- InfoMsg "\nRunning WineTest in the $Build Wine\n";
- SetupWineEnvironment($Build);
+ InfoMsg "\nRunning WineTest in the $Mission->{Build} Wine\n";
+ SetupWineEnvironment($Mission->{Build});
# Run WineTest. Ignore the exit code since it returns non-zero whenever
# there are test failures.
- my $Tag = SanitizeTag("$BaseTag-$Build");
- RunWine($Build, "./programs/winetest/winetest.exe.so",
- "-c -o '../$Build.report' -t $Tag ". ShArgv2Cmd(@$Args));
- if (!-f "$Build.report")
+ my $Tag = SanitizeTag("$BaseTag-$Mission->{Build}");
+ RunWine($Mission->{Build}, "./programs/winetest/winetest.exe.so",
+ "-c -o '../$Mission->{Build}.report' -t $Tag ".
+ ShArgv2Cmd(@$Args));
+ if (!-f "$Mission->{Build}.report")
{
LogMsg "WineTest did not produce a report file\n";
return 0;
}
# Send the report to the website
- if (!$NoSubmit and
- RunWine($Build, "./programs/winetest/winetest.exe.so",
- "-c -s '../$Build.report'"))
+ if ((!$NoSubmit and !$Mission->{nosubmit}) and
+ RunWine($Mission->{Build}, "./programs/winetest/winetest.exe.so",
+ "-c -s '../$Mission->{Build}.report'"))
{
- LogMsg "WineTest failed to send the $Build report\n";
+ LogMsg "WineTest failed to send the $Mission->{Build} report\n";
# Soldier on in case it's just a network issue
}
return 1;
}
-sub TestPatch($$$)
+sub TestPatch($$)
{
- my ($Targets, $Build, $Impacts) = @_;
-
- return 1 if (!$Targets->{"test$Build"});
+ my ($Mission, $Impacts) = @_;
my @TestList;
foreach my $Module (sort keys %{$Impacts->{Tests}})
@@ -131,14 +128,15 @@ sub TestPatch($$$)
}
return 1 if (!@TestList);
- InfoMsg "\nRunning the tests in the $Build Wine\n";
- SetupWineEnvironment($Build);
+ InfoMsg "\nRunning the tests in the $Mission->{Build} Wine\n";
+ SetupWineEnvironment($Mission->{Build});
# Run WineTest. Ignore the exit code since it returns non-zero whenever
# there are test failures.
- RunWine($Build, "./programs/winetest/winetest.exe.so",
- "-c -o '../$Build.report' -t test-$Build ". join(" ", @TestList));
- if (!-f "$Build.report")
+ RunWine($Mission->{Build}, "./programs/winetest/winetest.exe.so",
+ "-c -o '../$Mission->{Build}.report' -t test-$Mission->{Build} ".
+ join(" ", @TestList));
+ if (!-f "$Mission->{Build}.report")
{
LogMsg "WineTest did not produce a report file\n";
return 0;
@@ -155,11 +153,8 @@ sub TestPatch($$$)
$ENV{PATH} = "/usr/lib/ccache:/usr/bin:/bin";
delete $ENV{ENV};
-my %AllTargets;
-map { $AllTargets{$_} = 1 } qw(win32 wow32 wow64);
-
my $Action = "";
-my ($Usage, $OptNoSubmit, $TargetList, $FileName, $BaseTag);
+my ($Usage, $OptNoSubmit, $MissionStatement, $FileName, $BaseTag);
while (@ARGV)
{
my $Arg = shift @ARGV;
@@ -186,9 +181,9 @@ while (@ARGV)
$Usage = 2;
last;
}
- elsif (!defined $TargetList)
+ elsif (!defined $MissionStatement)
{
- $TargetList = $Arg;
+ $MissionStatement = $Arg;
}
elsif ($Action eq "winetest")
{
@@ -223,24 +218,35 @@ while (@ARGV)
}
# Check and untaint parameters
-my $Targets;
+my $TaskMissions;
if (!defined $Usage)
{
- if (defined $TargetList)
+ if (defined $MissionStatement)
{
- foreach my $Target (split /[,:]/, $TargetList)
+ my ($ErrMessage, $Missions) = ParseMissionStatement($MissionStatement);
+ if (defined $ErrMessage)
{
- if (!$AllTargets{$Target})
- {
- Error "invalid target name $Target\n";
- $Usage = 2;
- }
- $Targets->{$Target} = 1;
+ Error "$ErrMessage\n";
+ $Usage = 2;
+ }
+ elsif (!@$Missions)
+ {
+ Error "empty mission statement\n";
+ $Usage = 2;
+ }
+ elsif (@$Missions > 1)
+ {
+ Error "cannot specify missions for multiple tasks\n";
+ $Usage = 2;
+ }
+ else
+ {
+ $TaskMissions = $Missions->[0];
}
}
else
{
- Error "specify at least one target\n";
+ Error "you must specify the mission statement\n";
$Usage = 2;
}
@@ -268,16 +274,13 @@ if (!defined $Usage)
}
else
{
- foreach my $Build ("win32", "wow32", "wow64")
- {
- $Targets->{"test$Build"} = 1 if ($Targets->{$Build});
- }
- if ($Targets->{"wow32"} or $Targets->{"wow64"})
+ my $Builds = $TaskMissions->{Builds};
+ if ($Builds->{"wow32"} or $Builds->{"wow64"})
{
# Always rebuild both WoW targets before running the tests to make sure
# we don't run into issues caused by the two Wine builds being out of
# sync.
- $Targets->{"wow32"} = $Targets->{"wow64"} = 1;
+ $Builds->{"wow32"} = $Builds->{"wow64"} = 1;
}
}
@@ -295,8 +298,8 @@ if (defined $Usage)
Error "try '$Name0 --help' for more information\n";
exit $Usage;
}
- print "Usage: $Name0 [--help] --testpatch TARGETS PATCH\n";
- print "or $Name0 [--help] --winetest [--no-submit] TARGETS BASETAG ARGS\n";
+ print "Usage: $Name0 [--help] --testpatch MISSIONS PATCH\n";
+ print "or $Name0 [--help] --winetest [--no-submit] MISSIONS BASETAG ARGS\n";
print "\n";
print "Tests the specified patch or runs WineTest in Wine.\n";
print "\n";
@@ -304,7 +307,7 @@ if (defined $Usage)
print " --testpatch Verify that the patch compiles and run the impacted tests.\n";
print " --winetest Run WineTest and submit the result to the website.\n";
print " --no-submit Do not submit the WineTest results to the website.\n";
- print " TARGETS Is a comma-separated list of targets for the specified action.\n";
+ print " MISSIONS Is a colon-separated list of missions for the specified action.\n";
print " - win32: The regular 32 bit Wine build.\n";
print " - wow32: The 32 bit WoW Wine build.\n";
print " - wow64: The 64 bit WoW Wine build.\n";
@@ -328,26 +331,26 @@ if ($DataDir =~ /'/)
#
# Clean up old reports
-map { unlink("$_.report") } keys %AllTargets;
+map { unlink("$_.report") } keys %{$TaskMissions->{Builds}};
+my $Impacts;
if ($Action eq "testpatch")
{
- my $Impacts = ApplyPatch("wine", $FileName);
+ $Impacts = ApplyPatch("wine", $FileName);
exit(1) if (!$Impacts or
- !BuildWine($Targets, "win32") or
- !BuildWine($Targets, "wow64") or
- !BuildWine($Targets, "wow32") or
- !TestPatch($Targets, "win32", $Impacts) or
- !TestPatch($Targets, "wow64", $Impacts) or
- !TestPatch($Targets, "wow32", $Impacts));
+ !BuildWine($TaskMissions, "win32") or
+ !BuildWine($TaskMissions, "wow64") or
+ !BuildWine($TaskMissions, "wow32"));
}
-elsif ($Action eq "winetest")
+foreach my $Mission (@{$TaskMissions->{Missions}})
{
- if (!DailyWineTest($Targets, "win32", $OptNoSubmit, $BaseTag, \@ARGV) or
- !DailyWineTest($Targets, "wow64", $OptNoSubmit, $BaseTag, \@ARGV) or
- !DailyWineTest($Targets, "wow32", $OptNoSubmit, $BaseTag, \@ARGV))
+ if ($Action eq "testpatch")
{
- exit(1);
+ exit(1) if (!TestPatch($Mission, $Impacts));
+ }
+ elsif ($Action eq "winetest")
+ {
+ exit(1) if (!DailyWineTest($Mission, $OptNoSubmit, $BaseTag, \@ARGV));
}
}
diff --git a/testbot/ddl/update41.sql b/testbot/ddl/update41.sql
new file mode 100644
index 0000000000..f1a5e1a240
--- /dev/null
+++ b/testbot/ddl/update41.sql
@@ -0,0 +1,48 @@
+USE winetestbot;
+
+ALTER TABLE Tasks
+ ADD Missions VARCHAR(256) NULL
+ AFTER Timeout;
+
+UPDATE Tasks, VMs
+ SET Tasks.Missions = 'build'
+ WHERE Tasks.Missions is NULL AND Tasks.VMName = VMs.Name AND VMs.Type = 'build';
+
+UPDATE Tasks, VMs
+ SET Tasks.Missions = 'exe32'
+ WHERE Tasks.Missions is NULL AND Tasks.VMName = VMs.Name AND VMs.Type = 'win32';
+
+UPDATE Tasks, VMs
+ SET Tasks.Missions = 'exe32|exe64'
+ WHERE Tasks.Missions is NULL AND Tasks.VMName = VMs.Name AND VMs.Type = 'win64';
+
+UPDATE Tasks, VMs
+ SET Tasks.Missions = 'win32:wow64'
+ WHERE Tasks.Missions is NULL AND Tasks.VMName = VMs.Name AND VMs.Type = 'wine';
+
+ALTER TABLE Tasks
+ MODIFY Missions VARCHAR(256) NOT NULL;
+
+
+ALTER TABLE VMs
+ ADD Missions VARCHAR(256) NULL
+ AFTER Role;
+
+UPDATE VMs
+ SET Missions = 'build'
+ WHERE Missions is NULL AND Type = 'build';
+
+UPDATE VMs
+ SET Missions = 'exe32'
+ WHERE Missions is NULL AND Type = 'win32';
+
+UPDATE VMs
+ SET Missions = 'exe32|exe64'
+ WHERE Missions is NULL AND Type = 'win64';
+
+UPDATE VMs
+ SET Missions = 'win32|wow64'
+ WHERE Missions is NULL AND Type = 'wine';
+
+ALTER TABLE VMs
+ MODIFY Missions VARCHAR(256) NOT NULL;
diff --git a/testbot/ddl/winetestbot.sql b/testbot/ddl/winetestbot.sql
index 8862c6c9dc..e6114e0917 100644
--- a/testbot/ddl/winetestbot.sql
+++ b/testbot/ddl/winetestbot.sql
@@ -48,6 +48,7 @@ CREATE TABLE VMs
SortOrder INT(3) NOT NULL,
Type ENUM('win32', 'win64', 'build', 'wine') NOT NULL,
Role ENUM('extra', 'base', 'winetest', 'retired', 'deleted') NOT NULL,
+ Missions VARCHAR(256) NOT NULL,
Status ENUM('dirty', 'reverting', 'sleeping', 'idle', 'running', 'off', 'offline', 'maintenance') NOT NULL,
Errors INT(2) NULL,
ChildPid INT(5) NULL,
@@ -149,6 +150,7 @@ CREATE TABLE Tasks
Status ENUM('queued', 'running', 'completed', 'badpatch', 'badbuild', 'boterror', 'canceled', 'skipped') NOT NULL,
VMName VARCHAR(20) NOT NULL,
Timeout INT(4) NOT NULL,
+ Missions VARCHAR(256) NOT NULL,
CmdLineArg VARCHAR(256) NULL,
Started DATETIME NULL,
Ended DATETIME NULL,
diff --git a/testbot/doc/winetestbot-schema.dia b/testbot/doc/winetestbot-schema.dia
index c54897a831..372d3bf006 100644
--- a/testbot/doc/winetestbot-schema.dia
+++ b/testbot/doc/winetestbot-schema.dia
@@ -2106,6 +2106,29 @@
<dia:string>##</dia:string>
</dia:attribute>
</dia:composite>
+ <dia:composite type="table_attribute">
+ <dia:attribute name="name">
+ <dia:string>#Missions#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="type">
+ <dia:string>#VARCHAR(256)#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="comment">
+ <dia:string>##</dia:string>
+ </dia:attribute>
+ <dia:attribute name="primary_key">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="nullable">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="unique">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="default_value">
+ <dia:string>##</dia:string>
+ </dia:attribute>
+ </dia:composite>
<dia:composite type="table_attribute">
<dia:attribute name="name">
<dia:string>#CmdLineArg#</dia:string>
@@ -2360,6 +2383,29 @@
<dia:string>##</dia:string>
</dia:attribute>
</dia:composite>
+ <dia:composite type="table_attribute">
+ <dia:attribute name="name">
+ <dia:string>#Missions#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="type">
+ <dia:string>#VARCHAR(256)#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="comment">
+ <dia:string>##</dia:string>
+ </dia:attribute>
+ <dia:attribute name="primary_key">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="nullable">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="unique">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="default_value">
+ <dia:string>##</dia:string>
+ </dia:attribute>
+ </dia:composite>
<dia:composite type="table_attribute">
<dia:attribute name="name">
<dia:string>#Status#</dia:string>
diff --git a/testbot/lib/WineTestBot/Missions.pm b/testbot/lib/WineTestBot/Missions.pm
new file mode 100644
index 0000000000..58b33403fb
--- /dev/null
+++ b/testbot/lib/WineTestBot/Missions.pm
@@ -0,0 +1,99 @@
+# -*- Mode: Perl; perl-indent-level: 2; indent-tabs-mode: nil -*-
+# Copyright 2018 Francois Gouget
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+use strict;
+
+package WineTestBot::Missions;
+
+=head1 NAME
+
+WineTestBot::Missions - Missions parser and helper functions
+
+=cut
+
+use Exporter 'import';
+our @EXPORT = qw(DumpMissions ParseMissionStatement
+ MergeMissionStatementTasks SplitMissionStatementTasks);
+
+
+sub DumpMissions($$)
+{
+ my ($Label, $Missions) = @_;
+
+ print STDERR "$Label:\n";
+ foreach my $TaskMissions (@$Missions)
+ {
+ print STDERR "Builds=", join(",", sort keys %{$TaskMissions->{Builds}}), "\n";
+ foreach my $Mission (@{$TaskMissions->{Missions}})
+ {
+ print STDERR " [$Mission->{Build}]\n";
+ print STDERR " \"$_\"=\"$Mission->{$_}\"\n" for (sort grep(!/^Build$/,keys %$Mission));
+ }
+ }
+}
+
+sub ParseMissionStatement($)
+{
+ my ($MissionStatement) = @_;
+
+ my @Missions;
+ foreach my $TaskStatement (split /[|]/, $MissionStatement)
+ {
+ my $TaskMissions = { Statement => $TaskStatement };
+ push @Missions, $TaskMissions;
+ foreach my $Statement (split /:/, $TaskStatement)
+ {
+ my ($Build, @Options) = split /,/, $Statement;
+ if ($Build !~ /^([a-z0-9]+)$/)
+ {
+ return ("Invalid mission name '$Build'", undef);
+ }
+ $Build = $1; # untaint
+ $TaskMissions->{Builds}->{$Build} = 1;
+ my $Mission = { Build => $Build, Statement => $Statement };
+ push @{$TaskMissions->{Missions}}, $Mission;
+
+ foreach my $Option (@Options)
+ {
+ if ($Option !~ s/^([a-z0-9_]+)//)
+ {
+ return ("Invalid option name '$Option'", undef);
+ }
+ my $Name = $1; # untaint
+ # do not untaint the value
+ $Mission->{$Name} = ($Option =~ s/^=//) ? $Option : 1;
+ }
+ }
+ }
+ return (undef, \@Missions);
+}
+
+sub MergeMissionStatementTasks($)
+{
+ my ($MissionStatement) = @_;
+ $MissionStatement =~ s/\|/:/g;
+ return $MissionStatement;
+}
+
+sub SplitMissionStatementTasks($)
+{
+ my ($MissionStatement) = @_;
+ $MissionStatement =~ s/:/|/g;
+ return $MissionStatement;
+}
+
+1;
diff --git a/testbot/lib/WineTestBot/PatchUtils.pm b/testbot/lib/WineTestBot/PatchUtils.pm
index 7df50f9b21..444ffaf491 100644
--- a/testbot/lib/WineTestBot/PatchUtils.pm
+++ b/testbot/lib/WineTestBot/PatchUtils.pm
@@ -457,10 +457,10 @@ sub GetPatchImpacts($)
sub GetBuildTimeout($$)
{
- my ($Impacts, $Builds) = @_;
+ my ($Impacts, $TaskMissions) = @_;
my ($ExeCount, $WineCount);
- map {$_ =~ /^exe/ ? $ExeCount++ : $WineCount++ } keys %$Builds;
+ map {$_ =~ /^exe/ ? $ExeCount++ : $WineCount++ } keys %{$TaskMissions->{Builds}};
# Set $ModuleCount to 0 if a full rebuild is needed
my $ModuleCount = (!$Impacts or $Impacts->{RebuildRoot}) ? 0 :
@@ -486,7 +486,7 @@ sub GetBuildTimeout($$)
sub GetTestTimeout($$)
{
- my ($Impacts, $Builds) = @_;
+ my ($Impacts, $TaskMissions) = @_;
my $Timeout = $SuiteTimeout;
if ($Impacts)
@@ -496,7 +496,7 @@ sub GetTestTimeout($$)
max(0, $UnitCount - 2) * $SingleAvgTime;
$Timeout = min($SuiteTimeout, $TestsTimeout);
}
- return scalar(keys %$Builds) * $Timeout;
+ return @{$TaskMissions->{Missions}} * $Timeout;
}
1;
diff --git a/testbot/lib/WineTestBot/Patches.pm b/testbot/lib/WineTestBot/Patches.pm
index f685e86b32..e621e8845d 100644
--- a/testbot/lib/WineTestBot/Patches.pm
+++ b/testbot/lib/WineTestBot/Patches.pm
@@ -40,12 +40,13 @@ use Encode qw/decode/;
use File::Basename;
use WineTestBot::Config;
+use WineTestBot::Engine::Notify;
use WineTestBot::Jobs;
+use WineTestBot::Missions;
use WineTestBot::PatchUtils;
use WineTestBot::Users;
use WineTestBot::Utils;
use WineTestBot::VMs;
-use WineTestBot::Engine::Notify;
sub InitializeNew($$)
@@ -175,23 +176,8 @@ sub Submit($$$)
$BuildVMs->AddFilter("Role", ["base"]);
if ($Impacts->{TestUnitCount} and !$BuildVMs->IsEmpty())
{
- # Create the Build Step
- my $BuildStep = $NewJob->Steps->Add();
- $BuildStep->FileName("patch.diff");
- $BuildStep->FileType("patch");
- $BuildStep->Type("build");
- $BuildStep->DebugLevel(0);
-
- # Save the build step so the others can reference it.
- my ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
- if (defined($ErrMessage))
- {
- $self->Disposition("Failed to submit build step");
- return $ErrMessage;
- }
-
# Create steps for the Windows tests
- my $Builds;
+ my ($BuildStep, $BuildMissions);
foreach my $Module (sort keys %{$Impacts->{Tests}})
{
my $TestInfo = $Impacts->{Tests}->{$Module};
@@ -202,26 +188,54 @@ sub Submit($$$)
my $WinVMs = CreateVMs();
$WinVMs->AddFilter("Type", $Bits eq "32" ? ["win32", "win64"] : ["win64"]);
$WinVMs->AddFilter("Role", ["base"]);
- if (!$WinVMs->IsEmpty())
+ my $SortedKeys = $WinVMs->SortKeysBySortOrder($WinVMs->GetKeys());
+
+ my $Tasks;
+ foreach my $VMKey (@$SortedKeys)
{
- # Create one Step per (module, unit, bitness) combination
- my $NewStep = $NewJob->Steps->Add();
- $NewStep->PreviousNo($BuildStep->No);
- my $FileName = $TestInfo->{ExeBase};
- $FileName .= "64" if ($Bits eq "64");
- $NewStep->FileName("$FileName.exe");
- $NewStep->FileType("exe$Bits");
- $Builds->{"exe$Bits"} = 1;
-
- # And a task for each VM
- my $Tasks = $NewStep->Tasks;
- my $SortedKeys = $WinVMs->SortKeysBySortOrder($WinVMs->GetKeys());
- foreach my $VMKey (@$SortedKeys)
+ my $VM = $WinVMs->GetItem($VMKey);
+ my ($ErrMessage, $Missions) = ParseMissionStatement($VM->Missions);
+ next if (defined $ErrMessage);
+
+ foreach my $TaskMissions (@$Missions)
{
- my $VM = $WinVMs->GetItem($VMKey);
+ next if (!$TaskMissions->{Builds}->{"exe$Bits"});
+
+ if (!$BuildStep)
+ {
+ # Create the Build Step
+ $BuildStep = $NewJob->Steps->Add();
+ $BuildStep->FileName("patch.diff");
+ $BuildStep->FileType("patch");
+ $BuildStep->Type("build");
+ $BuildStep->DebugLevel(0);
+
+ # Save the build step so the others can reference it.
+ my ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
+ if (defined($ErrMessage))
+ {
+ $self->Disposition("Failed to submit build step");
+ return $ErrMessage;
+ }
+ }
+
+ if (!$Tasks)
+ {
+ # Create one Step per (module, unit, bitness) combination
+ my $NewStep = $NewJob->Steps->Add();
+ $NewStep->PreviousNo($BuildStep->No);
+ my $FileName = $TestInfo->{ExeBase};
+ $FileName .= "64" if ($Bits eq "64");
+ $NewStep->FileName("$FileName.exe");
+ $NewStep->FileType("exe$Bits");
+ $BuildMissions->{Builds}->{"exe$Bits"} = 1;
+ $Tasks = $NewStep->Tasks;
+ }
+
my $Task = $Tasks->Add();
$Task->VM($VM);
$Task->Timeout($SingleTimeout);
+ $Task->Missions($TaskMissions->{Statement});
$Task->CmdLineArg($Unit);
}
}
@@ -229,38 +243,48 @@ sub Submit($$$)
}
}
- # Add the build task
- my $BuildVM = ${$BuildVMs->GetItems()}[0];
- my $BuildTask = $BuildStep->Tasks->Add();
- $BuildTask->VM($BuildVM);
- $BuildTask->Timeout(GetBuildTimeout($Impacts, $Builds));
+ if ($BuildStep)
+ {
+ # Add the build task
+ my $BuildVM = ${$BuildVMs->GetItems()}[0];
+ my $BuildTask = $BuildStep->Tasks->Add();
+ $BuildTask->VM($BuildVM);
+ $BuildMissions->{Statement} = join(":", keys %{$BuildMissions->{Builds}});
+ $BuildTask->Timeout(GetBuildTimeout($Impacts, $BuildMissions));
+ $BuildTask->Missions($BuildMissions->{Statement});
+ }
}
my $WineVMs = CreateVMs();
$WineVMs->AddFilter("Type", ["wine"]);
$WineVMs->AddFilter("Role", ["base"]);
- if (!$WineVMs->IsEmpty())
+ my $SortedKeys = $WineVMs->SortKeysBySortOrder($WineVMs->GetKeys());
+
+ my $Tasks;
+ foreach my $VMKey (@$SortedKeys)
{
- # Add a Wine step to the job
- my $NewStep = $NewJob->Steps->Add();
- $NewStep->FileName("patch.diff");
- $NewStep->FileType("patch");
- $NewStep->Type("single");
- $NewStep->DebugLevel(0);
-
- # And a task for each VM
- my $Tasks = $NewStep->Tasks;
- my $SortedKeys = $WineVMs->SortKeysBySortOrder($WineVMs->GetKeys());
- foreach my $VMKey (@$SortedKeys)
+ my $VM = $WineVMs->GetItem($VMKey);
+ my ($ErrMessage, $Missions) = ParseMissionStatement($VM->Missions);
+ next if (defined $ErrMessage);
+
+ foreach my $TaskMissions (@$Missions)
{
- my $VM = $WineVMs->GetItem($VMKey);
+ if (!$Tasks)
+ {
+ # Add a Wine step to the job
+ my $TestStep = $NewJob->Steps->Add();
+ $TestStep->FileName("patch.diff");
+ $TestStep->FileType("patch");
+ $TestStep->Type("single");
+ $TestStep->DebugLevel(0);
+ $Tasks = $TestStep->Tasks;
+ }
+
my $Task = $Tasks->Add();
$Task->VM($VM);
- # Only verify that the win32 version compiles
- my $Builds = { "win32" => 1 };
- $Task->Timeout(GetBuildTimeout($Impacts, $Builds) +
- GetTestTimeout($Impacts, $Builds));
- $Task->CmdLineArg(join(",", keys %$Builds));
+ $Task->Timeout(GetBuildTimeout($Impacts, $TaskMissions) +
+ GetTestTimeout($Impacts, $TaskMissions));
+ $Task->Missions($TaskMissions->{Statement});
}
}
diff --git a/testbot/lib/WineTestBot/StepsTasks.pm b/testbot/lib/WineTestBot/StepsTasks.pm
index a50f61ada3..cfd78e52e2 100644
--- a/testbot/lib/WineTestBot/StepsTasks.pm
+++ b/testbot/lib/WineTestBot/StepsTasks.pm
@@ -30,6 +30,7 @@ use WineTestBot::WineTestBotObjects;
our @ISA = qw(WineTestBot::WineTestBotItem);
use WineTestBot::Config;
+use WineTestBot::Missions;
sub GetStepDir($)
@@ -68,34 +69,50 @@ sub GetTitle($)
{
my ($self) = @_;
- my $Title = "";
- if ($self->Type eq "single")
+ my @TitleParts;
+ if ($self->Type eq "build")
{
- if ($self->FileType eq "exe32")
- {
- $Title .= "32 bit ";
- }
- elsif ($self->FileType eq "exe64")
- {
- $Title .= "64 bit ";
- }
- $Title .= $self->CmdLineArg || "";
+ push @TitleParts, "build";
}
- elsif ($self->Type eq "build")
+ elsif ($self->FileType eq "exe32")
{
- $Title = "build";
+ push @TitleParts, "32 bit";
}
- $Title =~ s/\s*$//;
-
- if ($Title)
+ elsif ($self->FileType eq "exe64")
{
- $Title = $self->VM->Name . " (" . $Title . ")";
+ push @TitleParts, "64 bit";
}
else
{
- $Title = $self->VM->Name;
+ my ($ErrMessage, $Missions) = ParseMissionStatement($self->Missions);
+ if (!defined $ErrMessage and @$Missions == 1)
+ {
+ my $Builds = $Missions->[0]->{Builds};
+ if ($Builds->{build})
+ {
+ push @TitleParts, "build";
+ }
+ elsif ($Builds->{wow64} and ($Builds->{win32} or $Builds->{wow32}))
+ {
+ push @TitleParts, "32 & 64 bit";
+ }
+ elsif ($Builds->{win32} or $Builds->{wow32})
+ {
+ push @TitleParts, "32 bit";
+ }
+ elsif ($Builds->{wow64})
+ {
+ push @TitleParts, "64 bit";
+ }
+ }
+ }
+ if ($self->Type ne "suite" and $self->CmdLineArg)
+ {
+ push @TitleParts, $self->CmdLineArg;
}
+ my $Title = $self->VM->Name;
+ $Title .= " (@TitleParts)" if (@TitleParts);
return $Title;
}
@@ -148,6 +165,7 @@ sub _initialize($$)
$StepTask->Timeout($Task->Timeout);
$StepTask->FileName($Step->FileName);
$StepTask->FileType($Step->FileType);
+ $StepTask->Missions($Task->Missions);
$StepTask->CmdLineArg($Task->CmdLineArg);
$StepTask->Started($Task->Started);
$StepTask->Ended($Task->Ended);
@@ -179,6 +197,7 @@ my @PropertyDescriptors = (
CreateBasicPropertyDescriptor("Timeout", "Timeout", !1, 1, "N", 4),
CreateBasicPropertyDescriptor("FileName", "File name", !1, !1, "A", 100),
CreateBasicPropertyDescriptor("FileType", "File Type", !1, 1, "A", 32),
+ CreateBasicPropertyDescriptor("Missions", "Missions", !1, 1, "A", 256),
CreateBasicPropertyDescriptor("CmdLineArg", "Command line args", !1, !1, "A", 256),
CreateBasicPropertyDescriptor("Started", "Execution started", !1, !1, "DT", 19),
CreateBasicPropertyDescriptor("Ended", "Execution ended", !1, !1, "DT", 19),
diff --git a/testbot/lib/WineTestBot/Tasks.pm b/testbot/lib/WineTestBot/Tasks.pm
index 084784a3ba..f9e84d471b 100644
--- a/testbot/lib/WineTestBot/Tasks.pm
+++ b/testbot/lib/WineTestBot/Tasks.pm
@@ -298,6 +298,7 @@ my @PropertyDescriptors = (
CreateEnumPropertyDescriptor("Status", "Status", !1, 1, ['queued', 'running', 'completed', 'badpatch', 'badbuild', 'boterror', 'canceled', 'skipped']),
CreateItemrefPropertyDescriptor("VM", "VM", !1, 1, \&CreateVMs, ["VMName"]),
CreateBasicPropertyDescriptor("Timeout", "Timeout", !1, 1, "N", 4),
+ CreateBasicPropertyDescriptor("Missions", "Missions", !1, 1, "A", 256),
CreateBasicPropertyDescriptor("CmdLineArg", "Command line args", !1, !1, "A", 256),
CreateBasicPropertyDescriptor("Started", "Execution started", !1, !1, "DT", 19),
CreateBasicPropertyDescriptor("Ended", "Execution ended", !1, !1, "DT", 19),
diff --git a/testbot/lib/WineTestBot/VMs.pm b/testbot/lib/WineTestBot/VMs.pm
index cf7bd5ab75..e7cd277494 100644
--- a/testbot/lib/WineTestBot/VMs.pm
+++ b/testbot/lib/WineTestBot/VMs.pm
@@ -151,6 +151,7 @@ use ObjectModel::BackEnd;
use WineTestBot::Config;
use WineTestBot::Engine::Notify;
use WineTestBot::LibvirtDomain;
+use WineTestBot::Missions;
use WineTestBot::RecordGroups;
use WineTestBot::TestAgent;
@@ -302,6 +303,24 @@ sub KillChild($)
$self->ChildPid(undef);
}
+sub PutColValue($$$)
+{
+ my ($self, $ColName, $Value) = @_;
+
+ $self->SUPER::PutColValue($ColName, $Value);
+ if ($self->{IsModified} and ($ColName eq "Type" or $ColName eq "Missions"))
+ {
+ $self->{ValidateMissions} = 1;
+ }
+}
+
+my $_SupportedMissions = {
+ "build" => { "build" => 1 },
+ "win32" => { "exe32" => 1 },
+ "win64" => { "exe32" => 1, "exe64" => 1 },
+ "wine" => { "win32" => 1, "wow32" => 1, "wow64" => 1 },
+};
+
sub Validate($)
{
my ($self) = @_;
@@ -311,6 +330,27 @@ sub Validate($)
{
return ("Role", "Only win32, win64 and wine VMs can have a role of '" . $self->Role . "'");
}
+ if ($self->{ValidateMissions})
+ {
+ my ($ErrMessage, $Missions) = ParseMissionStatement($self->Missions);
+ return ("Missions", $ErrMessage) if (defined $ErrMessage);
+ foreach my $TaskMissions (@$Missions)
+ {
+ if ($self->Type ne "wine" and @{$TaskMissions->{Missions}} > 1)
+ {
+ return ("Missions", "Only wine VMs can handle more than one mission per task");
+ }
+ foreach my $Mission (@{$TaskMissions->{Missions}})
+ {
+ if (!$_SupportedMissions->{$self->Type}->{$Mission->{Build}})
+ {
+ return ("Missions", ucfirst($self->Type) ." VMs only support ". join(", ", sort keys %{$_SupportedMissions->{$self->Type}}) ." missions");
+ }
+ }
+ }
+ delete $self->{ValidateMissions};
+ }
+
return $self->SUPER::Validate();
}
@@ -666,6 +706,7 @@ my @PropertyDescriptors = (
CreateBasicPropertyDescriptor("SortOrder", "Display order", !1, 1, "N", 3),
CreateEnumPropertyDescriptor("Type", "Type of VM", !1, 1, ['win32', 'win64', 'build', 'wine']),
CreateEnumPropertyDescriptor("Role", "VM Role", !1, 1, ['extra', 'base', 'winetest', 'retired', 'deleted']),
+ CreateBasicPropertyDescriptor("Missions", "Missions", !1, 1, "A", 256),
CreateEnumPropertyDescriptor("Status", "Current status", !1, 1, ['dirty', 'reverting', 'sleeping', 'idle', 'running', 'off', 'offline', 'maintenance']),
CreateBasicPropertyDescriptor("Errors", "Errors", !1, !1, "N", 2),
CreateBasicPropertyDescriptor("ChildPid", "Child process id", !1, !1, "N", 5),
diff --git a/testbot/web/JobDetails.pl b/testbot/web/JobDetails.pl
index 8efe5eefcf..de2f9ccb40 100644
--- a/testbot/web/JobDetails.pl
+++ b/testbot/web/JobDetails.pl
@@ -398,6 +398,7 @@ EOF
print "<details><summary>",
$self->CGI->escapeHTML($VM->Description || $VM->Name), "</summary>",
$self->CGI->escapeHTML($VM->Details || "No details!"),
+ ($StepTask->Missions ? "<br>Missions: ". $StepTask->Missions : ""),
"</details>\n";
my $MoreInfo = $self->{More}->{$Key};
diff --git a/testbot/web/Submit.pl b/testbot/web/Submit.pl
index c10a267909..bcb5458ad5 100644
--- a/testbot/web/Submit.pl
+++ b/testbot/web/Submit.pl
@@ -36,6 +36,7 @@ use WineTestBot::Branches;
use WineTestBot::Config;
use WineTestBot::Engine::Notify;
use WineTestBot::Jobs;
+use WineTestBot::Missions;
use WineTestBot::PatchUtils;
use WineTestBot::Steps;
use WineTestBot::Utils;
@@ -814,13 +815,18 @@ sub SubmitJob($$$)
my $Task = $BuildStep->Tasks->Add();
$Task->VM($BuildVM);
- my $Builds = { "exe32" => 1 };
- $Builds->{"exe64"} = 1 if defined $self->GetParam("Run64");
- $Task->Timeout(GetBuildTimeout($Impacts, $Builds));
+ my $MissionStatement = "exe32";
+ $MissionStatement .= ":exe64" if (defined $self->GetParam("Run64"));
+ my ($ErrMessage, $Missions) = ParseMissionStatement($MissionStatement);
+ if (!defined $ErrMessage)
+ {
+ $Task->Timeout(GetBuildTimeout($Impacts, $Missions->[0]));
+ $Task->Missions($Missions->[0]);
- # Save the build step so the others can reference it
- my ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
- if (defined($ErrMessage))
+ # Save the build step so the others can reference it
+ (my $ErrKey, my $ErrProperty, $ErrMessage) = $Jobs->Save();
+ }
+ if (defined $ErrMessage)
{
$self->{ErrMessage} = $ErrMessage;
return !1;
@@ -851,51 +857,53 @@ sub SubmitJob($$$)
my $Task = $Tasks->Add();
$Task->VM($VM);
$Task->Timeout($SingleTimeout);
+ $Task->Missions("exe$Bits");
$Task->CmdLineArg($self->GetParam("CmdLineArg"));
}
}
if ($FileType eq "patch")
{
- my $Tasks;
+ my ($Tasks, $MissionStatement, $Timeout);
my $VMs = CreateVMs();
$VMs->AddFilter("Type", ["wine"]);
my $SortedKeys = $VMs->SortKeysBySortOrder($VMs->GetKeys());
- foreach my $Build ("win32", "wow64")
+ foreach my $VMKey (@$SortedKeys)
{
- next if ($Build eq "wow64" and !defined($self->GetParam("Run64")));
+ my $VM = $VMs->GetItem($VMKey);
+ my $FieldName = "vm_" . $self->CGI->escapeHTML($VMKey);
+ next if (!$self->GetParam($FieldName)); # skip unselected VMs
- my $Timeout;
- foreach my $VMKey (@$SortedKeys)
+ if (!$Tasks)
{
- my $VM = $VMs->GetItem($VMKey);
- my $FieldName = "vm_" . $self->CGI->escapeHTML($VMKey);
- next if (!$self->GetParam($FieldName)); # skip unselected VMs
-
- if (!$Tasks)
+ # First create the Wine test step
+ my $WineStep = $Steps->Add();
+ $WineStep->FileName($BaseName);
+ $WineStep->FileType($FileType);
+ $WineStep->Type("single");
+ $WineStep->DebugLevel($self->GetParam("DebugLevel"));
+ $WineStep->ReportSuccessfulTests(defined($self->GetParam("ReportSuccessfulTests")));
+ $Tasks = $WineStep->Tasks;
+
+ $MissionStatement = "win32";
+ $MissionStatement.= ":wow64" if (defined $self->GetParam("Run64"));
+
+ my ($ErrMessage, $Missions) = ParseMissionStatement($MissionStatement);
+ if (defined $ErrMessage)
{
- # First create the Wine test step
- my $WineStep = $Steps->Add();
- $WineStep->FileName($BaseName);
- $WineStep->FileType($FileType);
- $WineStep->Type("single");
- $WineStep->DebugLevel($self->GetParam("DebugLevel"));
- $WineStep->ReportSuccessfulTests(defined($self->GetParam("ReportSuccessfulTests")));
- $Tasks = $WineStep->Tasks;
+ $self->{ErrMessage} = $ErrMessage;
+ return !1;
}
- if (!defined $Timeout)
- {
- my $Builds = { $Build => 1 };
- $Timeout = GetBuildTimeout($Impacts, $Builds) +
- GetTestTimeout($Impacts, $Builds);
- }
-
- # Then add a task for this VM
- my $Task = $Tasks->Add();
- $Task->VM($VM);
- $Task->CmdLineArg($Build);
- $Task->Timeout($Timeout);
+ $Missions = $Missions->[0];
+ $Timeout = GetBuildTimeout($Impacts, $Missions) +
+ GetTestTimeout($Impacts, $Missions);
}
+
+ # Then add a task for this VM
+ my $Task = $Tasks->Add();
+ $Task->VM($VM);
+ $Task->Timeout($Timeout);
+ $Task->Missions($MissionStatement);
}
}
diff --git a/testbot/web/admin/VMDetails.pl b/testbot/web/admin/VMDetails.pl
index 9603e346a8..5f49c684d8 100644
--- a/testbot/web/admin/VMDetails.pl
+++ b/testbot/web/admin/VMDetails.pl
@@ -67,6 +67,22 @@ sub Save($)
return ! defined($self->{ErrMessage});
}
+sub GenerateFooter($)
+{
+ my ($self) = @_;
+ print "<p></p><div class='CollectionBlock'><table>\n";
+ print "<thead><tr><th class='Record'>Legend</th></tr></thead>\n";
+ print "<tbody><tr><td class='Record'>\n";
+
+ print "<p>The Missions syntax is <i>mission1:mission2:...|mission3|...</i> where <i>mission1</i> and <i>mission2</i> will be run in the same task, and <i>mission3</i> in a separate task.<br>\n";
+ print "Each mission is composed of a build and options separated by commas: <i>build,option1=value,option2,...</i>. The value can be omitted for boolean options and defaults to true.<br>\n";
+ print "The supported builds are <i>build</i> for build VMs; <i>exe32</i> and <i>exe64</i> for Windows VMs;<i> win32</i>, <i>wow32</i> and <i>wow64</i> for Wine VMs.</p>\n";
+ print "<p>On Wine VMs:<br>\n";
+ print "If set, the <i>nosubmit</i> option specifies that the WineTest results should not be published online.</p>\n";
+ print "</td></tr></tbody>\n";
+ print "</table></div>\n";
+ $self->SUPER::GenerateFooter();
+}
package main;
--
2.19.1