<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[159955] trunk/Tools</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/159955">159955</a></dd>
<dt>Author</dt> <dd>mhahnenberg@apple.com</dd>
<dt>Date</dt> <dd>2013-12-02 12:55:28 -0800 (Mon, 02 Dec 2013)</dd>
</dl>

<h3>Log Message</h3>
<pre>run-jsc-stress-tests should be able to package its tests and move them places
https://bugs.webkit.org/show_bug.cgi?id=124549

Reviewed by Filip Pizlo.

* Scripts/jsc-stress-test-helpers/check-mozilla-failure: Removed. Was just a ruby reimplementation
of grep -i -q
* Scripts/run-javascriptcore-tests: Pass through the --tarball flag.
* Scripts/run-jsc-stress-tests: Changed to create a bundle of tests inside the results directory.
We now also copy whatever VM was specified, along with its associated framework, into this directory.
All of the generated scripts now are completely relative within the results directory. This allows
run-jsc-stress-tests to execute a bundle from anywhere. Also added a --tarball flag which creates a
tarball of the generated results directory. Also refactored several portions of the script into
separate functions to make it easier to run them conditionally depending on which mode we're running in.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsScriptsrunjavascriptcoretests">trunk/Tools/Scripts/run-javascriptcore-tests</a></li>
<li><a href="#trunkToolsScriptsrunjscstresstests">trunk/Tools/Scripts/run-jsc-stress-tests</a></li>
</ul>

<h3>Removed Paths</h3>
<ul>
<li><a href="#trunkToolsScriptsjscstresstesthelperscheckmozillafailure">trunk/Tools/Scripts/jsc-stress-test-helpers/check-mozilla-failure</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (159954 => 159955)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2013-12-02 20:52:11 UTC (rev 159954)
+++ trunk/Tools/ChangeLog        2013-12-02 20:55:28 UTC (rev 159955)
</span><span class="lines">@@ -1,3 +1,20 @@
</span><ins>+2013-12-02  Mark Hahnenberg  &lt;mhahnenberg@apple.com&gt;
+
+        run-jsc-stress-tests should be able to package its tests and move them places
+        https://bugs.webkit.org/show_bug.cgi?id=124549
+
+        Reviewed by Filip Pizlo.
+
+        * Scripts/jsc-stress-test-helpers/check-mozilla-failure: Removed. Was just a ruby reimplementation
+        of grep -i -q
+        * Scripts/run-javascriptcore-tests: Pass through the --tarball flag.
+        * Scripts/run-jsc-stress-tests: Changed to create a bundle of tests inside the results directory.
+        We now also copy whatever VM was specified, along with its associated framework, into this directory.
+        All of the generated scripts now are completely relative within the results directory. This allows
+        run-jsc-stress-tests to execute a bundle from anywhere. Also added a --tarball flag which creates a
+        tarball of the generated results directory. Also refactored several portions of the script into
+        separate functions to make it easier to run them conditionally depending on which mode we're running in. 
+
</ins><span class="cx"> 2013-12-02  Andrzej Badowski  &lt;a.badowski@samsung.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [ATK] Support active state for listbox elements.
</span></span></pre></div>
<a id="trunkToolsScriptsjscstresstesthelperscheckmozillafailure"></a>
<div class="delfile"><h4>Deleted: trunk/Tools/Scripts/jsc-stress-test-helpers/check-mozilla-failure (159954 => 159955)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/jsc-stress-test-helpers/check-mozilla-failure        2013-12-02 20:52:11 UTC (rev 159954)
+++ trunk/Tools/Scripts/jsc-stress-test-helpers/check-mozilla-failure        2013-12-02 20:55:28 UTC (rev 159955)
</span><span class="lines">@@ -1,34 +0,0 @@
</span><del>-#!/usr/bin/env ruby
-
-# Copyright (C) 2013 Apple Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# 1.  Redistributions of source code must retain the above copyright
-#     notice, this list of conditions and the following disclaimer. 
-# 2.  Redistributions in binary form must reproduce the above copyright
-#     notice, this list of conditions and the following disclaimer in the
-#     documentation and/or other materials provided with the distribution. 
-#
-# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS &quot;AS IS&quot; AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-File.open(ARGV[0], &quot;r&quot;) {
-    | inp |
-    inp.each_line {
-        | line |
-        exit 0 if line =~ /failed!/i
-    }
-}
-
-exit 1
</del></span></pre></div>
<a id="trunkToolsScriptsrunjavascriptcoretests"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/run-javascriptcore-tests (159954 => 159955)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/run-javascriptcore-tests        2013-12-02 20:52:11 UTC (rev 159954)
+++ trunk/Tools/Scripts/run-javascriptcore-tests        2013-12-02 20:55:28 UTC (rev 159955)
</span><span class="lines">@@ -79,6 +79,8 @@
</span><span class="cx"> 
</span><span class="cx"> my $enableFTL = 0;
</span><span class="cx"> 
</span><ins>+my $createTarball = 0;
+
</ins><span class="cx"> my $programName = basename($0);
</span><span class="cx"> my $buildJSCDefault = $buildJSC ? &quot;will check&quot; : &quot;will not check&quot;;
</span><span class="cx"> my $testapiDefault = $runTestAPI ? &quot;will run&quot; : &quot;will not run&quot;;
</span><span class="lines">@@ -94,6 +96,7 @@
</span><span class="cx">   --[no-]build                  Check (or don't check) to see if the jsc build is up-to-date (default: $buildJSCDefault)
</span><span class="cx">   --[no-]testapi                Run (or don't run) testapi (default: $testapiDefault)
</span><span class="cx">   --[no-]jsc-stress             Run (or don't run) the JSC stress tests (default: $jscStressDefault)
</span><ins>+  --tarball                     Create a tarball of the bundle produced by running the JSC stress tests.
</ins><span class="cx"> EOF
</span><span class="cx"> 
</span><span class="cx"> GetOptions(
</span><span class="lines">@@ -104,6 +107,7 @@
</span><span class="cx">     'ftl-jit!' =&gt; \$enableFTL,
</span><span class="cx">     'testapi!' =&gt; \$runTestAPI,
</span><span class="cx">     'jsc-stress!' =&gt; \$runJSCStress,
</span><ins>+    'tarball!' =&gt; \$createTarball,
</ins><span class="cx">     'help' =&gt; \$showHelp
</span><span class="cx"> );
</span><span class="cx"> 
</span><span class="lines">@@ -239,6 +243,9 @@
</span><span class="cx">     if ($enableFTL) {
</span><span class="cx">         push(@jscStressDriverCmd, &quot;--ftl-jit&quot;);
</span><span class="cx">     }
</span><ins>+    if ($createTarball) {
+        push(@jscStressDriverCmd, &quot;--tarball&quot;);
+    }
</ins><span class="cx">     if (defined($extraTests)) {
</span><span class="cx">         push(@jscStressDriverCmd, $extraTests);
</span><span class="cx">     }
</span></span></pre></div>
<a id="trunkToolsScriptsrunjscstresstests"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/run-jsc-stress-tests (159954 => 159955)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/run-jsc-stress-tests        2013-12-02 20:52:11 UTC (rev 159954)
+++ trunk/Tools/Scripts/run-jsc-stress-tests        2013-12-02 20:55:28 UTC (rev 159955)
</span><span class="lines">@@ -23,6 +23,7 @@
</span><span class="cx"> # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
</span><span class="cx"> # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</span><span class="cx"> 
</span><ins>+require 'fileutils'
</ins><span class="cx"> require 'getoptlong'
</span><span class="cx"> require 'pathname'
</span><span class="cx"> require 'yaml'
</span><span class="lines">@@ -36,7 +37,7 @@
</span><span class="cx"> 
</span><span class="cx"> HELPERS_PATH = SCRIPTS_PATH + &quot;jsc-stress-test-helpers&quot;
</span><span class="cx"> 
</span><del>-IMPORTANT_ENVS = [&quot;JSC_timeout&quot;, &quot;DYLD_FRAMEWORK_PATH&quot;]
</del><ins>+IMPORTANT_ENVS = [&quot;JSC_timeout&quot;]
</ins><span class="cx"> 
</span><span class="cx"> begin
</span><span class="cx">     require 'shellwords'
</span><span class="lines">@@ -81,10 +82,10 @@
</span><span class="cx"> 
</span><span class="cx"> $jscPath = nil
</span><span class="cx"> $enableFTL = false
</span><del>-$collections = []
</del><span class="cx"> $outputDir = Pathname.new(&quot;results&quot;)
</span><span class="cx"> $verbosity = 0
</span><del>-$errorOnFailure = false
</del><ins>+$bundle = nil
+$tarball = false
</ins><span class="cx"> 
</span><span class="cx"> def usage
</span><span class="cx">     puts &quot;run-jsc-stress-tests -j &lt;shell path&gt; &lt;collections path&gt; [&lt;collections path&gt; ...]&quot;
</span><span class="lines">@@ -92,8 +93,9 @@
</span><span class="cx">     puts &quot;--jsc                (-j)   Path to JavaScriptCore. This option is required.&quot;
</span><span class="cx">     puts &quot;--ftl-jit                   Indicate that we have the FTL JIT.&quot;
</span><span class="cx">     puts &quot;--output-dir         (-o)   Path where to put results. Default is #{$outputDir}.&quot;
</span><del>-    puts &quot;--[no-]error-on-failure     Exit with exit code 1 if any tests fail. Default is #{$errorOnFailure}.&quot;
</del><span class="cx">     puts &quot;--verbose            (-v)   Print more things while running.&quot;
</span><ins>+    puts &quot;--run-bundle                Runs a bundle previously created by run-jsc-stress-tests.&quot;
+    puts &quot;--tarball                   Creates a tarball of the final bundle.&quot;
</ins><span class="cx">     puts &quot;--help               (-h)   Print this message.&quot;
</span><span class="cx">     exit 1
</span><span class="cx"> end
</span><span class="lines">@@ -102,9 +104,9 @@
</span><span class="cx">                ['--jsc', '-j', GetoptLong::REQUIRED_ARGUMENT],
</span><span class="cx">                ['--ftl-jit', GetoptLong::NO_ARGUMENT],
</span><span class="cx">                ['--output-dir', '-o', GetoptLong::REQUIRED_ARGUMENT],
</span><del>-               ['--verbose', '-v', GetoptLong::NO_ARGUMENT],
-               ['--error-on-failure', GetoptLong::NO_ARGUMENT],
-               ['--no-error-on-failure', GetoptLong::NO_ARGUMENT]).each {
</del><ins>+               ['--run-bundle', GetoptLong::REQUIRED_ARGUMENT],
+               ['--tarball', GetoptLong::NO_ARGUMENT],
+               ['--verbose', '-v', GetoptLong::NO_ARGUMENT]).each {
</ins><span class="cx">     | opt, arg |
</span><span class="cx">     case opt
</span><span class="cx">     when '--help'
</span><span class="lines">@@ -115,17 +117,22 @@
</span><span class="cx">         $outputDir = Pathname.new(arg)
</span><span class="cx">     when '--ftl-jit'
</span><span class="cx">         $enableFTL = true
</span><del>-    when '--error-on-failure'
-        $errorOnFailure = true
-    when '--no-error-on-failure'
-        $errorOnFailure = false
</del><span class="cx">     when '--verbose'
</span><span class="cx">         $verbosity += 1
</span><ins>+    when '--run-bundle'
+        $bundle = Pathname.new(arg)
+    when '--tarball'
+        $tarball = true
</ins><span class="cx">     end
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> $progressMeter = ($verbosity == 0 and $stdin.tty?)
</span><span class="cx"> 
</span><ins>+if $bundle
+    $jscPath = $bundle + &quot;.vm&quot; + &quot;JavaScriptCore.framework&quot; + &quot;Resources&quot; + &quot;jsc&quot;
+    $outputDir = $bundle
+end
+
</ins><span class="cx"> unless $jscPath
</span><span class="cx">     $stderr.puts &quot;Error: must specify -j &lt;path&gt;.&quot;
</span><span class="cx">     exit 1
</span><span class="lines">@@ -137,6 +144,55 @@
</span><span class="cx"> 
</span><span class="cx"> $runlist = []
</span><span class="cx"> 
</span><ins>+def frameworkFromJSCPath(jscPath)
+    if jscPath.dirname.basename.to_s == &quot;Resources&quot; and jscPath.dirname.dirname.basename.to_s == &quot;JavaScriptCore.framework&quot;
+        jscPath.dirname.dirname
+    elsif jscPath.dirname.dirname.basename.to_s == &quot;WebKitBuild&quot;
+        parentDirName = jscPath.dirname.basename.to_s
+        if parentDirName == &quot;Debug&quot; or parentDirName == &quot;Release&quot; 
+            jscPath.dirname + &quot;JavaScriptCore.framework&quot;
+        else
+            raise &quot;Unknown JSC path, cannot copy full framework: #{jscPath}&quot;
+        end
+    else
+        $stderr.puts &quot;Warning: cannot identify JSC framework, doing generic VM copy.&quot;
+        nil
+    end
+end
+
+def prepareFramework(jscPath)
+    frameworkPath = frameworkFromJSCPath(jscPath)
+    $frameworkPath = Pathname.new(&quot;.vm&quot;) + &quot;JavaScriptCore.framework&quot;
+    $jscPath = $frameworkPath + &quot;Resources&quot; + &quot;jsc&quot;
+
+    if frameworkPath
+        FileUtils.cp_r frameworkPath, vmDir
+    else
+        Dir.chdir($outputDir) {
+            FileUtils.mkdir_p $jscPath.dirname
+            FileUtils.cp jscPath, $jscPath
+        }
+    end
+end
+
+def copyVMToBundle
+    raise if $bundle
+
+    vmDir = $outputDir + &quot;.vm&quot;
+    FileUtils.mkdir_p vmDir
+   
+    prepareFramework($jscPath) 
+end
+
+def pathToVM
+    dir = Pathname.new(&quot;.&quot;)
+    $benchmarkDirectory.each_filename {
+        | pathComponent |
+        dir += &quot;..&quot;
+    }
+    dir + $jscPath
+end
+
</ins><span class="cx"> def prefixCommand(prefix)
</span><span class="cx">     &quot;awk &quot; + Shellwords.shellescape(&quot;{ printf #{(prefix + ': ').inspect}; print }&quot;)
</span><span class="cx"> end
</span><span class="lines">@@ -149,7 +205,7 @@
</span><span class="cx"> def silentOutputHandler
</span><span class="cx">     Proc.new {
</span><span class="cx">         | name |
</span><del>-        &quot; | &quot; + pipeAndPrefixCommand(($outputDir + (name + &quot;.out&quot;)).to_s, name)
</del><ins>+        &quot; | &quot; + pipeAndPrefixCommand((Pathname(&quot;..&quot;) + (name + &quot;.out&quot;)).to_s, name)
</ins><span class="cx">     }
</span><span class="cx"> end
</span><span class="cx"> 
</span><span class="lines">@@ -157,7 +213,7 @@
</span><span class="cx"> def noisyOutputHandler
</span><span class="cx">     Proc.new {
</span><span class="cx">         | name |
</span><del>-        &quot; | cat &gt; &quot; + Shellwords.shellescape(($outputDir + (name + &quot;.out&quot;)).to_s)
</del><ins>+        &quot; | cat &gt; &quot; + Shellwords.shellescape((Pathname(&quot;..&quot;) + (name + &quot;.out&quot;)).to_s)
</ins><span class="cx">     }
</span><span class="cx"> end
</span><span class="cx"> 
</span><span class="lines">@@ -179,16 +235,16 @@
</span><span class="cx"> def diffErrorHandler(expectedFilename)
</span><span class="cx">     Proc.new {
</span><span class="cx">         | outp, plan |
</span><del>-        outputFilename = Shellwords.shellescape(($outputDir + (plan.name + &quot;.out&quot;)).to_s)
-        diffFilename = Shellwords.shellescape(($outputDir + (plan.name + &quot;.diff&quot;)).to_s)
</del><ins>+        outputFilename = Shellwords.shellescape((Pathname(&quot;..&quot;) + (plan.name + &quot;.out&quot;)).to_s)
+        diffFilename = Shellwords.shellescape((Pathname(&quot;..&quot;) + (plan.name + &quot;.diff&quot;)).to_s)
</ins><span class="cx">         
</span><span class="cx">         outp.puts &quot;if test -e #{plan.failFile}&quot;
</span><span class="cx">         outp.puts &quot;then&quot;
</span><span class="cx">         outp.puts &quot;    (cat #{outputFilename} &amp;&amp; echo ERROR: Unexpected exit code: `cat #{plan.failFile}`) | &quot; + prefixCommand(plan.name)
</span><span class="cx">         outp.puts &quot;    &quot; + plan.failCommand
</span><del>-        outp.puts &quot;elif test -e #{Shellwords.shellescape(expectedFilename)}&quot;
</del><ins>+        outp.puts &quot;elif test -e ../#{Shellwords.shellescape(expectedFilename)}&quot;
</ins><span class="cx">         outp.puts &quot;then&quot;
</span><del>-        outp.puts &quot;    diff -u #{Shellwords.shellescape(expectedFilename)} #{outputFilename} &gt; #{diffFilename}&quot;
</del><ins>+        outp.puts &quot;    diff -u ../#{Shellwords.shellescape(expectedFilename)} #{outputFilename} &gt; #{diffFilename}&quot;
</ins><span class="cx">         outp.puts &quot;    if [ $? -eq 0 ]&quot;
</span><span class="cx">         outp.puts &quot;    then&quot;
</span><span class="cx">         outp.puts &quot;    &quot; + plan.successCommand
</span><span class="lines">@@ -208,13 +264,13 @@
</span><span class="cx"> def mozillaErrorHandler
</span><span class="cx">     Proc.new {
</span><span class="cx">         | outp, plan |
</span><del>-        outputFilename = Shellwords.shellescape(($outputDir + (plan.name + &quot;.out&quot;)).to_s)
</del><ins>+        outputFilename = Shellwords.shellescape((Pathname(&quot;..&quot;) + (plan.name + &quot;.out&quot;)).to_s)
</ins><span class="cx"> 
</span><span class="cx">         outp.puts &quot;if test -e #{plan.failFile}&quot;
</span><span class="cx">         outp.puts &quot;then&quot;
</span><span class="cx">         outp.puts &quot;    (cat #{outputFilename} &amp;&amp; echo ERROR: Unexpected exit code: `cat #{plan.failFile}`) | &quot; + prefixCommand(plan.name)
</span><span class="cx">         outp.puts &quot;    &quot; + plan.failCommand
</span><del>-        outp.puts &quot;elif ruby &quot; + Shellwords.shellescape((HELPERS_PATH + &quot;check-mozilla-failure&quot;).to_s) + &quot; #{outputFilename}&quot;
</del><ins>+        outp.puts &quot;elif grep -i -q failed! #{outputFilename}&quot;
</ins><span class="cx">         outp.puts &quot;then&quot;
</span><span class="cx">         outp.puts &quot;    (echo Detected failures: &amp;&amp; cat #{outputFilename}) | &quot; + prefixCommand(plan.name)
</span><span class="cx">         outp.puts &quot;    &quot; + plan.failCommand
</span><span class="lines">@@ -229,12 +285,12 @@
</span><span class="cx"> def mozillaFailErrorHandler
</span><span class="cx">     Proc.new {
</span><span class="cx">         | outp, plan |
</span><del>-        outputFilename = Shellwords.shellescape(($outputDir + (plan.name + &quot;.out&quot;)).to_s)
</del><ins>+        outputFilename = Shellwords.shellescape((Pathname(&quot;..&quot;) + (plan.name + &quot;.out&quot;)).to_s)
</ins><span class="cx"> 
</span><span class="cx">         outp.puts &quot;if test -e #{plan.failFile}&quot;
</span><span class="cx">         outp.puts &quot;then&quot;
</span><span class="cx">         outp.puts &quot;    &quot; + plan.successCommand
</span><del>-        outp.puts &quot;elif ruby &quot; + Shellwords.shellescape((HELPERS_PATH + &quot;check-mozilla-failure&quot;).to_s) + &quot; #{outputFilename}&quot;
</del><ins>+        outp.puts &quot;elif grep -i -q failed! #{outputFilename}&quot;
</ins><span class="cx">         outp.puts &quot;then&quot;
</span><span class="cx">         outp.puts &quot;    &quot; + plan.successCommand
</span><span class="cx">         outp.puts &quot;else&quot;
</span><span class="lines">@@ -249,13 +305,13 @@
</span><span class="cx"> def mozillaExit3ErrorHandler
</span><span class="cx">     Proc.new {
</span><span class="cx">         | outp, plan |
</span><del>-        outputFilename = Shellwords.shellescape(($outputDir + (plan.name + &quot;.out&quot;)).to_s)
</del><ins>+        outputFilename = Shellwords.shellescape((Pathname(&quot;..&quot;) + (plan.name + &quot;.out&quot;)).to_s)
</ins><span class="cx"> 
</span><span class="cx">         outp.puts &quot;if test -e #{plan.failFile}&quot;
</span><span class="cx">         outp.puts &quot;then&quot;
</span><span class="cx">         outp.puts &quot;    if [ `cat #{plan.failFile}` -eq 3 ]&quot;
</span><span class="cx">         outp.puts &quot;    then&quot;
</span><del>-        outp.puts &quot;        if ruby &quot; + Shellwords.shellescape((HELPERS_PATH + &quot;check-mozilla-failure&quot;).to_s) + &quot; #{outputFilename}&quot;
</del><ins>+        outp.puts &quot;        if grep -i -q failed! #{outputFilename}&quot;
</ins><span class="cx">         outp.puts &quot;        then&quot;
</span><span class="cx">         outp.puts &quot;            (echo Detected failures: &amp;&amp; cat #{outputFilename}) | &quot; + prefixCommand(plan.name)
</span><span class="cx">         outp.puts &quot;            &quot; + plan.failCommand
</span><span class="lines">@@ -280,7 +336,7 @@
</span><span class="cx">     attr_accessor :index
</span><span class="cx">     
</span><span class="cx">     def initialize(directory, arguments, name, outputHandler, errorHandler)
</span><del>-        @directory = directory.realpath
</del><ins>+        @directory = directory
</ins><span class="cx">         @arguments = arguments
</span><span class="cx">         @name = name
</span><span class="cx">         @outputHandler = outputHandler
</span><span class="lines">@@ -289,7 +345,10 @@
</span><span class="cx">     end
</span><span class="cx">     
</span><span class="cx">     def shellCommand
</span><del>-        &quot;(cd #{Shellwords.shellescape(@directory.to_s)} &amp;&amp; \&quot;$@\&quot; &quot; + @arguments.map{
</del><ins>+        # It's important to remember that the test is actually run in a subshell, so if we change directory
+        # in the subshell when we return we will be in our original directory. This is nice because we don't
+        # have to bend over backwards to do things relative to the root.
+        &quot;(cd ../#{Shellwords.shellescape(@directory.to_s)} &amp;&amp; \&quot;$@\&quot; &quot; + @arguments.map{
</ins><span class="cx">             | v |
</span><span class="cx">             raise &quot;Detected a non-string in #{inspect}&quot; unless v.is_a? String
</span><span class="cx">             Shellwords.shellescape(v)
</span><span class="lines">@@ -297,7 +356,17 @@
</span><span class="cx">     end
</span><span class="cx">     
</span><span class="cx">     def reproScriptCommand
</span><del>-        script = &quot;&quot;
</del><ins>+        # We have to find our way back to the .parallel directory since that's where all of the relative
+        # paths assume they start out from.
+        script = &quot;CURRENT_DIR=\&quot;$( cd \&quot;$( dirname \&quot;${BASH_SOURCE[0]}\&quot; )\&quot; &amp;&amp; pwd )\&quot;\n&quot;
+        script += &quot;cd $CURRENT_DIR\n&quot;
+        Pathname.new(@name).dirname.each_filename {
+            | pathComponent |
+            script += &quot;cd ..\n&quot;
+        }
+        script += &quot;cd .parallel\n&quot;
+
+        script += &quot;export DYLD_FRAMEWORK_PATH=$(cd ../#{$frameworkPath.dirname}; pwd)\n&quot;
</ins><span class="cx">         IMPORTANT_ENVS.each {
</span><span class="cx">             | key |
</span><span class="cx">             if ENV[key]
</span><span class="lines">@@ -305,7 +374,7 @@
</span><span class="cx">             end
</span><span class="cx">         }
</span><span class="cx">         script += &quot;#{shellCommand} || exit 1&quot;
</span><del>-        &quot;echo #{Shellwords.shellescape(script)} &gt; #{Shellwords.shellescape(($outputDir + @name).to_s)}&quot;
</del><ins>+        &quot;echo #{Shellwords.shellescape(script)} &gt; #{Shellwords.shellescape((Pathname.new(&quot;..&quot;) + @name).to_s)}&quot;
</ins><span class="cx">     end
</span><span class="cx">     
</span><span class="cx">     def failCommand
</span><span class="lines">@@ -366,22 +435,26 @@
</span><span class="cx"> # returns false, in which case you're supposed to add your own run commands.
</span><span class="cx"> def parseRunCommands
</span><span class="cx">     didRun = false
</span><del>-    File.open($benchmarkDirectory + $benchmark) {
-        | inp |
-        inp.each_line {
-            | line |
-            begin
-                doesMatch = line =~ /^\/\/@/
-            rescue Exception =&gt; e
-                # Apparently this happens in the case of some UTF8 stuff in some files, where
-                # Ruby tries to be strict and throw exceptions.
-                next
-            end
-            next unless doesMatch
-            eval $~.post_match
-            didRun = true
</del><ins>+
+    Dir.chdir($outputDir) {
+        File.open($benchmarkDirectory + $benchmark) {
+            | inp |
+            inp.each_line {
+                | line |
+                begin
+                    doesMatch = line =~ /^\/\/@/
+                rescue Exception =&gt; e
+                    # Apparently this happens in the case of some UTF8 stuff in some files, where
+                    # Ruby tries to be strict and throw exceptions.
+                    next
+                end
+                next unless doesMatch
+                eval $~.post_match
+                didRun = true
+            }
</ins><span class="cx">         }
</span><span class="cx">     }
</span><ins>+
</ins><span class="cx">     didRun
</span><span class="cx"> end
</span><span class="cx"> 
</span><span class="lines">@@ -390,7 +463,7 @@
</span><span class="cx"> end
</span><span class="cx"> 
</span><span class="cx"> def run(kind, *options)
</span><del>-    addRunCommand(kind, [$jscPath.to_s] + options + [$benchmark.to_s], silentOutputHandler, simpleErrorHandler)
</del><ins>+    addRunCommand(kind, [pathToVM.to_s] + options + [$benchmark.to_s], silentOutputHandler, simpleErrorHandler)
</ins><span class="cx"> end
</span><span class="cx"> 
</span><span class="cx"> def runDefault
</span><span class="lines">@@ -478,7 +551,7 @@
</span><span class="cx"> def runProfiler
</span><span class="cx">     profilerOutput = uniqueFilename(&quot;.json&quot;)
</span><span class="cx">     if $canRunDisplayProfilerOutput
</span><del>-        addRunCommand(&quot;profiler&quot;, [&quot;ruby&quot;, (HELPERS_PATH + &quot;profiler-test-helper&quot;).to_s, (SCRIPTS_PATH + &quot;display-profiler-output&quot;).to_s, profilerOutput.to_s, $jscPath.to_s, &quot;-p&quot;, profilerOutput.to_s, $benchmark.to_s], silentOutputHandler, simpleErrorHandler)
</del><ins>+        addRunCommand(&quot;profiler&quot;, [&quot;ruby&quot;, (HELPERS_PATH + &quot;profiler-test-helper&quot;).to_s, (SCRIPTS_PATH + &quot;display-profiler-output&quot;).to_s, profilerOutput.to_s, pathToVM.to_s, &quot;-p&quot;, profilerOutput.to_s, $benchmark.to_s], silentOutputHandler, simpleErrorHandler)
</ins><span class="cx">     else
</span><span class="cx">         puts &quot;Running simple version of #{$collectionName}/#{$benchmark} because some required Ruby features are unavailable.&quot;
</span><span class="cx">         run(&quot;profiler-simple&quot;, &quot;-p&quot;, profilerOutput.to_s)
</span><span class="lines">@@ -493,12 +566,15 @@
</span><span class="cx">     else
</span><span class="cx">         kind = &quot;layout&quot;
</span><span class="cx">     end
</span><del>-    args =
-        [$jscPath.to_s] + options +
-        [(LAYOUTTESTS_PATH + &quot;resources&quot; + &quot;standalone-pre.js&quot;).to_s,
</del><ins>+
+    prepareExtraRelativeFiles([&quot;../#{testName}-expected.txt&quot;], $benchmarkDirectory)
+    prepareExtraAbsoluteFiles(LAYOUTTESTS_PATH, [&quot;resources/standalone-pre.js&quot;, &quot;resources/standalone-post.js&quot;])
+
+    args = [pathToVM.to_s] + options +
+        [(Pathname.new(&quot;resources&quot;) + &quot;standalone-pre.js&quot;).to_s,
</ins><span class="cx">          $benchmark.to_s,
</span><del>-         (LAYOUTTESTS_PATH + &quot;resources&quot; + &quot;standalone-post.js&quot;).to_s]
-    addRunCommand(kind, args, noisyOutputHandler, diffErrorHandler(($benchmarkDirectory.dirname + &quot;#{testName}-expected.txt&quot;).to_s))
</del><ins>+         (Pathname.new(&quot;resources&quot;) + &quot;standalone-post.js&quot;).to_s]
+    addRunCommand(kind, args, noisyOutputHandler, diffErrorHandler(($benchmarkDirectory + &quot;../#{testName}-expected.txt&quot;).to_s))
</ins><span class="cx"> end
</span><span class="cx"> 
</span><span class="cx"> def runLayoutTestDefault
</span><span class="lines">@@ -524,13 +600,40 @@
</span><span class="cx">     runLayoutTestDFGEagerNoCJIT
</span><span class="cx"> end
</span><span class="cx"> 
</span><ins>+def prepareExtraRelativeFiles(extraFiles, destination)
+    Dir.chdir($outputDir) {
+        extraFiles.each {
+            | file |
+            FileUtils.cp $extraFilesBaseDir + file, destination + file
+        }
+    }
+end
+
+def baseDirForCollection(collectionName)
+    Pathname(&quot;.tests&quot;) + collectionName
+end
+
+def prepareExtraAbsoluteFiles(absoluteBase, extraFiles)
+    raise unless absoluteBase.absolute?
+    Dir.chdir($outputDir) {
+        collectionBaseDir = baseDirForCollection($collectionName)
+        extraFiles.each {
+            | file |
+            destination = collectionBaseDir + file
+            FileUtils.mkdir_p destination.dirname unless destination.directory?
+            FileUtils.cp absoluteBase + file, destination
+        }
+    }
+end
+
</ins><span class="cx"> def runMozillaTest(kind, mode, extraFiles, *options)
</span><span class="cx">     if kind
</span><span class="cx">         kind = &quot;mozilla-&quot; + kind
</span><span class="cx">     else
</span><span class="cx">         kind = &quot;mozilla&quot;
</span><span class="cx">     end
</span><del>-    args = [$jscPath.to_s] + options + extraFiles.map{|v| ($benchmarkDirectory + v).to_s} + [$benchmark.to_s]
</del><ins>+    prepareExtraRelativeFiles(extraFiles.map{|v| (Pathname(&quot;..&quot;) + v).to_s}, $collection)
+    args = [pathToVM.to_s] + options + extraFiles.map{|v| v.to_s} + [$benchmark.to_s]
</ins><span class="cx">     case mode
</span><span class="cx">     when :normal
</span><span class="cx">         errorHandler = mozillaErrorHandler
</span><span class="lines">@@ -573,14 +676,6 @@
</span><span class="cx">     puts &quot;Skipping #{$collectionName}/#{$benchmark}&quot;
</span><span class="cx"> end
</span><span class="cx"> 
</span><del>-Dir.mkdir($outputDir) unless $outputDir.directory?
-begin
-    File.delete($outputDir + &quot;failed&quot;)
-rescue
-end
-
-$outputDir = $outputDir.realpath
-
</del><span class="cx"> def allJSFiles(path)
</span><span class="cx">     if path.file?
</span><span class="cx">         [path]
</span><span class="lines">@@ -622,11 +717,29 @@
</span><span class="cx"> end
</span><span class="cx"> 
</span><span class="cx"> def prepareCollection(name)
</span><del>-    dir = $outputDir
-    Pathname.new(name).each_filename {
-        | filename |
-        dir = dir + filename
-        Dir.mkdir(dir) unless dir.directory?
</del><ins>+    FileUtils.mkdir_p $outputDir + name
+
+    absoluteCollection = $collection.realpath
+
+    Dir.chdir($outputDir) {
+        bundleDir = baseDirForCollection(name)
+
+        # Create the proper directory structures.
+        FileUtils.mkdir_p bundleDir
+        if bundleDir.basename == $collection.basename
+            FileUtils.cp_r absoluteCollection, bundleDir.dirname
+        else
+            FileUtils.cp_r absoluteCollection, bundleDir
+        end
+
+        $extraFilesBaseDir = absoluteCollection
+
+        # Redirect the collection's location to the newly constructed bundle.
+        if absoluteCollection.directory?
+            $collection = bundleDir
+        else
+            $collection = bundleDir + $collection.basename
+        end
</ins><span class="cx">     }
</span><span class="cx"> end
</span><span class="cx"> 
</span><span class="lines">@@ -634,7 +747,7 @@
</span><span class="cx"> 
</span><span class="cx"> def handleCollectionFile(collection)
</span><span class="cx">     collectionName = simplifyCollectionName(collection)
</span><del>-    
</del><ins>+   
</ins><span class="cx">     paths = {}
</span><span class="cx">     subCollections = []
</span><span class="cx">     YAML::load(IO::read(collection)).each {
</span><span class="lines">@@ -670,17 +783,17 @@
</span><span class="cx">         $collectionName = $collectionName.to_s
</span><span class="cx">         
</span><span class="cx">         prepareCollection($collectionName)
</span><del>-        
-        allJSFiles(subCollection).each {
-            | path |
-            
-            path = path.realpath
-            
-            $benchmark = path.basename
-            $benchmarkDirectory = path.dirname
-            
-            $runCommandOptions = {}
-            eval entry[&quot;cmd&quot;]
</del><ins>+      
+        Dir.chdir($outputDir) {
+            allJSFiles($collection).each {
+                | path |
+               
+                $benchmark = path.basename
+                $benchmarkDirectory = path.dirname
+                
+                $runCommandOptions = {}
+                eval entry[&quot;cmd&quot;]
+            }
</ins><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="lines">@@ -693,18 +806,20 @@
</span><span class="cx"> def handleCollectionDirectory(collection)
</span><span class="cx">     collectionName = simplifyCollectionName(collection)
</span><span class="cx">     
</span><del>-    prepareCollection(collectionName)
-    
</del><span class="cx">     $collection = collection
</span><span class="cx">     $collectionName = collectionName
</span><del>-    $benchmarkDirectory = $collection
-    allJSFiles($collection).each {
-        | path |
-        
-        $benchmark = path.basename
-        
-        $runCommandOptions = {}
-        defaultRun unless parseRunCommands
</del><ins>+    prepareCollection(collectionName)
+   
+    Dir.chdir($outputDir) {
+        $benchmarkDirectory = $collection
+        allJSFiles($collection).each {
+            | path |
+            
+            $benchmark = path.basename
+            
+            $runCommandOptions = {}
+            defaultRun unless parseRunCommands
+        }
</ins><span class="cx">     }
</span><span class="cx"> end
</span><span class="cx"> 
</span><span class="lines">@@ -718,11 +833,6 @@
</span><span class="cx">     end
</span><span class="cx"> end
</span><span class="cx"> 
</span><del>-ARGV.each {
-    | collection |
-    handleCollection(collection)
-}
-
</del><span class="cx"> def appendFailure(plan)
</span><span class="cx">     File.open($outputDir + &quot;failed&quot;, &quot;a&quot;) {
</span><span class="cx">         | outp |
</span><span class="lines">@@ -731,6 +841,126 @@
</span><span class="cx">     $numFailures += 1
</span><span class="cx"> end
</span><span class="cx"> 
</span><ins>+def prepareBundle
+    raise if $bundle
+
+    copyVMToBundle
+
+    ARGV.each {
+        | collection |
+        handleCollection(collection)
+    }
+end
+
+def cleanOldResults
+    raise unless $bundle
+
+    eachResultFile($outputDir) {
+        | path |
+        FileUtils.rm_f path
+    }
+end
+
+def cleanEmptyResultFiles
+    eachResultFile($outputDir) {
+        | path |
+        next unless path.basename.to_s =~ /\.out$/
+        next unless FileTest.size(path) == 0
+        FileUtils.rm_f path
+    }
+end
+
+def eachResultFile(startingDir, &amp;block)
+    dirsToClean = [startingDir]
+    until dirsToClean.empty? do
+        nextDir = dirsToClean.pop
+        Dir.foreach(nextDir) {
+            | entry |
+            next if entry =~ /^\./
+            path = nextDir + entry
+            if path.directory?
+                dirsToClean.push(path)
+            else
+                block.call(path)
+            end
+        }
+    end
+end
+
+def prepareParallelTestRunner
+    # The goals of our parallel test runner are scalability and simplicity. The
+    # simplicity part is particularly important. We don't want to have to have
+    # a full-time contributor just philosophising about parallel testing.
+    #
+    # As such, we just pass off all of the hard work to 'make'. This creates a
+    # dummy directory (&quot;$outputDir/.parallel&quot;) in which we create a dummy
+    # Makefile. The Makefile has an 'all' rule that depends on all of the tests.
+    # That is, for each test we know we will run, there is a rule in the
+    # Makefile and 'all' depends on it. Running 'make -j &lt;whatever&gt;' on this
+    # Makefile results in 'make' doing all of the hard work:
+    #
+    # - Load balancing just works. Most systems have a great load balancer in
+    #   'make'. If your system doesn't then just install a real 'make'.
+    #
+    # - Interruptions just work. For example Ctrl-C handling in 'make' is
+    #   exactly right. You don't have to worry about zombie processes.
+    #
+    # We then do some tricks to make failure detection work and to make this
+    # totally sound. If a test fails, we don't want the whole 'make' job to
+    # stop. We also don't have any facility for makefile-escaping of path names.
+    # We do have such a thing for shell-escaping, though. We fix both problems
+    # by having the actual work for each of the test rules be done in a shell
+    # script on the side. There is one such script per test. The script responds
+    # to failure by printing something on the console and then touching a
+    # failure file for that test, but then still returns 0. This makes 'make'
+    # continue past that failure and complete all the tests anyway.
+    #
+    # In the end, this script collects all of the failures by searching for
+    # files in the .parallel directory whose name matches /^test_fail_/, where
+    # the thing after the 'fail_' is the test index. Those are the files that
+    # would be created by the test scripts if they detect failure. We're
+    # basically using the filesystem as a concurrent database of test failures.
+    # Even if two tests fail at the same time, since they're touching different
+    # files we won't miss any failures.
+    
+    runIndices = []
+    $runlist.each_with_index {
+        | plan, index |
+        runIndices &lt;&lt; index
+        plan.index = index
+    }
+    
+    Dir.mkdir($parallelDir) unless $parallelDir.directory?
+    toDelete = []
+    Dir.foreach($parallelDir) {
+        | filename |
+        if filename =~ /^test_/
+            toDelete &lt;&lt; filename
+        end
+    }
+    
+    toDelete.each {
+        | filename |
+        File.unlink($parallelDir + filename)
+    }
+
+    $runlist.each {
+        | plan |
+        plan.writeRunScript($parallelDir + &quot;test_script_#{plan.index}&quot;)
+    }
+    
+    File.open($parallelDir + &quot;Makefile&quot;, &quot;w&quot;) {
+        | outp |
+        outp.puts(&quot;all: &quot; + runIndices.map{|v| &quot;test_done_#{v}&quot;}.join(' '))
+        runIndices.each {
+            | index |
+            plan = $runlist[index]
+            outp.puts &quot;test_done_#{index}:&quot;
+            outp.puts &quot;\tsh test_script_#{plan.index}&quot;
+        }
+    }
+end
+
</ins><span class="cx"> if $enableFTL and ENV[&quot;JSC_timeout&quot;]
</span><span class="cx">     # Currently, using the FTL is a performance regression particularly in real
</span><span class="cx">     # (i.e. non-loopy) benchmarks. Account for this in the timeout.
</span><span class="lines">@@ -742,154 +972,117 @@
</span><span class="cx">     # Increase the timeout proportionally to the number of processors.
</span><span class="cx">     ENV[&quot;JSC_timeout&quot;] = (ENV[&quot;JSC_timeout&quot;].to_i.to_f * Math.sqrt($numProcessors)).to_i.to_s
</span><span class="cx"> end
</span><del>-
-# The goals of our parallel test runner are scalability and simplicity. The
-# simplicity part is particularly important. We don't want to have to have
-# a full-time contributor just philosophising about parallel testing.
-#
-# As such, we just pass off all of the hard work to 'make'. This creates a
-# dummy directory (&quot;$outputDir/.parallel&quot;) in which we create a dummy
-# Makefile. The Makefile has an 'all' rule that depends on all of the tests.
-# That is, for each test we know we will run, there is a rule in the
-# Makefile and 'all' depends on it. Running 'make -j &lt;whatever&gt;' on this
-# Makefile results in 'make' doing all of the hard work:
-#
-# - Load balancing just works. Most systems have a great load balancer in
-#   'make'. If your system doesn't then just install a real 'make'.
-#
-# - Interruptions just work. For example Ctrl-C handling in 'make' is
-#   exactly right. You don't have to worry about zombie processes.
-#
-# We then do some tricks to make failure detection work and to make this
-# totally sound. If a test fails, we don't want the whole 'make' job to
-# stop. We also don't have any facility for makefile-escaping of path names.
-# We do have such a thing for shell-escaping, though. We fix both problems
-# by having the actual work for each of the test rules be done in a shell
-# script on the side. There is one such script per test. The script responds
-# to failure by printing something on the console and then touching a
-# failure file for that test, but then still returns 0. This makes 'make'
-# continue past that failure and complete all the tests anyway.
-#
-# In the end, this script collects all of the failures by searching for
-# files in the .parallel directory whose name matches /^test_fail_/, where
-# the thing after the 'fail_' is the test index. Those are the files that
-# would be created by the test scripts if they detect failure. We're
-# basically using the filesystem as a concurrent database of test failures.
-# Even if two tests fail at the same time, since they're touching different
-# files we won't miss any failures.
-
-runIndices = []
-$runlist.each_with_index {
-    | plan, index |
-    runIndices &lt;&lt; index
-    plan.index = index
-}
-
-parallelDir = $outputDir + &quot;.parallel&quot;
-Dir.mkdir(parallelDir) unless parallelDir.directory?
-toDelete = []
-Dir.foreach(parallelDir) {
-    | filename |
-    if filename =~ /^test_/
-        toDelete &lt;&lt; filename
-    end
-}
-
-toDelete.each {
-    | filename |
-    File.unlink(parallelDir + filename)
-}
-
</del><ins>+    
</ins><span class="cx"> puts
</span><span class="cx"> 
</span><del>-$runlist.each {
-    | plan |
-    plan.writeRunScript(parallelDir + &quot;test_script_#{plan.index}&quot;)
-}
-
-File.open(parallelDir + &quot;Makefile&quot;, &quot;w&quot;) {
-    | outp |
-    outp.puts(&quot;all: &quot; + runIndices.map{|v| &quot;test_done_#{v}&quot;}.join(' '))
-    runIndices.each {
-        | index |
-        plan = $runlist[index]
-        outp.puts &quot;test_done_#{index}:&quot;
-        outp.puts &quot;\tsh test_script_#{plan.index}&quot;
</del><ins>+def cleanParallelDirectory
+    raise unless $bundle
+    Dir.foreach($parallelDir) {
+        | filename |
+        next unless filename =~ /^test_fail/
+        FileUtils.rm_f $parallelDir + filename
</ins><span class="cx">     }
</span><del>-}
</del><ins>+end
</ins><span class="cx"> 
</span><del>-Dir.chdir(parallelDir) {
-    unless $progressMeter
-        mysys(&quot;make&quot;, &quot;-j&quot;, $numProcessors.to_s, &quot;-s&quot;, &quot;-f&quot;, &quot;Makefile&quot;)
-    else
-        cmd = &quot;make -j #{$numProcessors} -s -f Makefile&quot;
-        running = {}
-        didRun = {}
-        didFail = {}
-        blankLine = true
-        prevStringLength = 0
-        IO.popen(cmd, &quot;r&quot;) {
-            | inp |
-            inp.each_line {
-                | line |
-                line.chomp!
-                if line =~ /^Running /
-                    running[$~.post_match] = true
-                elsif line =~ /^PASS: /
-                    didRun[$~.post_match] = true
-                elsif line =~ /^FAIL: /
-                    didRun[$~.post_match] = true
-                    didFail[$~.post_match] = true
-                else
-                    unless blankLine
-                        print(&quot;\r&quot; + &quot; &quot; * prevStringLength + &quot;\r&quot;)
-                    end
-                    puts line
-                    blankLine = true
-                end
-                
-                def lpad(str, chars)
-                    str = str.to_s
-                    if str.length &gt; chars
-                        str
</del><ins>+def runParallelTestRunner
+    Dir.chdir($parallelDir) {
+        # -1 for the Makefile, and -2 for '..' and '.'
+        numberOfTests = Dir.entries(&quot;.&quot;).count - 3
+        unless $progressMeter
+            mysys(&quot;make&quot;, &quot;-j&quot;, $numProcessors.to_s, &quot;-s&quot;, &quot;-f&quot;, &quot;Makefile&quot;)
+        else
+            cmd = &quot;make -j #{$numProcessors} -s -f Makefile&quot;
+            running = {}
+            didRun = {}
+            didFail = {}
+            blankLine = true
+            prevStringLength = 0
+            IO.popen(cmd, &quot;r&quot;) {
+                | inp |
+                inp.each_line {
+                    | line |
+                    line.chomp!
+                    if line =~ /^Running /
+                        running[$~.post_match] = true
+                    elsif line =~ /^PASS: /
+                        didRun[$~.post_match] = true
+                    elsif line =~ /^FAIL: /
+                        didRun[$~.post_match] = true
+                        didFail[$~.post_match] = true
</ins><span class="cx">                     else
</span><del>-                        &quot;%#{chars}s&quot;%(str)
</del><ins>+                        unless blankLine
+                            print(&quot;\r&quot; + &quot; &quot; * prevStringLength + &quot;\r&quot;)
+                        end
+                        puts line
+                        blankLine = true
</ins><span class="cx">                     end
</span><del>-                end
-
-                string  = &quot;&quot;
-                string += &quot;\r#{lpad(didRun.size, $runlist.size.to_s.size)}/#{$runlist.size}&quot;
-                unless didFail.empty?
-                    string += &quot; (failed #{didFail.size})&quot;
-                end
-                string += &quot; &quot;
-                (running.size - didRun.size).times {
-                    string += &quot;.&quot;
</del><ins>+                    
+                    def lpad(str, chars)
+                        str = str.to_s
+                        if str.length &gt; chars
+                            str
+                        else
+                            &quot;%#{chars}s&quot;%(str)
+                        end
+                    end
+    
+                    string  = &quot;&quot;
+                    string += &quot;\r#{lpad(didRun.size, numberOfTests.to_s.size)}/#{numberOfTests}&quot;
+                    unless didFail.empty?
+                        string += &quot; (failed #{didFail.size})&quot;
+                    end
+                    string += &quot; &quot;
+                    (running.size - didRun.size).times {
+                        string += &quot;.&quot;
+                    }
+                    if string.length &lt; prevStringLength
+                        print string
+                        print(&quot; &quot; * (prevStringLength - string.length))
+                    end
+                    print string
+                    prevStringLength = string.length
+                    blankLine = false
+                    $stdout.flush
</ins><span class="cx">                 }
</span><del>-                if string.length &lt; prevStringLength
-                    print string
-                    print(&quot; &quot; * (prevStringLength - string.length))
-                end
-                print string
-                prevStringLength = string.length
-                blankLine = false
-                $stdout.flush
</del><span class="cx">             }
</span><del>-        }
-        puts
-        raise &quot;Failed to run #{cmd}: #{$?.inspect}&quot; unless $?.success?
-    end
-}
</del><ins>+            puts
+            raise &quot;Failed to run #{cmd}: #{$?.inspect}&quot; unless $?.success?
+        end
+    }
+end
</ins><span class="cx"> 
</span><del>-# Delete empty .out files to make life less confusing.
-$runlist.each {
-    | plan |
-    outputFilename = $outputDir + (plan.name + &quot;.out&quot;)
-    File.unlink outputFilename if FileTest.size(outputFilename) == 0
-}
</del><ins>+def detectFailures
+    raise if $bundle
</ins><span class="cx"> 
</span><del>-Dir.foreach(parallelDir) {
-    | filename |
-    next unless filename =~ /test_fail_/
-    appendFailure($runlist[$~.post_match.to_i])
-}
</del><ins>+    Dir.foreach($parallelDir) {
+        | filename |
+        next unless filename =~ /test_fail_/
+        appendFailure($runlist[$~.post_match.to_i])
+    }
+end
+
+def compressBundle
+    cmd = &quot;cd #{$outputDir}/.. &amp;&amp; tar -czf payload.tar.gz #{$outputDir.basename}&quot;
+    $stderr.puts &quot;&gt;&gt; #{cmd}&quot; if $verbosity &gt;= 2
+    raise unless system(cmd)
+end
+
+FileUtils.rm_rf $outputDir if $outputDir.directory? and not $bundle
+Dir.mkdir($outputDir) unless $outputDir.directory?
+
+begin
+    File.delete($outputDir + &quot;failed&quot;)
+rescue
+end
+
+$outputDir = $outputDir.realpath
+$parallelDir = $outputDir + &quot;.parallel&quot;
+
+prepareBundle unless $bundle
+prepareParallelTestRunner unless $bundle
+cleanParallelDirectory if $bundle
+cleanOldResults if $bundle
+runParallelTestRunner
+cleanEmptyResultFiles
+detectFailures unless $bundle
+compressBundle if $tarball
</ins></span></pre>
</div>
</div>

</body>
</html>