[PATCH 1/2] testbot/Submit: Preserve the old Submit page before replacing it.

Francois Gouget fgouget at codeweavers.com
Thu Sep 26 09:32:41 CDT 2019


Signed-off-by: Francois Gouget <fgouget at 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




More information about the wine-devel mailing list