Signed-off-by: Francois Gouget <fgouget(a)codeweavers.com>
---
The new submit page works well for me (and I find it much better) but
it's a significant rewrite. So should it not be usable for whatever
reason, this one will provide an alternative path to submit jobs until
the new page is fixed.
testbot/lib/WineTestBot/CGI/PageBase.pm | 2 +-
testbot/web/OldSubmit.pl | 1043 +++++++++++++++++++++++
2 files changed, 1044 insertions(+), 1 deletion(-)
create mode 100644 testbot/web/OldSubmit.pl
diff --git a/testbot/lib/WineTestBot/CGI/PageBase.pm b/testbot/lib/WineTestBot/CGI/PageBase.pm
index e3d2e04d5..0e6e25007 100644
--- a/testbot/lib/WineTestBot/CGI/PageBase.pm
+++ b/testbot/lib/WineTestBot/CGI/PageBase.pm
@@ -258,7 +258,7 @@ EOF
if ($self->SessionActive())
{
print " <li class='divider'> </li>\n";
- print " <li><p><a href='", MakeSecureURL("/Submit.pl"),
+ print " <li><p><a href='", MakeSecureURL("/OldSubmit.pl"),
"'>Submit job</a></p></li>\n";
print " <li><p><a href='/Activity.pl'>Activity</a></p></li>\n";
print " <li><p><a href='/Stats.pl'>Statistics</a></p></li>\n";
diff --git a/testbot/web/OldSubmit.pl b/testbot/web/OldSubmit.pl
new file mode 100644
index 000000000..e0ccda3a3
--- /dev/null
+++ b/testbot/web/OldSubmit.pl
@@ -0,0 +1,1043 @@
+# -*- Mode: Perl; perl-indent-level: 2; indent-tabs-mode: nil -*-
+# WineTestBot job submit page
+#
+# Copyright 2009 Ge van Geldorp
+# Copyright 2012-2014, 2017-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 SubmitPage;
+
+use ObjectModel::CGI::FreeFormPage;
+our @ISA = qw(ObjectModel::CGI::FreeFormPage);
+
+use CGI qw(:standard);
+use Fcntl; # For O_XXX
+use IO::Handle;
+use POSIX qw(:fcntl_h); # For SEEK_XXX
+use File::Basename;
+
+use ObjectModel::BasicPropertyDescriptor;
+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;
+use WineTestBot::VMs;
+
+
+sub _initialize($$$)
+{
+ my ($self, $Request, $RequiredRole) = @_;
+
+ $self->{Page} = $self->GetParam("Page") || 1;
+ # Page is a hidden parameter so fix it instead of issuing an error
+ $self->{Page} = 1 if ($self->{Page} !~ /^[1-4]$/);
+ $self->{LastPage} = $self->{Page};
+
+ my @PropertyDescriptors1 = (
+ CreateBasicPropertyDescriptor("Remarks", "Remarks", !1, !1, "A", 128),
+ );
+ $self->{PropertyDescriptors1} = \@PropertyDescriptors1;
+
+ my @PropertyDescriptors3 = (
+ CreateBasicPropertyDescriptor("TestExecutable", "Test executable", !1, 1, "A", 50),
+ CreateBasicPropertyDescriptor("CmdLineArg", "Command line arguments", !1, !1, "A", 50),
+ CreateBasicPropertyDescriptor("Run64", "Run 64-bit tests in addition to 32-bit tests", !1, 1, "B", 1),
+ CreateBasicPropertyDescriptor("DebugLevel", "Debug level (WINETEST_DEBUG)", !1, 1, "N", 2),
+ CreateBasicPropertyDescriptor("ReportSuccessfulTests", "Report successful tests (WINETEST_REPORT_SUCCESS)", !1, 1, "B", 1),
+ );
+ $self->{PropertyDescriptors3} = \@PropertyDescriptors3;
+
+ if ($self->{Page} == 2)
+ {
+ $self->{ShowAll} = defined($self->GetParam("ShowAll"));
+ }
+
+ $self->SUPER::_initialize($Request, $RequiredRole, undef);
+}
+
+sub GetTitle($)
+{
+ #my ($self) = @_;
+ return "Submit a job";
+}
+
+sub GetHeaderText($)
+{
+ my ($self) = @_;
+
+ if ($self->{Page} == 1)
+ {
+ return "Specify the patch file that you want to upload and submit " .
+ "for testing.<br>\n" .
+ "You can also specify a Windows .exe file, this would normally be " .
+ "a Wine test executable that you cross-compiled."
+ }
+ elsif ($self->{Page} == 2)
+ {
+ my $HeaderText = "Select the VMs on which you want to run your test.";
+ my $VMs = CreateVMs();
+ $VMs->AddFilter("Status", ["offline", "maintenance"]);
+ if (!$VMs->IsEmpty())
+ {
+ $HeaderText .= "<br>NOTE: Offline VMs and those undergoing maintenance will not be able to run your tests right away.";
+ }
+ return $HeaderText;
+ }
+ elsif ($self->{Page} == 4)
+ {
+ return "Your job was successfully queued, but the job engine that takes " .
+ "care of actually running it seems to be unavailable (perhaps it " .
+ "crashed). Your job will remain queued until the engine is " .
+ "restarted.";
+ }
+
+ return "";
+}
+
+sub GetPropertyDescriptors($)
+{
+ my ($self) = @_;
+
+ if ($self->{Page} == 1)
+ {
+ return $self->{PropertyDescriptors1};
+ }
+ elsif ($self->{Page} == 3)
+ {
+ my $IsPatch = ($self->GetParam("FileType") eq "patch");
+ $self->{PropertyDescriptors3}[0]->{IsRequired} = $IsPatch;
+ return $self->{PropertyDescriptors3};
+ }
+
+ return $self->SUPER::GetPropertyDescriptors();
+}
+
+sub GenerateFields($)
+{
+ my ($self) = @_;
+
+ print "<div><input type='hidden' name='Page' value='$self->{Page}'></div>\n";
+ if ($self->{Page} == 1)
+ {
+ print "<div class='ItemProperty'><label>File</label>",
+ "<div class='ItemValue'>",
+ "<input type='file' name='File' size='64' maxlength='64' />",
+ " <span class='Required'>*</span></div></div>\n";
+ my $Branches = CreateBranches();
+ my $SelectedBranchKey = $self->GetParam("Branch");
+ if (! defined($SelectedBranchKey))
+ {
+ $SelectedBranchKey = $Branches->GetDefaultBranch()->GetKey();
+ }
+ if (! $Branches->MultipleBranchesPresent())
+ {
+ print "<div><input type='hidden' name='Branch' value='",
+ $self->CGI->escapeHTML($SelectedBranchKey),
+ "'></div>\n";
+ }
+ else
+ {
+ print "<div class='ItemProperty'><label>Branch</label>",
+ "<div class='ItemValue'>",
+ "<select name='Branch' size='1'>";
+ my @SortedKeys = sort { $a cmp $b } @{$Branches->GetKeys()};
+ foreach my $Key (@SortedKeys)
+ {
+ my $Branch = $Branches->GetItem($Key);
+ print "<option value='", $self->CGI->escapeHTML($Key), "'";
+ if ($Key eq $SelectedBranchKey)
+ {
+ print " selected";
+ }
+ print ">", $self->CGI->escapeHTML($Branch->Name), "</option>";
+ }
+ print "</select>",
+ " <span class='Required'>*</span></div></div>\n";
+ }
+
+ $self->{HasRequired} = 1;
+ }
+ else
+ {
+ if (! defined($self->{FileName}))
+ {
+ $self->{FileName} = $self->GetParam("FileName");
+ }
+ if (! defined($self->{FileType}))
+ {
+ $self->{FileType} = $self->GetParam("FileType");
+ }
+ if (! defined($self->{TestExecutable}))
+ {
+ $self->{TestExecutable} = $self->GetParam("TestExecutable");
+ }
+ if (! defined($self->{CmdLineArg}))
+ {
+ $self->{CmdLineArg} = $self->GetParam("CmdLineArg");
+ }
+ print "<div><input type='hidden' name='Remarks' value='",
+ $self->CGI->escapeHTML($self->GetParam("Remarks")), "'></div>\n";
+ print "<div><input type='hidden' name='FileName' value='",
+ $self->CGI->escapeHTML($self->{FileName}), "'></div>\n";
+ print "<div><input type='hidden' name='FileType' value='",
+ $self->CGI->escapeHTML($self->{FileType}), "'></div>\n";
+ print "<div><input type='hidden' name='Branch' value='",
+ $self->CGI->escapeHTML($self->GetParam("Branch")), "'></div>\n";
+ if ($self->{Page} != 3)
+ {
+ if (defined($self->{TestExecutable}))
+ {
+ print "<div><input type='hidden' name='TestExecutable' value='",
+ $self->CGI->escapeHTML($self->{TestExecutable}), "'></div>\n";
+ }
+ if (defined($self->{CmdLineArg}))
+ {
+ print "<div><input type='hidden' name='CmdLineArg' value='",
+ $self->CGI->escapeHTML($self->{CmdLineArg}), "'></div>\n";
+ }
+ }
+ if ($self->{Page} == 2)
+ {
+ if ($self->{LastPage} == 3)
+ {
+ my $VMs = CreateVMs();
+ # VMs that are only visible with ShowAll
+ $VMs->AddFilter("Role", ["winetest", "extra"]);
+ foreach my $VMKey (@{$VMs->GetKeys()})
+ {
+ my $FieldName = "vm_" . $self->CGI->escapeHTML($VMKey);
+ if (defined $self->GetParam($FieldName))
+ {
+ $self->{ShowAll} = 1;
+ last;
+ }
+ }
+ }
+ if ($self->{ShowAll})
+ {
+ print "<div><input type='hidden' name='ShowAll' value='1'></div>\n";
+ }
+ print "<div class='CollectionBlock'><table>\n";
+ print "<thead><tr><th class='Record'></th>\n";
+ print "<th class='Record'>VM Name</th>\n";
+ print "<th class='Record'>Description</th>\n";
+ print "</thead><tbody>\n";
+
+ my $VMs = CreateVMs();
+ if ($self->{FileType} eq "exe64")
+ {
+ $VMs->AddFilter("Type", ["win64", "wine"]);
+ }
+ else
+ {
+ $VMs->AddFilter("Type", ["win32", "win64", "wine"]);
+ }
+ if ($self->{ShowAll})
+ {
+ # All but the retired and deleted ones
+ $VMs->AddFilter("Role", ["base", "winetest", "extra"]);
+ }
+ else
+ {
+ $VMs->AddFilter("Role", ["base"]);
+ }
+ my $Even = 1;
+ my $SortedKeys = $VMs->SortKeysBySortOrder($VMs->GetKeys());
+ foreach my $VMKey (@$SortedKeys)
+ {
+ my $VM = $VMs->GetItem($VMKey);
+ my $FieldName = "vm_" . $self->CGI->escapeHTML($VMKey);
+ print "<tr class='", ($Even ? "even" : "odd"),
+ "'><td><input name='$FieldName' type='checkbox'";
+ $Even = !$Even;
+ my ($Checked, $Status) = (1, "");
+ if ($VM->Status =~ /^(offline|maintenance)$/)
+ {
+ $Status = " [". $VM->Status ."]";
+ $Checked = undef;
+ }
+ if ($Checked and
+ ($self->{LastPage} == 1 || $self->GetParam($FieldName)))
+ {
+ print " checked='checked'";
+ }
+ print "/></td>\n";
+
+ print "<td>", $self->CGI->escapeHTML($VM->Name), "</td>\n";
+ print "<td><details><summary>",
+ $self->CGI->escapeHTML($VM->Description || $VM->Name),
+ "$Status</summary>",
+ $self->CGI->escapeHTML($VM->Details || "No details!"),
+ "</details></td>";
+ print "</tr>\n";
+ }
+ print "</tbody></table>\n";
+ print "</div><!--CollectionBlock-->\n";
+ }
+ else
+ {
+ if (defined($self->{NoCmdLineArgWarn}))
+ {
+ print "<div><input type='hidden' name='NoCmdLineArgWarn' value='on'>",
+ "</div>\n";
+ }
+ my $VMs = CreateVMs();
+ foreach my $VMKey (@{$VMs->GetKeys()})
+ {
+ my $FieldName = "vm_" . $self->CGI->escapeHTML($VMKey);
+ if ($self->GetParam($FieldName))
+ {
+ print "<div><input type='hidden' name='$FieldName' value='on'>",
+ "</div>\n";
+ }
+ }
+ }
+ }
+ if ($self->{Page} == 4)
+ {
+ if ($self->GetParam("JobKey"))
+ {
+ $self->{JobKey} = $self->GetParam("JobKey");
+ }
+ print "<div><input type='hidden' name='JobKey' value='", $self->{JobKey},
+ "'></div>\n";
+ }
+
+ $self->SUPER::GenerateFields();
+}
+
+sub GenerateActions($)
+{
+ my ($self) = @_;
+
+ if ($self->{Page} == 2)
+ {
+ print <<EOF;
+<script type='text/javascript'>
+<!--
+function ToggleAll()
+{
+ for (var i = 0; i < document.forms[0].elements.length; i++)
+ {
+ if(document.forms[0].elements[i].type == 'checkbox')
+ document.forms[0].elements[i].checked = !(document.forms[0].elements[i].checked);
+ }
+}
+
+// Only put javascript link in document if javascript is enabled
+document.write("<div class='ItemActions'><a href='javascript:void(0)' onClick='ToggleAll();'>Toggle All<\\\/a><\\\/div>");
+//-->
+</script>
+EOF
+
+ print "<div class='ItemActions'>\n";
+ print "<input type='submit' name='Action' value='",
+ $self->{ShowAll} ? "Show base VMs" : "Show all VMs", "'/>\n";
+ print "</div>\n";
+ }
+
+ $self->SUPER::GenerateActions();
+}
+
+sub GetActions($)
+{
+ my ($self) = @_;
+
+ my $Actions = $self->SUPER::GetActions();
+ if ($self->{Page} == 1)
+ {
+ push @$Actions, "Next >";
+ }
+ elsif ($self->{Page} == 2)
+ {
+ push @$Actions, "< Prev", "Next >";
+ }
+ elsif ($self->{Page} == 3)
+ {
+ push @$Actions, "< Prev", "Submit";
+ }
+ elsif ($self->{Page} == 4)
+ {
+ push @$Actions, "OK";
+ }
+
+ return $Actions;
+}
+
+sub DisplayProperty($$)
+{
+ my ($self, $PropertyDescriptor) = @_;
+
+ if ($self->{Page} == 3)
+ {
+ my $PropertyName = $PropertyDescriptor->GetName();
+ if ($self->GetParam("FileType") eq "patch")
+ {
+ if ($PropertyName eq "Run64")
+ {
+ my $Show64 = !1;
+ my $VMs = CreateVMs();
+ $VMs->AddFilter("Type", ["win64"]);
+ foreach my $VMKey (@{$VMs->GetKeys()})
+ {
+ my $FieldName = "vm_" . $self->CGI->escapeHTML($VMKey);
+ if ($self->GetParam($FieldName))
+ {
+ $Show64 = 1;
+ last;
+ }
+ }
+ if (! $Show64)
+ {
+ return "";
+ }
+ }
+ }
+ else
+ {
+ if ($PropertyName eq "TestExecutable" || $PropertyName eq "Run64")
+ {
+ return "";
+ }
+ }
+ }
+
+ return $self->SUPER::DisplayProperty($PropertyDescriptor);
+}
+
+sub GetPropertyValue($$)
+{
+ my ($self, $PropertyDescriptor) = @_;
+
+ if ($self->{Page} == 3)
+ {
+ my $PropertyName = $PropertyDescriptor->GetName();
+ if ($PropertyName eq "DebugLevel")
+ {
+ return 1;
+ }
+ if ($PropertyName eq "Run64")
+ {
+ return 1;
+ }
+ }
+
+ return $self->SUPER::GetPropertyValue($PropertyDescriptor);
+}
+
+sub GetTmpStagingFullPath($$)
+{
+ my ($self, $FileName) = @_;
+
+ return undef if (!$FileName);
+ return "$DataDir/staging/" . $self->GetCurrentSession()->Id . "-websubmit_$FileName";
+}
+
+sub Validate($)
+{
+ my ($self) = @_;
+
+ if ($self->{Page} == 2)
+ {
+ my $VMSelected = !1;
+ my $VMs = CreateVMs();
+ foreach my $VMKey (@{$VMs->GetKeys()})
+ {
+ my $FieldName = "vm_" . $self->CGI->escapeHTML($VMKey);
+ if ($self->GetParam($FieldName))
+ {
+ $VMSelected = 1;
+ last;
+ }
+ }
+
+ if (! $VMSelected)
+ {
+ $self->{ErrMessage} = "Select at least one VM";
+ return !1;
+ }
+ }
+ elsif ($self->{Page} == 3)
+ {
+ if (($self->GetParam("FileType") eq "patch" &&
+ $self->GetParam("TestExecutable") !~ m/^[\w_.]+_test\.exe$/) ||
+ !IsValidFileName($self->GetParam("TestExecutable")))
+ {
+ $self->{ErrField} = "TestExecutable";
+ $self->{ErrMessage} = "Invalid test executable filename";
+ return !1;
+ }
+
+ if ($self->GetParam("NoCmdLineArgWarn"))
+ {
+ $self->{NoCmdLineArgWarn} = 1;
+ }
+ elsif (! $self->GetParam("CmdLineArg"))
+ {
+ $self->{ErrMessage} = "You didn't specify a command line argument. " .
+ "This is most likely not correct, so please " .
+ "fix this. If you're sure that you really don't " .
+ 'want a command line argument, press "Submit" ' .
+ "again.";
+ $self->{ErrField} = "CmdLineArg";
+ $self->{NoCmdLineArgWarn} = 1;
+ return !1;
+ }
+ }
+
+ return $self->SUPER::Validate();
+}
+
+sub ValidateAndGetFileName($$)
+{
+ my ($self, $FieldName) = @_;
+
+ my $FileName = $self->GetParam($FieldName);
+ if (!$FileName)
+ {
+ $self->{ErrField} = $FieldName;
+ $self->{ErrMessage} = "You must provide a file to test";
+ return undef;
+ }
+ if (!IsValidFileName($FileName))
+ {
+ $self->{ErrField} = $FieldName;
+ $self->{ErrMessage} = "The filename contains invalid characters";
+ return undef;
+ }
+ my $PropertyDescriptor = CreateSteps()->GetPropertyDescriptorByName("FileName");
+ if ($PropertyDescriptor->GetMaxLength() - 32 - 1 < length($FileName))
+ {
+ $self->{ErrField} = $FieldName;
+ $self->{ErrMessage} = "The filename is too long";
+ return undef;
+ }
+ return $FileName;
+}
+
+sub DetermineFileType($$)
+{
+ my ($self, $FileName) = @_;
+
+ if (! sysopen(FH, $FileName, O_RDONLY))
+ {
+ return ("Unable to open $FileName", "unknown", undef, undef);
+ }
+
+ my $FileType = "unknown";
+ my $Buffer;
+ if (sysread(FH, $Buffer, 0x40))
+ {
+ # Unpack IMAGE_DOS_HEADER
+ my @Fields = unpack "S30I", $Buffer;
+ if ($Fields[0] == 0x5a4d)
+ {
+ seek FH, $Fields[30], SEEK_SET;
+ if (sysread(FH, $Buffer, 0x18))
+ {
+ @Fields = unpack "IS2I3S2", $Buffer;
+ if ($Fields[0] == 0x00004550)
+ {
+ if (($Fields[7] & 0x2000) == 0)
+ {
+ $FileType = "exe";
+ }
+ else
+ {
+ $FileType = "dll";
+ }
+ if ($Fields[1] == 0x014c)
+ {
+ $FileType .= "32";
+ }
+ elsif ($Fields[1] == 0x8664)
+ {
+ $FileType .= "64";
+ }
+ else
+ {
+ $FileType = "unknown";
+ }
+ }
+ }
+ }
+ # zip files start with PK, 0x03, 0x04
+ elsif ($Fields[0] == 0x4b50 && $Fields[1] == 0x0403)
+ {
+ $FileType = "zip";
+ }
+ }
+
+ close FH;
+
+ my ($ErrMessage, $ExeBase, $TestUnit);
+ if ($FileType eq "unknown")
+ {
+ my $Impacts = GetPatchImpacts($FileName);
+ if ($Impacts->{TestUnitCount} == 0)
+ {
+ $ErrMessage = "Patch doesn't affect tests";
+ }
+ elsif ($Impacts->{TestUnitCount} > 1)
+ {
+ $ErrMessage = "Patch contains changes to multiple tests";
+ }
+ else
+ {
+ foreach my $TestInfo (values %{$Impacts->{Tests}})
+ {
+ if ($TestInfo->{UnitCount})
+ {
+ $FileType = "patch";
+ $ExeBase = $TestInfo->{ExeBase};
+ $TestUnit = (keys %{$TestInfo->{PatchedUnits}})[0];
+ last;
+ }
+ }
+ }
+ }
+ elsif ($FileType eq "dll32" || $FileType eq "dll64" || $FileType eq "zip")
+ {
+ # We know what these are but not what to do with them. So reject them early.
+ $FileType = "unknown";
+ }
+
+ return ($ErrMessage, $FileType, $ExeBase, $TestUnit);
+}
+
+sub OnPage1Next($)
+{
+ my ($self) = @_;
+
+ my $BaseName = $self->ValidateAndGetFileName("File");
+ return !1 if (!$BaseName);
+
+ my $Fh = $self->CGI->upload("File");
+ if (defined($Fh))
+ {
+ my $StagingFile = $self->GetTmpStagingFullPath($BaseName);
+ my $OldUMask = umask(002);
+ if (! open (OUTFILE,">$StagingFile"))
+ {
+ umask($OldUMask);
+ $self->{ErrField} = "File";
+ $self->{ErrMessage} = "Unable to process uploaded file";
+ return !1;
+ }
+ umask($OldUMask);
+ my $Buffer;
+ while (sysread($Fh, $Buffer, 4096))
+ {
+ print OUTFILE $Buffer;
+ }
+ close OUTFILE;
+
+ my ($ErrMessage, $FileType, $ExeBase, $TestUnit) = $self->DetermineFileType($StagingFile);
+ if (defined($ErrMessage))
+ {
+ $self->{ErrField} = "File";
+ $self->{ErrMessage} = $ErrMessage;
+ return !1;
+ }
+ if ($FileType !~ /^(?:exe32|exe64|patch)$/)
+ {
+ $self->{ErrField} = "File";
+ $self->{ErrMessage} = "Unrecognized file type";
+ return !1;
+ }
+
+ $self->{FileName} = $BaseName;
+ $self->{FileType} = $FileType;
+ if (defined $ExeBase)
+ {
+ $self->{TestExecutable} = "$ExeBase.exe";
+ }
+ if (defined($TestUnit))
+ {
+ $self->{CmdLineArg} = $TestUnit;
+ }
+ }
+ else
+ {
+ $self->{ErrField} = "File";
+ $self->{ErrMessage} = "File upload failed";
+ return !1;
+ }
+
+ if (! $self->Validate)
+ {
+ return !1;
+ }
+
+ $self->{Page} = 2;
+
+ return 1;
+}
+
+sub OnPage2Next($)
+{
+ my ($self) = @_;
+
+ if (! $self->Validate)
+ {
+ return !1;
+ }
+
+ $self->{Page} = 3;
+
+ return 1;
+}
+
+sub OnPage2Prev($)
+{
+ my ($self) = @_;
+
+ my $FileName = $self->GetParam("FileName");
+ if ($FileName)
+ {
+ my $StagingFileName = $self->GetTmpStagingFullPath(basename($FileName));
+ unlink($StagingFileName) if ($StagingFileName);
+ }
+
+ $self->{Page} = 1;
+
+ return 1;
+}
+
+sub OnPage3Prev($)
+{
+ my ($self) = @_;
+
+ $self->{Page} = 2;
+
+ return 1;
+}
+
+
+sub SubmitJob($$$)
+{
+ my ($self, $BaseName, $Staging) = @_;
+
+ # See also Patches::Submit() in lib/WineTestBot/Patches.pm
+
+ # First create a new job
+ my $Jobs = CreateJobs();
+ my $NewJob = $Jobs->Add();
+ $NewJob->User($self->GetCurrentSession()->User);
+ $NewJob->Priority(5);
+ if ($self->GetParam("Remarks"))
+ {
+ $NewJob->Remarks($self->GetParam("Remarks"));
+ }
+ else
+ {
+ $NewJob->Remarks($self->GetParam("CmdLineArg"));
+ }
+ my $Branch = CreateBranches()->GetItem($self->GetParam("Branch"));
+ if (defined($Branch))
+ {
+ $NewJob->Branch($Branch);
+ }
+ my $Steps = $NewJob->Steps;
+
+ # Add steps and tasks for the 32 and 64-bit tests
+ my $FileType = $self->GetParam("FileType");
+ my $Impacts;
+ $Impacts = GetPatchImpacts($Staging) if ($FileType eq "patch");
+
+ my $BuildStep;
+ foreach my $Bits ("32", "64")
+ {
+ next if ($Bits eq "32" && $FileType eq "exe64");
+ next if ($Bits eq "64" && $FileType eq "exe32");
+ next if ($Bits eq "64" && $FileType eq "patch" && !defined($self->GetParam("Run64")));
+
+ my $Tasks;
+ my $VMs = CreateVMs();
+ $VMs->AddFilter("Type", $Bits eq "32" ? ["win32", "win64"] : ["win64"]);
+ my $SortedKeys = $VMs->SortKeysBySortOrder($VMs->GetKeys());
+ foreach my $VMKey (@$SortedKeys)
+ {
+ my $VM = $VMs->GetItem($VMKey);
+ my $FieldName = "vm_" . $self->CGI->escapeHTML($VMKey);
+ next if (!$self->GetParam($FieldName)); # skip unselected VMs
+
+ if (!$Tasks)
+ {
+ if (!$BuildStep and $FileType eq "patch")
+ {
+ # This is a patch so add a build step...
+ $BuildStep = $Steps->Add();
+ $BuildStep->FileName($BaseName);
+ $BuildStep->FileType($FileType);
+ $BuildStep->Type("build");
+ $BuildStep->DebugLevel(0);
+
+ # ...with a build task
+ my $VMs = CreateVMs();
+ $VMs->AddFilter("Type", ["build"]);
+ $VMs->AddFilter("Role", ["base"]);
+ my $BuildVM = ${$VMs->GetItems()}[0];
+ my $Task = $BuildStep->Tasks->Add();
+ $Task->VM($BuildVM);
+
+ 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($MissionStatement);
+
+ # 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;
+ }
+ }
+
+ # Then create the test step
+ my $TestStep = $Steps->Add();
+ if ($FileType eq "patch")
+ {
+ $TestStep->PreviousNo($BuildStep->No);
+ my $TestExe = basename($self->GetParam("TestExecutable"));
+ $TestExe =~ s/_test\.exe$/_test64.exe/ if ($Bits eq "64");
+ $TestStep->FileName($TestExe);
+ }
+ else
+ {
+ $TestStep->FileName($BaseName);
+ }
+ $TestStep->FileType("exe$Bits");
+ $TestStep->Type("single");
+ $TestStep->DebugLevel($self->GetParam("DebugLevel"));
+ $TestStep->ReportSuccessfulTests(defined($self->GetParam("ReportSuccessfulTests")));
+ $Tasks = $TestStep->Tasks;
+ }
+
+ # Then add a task for this VM
+ my $Task = $Tasks->Add();
+ $Task->VM($VM);
+ $Task->Timeout($SingleTimeout);
+ $Task->Missions("exe$Bits");
+ $Task->CmdLineArg($self->GetParam("CmdLineArg"));
+ }
+ }
+
+ my ($Tasks, $MissionStatement, $Timeout);
+ my $VMs = CreateVMs();
+ $VMs->AddFilter("Type", ["wine"]);
+ my $SortedKeys = $VMs->SortKeysBySortOrder($VMs->GetKeys());
+ foreach my $VMKey (@$SortedKeys)
+ {
+ 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 = ($FileType =~ /^(?:exe32|patch)$/) ? "win32" : "";
+ if ($FileType eq "exe64" or
+ ($FileType eq "patch" and defined $self->GetParam("Run64")))
+ {
+ $MissionStatement .= ":wow64";
+ }
+ $MissionStatement =~ s/^://;
+
+ my ($ErrMessage, $Missions) = ParseMissionStatement($MissionStatement);
+ if (defined $ErrMessage)
+ {
+ $self->{ErrMessage} = $ErrMessage;
+ return !1;
+ }
+ $Missions = $Missions->[0];
+ $Timeout = $FileType ne "patch" ?
+ $SingleTimeout :
+ 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);
+ $Task->CmdLineArg($self->GetParam("CmdLineArg")) if ($FileType ne "patch");
+ }
+
+ # Now save it all (or whatever's left to save)
+ my ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
+ if (defined($ErrMessage))
+ {
+ $self->{ErrMessage} = $ErrMessage;
+ return !1;
+ }
+
+ # Stage the test patch/executable so the job can pick it up
+ if (!rename($Staging, "$DataDir/staging/job". $NewJob->Id ."_$BaseName"))
+ {
+ $self->{ErrMessage} = "Could not stage '$BaseName': $!\n";
+ return !1;
+ }
+
+ # Switch Status to staging to indicate we are done setting up the job
+ $NewJob->Status("staging");
+ ($ErrKey, $ErrProperty, $ErrMessage) = $Jobs->Save();
+ if (defined($ErrMessage))
+ {
+ $self->{ErrMessage} = $ErrMessage;
+ return !1;
+ }
+
+ # Notify engine
+ my $ErrMessage = RescheduleJobs();
+ if (defined $ErrMessage)
+ {
+ $self->{ErrMessage} = $ErrMessage;
+ $self->{Page} = 4;
+ $self->{JobKey} = $NewJob->GetKey();
+ return !1;
+ }
+
+ $self->Redirect("/JobDetails.pl?Key=". $NewJob->GetKey()); # does not return
+ exit;
+}
+
+sub OnSubmit($)
+{
+ my ($self) = @_;
+
+ return !1 if (!$self->Validate());
+ my $BaseName = $self->ValidateAndGetFileName("FileName");
+ return !1 if (!$BaseName);
+
+ # Rename the staging file to avoid race conditions if the user clicks on
+ # Submit multiple times
+ my $OldStaging = $self->GetTmpStagingFullPath($BaseName);
+ my $Staging = CreateNewLink($OldStaging, "$DataDir/staging", $BaseName);
+ if (!defined $Staging)
+ {
+ $self->{ErrMessage} = "Could not rename '$BaseName': $!";
+ return !1;
+ }
+ if (!unlink $OldStaging)
+ {
+ unlink $Staging;
+ $self->{ErrMessage} = $!{ENOENT} ?
+ "$BaseName has already been submitted or has expired" :
+ "Could not remove the staging '$BaseName' file: $!";
+ return !1;
+ }
+
+ if (!$self->SubmitJob($BaseName, $Staging))
+ {
+ # Restore the file for the next attempt
+ rename($Staging, $OldStaging);
+ return !1;
+ }
+ return 1;
+}
+
+sub OnShowAllVMs($)
+{
+ my ($self) = @_;
+
+ $self->{ShowAll} = 1;
+
+ return !1;
+}
+
+sub OnShowBaseVMs($)
+{
+ my ($self) = @_;
+
+ $self->{ShowAll} = !1;
+
+ return !1;
+}
+
+sub OnOK($)
+{
+ my ($self) = @_;
+
+ if (defined($self->GetParam("JobKey")))
+ {
+ $self->Redirect("/JobDetails.pl?Key=" . $self->GetParam("JobKey")); # does not return
+ }
+ else
+ {
+ $self->Redirect("/index.pl"); # does not return
+ }
+}
+
+sub OnAction($$)
+{
+ my ($self, $Action) = @_;
+
+ if ($Action eq "Next >")
+ {
+ return $self->{Page} == 2 ? $self->OnPage2Next() : $self->OnPage1Next();
+ }
+ elsif ($Action eq "< Prev")
+ {
+ return $self->{Page} == 3 ? $self->OnPage3Prev() : $self->OnPage2Prev();
+ }
+ elsif ($Action eq "Submit")
+ {
+ return $self->OnSubmit();
+ }
+ elsif ($Action eq "Show base VMs")
+ {
+ return $self->OnShowBaseVMs();
+ }
+ elsif ($Action eq "Show all VMs")
+ {
+ return $self->OnShowAllVMs();
+ }
+ elsif ($Action eq "OK")
+ {
+ return $self->OnOK();
+ }
+
+ return $self->SUPER::OnAction($Action);
+}
+
+
+package main;
+
+my $Request = shift;
+
+my $SubmitPage = SubmitPage->new($Request, "wine-devel");
+$SubmitPage->GeneratePage();
--
2.20.1