<!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>[243953] trunk</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/243953">243953</a></dd>
<dt>Author</dt> <dd>drousso@apple.com</dd>
<dt>Date</dt> <dd>2019-04-05 16:39:16 -0700 (Fri, 05 Apr 2019)</dd>
</dl>

<h3>Log Message</h3>
<pre>Web Inspector: TestSuite test cases should have their own timeout to ensure tests fail with output instead of timeout by test runner
https://bugs.webkit.org/show_bug.cgi?id=162814
<rdar://problem/28574102>

Reviewed by Brian Burg.

Source/WebInspectorUI:

A 10s timer is started for every test case added to an async suite. The timer is cleared
when the test finishes, but if the timer fires, the test is forcibly ended with an error.

This timer can be configured by setting a `timeout` value when adding the test case. Values
are expected to be in milliseconds. The value `-1` will prevent a timer from being set.

This change also relaxes the expectation that any individual test case failure will stop the
rest of the suite from running. Since timers are set per test case, it is possible to
recover from a "bad" test case to still run the remaining test cases.

NOTE: there may be unexpected behaviour if a test times out, as the timer doesn't actually
stop the execution of the test, so it may still run and log information, which may appear
"out of nowhere" in the middle of other tests.

* UserInterface/Test/TestSuite.js:
(TestSuite.prototype.get passCount):
(AsyncTestSuite.prototype.runTestCases):
(SyncTestSuite.prototype.runTestCases):

LayoutTests:

* inspector/unit-tests/async-test-suite.html:
* inspector/unit-tests/async-test-suite-expected.txt:
* inspector/unit-tests/sync-test-suite.html:
* inspector/unit-tests/sync-test-suite-expected.txt:

* http/tests/inspector/network/set-resource-caching-disabled-disk-cache-expected.txt:
* inspector/canvas/recording-2d.html:
* inspector/canvas/recording-webgl-snapshots.html:
* inspector/canvas/recording-webgl.html:
* inspector/canvas/resources/shaderProgram-utilities.js:
(TestPage.registerInitializer.whenProgramAdded): Added.
(TestPage.registerInitializer.whenProgramRemoved): Added.
(TestPage.registerInitializer.window.initializeTestSuite):
(TestPage.registerInitializer.window.addSimpleTestCase):
(TestPage.registerInitializer.window.addParentCanvasRemovedTestCase):
(TestPage.registerInitializer.awaitProgramAdded): Added.
(TestPage.registerInitializer.awaitProgramRemoved): Added.
* inspector/console/command-line-api-expected.txt:
* inspector/console/heap-snapshot.html:
* inspector/debugger/async-stack-trace-truncate-expected.txt:
* inspector/debugger/pause-for-internal-scripts-expected.txt:
* inspector/formatting/resources/utilities.js:
(TestPage.registerInitializer.window.addFormattingTests):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestshttptestsinspectornetworksetresourcecachingdisableddiskcacheexpectedtxt">trunk/LayoutTests/http/tests/inspector/network/set-resource-caching-disabled-disk-cache-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorcanvasrecording2dhtml">trunk/LayoutTests/inspector/canvas/recording-2d.html</a></li>
<li><a href="#trunkLayoutTestsinspectorcanvasrecordingwebglsnapshotshtml">trunk/LayoutTests/inspector/canvas/recording-webgl-snapshots.html</a></li>
<li><a href="#trunkLayoutTestsinspectorcanvasrecordingwebglhtml">trunk/LayoutTests/inspector/canvas/recording-webgl.html</a></li>
<li><a href="#trunkLayoutTestsinspectorcanvasresourcesshaderProgramutilitiesjs">trunk/LayoutTests/inspector/canvas/resources/shaderProgram-utilities.js</a></li>
<li><a href="#trunkLayoutTestsinspectorconsolecommandlineapiexpectedtxt">trunk/LayoutTests/inspector/console/command-line-api-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorconsoleheapSnapshothtml">trunk/LayoutTests/inspector/console/heapSnapshot.html</a></li>
<li><a href="#trunkLayoutTestsinspectordebuggerasyncstacktracetruncateexpectedtxt">trunk/LayoutTests/inspector/debugger/async-stack-trace-truncate-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectordebuggerpauseforinternalscriptsexpectedtxt">trunk/LayoutTests/inspector/debugger/pause-for-internal-scripts-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorformattingresourcesutilitiesjs">trunk/LayoutTests/inspector/formatting/resources/utilities.js</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsasynctestsuiteexpectedtxt">trunk/LayoutTests/inspector/unit-tests/async-test-suite-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsasynctestsuitehtml">trunk/LayoutTests/inspector/unit-tests/async-test-suite.html</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestssynctestsuiteexpectedtxt">trunk/LayoutTests/inspector/unit-tests/sync-test-suite-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestssynctestsuitehtml">trunk/LayoutTests/inspector/unit-tests/sync-test-suite.html</a></li>
<li><a href="#trunkSourceWebInspectorUIChangeLog">trunk/Source/WebInspectorUI/ChangeLog</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceTestTestSuitejs">trunk/Source/WebInspectorUI/UserInterface/Test/TestSuite.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (243952 => 243953)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog      2019-04-05 23:25:27 UTC (rev 243952)
+++ trunk/LayoutTests/ChangeLog 2019-04-05 23:39:16 UTC (rev 243953)
</span><span class="lines">@@ -1,3 +1,35 @@
</span><ins>+2019-04-05  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: TestSuite test cases should have their own timeout to ensure tests fail with output instead of timeout by test runner
+        https://bugs.webkit.org/show_bug.cgi?id=162814
+        <rdar://problem/28574102>
+
+        Reviewed by Brian Burg.
+
+        * inspector/unit-tests/async-test-suite.html:
+        * inspector/unit-tests/async-test-suite-expected.txt:
+        * inspector/unit-tests/sync-test-suite.html:
+        * inspector/unit-tests/sync-test-suite-expected.txt:
+
+        * http/tests/inspector/network/set-resource-caching-disabled-disk-cache-expected.txt:
+        * inspector/canvas/recording-2d.html:
+        * inspector/canvas/recording-webgl-snapshots.html:
+        * inspector/canvas/recording-webgl.html:
+        * inspector/canvas/resources/shaderProgram-utilities.js:
+        (TestPage.registerInitializer.whenProgramAdded): Added.
+        (TestPage.registerInitializer.whenProgramRemoved): Added.
+        (TestPage.registerInitializer.window.initializeTestSuite):
+        (TestPage.registerInitializer.window.addSimpleTestCase):
+        (TestPage.registerInitializer.window.addParentCanvasRemovedTestCase):
+        (TestPage.registerInitializer.awaitProgramAdded): Added.
+        (TestPage.registerInitializer.awaitProgramRemoved): Added.
+        * inspector/console/command-line-api-expected.txt:
+        * inspector/console/heap-snapshot.html:
+        * inspector/debugger/async-stack-trace-truncate-expected.txt:
+        * inspector/debugger/pause-for-internal-scripts-expected.txt:
+        * inspector/formatting/resources/utilities.js:
+        (TestPage.registerInitializer.window.addFormattingTests):
+
</ins><span class="cx"> 2019-04-05  Ryan Haddad  <ryanhaddad@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [Mac WK2 iOS Sim] Layout Test imported/w3c/web-platform-tests/webrtc/RTCRtpReceiver-getSynchronizationSources.https.html is a flaky failure
</span></span></pre></div>
<a id="trunkLayoutTestshttptestsinspectornetworksetresourcecachingdisableddiskcacheexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/inspector/network/set-resource-caching-disabled-disk-cache-expected.txt (243952 => 243953)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/inspector/network/set-resource-caching-disabled-disk-cache-expected.txt     2019-04-05 23:25:27 UTC (rev 243952)
+++ trunk/LayoutTests/http/tests/inspector/network/set-resource-caching-disabled-disk-cache-expected.txt        2019-04-05 23:39:16 UTC (rev 243953)
</span><span class="lines">@@ -6,8 +6,8 @@
</span><span class="cx"> -- Running test case: PossibleNetworkLoad
</span><span class="cx"> PASS: Resource should be created.
</span><span class="cx"> PASS: Resource should receive a Response.
</span><ins>+
</ins><span class="cx"> -- Running test setup.
</span><del>-
</del><span class="cx"> -- Running test case: SetResourceCachingDisabled.DiskCache
</span><span class="cx"> PASS: Resource should be created.
</span><span class="cx"> PASS: Resource should receive a Response.
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorcanvasrecording2dhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/canvas/recording-2d.html (243952 => 243953)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/canvas/recording-2d.html     2019-04-05 23:25:27 UTC (rev 243952)
+++ trunk/LayoutTests/inspector/canvas/recording-2d.html        2019-04-05 23:39:16 UTC (rev 243953)
</span><span class="lines">@@ -444,6 +444,7 @@
</span><span class="cx">         test(resolve, reject) {
</span><span class="cx">             startRecording(WI.Canvas.ContextType.Canvas2D, resolve, reject, {frameCount: 1});
</span><span class="cx">         },
</span><ins>+        timeout: -1,
</ins><span class="cx">     });
</span><span class="cx"> 
</span><span class="cx">     suite.addTestCase({
</span><span class="lines">@@ -452,6 +453,7 @@
</span><span class="cx">         test(resolve, reject) {
</span><span class="cx">             startRecording(WI.Canvas.ContextType.Canvas2D, resolve, reject);
</span><span class="cx">         },
</span><ins>+        timeout: -1,
</ins><span class="cx">     });
</span><span class="cx"> 
</span><span class="cx">     suite.addTestCase({
</span><span class="lines">@@ -460,6 +462,7 @@
</span><span class="cx">         test(resolve, reject) {
</span><span class="cx">             startRecording(WI.Canvas.ContextType.Canvas2D, resolve, reject, {memoryLimit: 10});
</span><span class="cx">         },
</span><ins>+        timeout: -1,
</ins><span class="cx">     });
</span><span class="cx"> 
</span><span class="cx">     suite.addTestCase({
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorcanvasrecordingwebglsnapshotshtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/canvas/recording-webgl-snapshots.html (243952 => 243953)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/canvas/recording-webgl-snapshots.html        2019-04-05 23:25:27 UTC (rev 243952)
+++ trunk/LayoutTests/inspector/canvas/recording-webgl-snapshots.html   2019-04-05 23:39:16 UTC (rev 243953)
</span><span class="lines">@@ -102,6 +102,7 @@
</span><span class="cx">         test(resolve, reject) {
</span><span class="cx">             startRecording(WI.Canvas.ContextType.WebGL, resolve, reject, {frameCount: 1, checkForContentChange: true});
</span><span class="cx">         },
</span><ins>+        timeout: -1,
</ins><span class="cx">     });
</span><span class="cx"> 
</span><span class="cx">     suite.runTestCasesAndFinish();
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorcanvasrecordingwebglhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/canvas/recording-webgl.html (243952 => 243953)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/canvas/recording-webgl.html  2019-04-05 23:25:27 UTC (rev 243952)
+++ trunk/LayoutTests/inspector/canvas/recording-webgl.html     2019-04-05 23:39:16 UTC (rev 243953)
</span><span class="lines">@@ -526,6 +526,7 @@
</span><span class="cx">         test(resolve, reject) {
</span><span class="cx">             startRecording(WI.Canvas.ContextType.WebGL, resolve, reject, {frameCount: 1});
</span><span class="cx">         },
</span><ins>+        timeout: -1,
</ins><span class="cx">     });
</span><span class="cx"> 
</span><span class="cx">     suite.addTestCase({
</span><span class="lines">@@ -534,6 +535,7 @@
</span><span class="cx">         test(resolve, reject) {
</span><span class="cx">             startRecording(WI.Canvas.ContextType.WebGL, resolve, reject);
</span><span class="cx">         },
</span><ins>+        timeout: -1,
</ins><span class="cx">     });
</span><span class="cx"> 
</span><span class="cx">     suite.addTestCase({
</span><span class="lines">@@ -542,6 +544,7 @@
</span><span class="cx">         test(resolve, reject) {
</span><span class="cx">             startRecording(WI.Canvas.ContextType.WebGL, resolve, reject, {memoryLimit: 10});
</span><span class="cx">         },
</span><ins>+        timeout: -1,
</ins><span class="cx">     });
</span><span class="cx"> 
</span><span class="cx">     suite.addTestCase({
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorcanvasresourcesshaderProgramutilitiesjs"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/canvas/resources/shaderProgram-utilities.js (243952 => 243953)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/canvas/resources/shaderProgram-utilities.js  2019-04-05 23:25:27 UTC (rev 243952)
+++ trunk/LayoutTests/inspector/canvas/resources/shaderProgram-utilities.js     2019-04-05 23:39:16 UTC (rev 243953)
</span><span class="lines">@@ -46,25 +46,23 @@
</span><span class="cx"> TestPage.registerInitializer(() => {
</span><span class="cx">     let suite = null;
</span><span class="cx"> 
</span><del>-    function awaitProgramAdded() {
</del><ins>+    function whenProgramAdded(callback) {
</ins><span class="cx">         InspectorTest.assert(WI.canvasManager.canvases.length === 1, "There should only be one canvas.");
</span><del>-        return WI.canvasManager.canvases[0].shaderProgramCollection.awaitEvent(WI.Collection.Event.ItemAdded)
-        .then((event) => {
</del><ins>+        WI.canvasManager.canvases[0].shaderProgramCollection.singleFireEventListener(WI.Collection.Event.ItemAdded, (event) => {
</ins><span class="cx">             let program = event.data.item;
</span><span class="cx">             InspectorTest.expectThat(program instanceof WI.ShaderProgram, "Added ShaderProgram.");
</span><span class="cx">             InspectorTest.expectThat(program.canvas instanceof WI.Canvas, "ShaderProgram should have a parent Canvas.");
</span><del>-            return program;
</del><ins>+            callback(program);
</ins><span class="cx">         });
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    function awaitProgramRemoved() {
</del><ins>+    function whenProgramRemoved(callback) {
</ins><span class="cx">         InspectorTest.assert(WI.canvasManager.canvases.length === 1, "There should only be one canvas.");
</span><del>-        return WI.canvasManager.canvases[0].shaderProgramCollection.awaitEvent(WI.Collection.Event.ItemRemoved)
-        .then((event) => {
</del><ins>+        WI.canvasManager.canvases[0].shaderProgramCollection.singleFireEventListener(WI.Collection.Event.ItemRemoved, (event) => {
</ins><span class="cx">             let program = event.data.item;
</span><span class="cx">             InspectorTest.expectThat(program instanceof WI.ShaderProgram, "Removed ShaderProgram.");
</span><span class="cx">             InspectorTest.expectThat(program.canvas instanceof WI.Canvas, "ShaderProgram should have a parent Canvas.");
</span><del>-            return program;
</del><ins>+            callback(program);
</ins><span class="cx">         });
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -77,7 +75,9 @@
</span><span class="cx">             test(resolve, reject) {
</span><span class="cx">                 // This can't use `awaitEvent` since the promise resolution happens on the next tick.
</span><span class="cx">                 WI.canvasManager.singleFireEventListener(WI.CanvasManager.Event.CanvasAdded, (event) => {
</span><del>-                    awaitProgramAdded().then(resolve, reject);
</del><ins>+                    whenProgramAdded((program) => {
+                        resolve();
+                    });
</ins><span class="cx">                 });
</span><span class="cx"> 
</span><span class="cx">                 InspectorTest.reloadPage();
</span><span class="lines">@@ -92,13 +92,11 @@
</span><span class="cx">             name: `${suite.name}.ShaderProgramAdded`,
</span><span class="cx">             description: "Check that added/removed events are sent.",
</span><span class="cx">             test(resolve, reject) {
</span><del>-                awaitProgramAdded()
-                .then((addedProgram) => {
-                    awaitProgramRemoved()
-                    .then((removedProgram) => {
</del><ins>+                whenProgramAdded((addedProgram) => {
+                    whenProgramRemoved((removedProgram) => {
</ins><span class="cx">                         InspectorTest.expectEqual(removedProgram, addedProgram, "Removed the previously added ShaderProgram.");
</span><del>-                    })
-                    .then(resolve, reject);
</del><ins>+                        resolve();
+                    });
</ins><span class="cx"> 
</span><span class="cx">                     InspectorTest.evaluateInPage(`deleteProgram()`);
</span><span class="cx">                 });
</span><span class="lines">@@ -113,17 +111,17 @@
</span><span class="cx">             name: `${suite.name}.ParentCanvasRemoved`,
</span><span class="cx">             description: "Check that the ShaderProgram is removed before it's parent Canvas.",
</span><span class="cx">             test(resolve, reject) {
</span><del>-                Promise.race([
-                    awaitProgramRemoved()
-                    .then(() => {
-                        InspectorTest.pass("Removed ShaderProgram before Canvas.");
-                        resolve();
-                    }),
-                    WI.canvasManager.awaitEvent(WI.CanvasManager.Event.CanvasRemoved)
-                    .then(reject)
-                ])
-                .catch(() => { InspectorTest.fail("Removed Canvas before ShaderProgram."); });
</del><ins>+                let canvasRemoved = false;
</ins><span class="cx"> 
</span><ins>+                WI.canvasManager.singleFireEventListener(WI.CanvasManager.Event.CanvasRemoved, (event) => {
+                    canvasRemoved = true;
+                });
+
+                whenProgramRemoved((program) => {
+                    InspectorTest.expectFalse(canvasRemoved, "Removed ShaderProgram before Canvas.");
+                    resolve();
+                });
+
</ins><span class="cx">                 InspectorTest.evaluateInPage(`createProgram("${contextType}")`);
</span><span class="cx">                 InspectorTest.evaluateInPage(`deleteContext()`);
</span><span class="cx">             }
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorconsolecommandlineapiexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/console/command-line-api-expected.txt (243952 => 243953)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/console/command-line-api-expected.txt        2019-04-05 23:25:27 UTC (rev 243952)
+++ trunk/LayoutTests/inspector/console/command-line-api-expected.txt   2019-04-05 23:39:16 UTC (rev 243953)
</span><span class="lines">@@ -10,13 +10,13 @@
</span><span class="cx"> -- Running test case: EvaluateArrayValues
</span><span class="cx"> Input: values([3,4])
</span><span class="cx"> Output: 3,4
</span><ins>+
</ins><span class="cx"> -- Running test setup.
</span><del>-
</del><span class="cx"> -- Running test case: EvaluateDollarZero
</span><span class="cx"> Input: $0
</span><span class="cx"> Output: [object HTMLParagraphElement]
</span><ins>+
</ins><span class="cx"> -- Running test setup.
</span><del>-
</del><span class="cx"> -- Running test case: EvaluateInvalidSelector
</span><span class="cx"> Input: $('foo')
</span><span class="cx"> CONSOLE: The console function $() has changed from $=getElementById(id) to $=querySelector(selector). You might try $("#%s")
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorconsoleheapSnapshothtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/console/heapSnapshot.html (243952 => 243953)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/console/heapSnapshot.html    2019-04-05 23:25:27 UTC (rev 243952)
+++ trunk/LayoutTests/inspector/console/heapSnapshot.html       2019-04-05 23:39:16 UTC (rev 243953)
</span><span class="lines">@@ -27,7 +27,8 @@
</span><span class="cx">             });
</span><span class="cx"> 
</span><span class="cx">             ProtocolTest.evaluateInPage("triggerHeapSnapshotNoTitle()");
</span><del>-        }
</del><ins>+        },
+        timeout: -1,
</ins><span class="cx">     });
</span><span class="cx"> 
</span><span class="cx">     suite.addTestCase({
</span><span class="lines">@@ -42,7 +43,8 @@
</span><span class="cx">             });
</span><span class="cx"> 
</span><span class="cx">             ProtocolTest.evaluateInPage("triggerHeapSnapshotWithTitle()");
</span><del>-        }
</del><ins>+        },
+        timeout: -1,
</ins><span class="cx">     });
</span><span class="cx"> 
</span><span class="cx">     suite.runTestCasesAndFinish();
</span></span></pre></div>
<a id="trunkLayoutTestsinspectordebuggerasyncstacktracetruncateexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/debugger/async-stack-trace-truncate-expected.txt (243952 => 243953)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/debugger/async-stack-trace-truncate-expected.txt     2019-04-05 23:25:27 UTC (rev 243952)
+++ trunk/LayoutTests/inspector/debugger/async-stack-trace-truncate-expected.txt        2019-04-05 23:39:16 UTC (rev 243953)
</span><span class="lines">@@ -10,9 +10,9 @@
</span><span class="cx"> 0: [F] handleAnimationFrame
</span><span class="cx"> PASS: Async stack trace should be null.
</span><span class="cx"> -- Running test teardown.
</span><ins>+
</ins><span class="cx"> -- Running test setup.
</span><span class="cx"> Set maximum stack trace depth = 10.
</span><del>-
</del><span class="cx"> -- Running test case: AsyncStackTrace.CheckTruncated
</span><span class="cx"> PAUSED
</span><span class="cx"> CALL STACK:
</span><span class="lines">@@ -31,9 +31,9 @@
</span><span class="cx"> (remaining call frames truncated)
</span><span class="cx"> PASS: Async stack trace should be truncated.
</span><span class="cx"> -- Running test teardown.
</span><ins>+
</ins><span class="cx"> -- Running test setup.
</span><span class="cx"> Set maximum stack trace depth = 10.
</span><del>-
</del><span class="cx"> -- Running test case: AsyncStackTrace.CheckNotTruncated
</span><span class="cx"> PAUSED
</span><span class="cx"> CALL STACK:
</span></span></pre></div>
<a id="trunkLayoutTestsinspectordebuggerpauseforinternalscriptsexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/debugger/pause-for-internal-scripts-expected.txt (243952 => 243953)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/debugger/pause-for-internal-scripts-expected.txt     2019-04-05 23:25:27 UTC (rev 243952)
+++ trunk/LayoutTests/inspector/debugger/pause-for-internal-scripts-expected.txt        2019-04-05 23:39:16 UTC (rev 243953)
</span><span class="lines">@@ -65,8 +65,8 @@
</span><span class="cx"> ACTION: resume
</span><span class="cx"> RESUMED
</span><span class="cx"> PASS: Should have used all steps.
</span><ins>+
</ins><span class="cx"> -- Running test setup.
</span><del>-
</del><span class="cx"> -- Running test case: Debugger.setPauseForInternalScripts.Enabled
</span><span class="cx"> EXPRESSION: setTimeout(entryConsoleLog)
</span><span class="cx"> STEPS: over, in, in, in, resume
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorformattingresourcesutilitiesjs"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/formatting/resources/utilities.js (243952 => 243953)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/formatting/resources/utilities.js    2019-04-05 23:25:27 UTC (rev 243952)
+++ trunk/LayoutTests/inspector/formatting/resources/utilities.js       2019-04-05 23:39:16 UTC (rev 243953)
</span><span class="lines">@@ -43,7 +43,7 @@
</span><span class="cx"> 
</span><span class="cx">     window.addFormattingTests = function(suite, mode, tests) {
</span><span class="cx">         let testPageURL = WI.networkManager.mainFrame.mainResource.url;
</span><del>-        let testPageResourcesURL = testPageURL.substring(0, testPageURL.lastIndexOf("/"));            
</del><ins>+        let testPageResourcesURL = testPageURL.substring(0, testPageURL.lastIndexOf("/"));
</ins><span class="cx"> 
</span><span class="cx">         for (let test of tests) {
</span><span class="cx">             let testName = test.substring(test.lastIndexOf("/") + 1);
</span><span class="lines">@@ -52,7 +52,8 @@
</span><span class="cx">                 name: suite.name + "." + testName,
</span><span class="cx">                 test(resolve, reject) {
</span><span class="cx">                     runFormattingTest(mode, testName, testURL).then(resolve).catch(reject);
</span><del>-                }
</del><ins>+                },
+                timeout: -1,
</ins><span class="cx">             });
</span><span class="cx">         }
</span><span class="cx">     };
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsasynctestsuiteexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/unit-tests/async-test-suite-expected.txt (243952 => 243953)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/async-test-suite-expected.txt     2019-04-05 23:25:27 UTC (rev 243952)
+++ trunk/LayoutTests/inspector/unit-tests/async-test-suite-expected.txt        2019-04-05 23:39:16 UTC (rev 243953)
</span><span class="lines">@@ -1,152 +1,439 @@
</span><del>-PASS: instantiating AsyncTestSuite requires name argument.
-PASS: instantiating AsyncTestSuite requires string name argument.
-PASS: instantiating AsyncTestSuite requires non-whitespace name argument.
-PASS: instantiating AsyncTestSuite requires test harness argument.
-PASS: should not be able to add empty test case.
-PASS: should not be able to add non-object test case.
-PASS: test case should require string name.
-PASS: test case should require non-whitespace name.
-PASS: test case should require test function.
-PASS: should not be able to specify non-Function `setup` parameter.
-PASS: should not be able to specify non-Function `setup` parameter.
-PASS: should not be able to specify non-Function `setup` parameter.
-PASS: should not be able to specify non-Function `teardown` parameter.
-PASS: should not be able to specify non-Function `teardown` parameter.
-PASS: should not be able to specify non-Function `teardown` parameter.
</del><ins>+PASS: Should produce an exception.
+Error: Must pass the test's harness as the first argument.
+PASS: Should produce an exception.
+Error: Must pass the test's harness as the first argument.
+PASS: Should produce an exception.
+Error: Must pass the test's harness as the first argument.
+PASS: Should produce an exception.
+Error: Must pass the test's harness as the first argument.
+PASS: Should produce an exception.
+Error: Tried to add non-object test case.
+PASS: Should produce an exception.
+Error: Tried to add non-object test case.
+PASS: Should produce an exception.
+Error: Tried to add test case without a name.
+PASS: Should produce an exception.
+Error: Tried to add test case without a name.
+PASS: Should produce an exception.
+Error: Tried to add test case without `test` function.
+PASS: Should produce an exception.
+Error: Tried to add test case with invalid `setup` parameter (must be a function).
+PASS: Should produce an exception.
+Error: Tried to add test case with invalid `setup` parameter (must be a function).
+PASS: Should produce an exception.
+Error: Tried to add test case with invalid `setup` parameter (must be a function).
+PASS: Should produce an exception.
+Error: Tried to add test case with invalid `teardown` parameter (must be a function).
+PASS: Should produce an exception.
+Error: Tried to add test case with invalid `teardown` parameter (must be a function).
+PASS: Should produce an exception.
+Error: Tried to add test case with invalid `teardown` parameter (must be a function).
</ins><span class="cx"> PASS: should not be able to run empty test suite.
</span><span class="cx"> 
</span><span class="cx"> == Running test suite: AsyncTestSuite.RunTwiceSuite
</span><del>-PASS: should not be able to run a test suite twice.
</del><ins>+PASS: Should produce an exception.
+Error: Tried to call runTestCases() more than once.
</ins><span class="cx"> -- Running test case: DummyTest0
</span><ins>+PASS: DummyTest0 should only run once.
</ins><span class="cx"> 
</span><del>-== Running test suite: AsyncTestSuite.SequentialExecution
-PASS: AsyncTestSuite.RunTestCases() should return a Promise.
--- Running test case: DummyTest1
--- Running test case: DummyTest2
--- Running test case: DummyTest3
--- Running test case: FailingTest4
-!! EXCEPTION: [object Object]
</del><ins>+== Running test suite: AsyncTestSuite.PromiseFunctionSuccess
+-- Running test case: PromiseFunctionSuccess
+
+PASS: Promise from AsyncTestSuite.PromiseFunctionSuccess.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.PromiseFunctionSuccess should have executed 1 tests.
+PASS: AsyncTestSuite.PromiseFunctionSuccess should have passed 1 tests.
+PASS: AsyncTestSuite.PromiseFunctionSuccess should have failed 0 tests.
+PASS: AsyncTestSuite.PromiseFunctionSuccess should have skipped 0 tests.
+
+== Running test suite: PromiseTestSuite.PromiseFunctionException
+-- Running test case: PromiseFunctionException
+Throwing...
+!! EXCEPTION: PromiseFunctionException throw
</ins><span class="cx"> Stack Trace: (suppressed)
</span><del>-PASS: Promise from sequentialExecutionSuite.runTestCases() should reject when a test case fails.
-PASS: Promise from sequentialExecutionSuite.runTestCases() should reject without altering its result value.
-PASS: sequentialExecutionSuite should have executed four tests.
-PASS: sequentialExecutionSuite should have passed three tests.
-PASS: sequentialExecutionSuite should have failed 1 test.
-PASS: sequentialExecutionSuite should have skipped zero tests.
</del><span class="cx"> 
</span><del>-== Running test suite: AsyncTestSuite.AbortOnFailure
--- Running test case: PassingTest5
--- Running test case: FailingTest6
-!! EXCEPTION: {"token":666}
</del><ins>+PASS: Promise from PromiseTestSuite.PromiseFunctionException.runTestCases() should resolve even if a test case fails.
+PASS: PromiseTestSuite.PromiseFunctionException should have executed 1 tests.
+PASS: PromiseTestSuite.PromiseFunctionException should have passed 0 tests.
+PASS: PromiseTestSuite.PromiseFunctionException should have failed 1 tests.
+PASS: PromiseTestSuite.PromiseFunctionException should have skipped 0 tests.
+
+== Running test suite: PromiseTestSuite.PromiseFunctionFailure
+-- Running test case: PromiseFunctionFailure
+Rejecting...
+!! EXCEPTION: PromiseFunctionFailure reject
</ins><span class="cx"> Stack Trace: (suppressed)
</span><del>-PASS: Promise from abortOnFailureSuite.runTestCases() should reject when a test case fails.
-PASS: Promise from abortOnFailureSuite.runTestCases() should reject without altering its result value.
-PASS: abortOnFailureSuite should have executed two tests.
-PASS: abortOnFailureSuite should have passed one test.
-PASS: abortOnFailureSuite should have failed one test.
-PASS: abortOnFailureSuite should have skipped one test.
</del><span class="cx"> 
</span><del>-== Running test suite: AsyncTestSuite.SetupAndTeardown
</del><ins>+PASS: Promise from PromiseTestSuite.PromiseFunctionFailure.runTestCases() should resolve even if a test case fails.
+PASS: PromiseTestSuite.PromiseFunctionFailure should have executed 1 tests.
+PASS: PromiseTestSuite.PromiseFunctionFailure should have passed 0 tests.
+PASS: PromiseTestSuite.PromiseFunctionFailure should have failed 1 tests.
+PASS: PromiseTestSuite.PromiseFunctionFailure should have skipped 0 tests.
+
+== Running test suite: AsyncTestSuite.PromiseSequentialExecution
+-- Running test case: 1 (Pass)
+-- Running test case: 2 (Pass)
+-- Running test case: 3 (Pass)
+-- Running test case: 4 (Pass)
+
+PASS: Promise from AsyncTestSuite.PromiseSequentialExecution.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.PromiseSequentialExecution should have executed 4 tests.
+PASS: AsyncTestSuite.PromiseSequentialExecution should have passed 4 tests.
+PASS: AsyncTestSuite.PromiseSequentialExecution should have failed 0 tests.
+PASS: AsyncTestSuite.PromiseSequentialExecution should have skipped 0 tests.
+
+== Running test suite: AsyncTestSuite.PromiseContinueOnFailure
+-- Running test case: 1 (Pass)
+-- Running test case: 2 (Fail)
+Throwing...
+!! EXCEPTION: {"x":"PromiseContinueOnFailure throw"}
+Stack Trace: (suppressed)
+
+-- Running test case: 3 (Pass)
+-- Running test case: 4 (Fail)
+Rejecting...
+!! EXCEPTION: {"x":"PromiseContinueOnFailure reject"}
+Stack Trace: (suppressed)
+
+PASS: Promise from AsyncTestSuite.PromiseContinueOnFailure.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.PromiseContinueOnFailure should have executed 4 tests.
+PASS: AsyncTestSuite.PromiseContinueOnFailure should have passed 2 tests.
+PASS: AsyncTestSuite.PromiseContinueOnFailure should have failed 2 tests.
+PASS: AsyncTestSuite.PromiseContinueOnFailure should have skipped 0 tests.
+
+== Running test suite: AsyncTestSuite.PromiseSetupAndTeardown
</ins><span class="cx"> -- Running test setup.
</span><span class="cx"> -- Running test case: TestWithSetupAndTeardown
</span><del>-PASS: Test should see side effects of running setup() action.
</del><span class="cx"> -- Running test teardown.
</span><del>-PASS: Teardown should see side effects of running setup() action.
-
</del><span class="cx"> -- Running test case: TestRunningAfterTeardown
</span><del>-PASS: Test should see side effects of previous test's teardown() action.
-PASS: Promise from setupAndTeardownTestSuite.runTestCases() should resolve.
</del><span class="cx"> 
</span><del>-== Running test suite: AsyncTestSuite.SetupException
</del><ins>+PASS: Promise from AsyncTestSuite.PromiseSetupAndTeardown.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.PromiseSetupAndTeardown should have executed 2 tests.
+PASS: AsyncTestSuite.PromiseSetupAndTeardown should have passed 2 tests.
+PASS: AsyncTestSuite.PromiseSetupAndTeardown should have failed 0 tests.
+PASS: AsyncTestSuite.PromiseSetupAndTeardown should have skipped 0 tests.
+
+== Running test suite: AsyncTestSuite.PromiseSetupException
</ins><span class="cx"> -- Running test setup.
</span><del>-!! EXCEPTION: 
</del><ins>+Throwing...
+!! EXCEPTION: PromiseSetupException throw
</ins><span class="cx"> Stack Trace: (suppressed)
</span><del>-PASS: Promise from setupExceptionTestSuite.runTestCases() should reject.
</del><span class="cx"> 
</span><del>-== Running test suite: AsyncTestSuite.SetupFailure
</del><span class="cx"> -- Running test setup.
</span><del>-!! EXCEPTION: undefined
</del><ins>+PASS: Setup action should still execute if previous test's setup action threw an exception.
+-- Running test case: TestAfterSetupException
+PASS: Test should still execute if previous test's setup action threw an exception.
+-- Running test teardown.
+PASS: Teardown action should still execute if previous test's setup action threw an exception.
+
+PASS: Promise from AsyncTestSuite.PromiseSetupException.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.PromiseSetupException should have executed 1 tests.
+PASS: AsyncTestSuite.PromiseSetupException should have passed 1 tests.
+PASS: AsyncTestSuite.PromiseSetupException should have failed 1 tests.
+PASS: AsyncTestSuite.PromiseSetupException should have skipped 1 tests.
+
+== Running test suite: AsyncTestSuite.PromiseSetupFailure
+-- Running test setup.
+Rejecting...
+!! EXCEPTION: PromiseSetupFailure reject
</ins><span class="cx"> Stack Trace: (suppressed)
</span><del>-PASS: Promise from setupFailureTestSuite.runTestCases() should reject.
</del><span class="cx"> 
</span><del>-== Running test suite: AsyncTestSuite.TeardownException
</del><ins>+-- Running test setup.
+PASS: Setup action should still execute if previous test's setup action failed.
+-- Running test case: TestAfterSetupException
+PASS: Test should still execute if previous test's setup action failed.
+-- Running test teardown.
+PASS: Teardown action should still execute if previous test's setup action failed.
+
+PASS: Promise from AsyncTestSuite.PromiseSetupFailure.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.PromiseSetupFailure should have executed 1 tests.
+PASS: AsyncTestSuite.PromiseSetupFailure should have passed 1 tests.
+PASS: AsyncTestSuite.PromiseSetupFailure should have failed 1 tests.
+PASS: AsyncTestSuite.PromiseSetupFailure should have skipped 1 tests.
+
+== Running test suite: AsyncTestSuite.PromiseTeardownException
</ins><span class="cx"> -- Running test case: TestWithExceptionDuringTeardown
</span><span class="cx"> -- Running test teardown.
</span><del>-!! EXCEPTION: 
</del><ins>+Throwing...
+!! EXCEPTION: PromiseTeardownException throw
</ins><span class="cx"> Stack Trace: (suppressed)
</span><del>-PASS: Promise from teardownExceptionTestSuite.runTestCases() should reject.
</del><span class="cx"> 
</span><del>-== Running test suite: AsyncTestSuite.TeardownFailure
</del><ins>+-- Running test setup.
+PASS: Setup action should still execute if previous test's teardown action threw an exception.
+-- Running test case: TestAfterTeardownException
+PASS: Test should still execute if previous test's teardown action threw an exception.
+-- Running test teardown.
+PASS: Teardown action should still execute if previous test's teardown action threw an exception.
+
+PASS: Promise from AsyncTestSuite.PromiseTeardownException.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.PromiseTeardownException should have executed 2 tests.
+PASS: AsyncTestSuite.PromiseTeardownException should have passed 1 tests.
+PASS: AsyncTestSuite.PromiseTeardownException should have failed 1 tests.
+PASS: AsyncTestSuite.PromiseTeardownException should have skipped 0 tests.
+
+== Running test suite: AsyncTestSuite.PromiseTeardownFailure
</ins><span class="cx"> -- Running test case: TestWithExceptionDuringTeardown
</span><span class="cx"> -- Running test teardown.
</span><del>-!! EXCEPTION: undefined
</del><ins>+Rejecting...
+!! EXCEPTION: PromiseTeardownFailure reject
</ins><span class="cx"> Stack Trace: (suppressed)
</span><del>-PASS: Promise from teardownFailureTestSuite.runTestCases() should reject.
</del><span class="cx"> 
</span><ins>+-- Running test setup.
+PASS: Setup action should still execute if previous test's teardown action failed.
+-- Running test case: TestAfterTeardownException
+PASS: Test should still execute if previous test's teardown action failed.
+-- Running test teardown.
+PASS: Teardown action should still execute if previous test's teardown action failed.
+
+PASS: Promise from AsyncTestSuite.PromiseTeardownFailure.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.PromiseTeardownFailure should have executed 2 tests.
+PASS: AsyncTestSuite.PromiseTeardownFailure should have passed 1 tests.
+PASS: AsyncTestSuite.PromiseTeardownFailure should have failed 1 tests.
+PASS: AsyncTestSuite.PromiseTeardownFailure should have skipped 0 tests.
+
+== Running test suite: AsyncTestSuite.PromiseTimeout
+-- Running test case: PromiseTestWithTimeout
+Timeout...
+!! TIMEOUT: took longer than 5ms
+
+-- Running test setup.
+PASS: Setup action should still execute if previous test timed out.
+-- Running test case: PromiseTestAfterTimeout
+PASS: Test should still execute if previous test timed out.
+-- Running test teardown.
+PASS: Teardown action should still execute if previous test timed out.
+
+PASS: Promise from AsyncTestSuite.PromiseTimeout.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.PromiseTimeout should have executed 2 tests.
+PASS: AsyncTestSuite.PromiseTimeout should have passed 1 tests.
+PASS: AsyncTestSuite.PromiseTimeout should have failed 1 tests.
+PASS: AsyncTestSuite.PromiseTimeout should have skipped 0 tests.
+
</ins><span class="cx"> == Running test suite: AsyncTestSuite.AsyncFunctionSuccess
</span><span class="cx"> -- Running test case: AsyncFunctionSuccess
</span><del>-PASS: Promise from asyncFunctionSuccessTestSuite.runTestCases() should succeed.
-PASS: Promise did evaluate the async test function.
-PASS: Resolved value should be 42.
</del><span class="cx"> 
</span><del>-== Running test suite: AsyncTestSuite.AsyncFunctionExplicitFailure
--- Running test case: AsyncFunctionFailure
-!! EXCEPTION: AsyncFunctionFailure Exception Message
</del><ins>+PASS: Promise from AsyncTestSuite.AsyncFunctionSuccess.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.AsyncFunctionSuccess should have executed 1 tests.
+PASS: AsyncTestSuite.AsyncFunctionSuccess should have passed 1 tests.
+PASS: AsyncTestSuite.AsyncFunctionSuccess should have failed 0 tests.
+PASS: AsyncTestSuite.AsyncFunctionSuccess should have skipped 0 tests.
+
+== Running test suite: AsyncTestSuite.AsyncFunctionExplicitException
+-- Running test case: AsyncFunctionExplicitException
+Throwing...
+!! EXCEPTION: AsyncFunctionExplicitException throw
</ins><span class="cx"> Stack Trace: (suppressed)
</span><del>-PASS: Promise from asyncFunctionExplicitFailureTestSuite.runTestCases() should reject.
-PASS: Promise did evaluate the async test function.
-PASS: Rejected value should be thrown exception.
</del><span class="cx"> 
</span><del>-== Running test suite: AsyncTestSuite.AsyncFunctionRuntimeFailure
--- Running test case: AsyncFunctionFailure
</del><ins>+PASS: Promise from AsyncTestSuite.AsyncFunctionExplicitException.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.AsyncFunctionExplicitException should have executed 1 tests.
+PASS: AsyncTestSuite.AsyncFunctionExplicitException should have passed 0 tests.
+PASS: AsyncTestSuite.AsyncFunctionExplicitException should have failed 1 tests.
+PASS: AsyncTestSuite.AsyncFunctionExplicitException should have skipped 0 tests.
+
+== Running test suite: AsyncTestSuite.AsyncFunctionRuntimeException
+-- Running test case: AsyncFunctionRuntimeException
+Throwing...
</ins><span class="cx"> !! EXCEPTION: undefined is not an object (evaluating '({}).x.x')
</span><span class="cx"> Stack Trace: (suppressed)
</span><del>-PASS: Promise from asyncFunctionRuntimeFailureTestSuite.runTestCases() should reject.
-PASS: Promise did evaluate the async test function.
-PASS: Rejected value should be a runtime exception.
</del><span class="cx"> 
</span><del>-== Running test suite: AsyncTestSuite.AsyncSetupAndAsyncTeardown
</del><ins>+PASS: Promise from AsyncTestSuite.AsyncFunctionRuntimeException.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.AsyncFunctionRuntimeException should have executed 1 tests.
+PASS: AsyncTestSuite.AsyncFunctionRuntimeException should have passed 0 tests.
+PASS: AsyncTestSuite.AsyncFunctionRuntimeException should have failed 1 tests.
+PASS: AsyncTestSuite.AsyncFunctionRuntimeException should have skipped 0 tests.
+
+== Running test suite: AsyncTestSuite.AsyncFunctionFailure
+-- Running test case: AsyncFunctionException
+Rejecting...
+!! EXCEPTION: AsyncFunctionFailure reject
+Stack Trace: (suppressed)
+
+PASS: Promise from AsyncTestSuite.AsyncFunctionFailure.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.AsyncFunctionFailure should have executed 1 tests.
+PASS: AsyncTestSuite.AsyncFunctionFailure should have passed 0 tests.
+PASS: AsyncTestSuite.AsyncFunctionFailure should have failed 1 tests.
+PASS: AsyncTestSuite.AsyncFunctionFailure should have skipped 0 tests.
+
+== Running test suite: AsyncTestSuite.AsyncSequentialExecution
+-- Running test case: 1 (Pass)
+-- Running test case: 2 (Pass)
+-- Running test case: 3 (Pass)
+-- Running test case: 4 (Pass)
+
+PASS: Promise from AsyncTestSuite.AsyncSequentialExecution.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.AsyncSequentialExecution should have executed 4 tests.
+PASS: AsyncTestSuite.AsyncSequentialExecution should have passed 4 tests.
+PASS: AsyncTestSuite.AsyncSequentialExecution should have failed 0 tests.
+PASS: AsyncTestSuite.AsyncSequentialExecution should have skipped 0 tests.
+
+== Running test suite: AsyncTestSuite.AsyncContinueOnFailure
+-- Running test case: 1 (Pass)
+-- Running test case: 2 (Fail)
+Throwing...
+!! EXCEPTION: {"x":"AsyncContinueOnFailure throw"}
+Stack Trace: (suppressed)
+
+-- Running test case: 3 (Pass)
+-- Running test case: 4 (Fail)
+Throwing...
+!! EXCEPTION: undefined is not an object (evaluating '({}).x.x')
+Stack Trace: (suppressed)
+
+-- Running test case: 5 (Pass)
+-- Running test case: 6 (Fail)
+Rejecting...
+!! EXCEPTION: AsyncContinueOnFailure reject
+Stack Trace: (suppressed)
+
+PASS: Promise from AsyncTestSuite.AsyncContinueOnFailure.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.AsyncContinueOnFailure should have executed 6 tests.
+PASS: AsyncTestSuite.AsyncContinueOnFailure should have passed 3 tests.
+PASS: AsyncTestSuite.AsyncContinueOnFailure should have failed 3 tests.
+PASS: AsyncTestSuite.AsyncContinueOnFailure should have skipped 0 tests.
+
+== Running test suite: AsyncTestSuite.AsyncSetupAndTeardown
</ins><span class="cx"> -- Running test setup.
</span><del>--- Running test case: TestWithSetupAndTeardown
-PASS: Test should see side effects of running setup() action.
</del><ins>+-- Running test case: AsyncTestWithSetupAndTeardown
</ins><span class="cx"> -- Running test teardown.
</span><del>-PASS: Teardown should see side effects of running setup() action.
</del><ins>+-- Running test case: AsyncTestRunningAfterTeardown
</ins><span class="cx"> 
</span><del>--- Running test case: TestRunningAfterTeardown
-PASS: Test should see side effects of previous test's teardown() action.
-PASS: Promise from asyncSetupAndAsyncTeardownTestSuite.runTestCases() should resolve.
</del><ins>+PASS: Promise from AsyncTestSuite.AsyncSetupAndTeardown.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.AsyncSetupAndTeardown should have executed 2 tests.
+PASS: AsyncTestSuite.AsyncSetupAndTeardown should have passed 2 tests.
+PASS: AsyncTestSuite.AsyncSetupAndTeardown should have failed 0 tests.
+PASS: AsyncTestSuite.AsyncSetupAndTeardown should have skipped 0 tests.
</ins><span class="cx"> 
</span><del>-== Running test suite: AsyncTestSuite.AsyncSetupExplicitFailure
--- Running test case: AsyncFunctionFailure
-!! EXCEPTION: AsyncFunctionFailure Exception Message
</del><ins>+== Running test suite: AsyncTestSuite.AsyncSetupExplicitException
+-- Running test setup.
+Throwing...
+!! EXCEPTION: AsyncSetupExplicitException throw
</ins><span class="cx"> Stack Trace: (suppressed)
</span><del>-PASS: Promise from asyncSetupExplicitFailureTestSuite.runTestCases() should reject.
-PASS: Promise did evaluate the async setup function.
-PASS: Rejected value should be thrown exception.
</del><span class="cx"> 
</span><del>-== Running test suite: AsyncTestSuite.AsyncSetupRuntimeFailure
</del><span class="cx"> -- Running test setup.
</span><ins>+PASS: Setup action should still execute if previous test's setup action threw an exception.
+-- Running test case: AsyncTestAfterSetupExplicitException
+PASS: Test should still execute if previous test's setup action threw an exception.
+-- Running test teardown.
+PASS: Teardown action should still execute if previous test's setup action threw an exception.
+
+PASS: Promise from AsyncTestSuite.AsyncSetupExplicitException.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.AsyncSetupExplicitException should have executed 1 tests.
+PASS: AsyncTestSuite.AsyncSetupExplicitException should have passed 1 tests.
+PASS: AsyncTestSuite.AsyncSetupExplicitException should have failed 1 tests.
+PASS: AsyncTestSuite.AsyncSetupExplicitException should have skipped 1 tests.
+
+== Running test suite: AsyncTestSuite.AsyncSetupRuntimeException
+-- Running test setup.
+Throwing...
</ins><span class="cx"> !! EXCEPTION: undefined is not an object (evaluating '({}).x.x')
</span><span class="cx"> Stack Trace: (suppressed)
</span><del>-PASS: Promise from asyncSetupRuntimeFailureTestSuite.runTestCases() should reject.
-PASS: Promise did evaluate the async setup function.
-PASS: Rejected value should be a runtime exception.
</del><span class="cx"> 
</span><del>-== Running test suite: AsyncTestSuite.AsyncTeardownExplicitFailure
--- Running test case: AsyncFunctionFailure
</del><ins>+-- Running test setup.
+PASS: Setup action should still execute if previous test's setup action threw an exception.
+-- Running test case: AsyncTestAfterSetupRuntimeException
+PASS: Test should still execute if previous test's setup action threw an exception.
</ins><span class="cx"> -- Running test teardown.
</span><del>-!! EXCEPTION: AsyncFunctionFailure Exception Message
</del><ins>+PASS: Teardown action should still execute if previous test's setup action threw an exception.
+
+PASS: Promise from AsyncTestSuite.AsyncSetupRuntimeException.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.AsyncSetupRuntimeException should have executed 1 tests.
+PASS: AsyncTestSuite.AsyncSetupRuntimeException should have passed 1 tests.
+PASS: AsyncTestSuite.AsyncSetupRuntimeException should have failed 1 tests.
+PASS: AsyncTestSuite.AsyncSetupRuntimeException should have skipped 1 tests.
+
+== Running test suite: AsyncTestSuite.AsyncSetupFailure
+-- Running test setup.
+Rejecting...
+!! EXCEPTION: AsyncSetupFailure reject
</ins><span class="cx"> Stack Trace: (suppressed)
</span><del>-PASS: Promise from asyncTeardownExplicitFailureTestSuite.runTestCases() should reject.
-PASS: Promise did evaluate the async teardown function.
-PASS: Rejected value should be thrown exception.
</del><span class="cx"> 
</span><del>-== Running test suite: AsyncTestSuite.AsyncTeardownRuntimeFailure
--- Running test case: AsyncFunctionFailure
</del><ins>+-- Running test setup.
+PASS: Setup action should still execute if previous test's setup action failed.
+-- Running test case: AsyncTestAfterSetupFailure
+PASS: Test should still execute if previous test's setup action failed.
</ins><span class="cx"> -- Running test teardown.
</span><ins>+PASS: Setup action should still execute if previous test's setup action failed.
+
+PASS: Promise from AsyncTestSuite.AsyncSetupFailure.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.AsyncSetupFailure should have executed 1 tests.
+PASS: AsyncTestSuite.AsyncSetupFailure should have passed 1 tests.
+PASS: AsyncTestSuite.AsyncSetupFailure should have failed 1 tests.
+PASS: AsyncTestSuite.AsyncSetupFailure should have skipped 1 tests.
+
+== Running test suite: AsyncTestSuite.AsyncTeardownExplicitException
+-- Running test case: AsyncTestWithExplicitExceptionDuringTeardown
+-- Running test teardown.
+Throwing...
+!! EXCEPTION: AsyncTeardownExplicitException throw
+Stack Trace: (suppressed)
+
+-- Running test setup.
+PASS: Setup action should still execute if previous test's teardown action threw an exception.
+-- Running test case: AsyncTestAfterTeardownExplicitException
+PASS: Test should still execute if previous test's teardown action threw an exception.
+-- Running test teardown.
+PASS: Teardown action should still execute if previous test's teardown action threw an exception.
+
+PASS: Promise from AsyncTestSuite.AsyncTeardownExplicitException.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.AsyncTeardownExplicitException should have executed 2 tests.
+PASS: AsyncTestSuite.AsyncTeardownExplicitException should have passed 1 tests.
+PASS: AsyncTestSuite.AsyncTeardownExplicitException should have failed 1 tests.
+PASS: AsyncTestSuite.AsyncTeardownExplicitException should have skipped 0 tests.
+
+== Running test suite: AsyncTestSuite.AsyncTeardownRuntimeException
+-- Running test case: AsyncTestWithRuntimeExceptionDuringTeardown
+-- Running test teardown.
+Throwing...
</ins><span class="cx"> !! EXCEPTION: undefined is not an object (evaluating '({}).x.x')
</span><span class="cx"> Stack Trace: (suppressed)
</span><del>-PASS: Promise from asyncTeardownRuntimeFailureTestSuite.runTestCases() should reject.
-PASS: Promise did evaluate the async teardown function.
-PASS: Rejected value should be a runtime exception.
</del><span class="cx"> 
</span><ins>+-- Running test setup.
+PASS: Setup action should still execute if previous test's teardown action threw an exception.
+-- Running test case: AsyncTestAfterTeardownRuntimeException
+PASS: Test should still execute if previous test's teardown action threw an exception.
+-- Running test teardown.
+PASS: Teardown action should still execute if previous test's teardown action threw an exception.
+
+PASS: Promise from AsyncTestSuite.AsyncTeardownRuntimeException.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.AsyncTeardownRuntimeException should have executed 2 tests.
+PASS: AsyncTestSuite.AsyncTeardownRuntimeException should have passed 1 tests.
+PASS: AsyncTestSuite.AsyncTeardownRuntimeException should have failed 1 tests.
+PASS: AsyncTestSuite.AsyncTeardownRuntimeException should have skipped 0 tests.
+
+== Running test suite: AsyncTestSuite.AsyncTeardownFailure
+-- Running test case: AsyncTestWithFailureDuringTeardown
+-- Running test teardown.
+Rejecting...
+!! EXCEPTION: AsyncTeardownFailure reject
+Stack Trace: (suppressed)
+
+-- Running test setup.
+PASS: Setup action should still execute if previous test's teardown action failed.
+-- Running test case: AsyncTestAfterTeardownFailure
+PASS: Test should still execute if previous test's teardown action failed.
+-- Running test teardown.
+PASS: Teardown action should still execute if previous test's teardown action failed.
+
+PASS: Promise from AsyncTestSuite.AsyncTeardownFailure.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.AsyncTeardownFailure should have executed 2 tests.
+PASS: AsyncTestSuite.AsyncTeardownFailure should have passed 1 tests.
+PASS: AsyncTestSuite.AsyncTeardownFailure should have failed 1 tests.
+PASS: AsyncTestSuite.AsyncTeardownFailure should have skipped 0 tests.
+
+== Running test suite: AsyncTestSuite.AsyncTimeout
+-- Running test case: AsyncTestWithTimeout
+Timeout...
+!! TIMEOUT: took longer than 5ms
+
+-- Running test setup.
+PASS: Setup action should still execute if previous test timed out.
+-- Running test case: AsyncTestAfterTimeout
+PASS: Test should still execute if previous test timed out.
+-- Running test teardown.
+PASS: Teardown action should still execute if previous test timed out.
+
+PASS: Promise from AsyncTestSuite.AsyncTimeout.runTestCases() should resolve even if a test case fails.
+PASS: AsyncTestSuite.AsyncTimeout should have executed 2 tests.
+PASS: AsyncTestSuite.AsyncTimeout should have passed 1 tests.
+PASS: AsyncTestSuite.AsyncTimeout should have failed 1 tests.
+PASS: AsyncTestSuite.AsyncTimeout should have skipped 0 tests.
+
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsasynctestsuitehtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/unit-tests/async-test-suite.html (243952 => 243953)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/async-test-suite.html     2019-04-05 23:25:27 UTC (rev 243952)
+++ trunk/LayoutTests/inspector/unit-tests/async-test-suite.html        2019-04-05 23:39:16 UTC (rev 243953)
</span><span class="lines">@@ -1,4 +1,4 @@
</span><del>-<!doctype html>
</del><ins>+<!DOCTYPE html>
</ins><span class="cx"> <html>
</span><span class="cx"> <head>
</span><span class="cx"> <script src="../../http/tests/inspector/resources/protocol-test.js"></script>
</span><span class="lines">@@ -7,134 +7,97 @@
</span><span class="cx"> {
</span><span class="cx">     ProtocolTest.suppressStackTraces = true;
</span><span class="cx"> 
</span><del>-    try {
-        let result = new AsyncTestSuite(this);
-        ProtocolTest.fail("instantiating AsyncTestSuite requires name argument.");
-    } catch (e) {
-        ProtocolTest.pass("instantiating AsyncTestSuite requires name argument.");
-    }
</del><ins>+    ProtocolTest.expectException(() => {
+        new AsyncTestSuite(this);
+    });
</ins><span class="cx"> 
</span><del>-    try {
-        let result = new AsyncTestSuite(this, {});
-        ProtocolTest.fail("instantiating AsyncTestSuite requires string name argument.");
-    } catch (e) {
-        ProtocolTest.pass("instantiating AsyncTestSuite requires string name argument.");
-    }
</del><ins>+    ProtocolTest.expectException(() => {
+        new AsyncTestSuite(this, {});
+    });
</ins><span class="cx"> 
</span><del>-    try {
-        let result = new AsyncTestSuite(this, "      ");
-        ProtocolTest.fail("instantiating AsyncTestSuite requires non-whitespace name argument.");
-    } catch (e) {
-        ProtocolTest.pass("instantiating AsyncTestSuite requires non-whitespace name argument.");
-    }
</del><ins>+    ProtocolTest.expectException(() => {
+        new AsyncTestSuite(this, "      ");
+    });
</ins><span class="cx"> 
</span><del>-    try {
-        let result = new AsyncTestSuite("something", {});
-        ProtocolTest.fail("instantiating AsyncTestSuite requires test harness argument.");
-    } catch (e) {
-        ProtocolTest.pass("instantiating AsyncTestSuite requires test harness argument.");
-    }
</del><ins>+    ProtocolTest.expectException(() => {
+        new AsyncTestSuite("something", {});
+    });
</ins><span class="cx"> 
</span><span class="cx">     let badArgsSuite = ProtocolTest.createAsyncSuite("dummy");
</span><del>-    try {
</del><ins>+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase();
</span><del>-        ProtocolTest.fail("should not be able to add empty test case.");
-    } catch (e) {
-        ProtocolTest.pass("should not be able to add empty test case.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase("string");
</span><del>-        ProtocolTest.fail("should not be able to add non-object test case.");
-    } catch (e) {
-        ProtocolTest.pass("should not be able to add non-object test case.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: {},
</span><del>-            test() {},
</del><ins>+            test() {
+            },
</ins><span class="cx">         });
</span><del>-        ProtocolTest.fail("test case should require string name.");
-    } catch (e) {
-        ProtocolTest.pass("test case should require string name.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "        ",
</span><del>-            test() {},
</del><ins>+            test() {
+            },
</ins><span class="cx">         });
</span><del>-        ProtocolTest.fail("test case should require non-whitespace name.");
-    } catch (e) {
-        ProtocolTest.pass("test case should require non-whitespace name.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "foo",
</span><span class="cx">             test: null,
</span><span class="cx">         });
</span><del>-        ProtocolTest.fail("test case should require test function.");
-    } catch (e) {
-        ProtocolTest.pass("test case should require test function.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "foo",
</span><del>-            test() {},
-            setup: "astd"
</del><ins>+            test() {
+            },
+            setup: "astd",
</ins><span class="cx">         });
</span><del>-        ProtocolTest.fail("should not be able to specify non-Function `setup` parameter.");
-    } catch (e) {
-        ProtocolTest.pass("should not be able to specify non-Function `setup` parameter.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "foo",
</span><del>-            test() {},
-            setup: 123
</del><ins>+            test() {
+            },
+            setup: 123,
</ins><span class="cx">         });
</span><del>-        ProtocolTest.fail("should not be able to specify non-Function `setup` parameter.");
-    } catch (e) {
-        ProtocolTest.pass("should not be able to specify non-Function `setup` parameter.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "foo",
</span><del>-            test() {},
-            setup: {}
</del><ins>+            test() {
+            },
+            setup: {},
</ins><span class="cx">         });
</span><del>-        ProtocolTest.fail("should not be able to specify non-Function `setup` parameter.");
-    } catch (e) {
-        ProtocolTest.pass("should not be able to specify non-Function `setup` parameter.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "foo",
</span><del>-            test() {},
-            teardown: "astd"
</del><ins>+            test() {
+            },
+            teardown: "astd",
</ins><span class="cx">         });
</span><del>-        ProtocolTest.fail("should not be able to specify non-Function `teardown` parameter.");
-    } catch (e) {
-        ProtocolTest.pass("should not be able to specify non-Function `teardown` parameter.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "foo",
</span><del>-            test() {},
-            teardown: 123
</del><ins>+            test() {
+            },
+            teardown: 123,
</ins><span class="cx">         });
</span><del>-        ProtocolTest.fail("should not be able to specify non-Function `teardown` parameter.");
-    } catch (e) {
-        ProtocolTest.pass("should not be able to specify non-Function `teardown` parameter.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "foo",
</span><del>-            test() {},
-            teardown: {}
</del><ins>+            test() {
+            },
+            teardown: {},
</ins><span class="cx">         });
</span><del>-        ProtocolTest.fail("should not be able to specify non-Function `teardown` parameter.");
-    } catch (e) {
-        ProtocolTest.pass("should not be able to specify non-Function `teardown` parameter.");
-    }
</del><ins>+    });
</ins><span class="cx"> 
</span><span class="cx">     let runEmptySuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.RunEmptySuite");
</span><span class="cx">     try {
</span><span class="lines">@@ -144,459 +107,765 @@
</span><span class="cx">         ProtocolTest.pass("should not be able to run empty test suite.");
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    let runTwiceSuiteRunCount = 0;
</ins><span class="cx">     let runTwiceSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.RunTwiceSuite");
</span><span class="cx">     runTwiceSuite.addTestCase({
</span><span class="cx">         name: "DummyTest0",
</span><del>-        description: "Check that a suite can't run more than once.",
-        test(resolve, reject) { resolve(); }
</del><ins>+        test(resolve, reject) {
+            ProtocolTest.expectEqual(++runTwiceSuiteRunCount, 1, "DummyTest0 should only run once.");
+            resolve();
+        },
</ins><span class="cx">     });
</span><span class="cx"> 
</span><span class="cx">     let result = runTwiceSuite.runTestCases();
</span><del>-    try {
</del><ins>+    ProtocolTest.expectException(() => {
</ins><span class="cx">         // Test cases won't run in this event loop; this call should still throw.
</span><span class="cx">         // Later tests are chained to this suite to avoid nondeterminism.
</span><span class="cx">         runTwiceSuite.runTestCases();
</span><del>-        ProtocolTest.fail("should not be able to run a test suite twice.");
-    } catch (e) {
-        ProtocolTest.pass("should not be able to run a test suite twice.");
</del><ins>+    });
+
+    function checkResult(suite, expectedCounts) {
+        result = result.then(() => suite.runTestCases());
+
+        let message = `Promise from ${suite.name}.runTestCases() should resolve even if a test case fails.`;
+
+        result = result.then(function resolved() {
+            ProtocolTest.log("");
+            ProtocolTest.pass(message);
+
+            ProtocolTest.expectEqual(expectedCounts.runCount, suite.runCount, `${suite.name} should have executed ${expectedCounts.runCount} tests.`);
+            ProtocolTest.expectEqual(expectedCounts.passCount, suite.passCount, `${suite.name} should have passed ${expectedCounts.passCount} tests.`);
+            ProtocolTest.expectEqual(expectedCounts.failCount, suite.failCount, `${suite.name} should have failed ${expectedCounts.failCount} tests.`);
+            ProtocolTest.expectEqual(expectedCounts.skipCount, suite.skipCount, `${suite.name} should have skipped ${expectedCounts.skipCount} tests.`);
+        }, function rejected(e) {
+            ProtocolTest.log("");
+            ProtocolTest.fail(message);
+
+            ProtocolTest.log(e.message);
+        });
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    let rejectToken = {"token": 666};
-    let thrownError = new Error(rejectToken);
</del><ins>+    // Promise test functions.
</ins><span class="cx"> 
</span><del>-    let sequentialExecutionSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.SequentialExecution");
-    sequentialExecutionSuite.addTestCase({
-        name: "DummyTest1",
-        description: "Check test case execution order.",
-        test(resolve, reject) { resolve(); }
</del><ins>+    let promiseFunctionSuccessSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.PromiseFunctionSuccess");
+    promiseFunctionSuccessSuite.addTestCase({
+        name: "PromiseFunctionSuccess",
+        test(resolve, reject) {
+            resolve();
+        },
</ins><span class="cx">     });
</span><del>-    sequentialExecutionSuite.addTestCase({
-        name: "DummyTest2",
-        description: "Check test case execution order.",
-        test(resolve, reject) { resolve(); }
</del><ins>+    checkResult(promiseFunctionSuccessSuite, {
+        runCount: 1,
+        passCount: 1,
+        failCount: 0,
+        skipCount: 0,
</ins><span class="cx">     });
</span><del>-    sequentialExecutionSuite.addTestCase({
-        name: "DummyTest3",
-        description: "Check test case execution order.",
-        test(resolve, reject) { resolve(); }
</del><ins>+
+    let promiseFunctionExceptionSuite = ProtocolTest.createAsyncSuite("PromiseTestSuite.PromiseFunctionException");
+    promiseFunctionExceptionSuite.addTestCase({
+        name: "PromiseFunctionException",
+        test(resolve, reject) {
+            ProtocolTest.log("Throwing...");
+            throw "PromiseFunctionException throw";
+        },
</ins><span class="cx">     });
</span><del>-    sequentialExecutionSuite.addTestCase({
-        name: "FailingTest4",
-        description: "Check that test fails by throwing an Error instance.",
-        test(resolve, reject) { throw thrownError; }
</del><ins>+    checkResult(promiseFunctionExceptionSuite, {
+        runCount: 1,
+        passCount: 0,
+        failCount: 1,
+        skipCount: 0,
</ins><span class="cx">     });
</span><span class="cx"> 
</span><del>-    let abortOnFailureSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AbortOnFailure");
-    abortOnFailureSuite.addTestCase({
-        name: "PassingTest5",
-        description: "This test is a dummy.",
-        test(resolve, reject) { resolve(); }
</del><ins>+    let promiseFunctionFailureSuite = ProtocolTest.createAsyncSuite("PromiseTestSuite.PromiseFunctionFailure");
+    promiseFunctionFailureSuite.addTestCase({
+        name: "PromiseFunctionFailure",
+        test(resolve, reject) {
+            ProtocolTest.log("Rejecting...");
+            reject("PromiseFunctionFailure reject");
+        },
</ins><span class="cx">     });
</span><del>-    abortOnFailureSuite.addTestCase({
-        name: "FailingTest6",
-        description: "This test should fail by explicitly calling the `reject` callback.",
-        test(resolve, reject) { reject(rejectToken); }
</del><ins>+    checkResult(promiseFunctionFailureSuite, {
+        runCount: 1,
+        passCount: 0,
+        failCount: 1,
+        skipCount: 0,
</ins><span class="cx">     });
</span><del>-    abortOnFailureSuite.addTestCase({
-        name: "PassingTest7",
-        description: "This test should not executed when the preceding test fails.",
-        test(resolve, reject) { resolve(); }
-    });
</del><span class="cx"> 
</span><del>-    result = result.then(() => {
-        let promise = sequentialExecutionSuite.runTestCases();
-        ProtocolTest.expectThat(result instanceof Promise, "AsyncTestSuite.RunTestCases() should return a Promise.");
-        return promise;
</del><ins>+    let promiseSequentialExecutionPhase = 0;
+    let promiseSequentialExecutionSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.PromiseSequentialExecution");
+    promiseSequentialExecutionSuite.addTestCase({
+        name: "1 (Pass)",
+        test(resolve, reject) {
+            ProtocolTest.assert(promiseSequentialExecutionPhase === 0);
+            promiseSequentialExecutionPhase = 1;
+            resolve();
+        },
</ins><span class="cx">     });
</span><del>-    result = result.then(function resolved() {
-        ProtocolTest.fail("Promise from sequentialExecutionSuite.runTestCases() should reject when a test case fails.");
-        return Promise.resolve(); // Continue this test.
-    }, function rejected(e) {
-        ProtocolTest.pass("Promise from sequentialExecutionSuite.runTestCases() should reject when a test case fails.");
-        ProtocolTest.expectThat(e === thrownError, "Promise from sequentialExecutionSuite.runTestCases() should reject without altering its result value.");
-
-        ProtocolTest.expectThat(sequentialExecutionSuite.runCount === 4, "sequentialExecutionSuite should have executed four tests.");
-        ProtocolTest.expectThat(sequentialExecutionSuite.passCount === 3, "sequentialExecutionSuite should have passed three tests.");
-        ProtocolTest.expectThat(sequentialExecutionSuite.failCount === 1, "sequentialExecutionSuite should have failed 1 test.");
-        ProtocolTest.expectThat(sequentialExecutionSuite.skipCount === 0, "sequentialExecutionSuite should have skipped zero tests.");
-        return Promise.resolve(); // Continue this test.
</del><ins>+    promiseSequentialExecutionSuite.addTestCase({
+        name: "2 (Pass)",
+        test(resolve, reject) {
+            ProtocolTest.assert(promiseSequentialExecutionPhase === 1);
+            promiseSequentialExecutionPhase = 2;
+            resolve();
+        },
</ins><span class="cx">     });
</span><del>-
</del><ins>+    promiseSequentialExecutionSuite.addTestCase({
+        name: "3 (Pass)",
+        test(resolve, reject) {
+            ProtocolTest.assert(promiseSequentialExecutionPhase === 2);
+            promiseSequentialExecutionPhase = 3;
+            resolve();
+        },
+    });
+    promiseSequentialExecutionSuite.addTestCase({
+        name: "4 (Pass)",
+        test(resolve, reject) {
+            ProtocolTest.assert(promiseSequentialExecutionPhase === 3);
+            promiseSequentialExecutionPhase = 4;
+            resolve();
+        },
+    });
+    checkResult(promiseSequentialExecutionSuite, {
+        runCount: 4,
+        passCount: 4,
+        failCount: 0,
+        skipCount: 0,
+    });
</ins><span class="cx">     result = result.then(() => {
</span><del>-        return abortOnFailureSuite.runTestCases();
-    }).then(function resolved() {
-        ProtocolTest.fail("Promise from abortOnFailureSuite.runTestCases() should reject when a test case fails.");
-        return Promise.resolve(); // Continue this test.
-    }, function rejected(e) {
-        ProtocolTest.pass("Promise from abortOnFailureSuite.runTestCases() should reject when a test case fails.");
-        ProtocolTest.expectThat(e === rejectToken, "Promise from abortOnFailureSuite.runTestCases() should reject without altering its result value.");
-        ProtocolTest.expectThat(abortOnFailureSuite.runCount === 2, "abortOnFailureSuite should have executed two tests.");
-        ProtocolTest.expectThat(abortOnFailureSuite.passCount === 1, "abortOnFailureSuite should have passed one test.");
-        ProtocolTest.expectThat(abortOnFailureSuite.failCount === 1, "abortOnFailureSuite should have failed one test.");
-        ProtocolTest.expectThat(abortOnFailureSuite.skipCount === 1, "abortOnFailureSuite should have skipped one test.");
</del><ins>+        ProtocolTest.assert(promiseSequentialExecutionPhase === 4);
+    });
</ins><span class="cx"> 
</span><del>-        return Promise.resolve(); // Continue this test.
</del><ins>+    let promiseContinueOnFailureSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.PromiseContinueOnFailure");
+    promiseContinueOnFailureSuite.addTestCase({
+        name: "1 (Pass)",
+        test(resolve, reject) {
+            resolve();
+        },
</ins><span class="cx">     });
</span><ins>+    promiseContinueOnFailureSuite.addTestCase({
+        name: "2 (Fail)",
+        test(resolve, reject) {
+            ProtocolTest.log("Throwing...");
+            throw {x: "PromiseContinueOnFailure throw"};
+        },
+    });
+    promiseContinueOnFailureSuite.addTestCase({
+        name: "3 (Pass)",
+        test(resolve, reject) {
+            resolve();
+        },
+    });
+    promiseContinueOnFailureSuite.addTestCase({
+        name: "4 (Fail)",
+        test(resolve, reject) {
+            ProtocolTest.log("Rejecting...");
+            reject({x: "PromiseContinueOnFailure reject"});
+        },
+    });
+    checkResult(promiseContinueOnFailureSuite, {
+        runCount: 4,
+        passCount: 2,
+        failCount: 2,
+        skipCount: 0,
+    });
</ins><span class="cx"> 
</span><del>-    var setupAndTeardownSymbol = Symbol("async-suite-setup-and-teardown-token");
-    window[setupAndTeardownSymbol] = 0;
-
-    let setupAndTeardownTestSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.SetupAndTeardown");
-    setupAndTeardownTestSuite.addTestCase({
</del><ins>+    let promiseSetupAndTeardownPhase = 0;
+    let promiseSetupAndTeardownSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.PromiseSetupAndTeardown");
+    promiseSetupAndTeardownSuite.addTestCase({
</ins><span class="cx">         name: "TestWithSetupAndTeardown",
</span><del>-        description: "Check execution order for setup and teardown actions.",
-        setup: (resolve, reject) => {
-            window[setupAndTeardownSymbol] = 1;
</del><ins>+        setup(resolve, reject) {
+            ProtocolTest.assert(promiseSetupAndTeardownPhase === 0);
+            promiseSetupAndTeardownPhase = 1;
</ins><span class="cx">             resolve();
</span><span class="cx">         },
</span><span class="cx">         test(resolve, reject) {
</span><del>-            ProtocolTest.expectThat(window[setupAndTeardownSymbol] === 1, "Test should see side effects of running setup() action.");
-            window[setupAndTeardownSymbol] = 2;
</del><ins>+            ProtocolTest.assert(promiseSetupAndTeardownPhase === 1);
+            promiseSetupAndTeardownPhase = 2;
</ins><span class="cx">             resolve();
</span><span class="cx">         },
</span><del>-        teardown: (resolve, reject) => {
-            ProtocolTest.expectThat(window[setupAndTeardownSymbol] === 2, "Teardown should see side effects of running setup() action.");
-            window[setupAndTeardownSymbol] = 3;
</del><ins>+        teardown(resolve, reject) {
+            ProtocolTest.assert(promiseSetupAndTeardownPhase === 2);
+            promiseSetupAndTeardownPhase = 3;
</ins><span class="cx">             resolve();
</span><del>-        }
</del><ins>+        },
</ins><span class="cx">     });
</span><del>-    setupAndTeardownTestSuite.addTestCase({
</del><ins>+    promiseSetupAndTeardownSuite.addTestCase({
</ins><span class="cx">         name: "TestRunningAfterTeardown",
</span><del>-        description: "Check execution order for test after a teardown action.",
</del><span class="cx">         test(resolve, reject) {
</span><del>-            ProtocolTest.expectThat(window[setupAndTeardownSymbol] === 3, "Test should see side effects of previous test's teardown() action.");
</del><ins>+            ProtocolTest.assert(promiseSetupAndTeardownPhase === 3);
</ins><span class="cx">             resolve();
</span><span class="cx">         },
</span><span class="cx">     });
</span><del>-
-    result = result.then(() => {
-        return setupAndTeardownTestSuite.runTestCases();
-    }).then(function resolved() {
-        ProtocolTest.pass("Promise from setupAndTeardownTestSuite.runTestCases() should resolve.");
-        return Promise.resolve(); // Continue this test.
-    }, function rejected(e) {
-        ProtocolTest.fail("Promise from setupAndTeardownTestSuite.runTestCases() should resolve.");
-        return Promise.resolve(); // Continue this test.
</del><ins>+    checkResult(promiseSetupAndTeardownSuite, {
+        runCount: 2,
+        passCount: 2,
+        failCount: 0,
+        skipCount: 0,
</ins><span class="cx">     });
</span><span class="cx"> 
</span><del>-    let setupExceptionTestSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.SetupException");
-    setupExceptionTestSuite.addTestCase({
</del><ins>+    let promiseSetupExceptionSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.PromiseSetupException");
+    promiseSetupExceptionSuite.addTestCase({
</ins><span class="cx">         name: "TestWithExceptionDuringSetup",
</span><del>-        description: "Check execution order for setup action that throws an exception.",
-        setup: (resolve, reject) => { throw new Error() },
</del><ins>+        setup(resolve, reject) {
+            ProtocolTest.log("Throwing...");
+            throw "PromiseSetupException throw";
+        },
</ins><span class="cx">         test(resolve, reject) {
</span><del>-            ProtocolTest.assert(false, "Test should not execute if its setup action threw an exception.");
</del><ins>+            ProtocolTest.fail("Test should not execute if its setup action threw an exception.");
</ins><span class="cx">             reject();
</span><span class="cx">         },
</span><del>-        teardown: (resolve, reject) => {
-            ProtocolTest.assert(false, "Teardown action should not execute if its setup action threw an exception.");
</del><ins>+        teardown(resolve, reject) {
+            ProtocolTest.fail("Teardown action should not execute if its setup action threw an exception.");
</ins><span class="cx">             reject();
</span><del>-        }
</del><ins>+        },
</ins><span class="cx">     });
</span><del>-
-    result = result.then(() => {
-        return setupExceptionTestSuite.runTestCases();
-    }).then(function resolved() {
-        ProtocolTest.fail("Promise from setupExceptionTestSuite.runTestCases() should reject.");
-        return Promise.resolve(); // Continue this test.
-    }, function rejected(e) {
-        ProtocolTest.pass("Promise from setupExceptionTestSuite.runTestCases() should reject.");
-        return Promise.resolve(); // Continue this test.
</del><ins>+    promiseSetupExceptionSuite.addTestCase({
+        name: "TestAfterSetupException",
+        setup(resolve, reject) {
+            ProtocolTest.pass("Setup action should still execute if previous test's setup action threw an exception.");
+            resolve();
+        },
+        test(resolve, reject) {
+            ProtocolTest.pass("Test should still execute if previous test's setup action threw an exception.");
+            resolve();
+        },
+        teardown(resolve, reject) {
+            ProtocolTest.pass("Teardown action should still execute if previous test's setup action threw an exception.");
+            resolve();
+        },
</ins><span class="cx">     });
</span><ins>+    checkResult(promiseSetupExceptionSuite, {
+        runCount: 1,
+        passCount: 1,
+        failCount: 1,
+        skipCount: 1,
+    });
</ins><span class="cx"> 
</span><del>-    let setupFailureTestSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.SetupFailure");
-    setupFailureTestSuite.addTestCase({
</del><ins>+    let promiseSetupFailureSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.PromiseSetupFailure");
+    promiseSetupFailureSuite.addTestCase({
</ins><span class="cx">         name: "TestWithFailureDuringSetup",
</span><del>-        description: "Check execution order for setup action that has failed.",
-        setup: (resolve, reject) => { reject(); },
</del><ins>+        setup(resolve, reject) {
+            ProtocolTest.log("Rejecting...");
+            reject("PromiseSetupFailure reject");
+        },
</ins><span class="cx">         test(resolve, reject) {
</span><del>-            ProtocolTest.assert(false, "Test should not execute if its setup action failed.")
</del><ins>+            ProtocolTest.fail("Test should not execute if its setup action failed.");
</ins><span class="cx">             reject();
</span><span class="cx">         },
</span><del>-        teardown: (resolve, reject) => {
-            ProtocolTest.assert(false, "Teardown action should not execute if its setup action failed.")
</del><ins>+        teardown(resolve, reject) {
+            ProtocolTest.fail("Teardown action should not execute if its setup action failed.");
</ins><span class="cx">             reject();
</span><del>-        }
</del><ins>+        },
</ins><span class="cx">     });
</span><del>-
-    result = result.then(() => {
-        return setupFailureTestSuite.runTestCases();
-    }).then(function resolved() {
-        ProtocolTest.fail("Promise from setupFailureTestSuite.runTestCases() should reject.");
-        return Promise.resolve(); // Continue this test.
-    }, function rejected(e) {
-        ProtocolTest.pass("Promise from setupFailureTestSuite.runTestCases() should reject.");
-        return Promise.resolve(); // Continue this test.
</del><ins>+    promiseSetupFailureSuite.addTestCase({
+        name: "TestAfterSetupException",
+        setup(resolve, reject) {
+            ProtocolTest.pass("Setup action should still execute if previous test's setup action failed.");
+            resolve();
+        },
+        test(resolve, reject) {
+            ProtocolTest.pass("Test should still execute if previous test's setup action failed.");
+            resolve();
+        },
+        teardown(resolve, reject) {
+            ProtocolTest.pass("Teardown action should still execute if previous test's setup action failed.");
+            resolve();
+        },
</ins><span class="cx">     });
</span><ins>+    checkResult(promiseSetupFailureSuite, {
+        runCount: 1,
+        passCount: 1,
+        failCount: 1,
+        skipCount: 1,
+    });
</ins><span class="cx"> 
</span><del>-    let teardownExceptionTestSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.TeardownException");
-    teardownExceptionTestSuite.addTestCase({
</del><ins>+    let promiseTeardownExceptionSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.PromiseTeardownException");
+    promiseTeardownExceptionSuite.addTestCase({
</ins><span class="cx">         name: "TestWithExceptionDuringTeardown",
</span><del>-        description: "Check execution order for teardown action that throws an exception.",
-        test(resolve, reject) { resolve(); },
-        teardown: (resolve, reject) => { throw new Error() }
</del><ins>+        test(resolve, reject) {
+            resolve();
+        },
+        teardown(resolve, reject) {
+            ProtocolTest.log("Throwing...");
+            throw "PromiseTeardownException throw";
+        },
</ins><span class="cx">     });
</span><del>-    teardownExceptionTestSuite.addTestCase({
</del><ins>+    promiseTeardownExceptionSuite.addTestCase({
</ins><span class="cx">         name: "TestAfterTeardownException",
</span><del>-        descrption: "Check execution order for test after previous test's teardown throws an exception.",
-        setup: (resolve, reject) => {
-            ProtocolTest.assert(false, "Setup action should not execute if previous test's teardown action threw an exception.");
-            reject();
</del><ins>+        setup(resolve, reject) {
+            ProtocolTest.pass("Setup action should still execute if previous test's teardown action threw an exception.");
+            resolve();
</ins><span class="cx">         },
</span><span class="cx">         test(resolve, reject) {
</span><del>-            ProtocolTest.assert(false, "Test should not execute if previous test's teardown action threw an exception.");
-            reject();
-        }
</del><ins>+            ProtocolTest.pass("Test should still execute if previous test's teardown action threw an exception.");
+            resolve();
+        },
+        teardown(resolve, reject) {
+            ProtocolTest.pass("Teardown action should still execute if previous test's teardown action threw an exception.");
+            resolve();
+        },
</ins><span class="cx">     });
</span><del>-
-    result = result.then(() => {
-        return teardownExceptionTestSuite.runTestCases();
-    }).then(function resolved() {
-        ProtocolTest.fail("Promise from teardownExceptionTestSuite.runTestCases() should reject.");
-        return Promise.resolve(); // Continue this test.
-    }, function rejected(e) {
-        ProtocolTest.pass("Promise from teardownExceptionTestSuite.runTestCases() should reject.");
-        return Promise.resolve(); // Continue this test.
</del><ins>+    checkResult(promiseTeardownExceptionSuite, {
+        runCount: 2,
+        passCount: 1,
+        failCount: 1,
+        skipCount: 0,
</ins><span class="cx">     });
</span><span class="cx"> 
</span><del>-    let teardownFailureTestSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.TeardownFailure");
-    teardownFailureTestSuite.addTestCase({
</del><ins>+    let promiseTeardownFailureSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.PromiseTeardownFailure");
+    promiseTeardownFailureSuite.addTestCase({
</ins><span class="cx">         name: "TestWithExceptionDuringTeardown",
</span><del>-        description: "Check execution order for teardown action that has failed.",
-        test(resolve, reject) { resolve(); },
-        teardown: (resolve, reject) => { reject(); },
</del><ins>+        test(resolve, reject) {
+            resolve();
+        },
+        teardown(resolve, reject) {
+            ProtocolTest.log("Rejecting...");
+            reject("PromiseTeardownFailure reject");
+        },
</ins><span class="cx">     });
</span><del>-    teardownFailureTestSuite.addTestCase({
</del><ins>+    promiseTeardownFailureSuite.addTestCase({
</ins><span class="cx">         name: "TestAfterTeardownException",
</span><del>-        description: "Check execution order for test after previous test's teardown throws an exception",
-        setup: (resolve, reject) => {
-            ProtocolTest.assert(false, "Setup action should not execute if previous test's teardown action failed.");
-            reject();
</del><ins>+        setup(resolve, reject) {
+            ProtocolTest.pass("Setup action should still execute if previous test's teardown action failed.");
+            resolve();
</ins><span class="cx">         },
</span><span class="cx">         test(resolve, reject) {
</span><del>-            ProtocolTest.assert(false, "Test should not execute if previous test's teardown action failed.");
-            reject();
-        }
</del><ins>+            ProtocolTest.pass("Test should still execute if previous test's teardown action failed.");
+            resolve();
+        },
+        teardown(resolve, reject) {
+            ProtocolTest.pass("Teardown action should still execute if previous test's teardown action failed.");
+            resolve();
+        },
</ins><span class="cx">     });
</span><ins>+    checkResult(promiseTeardownFailureSuite, {
+        runCount: 2,
+        passCount: 1,
+        failCount: 1,
+        skipCount: 0,
+    });
</ins><span class="cx"> 
</span><del>-    result = result.then(() => {
-        return teardownFailureTestSuite.runTestCases();
-    }).then(function resolved() {
-        ProtocolTest.fail("Promise from teardownFailureTestSuite.runTestCases() should reject.");
-        return Promise.resolve(); // Continue this test.
-    }, function rejected(e) {
-        ProtocolTest.pass("Promise from teardownFailureTestSuite.runTestCases() should reject.");
-        return Promise.resolve(); // Continue this test.
</del><ins>+    let promiseTimeoutSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.PromiseTimeout");
+    promiseTimeoutSuite.addTestCase({
+        name: "PromiseTestWithTimeout",
+        test(resolve, reject) {
+            ProtocolTest.log("Timeout...");
+            setTimeout(() => {
+                resolve();
+            }, 50);
+        },
+        timeout: 5,
</ins><span class="cx">     });
</span><ins>+    promiseTimeoutSuite.addTestCase({
+        name: "PromiseTestAfterTimeout",
+        setup(resolve, reject) {
+            ProtocolTest.pass("Setup action should still execute if previous test timed out.");
+            resolve();
+        },
+        test(resolve, reject) {
+            ProtocolTest.pass("Test should still execute if previous test timed out.");
+            resolve();
+        },
+        teardown(resolve, reject) {
+            ProtocolTest.pass("Teardown action should still execute if previous test timed out.");
+            resolve();
+        },
+    });
+    checkResult(promiseTimeoutSuite, {
+        runCount: 2,
+        passCount: 1,
+        failCount: 1,
+        skipCount: 0,
+    });
</ins><span class="cx"> 
</span><span class="cx">     // Async test functions.
</span><del>-    let asyncFunctionSuccessTestSuiteDidEvaluate = false;
-    let asyncFunctionSuccessTestSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncFunctionSuccess");
-    asyncFunctionSuccessTestSuite.addTestCase({
</del><ins>+
+    let asyncFunctionSuccessSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncFunctionSuccess");
+    asyncFunctionSuccessSuite.addTestCase({
</ins><span class="cx">         name: "AsyncFunctionSuccess",
</span><del>-        description: "Check that an async suite with async test functions can succeed",
</del><span class="cx">         async test() {
</span><del>-            asyncFunctionSuccessTestSuiteDidEvaluate = true;
-            return 42;
-        }
</del><ins>+        },
</ins><span class="cx">     });
</span><ins>+    checkResult(asyncFunctionSuccessSuite, {
+        runCount: 1,
+        passCount: 1,
+        failCount: 0,
+        skipCount: 0,
+    });
</ins><span class="cx"> 
</span><del>-    result = result.then(() => {
-        return asyncFunctionSuccessTestSuite.runTestCases();
-    }).then(function resolve(x) {
-        ProtocolTest.pass("Promise from asyncFunctionSuccessTestSuite.runTestCases() should succeed.");
-        ProtocolTest.expectThat(asyncFunctionSuccessTestSuiteDidEvaluate, "Promise did evaluate the async test function.");
-        ProtocolTest.expectEqual(x, 42, "Resolved value should be 42.");
-        return Promise.resolve(); // Continue this test.
-    }, function rejected(e) {
-        ProtocolTest.fail("Promise from asyncFunctionSuccessTestSuite.runTestCases() should succeed.");
-        return Promise.resolve(); // Continue this test.
</del><ins>+    let asyncFunctionExplicitExceptionSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncFunctionExplicitException");
+    asyncFunctionExplicitExceptionSuite.addTestCase({
+        name: "AsyncFunctionExplicitException",
+        async test() {
+            ProtocolTest.log("Throwing...");
+            throw "AsyncFunctionExplicitException throw";
+        },
</ins><span class="cx">     });
</span><ins>+    checkResult(asyncFunctionExplicitExceptionSuite, {
+        runCount: 1,
+        passCount: 0,
+        failCount: 1,
+        skipCount: 0,
+    });
</ins><span class="cx"> 
</span><del>-    let asyncFunctionExplicitFailureTestSuiteDidEvaluate = false;
-    let asyncFunctionExplicitFailureTestSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncFunctionExplicitFailure");
-    asyncFunctionExplicitFailureTestSuite.addTestCase({
-        name: "AsyncFunctionFailure",
-        description: "Check that an async suite with async test functions that throws will reject",
</del><ins>+    let asyncFunctionRuntimeExceptionSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncFunctionRuntimeException");
+    asyncFunctionRuntimeExceptionSuite.addTestCase({
+        name: "AsyncFunctionRuntimeException",
</ins><span class="cx">         async test() {
</span><del>-            asyncFunctionExplicitFailureTestSuiteDidEvaluate = true;
-            throw "AsyncFunctionFailure Exception Message";
-        }
</del><ins>+            ProtocolTest.log("Throwing...");
+            ({}).x.x.x;
+        },
</ins><span class="cx">     });
</span><ins>+    checkResult(asyncFunctionRuntimeExceptionSuite, {
+        runCount: 1,
+        passCount: 0,
+        failCount: 1,
+        skipCount: 0,
+    });
</ins><span class="cx"> 
</span><ins>+    let asyncFunctionFailureSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncFunctionFailure");
+    asyncFunctionFailureSuite.addTestCase({
+        name: "AsyncFunctionException",
+        async test() {
+            ProtocolTest.log("Rejecting...");
+            return Promise.reject("AsyncFunctionFailure reject");
+        },
+    });
+    checkResult(asyncFunctionFailureSuite, {
+        runCount: 1,
+        passCount: 0,
+        failCount: 1,
+        skipCount: 0,
+    });
+
+    let asyncSequentialExecutionPhase = 0;
+    let asyncSequentialExecutionSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncSequentialExecution");
+    asyncSequentialExecutionSuite.addTestCase({
+        name: "1 (Pass)",
+        async test() {
+            ProtocolTest.assert(asyncSequentialExecutionPhase === 0);
+            asyncSequentialExecutionPhase = 1;
+        },
+    });
+    asyncSequentialExecutionSuite.addTestCase({
+        name: "2 (Pass)",
+        async test() {
+            ProtocolTest.assert(asyncSequentialExecutionPhase === 1);
+            asyncSequentialExecutionPhase = 2;
+        },
+    });
+    asyncSequentialExecutionSuite.addTestCase({
+        name: "3 (Pass)",
+        async test() {
+            ProtocolTest.assert(asyncSequentialExecutionPhase === 2);
+            asyncSequentialExecutionPhase = 3;
+        },
+    });
+    asyncSequentialExecutionSuite.addTestCase({
+        name: "4 (Pass)",
+        async test() {
+            ProtocolTest.assert(asyncSequentialExecutionPhase === 3);
+            asyncSequentialExecutionPhase = 4;
+        },
+    });
+    checkResult(asyncSequentialExecutionSuite, {
+        runCount: 4,
+        passCount: 4,
+        failCount: 0,
+        skipCount: 0,
+    });
</ins><span class="cx">     result = result.then(() => {
</span><del>-        return asyncFunctionExplicitFailureTestSuite.runTestCases();
-    }).then(function resolve() {
-        ProtocolTest.fail("Promise from asyncFunctionExplicitFailureTestSuite.runTestCases() should reject.");
-        return Promise.resolve(); // Continue this test.
-    }, function rejected(e) {
-        ProtocolTest.pass("Promise from asyncFunctionExplicitFailureTestSuite.runTestCases() should reject.");
-        ProtocolTest.expectThat(asyncFunctionExplicitFailureTestSuiteDidEvaluate, "Promise did evaluate the async test function.");
-        ProtocolTest.expectEqual(e, "AsyncFunctionFailure Exception Message", "Rejected value should be thrown exception.");
-        return Promise.resolve(); // Continue this test.
</del><ins>+        ProtocolTest.assert(asyncSequentialExecutionPhase === 4);
</ins><span class="cx">     });
</span><span class="cx"> 
</span><del>-    let asyncFunctionRuntimeFailureTestSuiteDidEvaluate = false;
-    let asyncFunctionRuntimeFailureTestSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncFunctionRuntimeFailure");
-    asyncFunctionRuntimeFailureTestSuite.addTestCase({
-        name: "AsyncFunctionFailure",
-        description: "Check that an async suite with async test functions that throws with a runtime error will reject",
</del><ins>+    let asyncContinueOnFailureSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncContinueOnFailure");
+    asyncContinueOnFailureSuite.addTestCase({
+        name: "1 (Pass)",
</ins><span class="cx">         async test() {
</span><del>-            asyncFunctionRuntimeFailureTestSuiteDidEvaluate = true;
</del><ins>+        },
+    });
+    asyncContinueOnFailureSuite.addTestCase({
+        name: "2 (Fail)",
+        async test() {
+            ProtocolTest.log("Throwing...");
+            throw {x: "AsyncContinueOnFailure throw"};
+        },
+    });
+    asyncContinueOnFailureSuite.addTestCase({
+        name: "3 (Pass)",
+        async test() {
+        },
+    });
+    asyncContinueOnFailureSuite.addTestCase({
+        name: "4 (Fail)",
+        async test() {
+            ProtocolTest.log("Throwing...");
</ins><span class="cx">             ({}).x.x.x;
</span><del>-        }
</del><ins>+        },
</ins><span class="cx">     });
</span><del>-
-    result = result.then(() => {
-        return asyncFunctionRuntimeFailureTestSuite.runTestCases();
-    }).then(function resolve() {
-        ProtocolTest.fail("Promise from asyncFunctionRuntimeFailureTestSuite.runTestCases() should reject.");
-        return Promise.resolve(); // Continue this test.
-    }, function rejected(e) {
-        ProtocolTest.pass("Promise from asyncFunctionRuntimeFailureTestSuite.runTestCases() should reject.");
-        ProtocolTest.expectThat(asyncFunctionRuntimeFailureTestSuiteDidEvaluate, "Promise did evaluate the async test function.");
-        ProtocolTest.expectThat(e instanceof TypeError, "Rejected value should be a runtime exception.");
-        return Promise.resolve(); // Continue this test.
</del><ins>+    asyncContinueOnFailureSuite.addTestCase({
+        name: "5 (Pass)",
+        async test() {
+        },
</ins><span class="cx">     });
</span><ins>+    asyncContinueOnFailureSuite.addTestCase({
+        name: "6 (Fail)",
+        async test() {
+            ProtocolTest.log("Rejecting...");
+            return Promise.reject("AsyncContinueOnFailure reject");
+        },
+    });
+    checkResult(asyncContinueOnFailureSuite, {
+        runCount: 6,
+        passCount: 3,
+        failCount: 3,
+        skipCount: 0,
+    });
</ins><span class="cx"> 
</span><del>-    // Async setup() and teardown() success test cases.
-    const asyncSetupAndTeardownSymbol = Symbol("async-suite-async-setup-and-teardown-token");
-    window[asyncSetupAndTeardownSymbol] = 0;
-
-    let asyncSetupAndAsyncTeardownTestSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncSetupAndAsyncTeardown");
-    asyncSetupAndAsyncTeardownTestSuite.addTestCase({
-        name: "TestWithSetupAndTeardown",
-        description: "Check execution order for setup and teardown actions.",
</del><ins>+    let asyncSetupAndTeardownPhase = 0;
+    let asyncSetupAndTeardownSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncSetupAndTeardown");
+    asyncSetupAndTeardownSuite.addTestCase({
+        name: "AsyncTestWithSetupAndTeardown",
</ins><span class="cx">         async setup() {
</span><del>-            window[asyncSetupAndTeardownSymbol] = 1;
</del><ins>+            ProtocolTest.assert(asyncSetupAndTeardownPhase === 0);
+            asyncSetupAndTeardownPhase = 1;
</ins><span class="cx">         },
</span><span class="cx">         async test() {
</span><del>-            ProtocolTest.expectThat(window[asyncSetupAndTeardownSymbol] === 1, "Test should see side effects of running setup() action.");
-            window[asyncSetupAndTeardownSymbol] = 2;
</del><ins>+            ProtocolTest.assert(asyncSetupAndTeardownPhase === 1);
+            asyncSetupAndTeardownPhase = 2;
</ins><span class="cx">         },
</span><span class="cx">         async teardown() {
</span><del>-            ProtocolTest.expectThat(window[asyncSetupAndTeardownSymbol] === 2, "Teardown should see side effects of running setup() action.");
-            window[asyncSetupAndTeardownSymbol] = 3;
-        }
</del><ins>+            ProtocolTest.assert(asyncSetupAndTeardownPhase === 2);
+            asyncSetupAndTeardownPhase = 3;
+        },
</ins><span class="cx">     });
</span><del>-    asyncSetupAndAsyncTeardownTestSuite.addTestCase({
-        name: "TestRunningAfterTeardown",
-        description: "Check execution order for test after a teardown action.",
-        test(resolve, reject) {
-            ProtocolTest.expectThat(window[asyncSetupAndTeardownSymbol] === 3, "Test should see side effects of previous test's teardown() action.");
-            resolve();
</del><ins>+    asyncSetupAndTeardownSuite.addTestCase({
+        name: "AsyncTestRunningAfterTeardown",
+        async test() {
+            ProtocolTest.assert(asyncSetupAndTeardownPhase === 3);
</ins><span class="cx">         },
</span><span class="cx">     });
</span><del>-
-    result = result.then(() => {
-        return asyncSetupAndAsyncTeardownTestSuite.runTestCases();
-    }).then(function resolved() {
-        ProtocolTest.pass("Promise from asyncSetupAndAsyncTeardownTestSuite.runTestCases() should resolve.");
-        return Promise.resolve(); // Continue this test.
-    }, function rejected(e) {
-        ProtocolTest.fail("Promise from asyncSetupAndAsyncTeardownTestSuite.runTestCases() should resolve.");
-        return Promise.resolve(); // Continue this test.
</del><ins>+    checkResult(asyncSetupAndTeardownSuite, {
+        runCount: 2,
+        passCount: 2,
+        failCount: 0,
+        skipCount: 0,
</ins><span class="cx">     });
</span><span class="cx"> 
</span><del>-    // Async setup() failure test cases.
-    let asyncSetupExplicitFailureTestSuiteDidEvaluate = false;
-    let asyncSetupExplicitFailureTestSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncSetupExplicitFailure");
-    asyncSetupExplicitFailureTestSuite.addTestCase({
-        name: "AsyncFunctionFailure",
-        description: "Check that an async suite with async test functions that throws will reject",
</del><ins>+    let asyncSetupExplicitExceptionSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncSetupExplicitException");
+    asyncSetupExplicitExceptionSuite.addTestCase({
+        name: "AsyncTestWithExplicitExceptionDuringSetup",
+        async setup() {
+            ProtocolTest.log("Throwing...");
+            throw "AsyncSetupExplicitException throw";
+        },
</ins><span class="cx">         async test() {
</span><del>-            asyncSetupExplicitFailureTestSuiteDidEvaluate = true;
-            throw "AsyncFunctionFailure Exception Message";
-        }
</del><ins>+            ProtocolTest.fail("Test should not execute if its setup action threw an exception.");
+        },
+        async teardown() {
+            ProtocolTest.fail("Teardown action should not execute if its setup action threw an exception.");
+        },
</ins><span class="cx">     });
</span><del>-
-    result = result.then(() => {
-        return asyncSetupExplicitFailureTestSuite.runTestCases();
-    }).then(function resolve() {
-        ProtocolTest.fail("Promise from asyncSetupExplicitFailureTestSuite.runTestCases() should reject.");
-        return Promise.resolve(); // Continue this test.
-    }, function rejected(e) {
-        ProtocolTest.pass("Promise from asyncSetupExplicitFailureTestSuite.runTestCases() should reject.");
-        ProtocolTest.expectThat(asyncSetupExplicitFailureTestSuiteDidEvaluate, "Promise did evaluate the async setup function.");
-        ProtocolTest.expectEqual(e, "AsyncFunctionFailure Exception Message", "Rejected value should be thrown exception.");
-        return Promise.resolve(); // Continue this test.
</del><ins>+    asyncSetupExplicitExceptionSuite.addTestCase({
+        name: "AsyncTestAfterSetupExplicitException",
+        async setup() {
+            ProtocolTest.pass("Setup action should still execute if previous test's setup action threw an exception.");
+        },
+        async test() {
+            ProtocolTest.pass("Test should still execute if previous test's setup action threw an exception.");
+        },
+        async teardown() {
+            ProtocolTest.pass("Teardown action should still execute if previous test's setup action threw an exception.");
+        },
</ins><span class="cx">     });
</span><ins>+    checkResult(asyncSetupExplicitExceptionSuite, {
+        runCount: 1,
+        passCount: 1,
+        failCount: 1,
+        skipCount: 1,
+    });
</ins><span class="cx"> 
</span><del>-    let asyncSetupRuntimeFailureTestSuiteDidEvaluate = false;
-    let asyncSetupRuntimeFailureTestSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncSetupRuntimeFailure");
-    asyncSetupRuntimeFailureTestSuite.addTestCase({
-        name: "AsyncFunctionFailure",
-        description: "Check that an async suite with an async setup function that throws with a runtime error will reject",
</del><ins>+    let asyncSetupRuntimeExceptionSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncSetupRuntimeException");
+    asyncSetupRuntimeExceptionSuite.addTestCase({
+        name: "AsyncTestWithRuntimeExceptionDuringSetup",
</ins><span class="cx">         async setup() {
</span><del>-            asyncSetupRuntimeFailureTestSuiteDidEvaluate = true;
</del><ins>+            ProtocolTest.log("Throwing...");
</ins><span class="cx">             ({}).x.x.x;
</span><span class="cx">         },
</span><del>-        async test() { return true; },
</del><ins>+        async test() {
+            ProtocolTest.fail("Test should not execute if its setup action threw an exception.");
+        },
+        async teardown() {
+            ProtocolTest.fail("Teardown action should not execute if its setup action threw an exception.");
+        },
</ins><span class="cx">     });
</span><ins>+    asyncSetupRuntimeExceptionSuite.addTestCase({
+        name: "AsyncTestAfterSetupRuntimeException",
+        async setup() {
+            ProtocolTest.pass("Setup action should still execute if previous test's setup action threw an exception.");
+        },
+        async test() {
+            ProtocolTest.pass("Test should still execute if previous test's setup action threw an exception.");
+        },
+        async teardown() {
+            ProtocolTest.pass("Teardown action should still execute if previous test's setup action threw an exception.");
+        },
+    });
+    checkResult(asyncSetupRuntimeExceptionSuite, {
+        runCount: 1,
+        passCount: 1,
+        failCount: 1,
+        skipCount: 1,
+    });
</ins><span class="cx"> 
</span><del>-    result = result.then(() => {
-        return asyncSetupRuntimeFailureTestSuite.runTestCases();
-    }).then(function resolve() {
-        ProtocolTest.fail("Promise from asyncSetupRuntimeFailureTestSuite.runTestCases() should reject.");
-        return Promise.resolve(); // Continue this test.
-    }, function rejected(e) {
-        ProtocolTest.pass("Promise from asyncSetupRuntimeFailureTestSuite.runTestCases() should reject.");
-        ProtocolTest.expectThat(asyncSetupRuntimeFailureTestSuiteDidEvaluate, "Promise did evaluate the async setup function.");
-        ProtocolTest.expectThat(e instanceof TypeError, "Rejected value should be a runtime exception.");
-        return Promise.resolve(); // Continue this test.
</del><ins>+    let asyncSetupFailureSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncSetupFailure");
+    asyncSetupFailureSuite.addTestCase({
+        name: "AsyncTestWithFailureDuringSetup",
+        async setup() {
+            ProtocolTest.log("Rejecting...");
+            return Promise.reject("AsyncSetupFailure reject");
+        },
+        async test() {
+        },
</ins><span class="cx">     });
</span><del>-
-    // Async teardown() failure test cases.
-    let asyncTeardownExplicitFailureTestSuiteDidEvaluate = false;
-    let asyncTeardownExplicitFailureTestSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncTeardownExplicitFailure");
-    asyncTeardownExplicitFailureTestSuite.addTestCase({
-        name: "AsyncFunctionFailure",
-        description: "Check that an async suite with async test functions that throws will reject",
-        async test() { return true; },
</del><ins>+    asyncSetupFailureSuite.addTestCase({
+        name: "AsyncTestAfterSetupFailure",
+        async setup() {
+            ProtocolTest.pass("Setup action should still execute if previous test's setup action failed.");
+        },
+        async test() {
+            ProtocolTest.pass("Test should still execute if previous test's setup action failed.");
+        },
</ins><span class="cx">         async teardown() {
</span><del>-            asyncTeardownExplicitFailureTestSuiteDidEvaluate = true;
-            throw "AsyncFunctionFailure Exception Message";
</del><ins>+            ProtocolTest.pass("Setup action should still execute if previous test's setup action failed.");
</ins><span class="cx">         },
</span><span class="cx">     });
</span><ins>+    checkResult(asyncSetupFailureSuite, {
+        runCount: 1,
+        passCount: 1,
+        failCount: 1,
+        skipCount: 1,
+    });
</ins><span class="cx"> 
</span><del>-    result = result.then(() => {
-        return asyncTeardownExplicitFailureTestSuite.runTestCases();
-    }).then(function resolve() {
-        ProtocolTest.fail("Promise from asyncTeardownExplicitFailureTestSuite.runTestCases() should reject.");
-        return Promise.resolve(); // Continue this test.
-    }, function rejected(e) {
-        ProtocolTest.pass("Promise from asyncTeardownExplicitFailureTestSuite.runTestCases() should reject.");
-        ProtocolTest.expectThat(asyncTeardownExplicitFailureTestSuiteDidEvaluate, "Promise did evaluate the async teardown function.");
-        ProtocolTest.expectEqual(e, "AsyncFunctionFailure Exception Message", "Rejected value should be thrown exception.");
-        return Promise.resolve(); // Continue this test.
</del><ins>+    let asyncTeardownExplicitExceptionSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncTeardownExplicitException");
+    asyncTeardownExplicitExceptionSuite.addTestCase({
+        name: "AsyncTestWithExplicitExceptionDuringTeardown",
+        async test() {
+        },
+        async teardown() {
+            ProtocolTest.log("Throwing...");
+            throw "AsyncTeardownExplicitException throw";
+        },
</ins><span class="cx">     });
</span><ins>+    asyncTeardownExplicitExceptionSuite.addTestCase({
+        name: "AsyncTestAfterTeardownExplicitException",
+        async setup() {
+            ProtocolTest.pass("Setup action should still execute if previous test's teardown action threw an exception.");
+        },
+        async test() {
+            ProtocolTest.pass("Test should still execute if previous test's teardown action threw an exception.");
+        },
+        async teardown() {
+            ProtocolTest.pass("Teardown action should still execute if previous test's teardown action threw an exception.");
+        },
+    });
+    checkResult(asyncTeardownExplicitExceptionSuite, {
+        runCount: 2,
+        passCount: 1,
+        failCount: 1,
+        skipCount: 0,
+    });
</ins><span class="cx"> 
</span><del>-    let asyncTeardownRuntimeFailureTestSuiteDidEvaluate = false;
-    let asyncTeardownRuntimeFailureTestSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncTeardownRuntimeFailure");
-    asyncTeardownRuntimeFailureTestSuite.addTestCase({
-        name: "AsyncFunctionFailure",
-        description: "Check that an async suite with an async teardown function that throws with a runtime error will reject",
-        async test() { return true; },
</del><ins>+    let asyncTeardownRuntimeExceptionSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncTeardownRuntimeException");
+    asyncTeardownRuntimeExceptionSuite.addTestCase({
+        name: "AsyncTestWithRuntimeExceptionDuringTeardown",
+        async test() {
+        },
</ins><span class="cx">         async teardown() {
</span><del>-            asyncTeardownRuntimeFailureTestSuiteDidEvaluate = true;
</del><ins>+            ProtocolTest.log("Throwing...");
</ins><span class="cx">             ({}).x.x.x;
</span><span class="cx">         },
</span><span class="cx">     });
</span><ins>+    asyncTeardownRuntimeExceptionSuite.addTestCase({
+        name: "AsyncTestAfterTeardownRuntimeException",
+        async setup() {
+            ProtocolTest.pass("Setup action should still execute if previous test's teardown action threw an exception.");
+        },
+        async test() {
+            ProtocolTest.pass("Test should still execute if previous test's teardown action threw an exception.");
+        },
+        async teardown() {
+            ProtocolTest.pass("Teardown action should still execute if previous test's teardown action threw an exception.");
+        },
+    });
+    checkResult(asyncTeardownRuntimeExceptionSuite, {
+        runCount: 2,
+        passCount: 1,
+        failCount: 1,
+        skipCount: 0,
+    });
</ins><span class="cx"> 
</span><del>-    result = result.then(() => {
-        return asyncTeardownRuntimeFailureTestSuite.runTestCases();
-    }).then(function resolve() {
-        ProtocolTest.fail("Promise from asyncTeardownRuntimeFailureTestSuite.runTestCases() should reject.");
-        return Promise.resolve(); // Continue this test.
-    }, function rejected(e) {
-        ProtocolTest.pass("Promise from asyncTeardownRuntimeFailureTestSuite.runTestCases() should reject.");
-        ProtocolTest.expectThat(asyncTeardownRuntimeFailureTestSuiteDidEvaluate, "Promise did evaluate the async teardown function.");
-        ProtocolTest.expectThat(e instanceof TypeError, "Rejected value should be a runtime exception.");
-        return Promise.resolve(); // Continue this test.
</del><ins>+    let asyncTeardownFailureSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncTeardownFailure");
+    asyncTeardownFailureSuite.addTestCase({
+        name: "AsyncTestWithFailureDuringTeardown",
+        async test() {
+        },
+        async teardown() {
+            ProtocolTest.log("Rejecting...");
+            return Promise.reject("AsyncTeardownFailure reject");
+        },
</ins><span class="cx">     });
</span><ins>+    asyncTeardownFailureSuite.addTestCase({
+        name: "AsyncTestAfterTeardownFailure",
+        async setup() {
+            ProtocolTest.pass("Setup action should still execute if previous test's teardown action failed.");
+        },
+        async test() {
+            ProtocolTest.pass("Test should still execute if previous test's teardown action failed.");
+        },
+        async teardown() {
+            ProtocolTest.pass("Teardown action should still execute if previous test's teardown action failed.");
+        },
+    });
+    checkResult(asyncTeardownFailureSuite, {
+        runCount: 2,
+        passCount: 1,
+        failCount: 1,
+        skipCount: 0,
+    });
</ins><span class="cx"> 
</span><ins>+    let asyncTimeoutSuite = ProtocolTest.createAsyncSuite("AsyncTestSuite.AsyncTimeout");
+    asyncTimeoutSuite.addTestCase({
+        name: "AsyncTestWithTimeout",
+        async test() {
+            ProtocolTest.log("Timeout...");
+            await Promise.delay(50);
+        },
+        timeout: 5,
+    });
+    asyncTimeoutSuite.addTestCase({
+        name: "AsyncTestAfterTimeout",
+        async setup() {
+            ProtocolTest.pass("Setup action should still execute if previous test timed out.");
+        },
+        async test() {
+            ProtocolTest.pass("Test should still execute if previous test timed out.");
+        },
+        async teardown() {
+            ProtocolTest.pass("Teardown action should still execute if previous test timed out.");
+        },
+    });
+    checkResult(asyncTimeoutSuite, {
+        runCount: 2,
+        passCount: 1,
+        failCount: 1,
+        skipCount: 0,
+    });
+
</ins><span class="cx">     // This will finish the test whether the chain was resolved or rejected.
</span><del>-    result = result.then(() => { ProtocolTest.completeTest(); });
</del><ins>+    result = result.then(() => {
+        ProtocolTest.completeTest();
+    });
</ins><span class="cx"> }
</span><span class="cx"> </script>
</span><span class="cx"> </head>
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestssynctestsuiteexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/unit-tests/sync-test-suite-expected.txt (243952 => 243953)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/sync-test-suite-expected.txt      2019-04-05 23:25:27 UTC (rev 243952)
+++ trunk/LayoutTests/inspector/unit-tests/sync-test-suite-expected.txt 2019-04-05 23:39:16 UTC (rev 243953)
</span><span class="lines">@@ -1,77 +1,155 @@
</span><del>-PASS: instantiating SyncTestSuite requires name argument.
-PASS: instantiating SyncTestSuite requires string name argument.
-PASS: instantiating SyncTestSuite requires non-whitespace name argument.
-PASS: instantiating SyncTestSuite requires test harness argument.
-PASS: should not be able to add empty test case.
-PASS: should not be able to add non-object test case.
-PASS: test case should require string name.
-PASS: test case should require non-whitespace name.
-PASS: test case should require test function.
-PASS: should not be able to specify non-Function `setup` parameter.
-PASS: should not be able to specify non-Function `setup` parameter.
-PASS: should not be able to specify non-Function `setup` parameter.
-PASS: should not be able to specify non-Function `teardown` parameter.
-PASS: should not be able to specify non-Function `teardown` parameter.
-PASS: should not be able to specify non-Function `teardown` parameter.
-PASS: should not be able to specify async `test` parameter.
-PASS: should not be able to specify async `setup` parameter.
-PASS: should not be able to specify async `teardown` parameter.
-PASS: should not be able to run empty test suite.
</del><ins>+PASS: Should produce an exception.
+Error: Must pass the test's harness as the first argument.
+PASS: Should produce an exception.
+Error: Must pass the test's harness as the first argument.
+PASS: Should produce an exception.
+Error: Must pass the test's harness as the first argument.
+PASS: Should produce an exception.
+Error: Must pass the test's harness as the first argument.
+PASS: Should produce an exception.
+TypeError: undefined is not an object (evaluating 'testcase.setup')
+PASS: Should produce an exception.
+Error: Tried to add non-object test case.
+PASS: Should produce an exception.
+Error: Tried to add test case without a name.
+PASS: Should produce an exception.
+Error: Tried to add test case without a name.
+PASS: Should produce an exception.
+Error: Tried to add test case without `test` function.
+PASS: Should produce an exception.
+Error: Tried to add test case with invalid `setup` parameter (must be a function).
+PASS: Should produce an exception.
+Error: Tried to add test case with invalid `setup` parameter (must be a function).
+PASS: Should produce an exception.
+Error: Tried to add test case with invalid `setup` parameter (must be a function).
+PASS: Should produce an exception.
+Error: Tried to add test case with invalid `teardown` parameter (must be a function).
+PASS: Should produce an exception.
+Error: Tried to add test case with invalid `teardown` parameter (must be a function).
+PASS: Should produce an exception.
+Error: Tried to add test case with invalid `teardown` parameter (must be a function).
+PASS: Should produce an exception.
+Error: Tried to pass a test case with an async `setup`, `test`, or `teardown` function, but this is a synchronous test suite.
+PASS: Should produce an exception.
+Error: Tried to pass a test case with an async `setup`, `test`, or `teardown` function, but this is a synchronous test suite.
+PASS: Should produce an exception.
+Error: Tried to pass a test case with an async `setup`, `test`, or `teardown` function, but this is a synchronous test suite.
+PASS: Should produce an exception.
+Error: Tried to call runTestCases() for suite with no test cases
</ins><span class="cx"> 
</span><span class="cx"> == Running test suite: SyncTestSuite.RunTwiceSuite
</span><span class="cx"> -- Running test case: DummyTest0
</span><span class="cx"> PASS: Return value of runTwiceSuite.runTestCases() should be true when all tests pass.
</span><del>-PASS: should not be able to run a test suite twice.
</del><ins>+PASS: Should produce an exception.
+Error: Tried to call runTestCases() more than once.
</ins><span class="cx"> 
</span><span class="cx"> == Running test suite: SyncTestSuite.SequentialExecution
</span><del>--- Running test case: DummyTest1
--- Running test case: DummyTest2
--- Running test case: DummyTest3
--- Running test case: FailingTest4
-!! EXCEPTION: [object Object]
</del><ins>+-- Running test case: 1 (Pass)
+-- Running test case: 2 (Pass)
+-- Running test case: 3 (Pass)
+-- Running test case: 4 (Pass)
+PASS: Return value of SyncTestSuite.SequentialExecution.runTestCases() should be true even if a test case fails.
+PASS: SyncTestSuite.SequentialExecution should have executed 4 tests.
+PASS: SyncTestSuite.SequentialExecution should have passed 4 tests.
+PASS: SyncTestSuite.SequentialExecution should have failed 0 tests.
+PASS: SyncTestSuite.SequentialExecution should have skipped 0 tests.
+
+== Running test suite: SyncTestSuite.ContinueOnFailure
+-- Running test case: 1 (Pass)
+-- Running test case: 2 (Fail)
+Throwing...
+!! EXCEPTION: {"x":"ContinueOnFailure throw"}
</ins><span class="cx"> Stack Trace: (suppressed)
</span><del>-PASS: Return value of sequentialExecutionSuite.runTestCases() should be false when a test case fails.
-PASS: sequentialExecutionSuite should have executed four tests.
-PASS: sequentialExecutionSuite should have passed three tests.
-PASS: sequentialExecutionSuite should have failed 1 test.
-PASS: sequentialExecutionSuite should have skipped zero tests.
</del><span class="cx"> 
</span><del>-== Running test suite: SyncTestSuite.AbortOnFailure
--- Running test case: PassingTest5
--- Running test case: FailingTest6
-PASS: Return value of abortOnFailureSuite.runTestCases() should be false when a test case fails.
-PASS: abortOnFailureSuite should have executed two tests.
-PASS: abortOnFailureSuite should have passed one test.
-PASS: abortOnFailureSuite should have failed one test.
-PASS: abortOnFailureSuite should have skipped one test.
</del><ins>+-- Running test case: 3 (Pass)
+-- Running test case: 4 (Fail)
+Failing...
+PASS: Return value of SyncTestSuite.ContinueOnFailure.runTestCases() should be true even if a test case fails.
+PASS: SyncTestSuite.ContinueOnFailure should have executed 4 tests.
+PASS: SyncTestSuite.ContinueOnFailure should have passed 2 tests.
+PASS: SyncTestSuite.ContinueOnFailure should have failed 2 tests.
+PASS: SyncTestSuite.ContinueOnFailure should have skipped 0 tests.
</ins><span class="cx"> 
</span><span class="cx"> == Running test suite: SyncTestSuite.SetupAndTeardown
</span><span class="cx"> -- Running test setup.
</span><span class="cx"> -- Running test case: TestWithSetupAndTeardown
</span><del>-PASS: Test should see side effects of running setup() action.
</del><span class="cx"> -- Running test teardown.
</span><del>-PASS: Teardown should see side effects of running setup() action.
-
</del><span class="cx"> -- Running test case: TestRunningAfterTeardown
</span><del>-PASS: Test should see side effects of previous test's teardown() action.
</del><ins>+PASS: Return value of SyncTestSuite.SetupAndTeardown.runTestCases() should be true even if a test case fails.
+PASS: SyncTestSuite.SetupAndTeardown should have executed 2 tests.
+PASS: SyncTestSuite.SetupAndTeardown should have passed 2 tests.
+PASS: SyncTestSuite.SetupAndTeardown should have failed 0 tests.
+PASS: SyncTestSuite.SetupAndTeardown should have skipped 0 tests.
</ins><span class="cx"> 
</span><span class="cx"> == Running test suite: SyncTestSuite.SetupException
</span><span class="cx"> -- Running test setup.
</span><del>-!! EXCEPTION: 
</del><ins>+Throwing...
+!! EXCEPTION: SetupException throw
</ins><span class="cx"> Stack Trace: (suppressed)
</span><span class="cx"> 
</span><ins>+-- Running test setup.
+PASS: Setup action should still execute if previous test's setup action threw an exception.
+-- Running test case: TestAfterSetupException
+PASS: Test should still execute if previous test's setup action threw an exception.
+-- Running test teardown.
+PASS: Teardown action should still execute if previous test's setup action threw an exception.
+PASS: Return value of SyncTestSuite.SetupException.runTestCases() should be true even if a test case fails.
+PASS: SyncTestSuite.SetupException should have executed 1 tests.
+PASS: SyncTestSuite.SetupException should have passed 1 tests.
+PASS: SyncTestSuite.SetupException should have failed 1 tests.
+PASS: SyncTestSuite.SetupException should have skipped 1 tests.
+
</ins><span class="cx"> == Running test suite: SyncTestSuite.SetupFailure
</span><span class="cx"> -- Running test setup.
</span><ins>+Failing...
</ins><span class="cx"> !! SETUP FAILED
</span><span class="cx"> 
</span><ins>+-- Running test setup.
+PASS: Setup action should still execute if previous test's setup action failed.
+-- Running test case: TestAfterSetupException
+PASS: Test should still execute if previous test's setup action failed.
+-- Running test teardown.
+PASS: Teardown action should still execute if previous test's setup action failed.
+PASS: Return value of SyncTestSuite.SetupFailure.runTestCases() should be true even if a test case fails.
+PASS: SyncTestSuite.SetupFailure should have executed 1 tests.
+PASS: SyncTestSuite.SetupFailure should have passed 1 tests.
+PASS: SyncTestSuite.SetupFailure should have failed 1 tests.
+PASS: SyncTestSuite.SetupFailure should have skipped 1 tests.
+
</ins><span class="cx"> == Running test suite: SyncTestSuite.TeardownException
</span><span class="cx"> -- Running test case: TestWithExceptionDuringTeardown
</span><span class="cx"> -- Running test teardown.
</span><del>-!! EXCEPTION: 
</del><ins>+Throwing...
+!! EXCEPTION: TeardownException throw
</ins><span class="cx"> Stack Trace: (suppressed)
</span><span class="cx"> 
</span><ins>+-- Running test setup.
+PASS: Setup action should still execute if previous test's teardown action threw an exception.
+-- Running test case: TestAfterTeardownException
+PASS: Test should still execute if previous test's teardown action threw an exception.
+-- Running test teardown.
+PASS: Teardown action should still execute if previous test's teardown action threw an exception.
+PASS: Return value of SyncTestSuite.TeardownException.runTestCases() should be true even if a test case fails.
+PASS: SyncTestSuite.TeardownException should have executed 2 tests.
+PASS: SyncTestSuite.TeardownException should have passed 1 tests.
+PASS: SyncTestSuite.TeardownException should have failed 1 tests.
+PASS: SyncTestSuite.TeardownException should have skipped 0 tests.
+
</ins><span class="cx"> == Running test suite: SyncTestSuite.TeardownFailure
</span><span class="cx"> -- Running test case: TestWithExceptionDuringTeardown
</span><span class="cx"> -- Running test teardown.
</span><ins>+Failing...
</ins><span class="cx"> !! TEARDOWN FAILED
</span><span class="cx"> 
</span><ins>+-- Running test setup.
+PASS: Setup action should still execute if previous test's teardown action failed.
+-- Running test case: TestAfterTeardownException
+PASS: Test should still execute if previous test's teardown action failed.
+-- Running test teardown.
+PASS: Teardown action should still execute if previous test's teardown action failed.
+PASS: Return value of SyncTestSuite.TeardownFailure.runTestCases() should be true even if a test case fails.
+PASS: SyncTestSuite.TeardownFailure should have executed 2 tests.
+PASS: SyncTestSuite.TeardownFailure should have passed 1 tests.
+PASS: SyncTestSuite.TeardownFailure should have failed 1 tests.
+PASS: SyncTestSuite.TeardownFailure should have skipped 0 tests.
+
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestssynctestsuitehtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/unit-tests/sync-test-suite.html (243952 => 243953)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/sync-test-suite.html      2019-04-05 23:25:27 UTC (rev 243952)
+++ trunk/LayoutTests/inspector/unit-tests/sync-test-suite.html 2019-04-05 23:39:16 UTC (rev 243953)
</span><span class="lines">@@ -1,4 +1,4 @@
</span><del>-<!doctype html>
</del><ins>+<!DOCTYPE html>
</ins><span class="cx"> <html>
</span><span class="cx"> <head>
</span><span class="cx"> <script src="../../http/tests/inspector/resources/protocol-test.js"></script>
</span><span class="lines">@@ -7,350 +7,404 @@
</span><span class="cx"> {
</span><span class="cx">     ProtocolTest.suppressStackTraces = true;
</span><span class="cx"> 
</span><del>-    try {
-        let result = new SyncTestSuite(this);
-        ProtocolTest.log("FAIL: instantiating SyncTestSuite requires name argument.");
-    } catch (e) {
-        ProtocolTest.log("PASS: instantiating SyncTestSuite requires name argument.");
-    }
</del><ins>+    ProtocolTest.expectException(() => {
+        new SyncTestSuite(this);
+    });
</ins><span class="cx"> 
</span><del>-    try {
-        let result = new SyncTestSuite(this, {});
-        ProtocolTest.log("FAIL: instantiating SyncTestSuite requires string name argument.");
-    } catch (e) {
-        ProtocolTest.log("PASS: instantiating SyncTestSuite requires string name argument.");
-    }
</del><ins>+    ProtocolTest.expectException(() => {
+        new SyncTestSuite(this, {});
+    });
</ins><span class="cx"> 
</span><del>-    try {
-        let result = new SyncTestSuite(this, "      ");
-        ProtocolTest.log("FAIL: instantiating SyncTestSuite requires non-whitespace name argument.");
-    } catch (e) {
-        ProtocolTest.log("PASS: instantiating SyncTestSuite requires non-whitespace name argument.");
-    }
</del><ins>+    ProtocolTest.expectException(() => {
+        new SyncTestSuite(this, "      ");
+    });
</ins><span class="cx"> 
</span><del>-    try {
-        let result = new SyncTestSuite("something", {});
-        ProtocolTest.log("FAIL: instantiating SyncTestSuite requires test harness argument.");
-    } catch (e) {
-        ProtocolTest.log("PASS: instantiating SyncTestSuite requires test harness argument.");
-    }
</del><ins>+    ProtocolTest.expectException(() => {
+        new SyncTestSuite("something", {});
+    });
</ins><span class="cx"> 
</span><span class="cx">     let badArgsSuite = ProtocolTest.createSyncSuite("dummy");
</span><del>-    try {
</del><ins>+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase();
</span><del>-        ProtocolTest.log("FAIL: should not be able to add empty test case.");
-    } catch (e) {
-        ProtocolTest.log("PASS: should not be able to add empty test case.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase("string");
</span><del>-        ProtocolTest.log("FAIL: should not be able to add non-object test case.");
-    } catch (e) {
-        ProtocolTest.log("PASS: should not be able to add non-object test case.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: {},
</span><del>-            test: () => true,
</del><ins>+            test() {
+                return true;
+            },
</ins><span class="cx">         });
</span><del>-        ProtocolTest.log("FAIL: test case should require string name.");
-    } catch (e) {
-        ProtocolTest.log("PASS: test case should require string name.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "        ",
</span><del>-            test() {},
</del><ins>+            test() {
+            },
</ins><span class="cx">         });
</span><del>-        ProtocolTest.log("FAIL: test case should require non-whitespace name.");
-    } catch (e) {
-        ProtocolTest.log("PASS: test case should require non-whitespace name.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "foo",
</span><span class="cx">             test: null,
</span><span class="cx">         });
</span><del>-        ProtocolTest.log("FAIL: test case should require test function.");
-    } catch (e) {
-        ProtocolTest.log("PASS: test case should require test function.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "foo",
</span><del>-            test() {},
</del><ins>+            test() {
+            },
</ins><span class="cx">             setup: "astd"
</span><span class="cx">         });
</span><del>-        ProtocolTest.log("FAIL: should not be able to specify non-Function `setup` parameter.");
-    } catch (e) {
-        ProtocolTest.log("PASS: should not be able to specify non-Function `setup` parameter.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "foo",
</span><del>-            test() {},
</del><ins>+            test() {
+            },
</ins><span class="cx">             setup: 123
</span><span class="cx">         });
</span><del>-        ProtocolTest.log("FAIL: should not be able to specify non-Function `setup` parameter.");
-    } catch (e) {
-        ProtocolTest.log("PASS: should not be able to specify non-Function `setup` parameter.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "foo",
</span><del>-            test() {},
</del><ins>+            test() {
+            },
</ins><span class="cx">             setup: Promise.resolve()
</span><span class="cx">         });
</span><del>-        ProtocolTest.log("FAIL: should not be able to specify non-Function `setup` parameter.");
-    } catch (e) {
-        ProtocolTest.log("PASS: should not be able to specify non-Function `setup` parameter.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "foo",
</span><del>-            test() {},
</del><ins>+            test() {
+            },
</ins><span class="cx">             teardown: "astd"
</span><span class="cx">         });
</span><del>-        ProtocolTest.log("FAIL: should not be able to specify non-Function `teardown` parameter.");
-    } catch (e) {
-        ProtocolTest.log("PASS: should not be able to specify non-Function `teardown` parameter.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "foo",
</span><del>-            test() {},
</del><ins>+            test() {
+            },
</ins><span class="cx">             teardown: 123
</span><span class="cx">         });
</span><del>-        ProtocolTest.log("FAIL: should not be able to specify non-Function `teardown` parameter.");
-    } catch (e) {
-        ProtocolTest.log("PASS: should not be able to specify non-Function `teardown` parameter.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "foo",
</span><del>-            test() {},
</del><ins>+            test() {
+            },
</ins><span class="cx">             teardown: Promise.resolve()
</span><span class="cx">         });
</span><del>-        ProtocolTest.log("FAIL: should not be able to specify non-Function `teardown` parameter.");
-    } catch (e) {
-        ProtocolTest.log("PASS: should not be able to specify non-Function `teardown` parameter.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "foo",
</span><del>-            async test() {},
</del><ins>+            async test() {
+            },
</ins><span class="cx">         });
</span><del>-        ProtocolTest.log("FAIL: should not be able to specify async `test` parameter.");
-    } catch (e) {
-        ProtocolTest.log("PASS: should not be able to specify async `test` parameter.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "foo",
</span><del>-            async setup() {},
-            test() {},
</del><ins>+            async setup() {
+            },
+            test() {
+            },
</ins><span class="cx">         });
</span><del>-        ProtocolTest.log("FAIL: should not be able to specify async `setup` parameter.");
-    } catch (e) {
-        ProtocolTest.log("PASS: should not be able to specify async `setup` parameter.");
-    }
-    try {
</del><ins>+    });
+    ProtocolTest.expectException(() => {
</ins><span class="cx">         badArgsSuite.addTestCase({
</span><span class="cx">             name: "foo",
</span><del>-            test() {},
-            async teardown() {},
</del><ins>+            test() {
+            },
+            async teardown() {
+            },
</ins><span class="cx">         });
</span><del>-        ProtocolTest.log("FAIL: should not be able to specify async `teardown` parameter.");
-    } catch (e) {
-        ProtocolTest.log("PASS: should not be able to specify async `teardown` parameter.");
-    }
</del><ins>+    });
</ins><span class="cx"> 
</span><del>-    let runEmptySuite = ProtocolTest.createSyncSuite("SyncTestSuite.RunEmptySuite");
-    try {
</del><ins>+    ProtocolTest.expectException(() => {
+        let runEmptySuite = ProtocolTest.createSyncSuite("SyncTestSuite.RunEmptySuite");
</ins><span class="cx">         runEmptySuite.runTestCases();
</span><del>-        ProtocolTest.log("FAIL: should not be able to run empty test suite.");
-    } catch (e) {
-        ProtocolTest.log("PASS: should not be able to run empty test suite.");
-    }
</del><ins>+    });
</ins><span class="cx"> 
</span><span class="cx">     let runTwiceSuite = ProtocolTest.createSyncSuite("SyncTestSuite.RunTwiceSuite");
</span><span class="cx">     runTwiceSuite.addTestCase({
</span><span class="cx">         name: "DummyTest0",
</span><del>-        description: "Check that a suite can't run more than once.",
-        test: () => true,
</del><ins>+        test() {
+            return true;
+        },
</ins><span class="cx">     });
</span><span class="cx"> 
</span><del>-    try {
-        let result = runTwiceSuite.runTestCases();
-        ProtocolTest.expectThat(result === true, "Return value of runTwiceSuite.runTestCases() should be true when all tests pass.");
</del><ins>+    ProtocolTest.expectThat(runTwiceSuite.runTestCases() === true, "Return value of runTwiceSuite.runTestCases() should be true when all tests pass.");
</ins><span class="cx"> 
</span><del>-        runTwiceSuite.runTestCases(); // Try to trigger an error.
-        ProtocolTest.log("FAIL: should not be able to run a test suite twice.");
-    } catch (e) {
-        ProtocolTest.log("PASS: should not be able to run a test suite twice.");
</del><ins>+    ProtocolTest.expectException(() => {
+        runTwiceSuite.runTestCases();
+    });
+
+    function checkResult(suite, expectedCounts) {
+        ProtocolTest.expectThat(suite.runTestCases(), `Return value of ${suite.name}.runTestCases() should be true even if a test case fails.`);
+        ProtocolTest.expectEqual(expectedCounts.runCount, suite.runCount, `${suite.name} should have executed ${expectedCounts.runCount} tests.`);
+        ProtocolTest.expectEqual(expectedCounts.passCount, suite.passCount, `${suite.name} should have passed ${expectedCounts.passCount} tests.`);
+        ProtocolTest.expectEqual(expectedCounts.failCount, suite.failCount, `${suite.name} should have failed ${expectedCounts.failCount} tests.`);
+        ProtocolTest.expectEqual(expectedCounts.skipCount, suite.skipCount, `${suite.name} should have skipped ${expectedCounts.skipCount} tests.`);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     let thrownError = new Error({"token": 666});
</span><span class="cx"> 
</span><ins>+    let sequentialExecutionPhase = 0;
</ins><span class="cx">     let sequentialExecutionSuite = ProtocolTest.createSyncSuite("SyncTestSuite.SequentialExecution");
</span><span class="cx">     sequentialExecutionSuite.addTestCase({
</span><del>-        name: "DummyTest1",
-        description: "Check test case execution order.",
-        test: () => true
</del><ins>+        name: "1 (Pass)",
+        test() {
+            ProtocolTest.assert(sequentialExecutionPhase === 0);
+            sequentialExecutionPhase = 1;
+            return true;
+        },
</ins><span class="cx">     });
</span><span class="cx">     sequentialExecutionSuite.addTestCase({
</span><del>-        name: "DummyTest2",
-        description: "Check test case execution order.",
-        test: () => true
</del><ins>+        name: "2 (Pass)",
+        test() {
+            ProtocolTest.assert(sequentialExecutionPhase === 1);
+            sequentialExecutionPhase = 2;
+            return true;
+        },
</ins><span class="cx">     });
</span><span class="cx">     sequentialExecutionSuite.addTestCase({
</span><del>-        name: "DummyTest3",
-        description: "Check test case execution order.",
-        test: () => true
</del><ins>+        name: "3 (Pass)",
+        test() {
+            ProtocolTest.assert(sequentialExecutionPhase === 2);
+            sequentialExecutionPhase = 3;
+            return true;
+        },
</ins><span class="cx">     });
</span><span class="cx">     sequentialExecutionSuite.addTestCase({
</span><del>-        name: "FailingTest4",
-        description: "Check that test fails by throwing an Error instance.",
-        test() { throw thrownError; }
</del><ins>+        name: "4 (Pass)",
+        test() {
+            ProtocolTest.assert(sequentialExecutionPhase === 3);
+            sequentialExecutionPhase = 4;
+            return true;
+        },
</ins><span class="cx">     });
</span><ins>+    checkResult(sequentialExecutionSuite, {
+        runCount: 4,
+        passCount: 4,
+        failCount: 0,
+        skipCount: 0,
+    });
</ins><span class="cx"> 
</span><del>-    let result = sequentialExecutionSuite.runTestCases();
-    ProtocolTest.expectThat(result === false, "Return value of sequentialExecutionSuite.runTestCases() should be false when a test case fails.");
-    ProtocolTest.expectThat(sequentialExecutionSuite.runCount === 4, "sequentialExecutionSuite should have executed four tests.");
-    ProtocolTest.expectThat(sequentialExecutionSuite.passCount === 3, "sequentialExecutionSuite should have passed three tests.");
-    ProtocolTest.expectThat(sequentialExecutionSuite.failCount === 1, "sequentialExecutionSuite should have failed 1 test.");
-    ProtocolTest.expectThat(sequentialExecutionSuite.skipCount === 0, "sequentialExecutionSuite should have skipped zero tests.");
-
-    let abortOnFailureSuite = ProtocolTest.createSyncSuite("SyncTestSuite.AbortOnFailure");
-    abortOnFailureSuite.addTestCase({
-        name: "PassingTest5",
-        description: "This test is a dummy.",
-        test: () => true
</del><ins>+    let continueOnFailureSuite = ProtocolTest.createSyncSuite("SyncTestSuite.ContinueOnFailure");
+    continueOnFailureSuite.addTestCase({
+        name: "1 (Pass)",
+        test() {
+            return true;
+        },
</ins><span class="cx">     });
</span><del>-    abortOnFailureSuite.addTestCase({
-        name: "FailingTest6",
-        description: "This test should fail by explicitly returning `false`.",
-        test: () => false
</del><ins>+    continueOnFailureSuite.addTestCase({
+        name: "2 (Fail)",
+        test() {
+            ProtocolTest.log("Throwing...");
+            throw {x: "ContinueOnFailure throw"};
+        },
</ins><span class="cx">     });
</span><del>-    abortOnFailureSuite.addTestCase({
-        name: "PassingTest7",
-        description: "This test should not executed when the preceding test fails.",
-        test: () => true
</del><ins>+    continueOnFailureSuite.addTestCase({
+        name: "3 (Pass)",
+        test() {
+            return true;
+        },
</ins><span class="cx">     });
</span><ins>+    continueOnFailureSuite.addTestCase({
+        name: "4 (Fail)",
+        test() {
+            ProtocolTest.log("Failing...");
+            return false;
+        },
+    });
+    checkResult(continueOnFailureSuite, {
+        runCount: 4,
+        passCount: 2,
+        failCount: 2,
+        skipCount: 0,
+    });
</ins><span class="cx"> 
</span><del>-    abortOnFailureSuite.runTestCases();
-    ProtocolTest.expectThat(result === false, "Return value of abortOnFailureSuite.runTestCases() should be false when a test case fails.");
-    ProtocolTest.expectThat(abortOnFailureSuite.runCount === 2, "abortOnFailureSuite should have executed two tests.");
-    ProtocolTest.expectThat(abortOnFailureSuite.passCount === 1, "abortOnFailureSuite should have passed one test.");
-    ProtocolTest.expectThat(abortOnFailureSuite.failCount === 1, "abortOnFailureSuite should have failed one test.");
-    ProtocolTest.expectThat(abortOnFailureSuite.skipCount === 1, "abortOnFailureSuite should have skipped one test.");
-
-    let setupAndTeardownSymbol = Symbol("sync-suite-setup-and-teardown-token");
-    window[setupAndTeardownSymbol] = 0;
-
-    let setupAndTeardownTestSuite = ProtocolTest.createSyncSuite("SyncTestSuite.SetupAndTeardown");
-    setupAndTeardownTestSuite.addTestCase({
</del><ins>+    let setupAndTeardownPhase = 0;
+    let setupAndTeardownSuite = ProtocolTest.createSyncSuite("SyncTestSuite.SetupAndTeardown");
+    setupAndTeardownSuite.addTestCase({
</ins><span class="cx">         name: "TestWithSetupAndTeardown",
</span><del>-        description: "Check execution order for setup and teardown actions.",
-        setup: () => {
-            window[setupAndTeardownSymbol] = 1;
</del><ins>+        setup() {
+            ProtocolTest.assert(setupAndTeardownPhase === 0);
+            setupAndTeardownPhase = 1;
</ins><span class="cx">             return true;
</span><span class="cx">         },
</span><span class="cx">         test() {
</span><del>-            ProtocolTest.expectThat(window[setupAndTeardownSymbol] === 1, "Test should see side effects of running setup() action.");
-            window[setupAndTeardownSymbol] = 2;
</del><ins>+            ProtocolTest.assert(setupAndTeardownPhase === 1);
+            setupAndTeardownPhase = 2;
</ins><span class="cx">             return true;
</span><span class="cx">         },
</span><del>-        teardown: () => {
-            ProtocolTest.expectThat(window[setupAndTeardownSymbol] === 2, "Teardown should see side effects of running setup() action.");
-            window[setupAndTeardownSymbol] = 3;
</del><ins>+        teardown() {
+            ProtocolTest.assert(setupAndTeardownPhase === 2);
+            setupAndTeardownPhase = 3;
</ins><span class="cx">             return true;
</span><del>-        }
</del><ins>+        },
</ins><span class="cx">     });
</span><del>-    setupAndTeardownTestSuite.addTestCase({
</del><ins>+    setupAndTeardownSuite.addTestCase({
</ins><span class="cx">         name: "TestRunningAfterTeardown",
</span><del>-        description: "Check execution order for test after a teardown action.",
</del><span class="cx">         test() {
</span><del>-            ProtocolTest.expectThat(window[setupAndTeardownSymbol] === 3, "Test should see side effects of previous test's teardown() action.");
</del><ins>+            ProtocolTest.assert(setupAndTeardownPhase === 3);
</ins><span class="cx">             return true;
</span><span class="cx">         },
</span><span class="cx">     });
</span><ins>+    checkResult(setupAndTeardownSuite, {
+        runCount: 2,
+        passCount: 2,
+        failCount: 0,
+        skipCount: 0,
+    });
</ins><span class="cx"> 
</span><del>-    setupAndTeardownTestSuite.runTestCases();
-
-    let setupExceptionTestSuite = ProtocolTest.createSyncSuite("SyncTestSuite.SetupException");
-    setupExceptionTestSuite.addTestCase({
</del><ins>+    
+    let setupExceptionSuite = ProtocolTest.createSyncSuite("SyncTestSuite.SetupException");
+    setupExceptionSuite.addTestCase({
</ins><span class="cx">         name: "TestWithExceptionDuringSetup",
</span><del>-        description: "Check execution order for setup action that throws an exception.",
-        setup: () => { throw new Error() },
</del><ins>+        setup() {
+            ProtocolTest.log("Throwing...");
+            throw "SetupException throw";
+        },
</ins><span class="cx">         test() {
</span><del>-            ProtocolTest.assert(false, "Test should not execute if its setup action threw an exception.");
</del><ins>+            ProtocolTest.fail("Test should not execute if its setup action threw an exception.");
</ins><span class="cx">             return false;
</span><span class="cx">         },
</span><del>-        teardown: () => {
-            ProtocolTest.assert(false, "Teardown action should not execute if its setup action threw an exception.");
-            return false;           
-        }
</del><ins>+        teardown() {
+            ProtocolTest.fail("Teardown action should not execute if its setup action threw an exception.");
+            return false;
+        },
</ins><span class="cx">     });
</span><del>-    setupExceptionTestSuite.runTestCases();
</del><ins>+    setupExceptionSuite.addTestCase({
+        name: "TestAfterSetupException",
+        setup() {
+            ProtocolTest.pass("Setup action should still execute if previous test's setup action threw an exception.");
+            return true;
+        },
+        test() {
+            ProtocolTest.pass("Test should still execute if previous test's setup action threw an exception.");
+            return true;
+        },
+        teardown() {
+            ProtocolTest.pass("Teardown action should still execute if previous test's setup action threw an exception.");
+            return true;
+        },
+    });
+    checkResult(setupExceptionSuite, {
+        runCount: 1,
+        passCount: 1,
+        failCount: 1,
+        skipCount: 1,
+    });
</ins><span class="cx"> 
</span><del>-    let setupFailureTestSuite = ProtocolTest.createSyncSuite("SyncTestSuite.SetupFailure");
-    setupFailureTestSuite.addTestCase({
</del><ins>+    let setupFailureSuite = ProtocolTest.createSyncSuite("SyncTestSuite.SetupFailure");
+    setupFailureSuite.addTestCase({
</ins><span class="cx">         name: "TestWithFailureDuringSetup",
</span><del>-        description: "Check execution order for setup action that has failed.",
-        setup: () => false,
</del><ins>+        setup() {
+            ProtocolTest.log("Failing...");
+            return false;
+        },
</ins><span class="cx">         test() {
</span><del>-            ProtocolTest.assert(false, "Test should not execute if its setup action returned false.")
</del><ins>+            ProtocolTest.fail("Test should not execute if its setup action failed.");
</ins><span class="cx">             return false;
</span><span class="cx">         },
</span><del>-        teardown: () => {
-            ProtocolTest.assert(false, "Teardown action should not execute if its setup action returned false.")
-            return false;           
-        }
</del><ins>+        teardown() {
+            ProtocolTest.fail("Teardown action should not execute if its setup action failed.");
+            return false;
+        },
</ins><span class="cx">     });
</span><del>-    setupFailureTestSuite.runTestCases();
</del><ins>+    setupFailureSuite.addTestCase({
+        name: "TestAfterSetupException",
+        setup() {
+            ProtocolTest.pass("Setup action should still execute if previous test's setup action failed.");
+            return true;
+        },
+        test() {
+            ProtocolTest.pass("Test should still execute if previous test's setup action failed.");
+            return true;
+        },
+        teardown() {
+            ProtocolTest.pass("Teardown action should still execute if previous test's setup action failed.");
+            return true;
+        },
+    });
+    checkResult(setupFailureSuite, {
+        runCount: 1,
+        passCount: 1,
+        failCount: 1,
+        skipCount: 1,
+    });
</ins><span class="cx"> 
</span><del>-    let teardownExceptionTestSuite = ProtocolTest.createSyncSuite("SyncTestSuite.TeardownException");
-    teardownExceptionTestSuite.addTestCase({
</del><ins>+    let teardownExceptionSuite = ProtocolTest.createSyncSuite("SyncTestSuite.TeardownException");
+    teardownExceptionSuite.addTestCase({
</ins><span class="cx">         name: "TestWithExceptionDuringTeardown",
</span><del>-        description: "Check execution order for teardown action that throws an exception.",
-        test: () => true,
-        teardown: () => { throw new Error() }
</del><ins>+        test() {
+            return true;
+        },
+        teardown() {
+            ProtocolTest.log("Throwing...");
+            throw "TeardownException throw";
+        },
</ins><span class="cx">     });
</span><del>-    teardownExceptionTestSuite.addTestCase({
</del><ins>+    teardownExceptionSuite.addTestCase({
</ins><span class="cx">         name: "TestAfterTeardownException",
</span><del>-        descrption: "Check execution order for test after previous test's teardown throws an exception",
-        setup: () => {
-            ProtocolTest.assert(false, "Setup action should not execute if previous test's teardown action threw an exception.");
-            return false;
</del><ins>+        setup() {
+            ProtocolTest.pass("Setup action should still execute if previous test's teardown action threw an exception.");
+            return true;
</ins><span class="cx">         },
</span><span class="cx">         test() {
</span><del>-            ProtocolTest.assert(false, "Test should not execute if previous test's teardown action threw an exception.");
-            return false;
-        }
</del><ins>+            ProtocolTest.pass("Test should still execute if previous test's teardown action threw an exception.");
+            return true;
+        },
+        teardown() {
+            ProtocolTest.pass("Teardown action should still execute if previous test's teardown action threw an exception.");
+            return true;
+        },
</ins><span class="cx">     });
</span><del>-    teardownExceptionTestSuite.runTestCases();
</del><ins>+    checkResult(teardownExceptionSuite, {
+        runCount: 2,
+        passCount: 1,
+        failCount: 1,
+        skipCount: 0,
+    });
</ins><span class="cx"> 
</span><del>-    let teardownFailureTestSuite = ProtocolTest.createSyncSuite("SyncTestSuite.TeardownFailure");
-    teardownFailureTestSuite.addTestCase({
</del><ins>+    let teardownFailureSuite = ProtocolTest.createSyncSuite("SyncTestSuite.TeardownFailure");
+    teardownFailureSuite.addTestCase({
</ins><span class="cx">         name: "TestWithExceptionDuringTeardown",
</span><del>-        description: "Check execution order for teardown action that has failed.",
-        test: () => true,
-        teardown: () => false,
</del><ins>+        test() {
+            return true;
+        },
+        teardown() {
+            ProtocolTest.log("Failing...");
+            return false;
+        },
</ins><span class="cx">     });
</span><del>-    teardownFailureTestSuite.addTestCase({
</del><ins>+    teardownFailureSuite.addTestCase({
</ins><span class="cx">         name: "TestAfterTeardownException",
</span><del>-        descrption: "Check execution order for test after previous test's teardown throws an exception",
-        setup: () => {
-            ProtocolTest.assert(false, "Setup action should not execute if previous test's teardown action failed.");
-            return false;
</del><ins>+        setup() {
+            ProtocolTest.pass("Setup action should still execute if previous test's teardown action failed.");
+            return true;
</ins><span class="cx">         },
</span><span class="cx">         test() {
</span><del>-            ProtocolTest.assert(false, "Test should not execute if previous test's teardown action failed.");
-            return false;
-        }
</del><ins>+            ProtocolTest.pass("Test should still execute if previous test's teardown action failed.");
+            return true;
+        },
+        teardown() {
+            ProtocolTest.pass("Teardown action should still execute if previous test's teardown action failed.");
+            return true;
+        },
</ins><span class="cx">     });
</span><del>-    teardownFailureTestSuite.runTestCases();
</del><ins>+    checkResult(teardownFailureSuite, {
+        runCount: 2,
+        passCount: 1,
+        failCount: 1,
+        skipCount: 0,
+    });
</ins><span class="cx"> 
</span><span class="cx">     ProtocolTest.completeTest();
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/ChangeLog (243952 => 243953)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/ChangeLog    2019-04-05 23:25:27 UTC (rev 243952)
+++ trunk/Source/WebInspectorUI/ChangeLog       2019-04-05 23:39:16 UTC (rev 243953)
</span><span class="lines">@@ -1,3 +1,30 @@
</span><ins>+2019-04-05  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: TestSuite test cases should have their own timeout to ensure tests fail with output instead of timeout by test runner
+        https://bugs.webkit.org/show_bug.cgi?id=162814
+        <rdar://problem/28574102>
+
+        Reviewed by Brian Burg.
+
+        A 10s timer is started for every test case added to an async suite. The timer is cleared
+        when the test finishes, but if the timer fires, the test is forcibly ended with an error.
+
+        This timer can be configured by setting a `timeout` value when adding the test case. Values
+        are expected to be in milliseconds. The value `-1` will prevent a timer from being set.
+
+        This change also relaxes the expectation that any individual test case failure will stop the
+        rest of the suite from running. Since timers are set per test case, it is possible to
+        recover from a "bad" test case to still run the remaining test cases.
+
+        NOTE: there may be unexpected behaviour if a test times out, as the timer doesn't actually
+        stop the execution of the test, so it may still run and log information, which may appear
+        "out of nowhere" in the middle of other tests.
+
+        * UserInterface/Test/TestSuite.js:
+        (TestSuite.prototype.get passCount):
+        (AsyncTestSuite.prototype.runTestCases):
+        (SyncTestSuite.prototype.runTestCases):
+
</ins><span class="cx"> 2019-04-03  Devin Rousso  <drousso@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Web Inspector: Single click on links in non-read-only TextEditors should not follow links
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceTestTestSuitejs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Test/TestSuite.js (243952 => 243953)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Test/TestSuite.js      2019-04-05 23:25:27 UTC (rev 243952)
+++ trunk/Source/WebInspectorUI/UserInterface/Test/TestSuite.js 2019-04-05 23:39:16 UTC (rev 243953)
</span><span class="lines">@@ -54,7 +54,7 @@
</span><span class="cx"> 
</span><span class="cx">     get passCount()
</span><span class="cx">     {
</span><del>-        return this.runCount - this.failCount;
</del><ins>+        return this.runCount - (this.failCount - this.skipCount);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     get skipCount()
</span><span class="lines">@@ -135,45 +135,73 @@
</span><span class="cx"> 
</span><span class="cx">         // Avoid adding newlines if nothing was logged.
</span><span class="cx">         let priorLogCount = this._harness.logCount;
</span><del>-        let result = this.testcases.reduce((chain, testcase, i) => {
-            if (testcase.setup) {
-                chain = chain.then(() => {
</del><ins>+
+        return Promise.resolve().then(() => Promise.chain(this.testcases.map((testcase, i) => () => new Promise(async (resolve, reject) => {
+            if (i > 0 && priorLogCount < this._harness.logCount)
+                this._harness.log("");
+            priorLogCount = this._harness.logCount;
+
+            let hasTimeout = testcase.timeout !== -1;
+            let timeoutId = undefined;
+            if (hasTimeout) {
+                let delay = testcase.timeout || 10000;
+                timeoutId = setTimeout(() => {
+                    if (!timeoutId)
+                        return;
+
+                    timeoutId = undefined;
+
+                    this.failCount++;
+                    this._harness.log(`!! TIMEOUT: took longer than ${delay}ms`);
+
+                    resolve();
+                }, delay);
+            }
+
+            try {
+                if (testcase.setup) {
</ins><span class="cx">                     this._harness.log("-- Running test setup.");
</span><ins>+                    priorLogCount++;
+
</ins><span class="cx">                     if (testcase.setup[Symbol.toStringTag] === "AsyncFunction")
</span><del>-                        return testcase.setup();
-                    return new Promise(testcase.setup);
-                });
-            }
</del><ins>+                        await testcase.setup();
+                    else
+                        await new Promise(testcase.setup);
+                }
</ins><span class="cx"> 
</span><del>-            chain = chain.then(() => {
-                if (i > 0 && priorLogCount + 1 < this._harness.logCount)
-                    this._harness.log("");
</del><ins>+                this.runCount++;
</ins><span class="cx"> 
</span><del>-                priorLogCount = this._harness.logCount;
</del><span class="cx">                 this._harness.log(`-- Running test case: ${testcase.name}`);
</span><del>-                this.runCount++;
</del><ins>+                priorLogCount++;
+
</ins><span class="cx">                 if (testcase.test[Symbol.toStringTag] === "AsyncFunction")
</span><del>-                    return testcase.test();
-                return new Promise(testcase.test);
-            });
</del><ins>+                    await testcase.test();
+                else
+                    await new Promise(testcase.test);
</ins><span class="cx"> 
</span><del>-            if (testcase.teardown) {
-                chain = chain.then(() => {
</del><ins>+                if (testcase.teardown) {
</ins><span class="cx">                     this._harness.log("-- Running test teardown.");
</span><ins>+                    priorLogCount++;
+
</ins><span class="cx">                     if (testcase.teardown[Symbol.toStringTag] === "AsyncFunction")
</span><del>-                        return testcase.teardown();
-                    return new Promise(testcase.teardown);
-                });
</del><ins>+                        await testcase.teardown();
+                    else
+                        await new Promise(testcase.teardown);
+                }
+            } catch (e) {
+                this.failCount++;
+                this.logThrownObject(e);
</ins><span class="cx">             }
</span><del>-            return chain;
-        }, Promise.resolve());
</del><span class="cx"> 
</span><del>-        return result.catch((e) => {
-            this.failCount++;
-            this.logThrownObject(e);
</del><ins>+            if (!hasTimeout || timeoutId) {
+                clearTimeout(timeoutId);
+                timeoutId = undefined;
</ins><span class="cx"> 
</span><del>-            throw e; // Reject this promise by re-throwing the error.
-        });
</del><ins>+                resolve();
+            }
+        })))
+        // Clear result value.
+        .then(() => {}));
</ins><span class="cx">     }
</span><span class="cx"> };
</span><span class="cx"> 
</span><span class="lines">@@ -208,53 +236,51 @@
</span><span class="cx">         let priorLogCount = this._harness.logCount;
</span><span class="cx">         for (let i = 0; i < this.testcases.length; i++) {
</span><span class="cx">             let testcase = this.testcases[i];
</span><del>-            if (i > 0 && priorLogCount + 1 < this._harness.logCount)
</del><ins>+
+            if (i > 0 && priorLogCount < this._harness.logCount)
</ins><span class="cx">                 this._harness.log("");
</span><del>-
</del><span class="cx">             priorLogCount = this._harness.logCount;
</span><span class="cx"> 
</span><del>-            // Run the setup action, if one was provided.
-            if (testcase.setup) {
-                this._harness.log("-- Running test setup.");
-                try {
-                    let result = testcase.setup.call(null);
-                    if (result === false) {
</del><ins>+            try {
+                // Run the setup action, if one was provided.
+                if (testcase.setup) {
+                    this._harness.log("-- Running test setup.");
+                    priorLogCount++;
+
+                    let setupResult = testcase.setup();
+                    if (setupResult === false) {
</ins><span class="cx">                         this._harness.log("!! SETUP FAILED");
</span><del>-                        return false;
</del><ins>+                        this.failCount++;
+                        continue;
</ins><span class="cx">                     }
</span><del>-                } catch (e) {
-                    this.logThrownObject(e);
-                    return false;
</del><span class="cx">                 }
</span><del>-            }
</del><span class="cx"> 
</span><del>-            this._harness.log("-- Running test case: " + testcase.name);
-            this.runCount++;
-            try {
-                let result = testcase.test.call(null);
-                if (result === false) {
</del><ins>+                this.runCount++;
+
+                this._harness.log(`-- Running test case: ${testcase.name}`);
+                priorLogCount++;
+
+                let testResult = testcase.test();
+                if (testResult === false) {
</ins><span class="cx">                     this.failCount++;
</span><del>-                    return false;
</del><ins>+                    continue;
</ins><span class="cx">                 }
</span><del>-            } catch (e) {
-                this.failCount++;
-                this.logThrownObject(e);
-                return false;
-            }
</del><span class="cx"> 
</span><del>-            // Run the teardown action, if one was provided.
-            if (testcase.teardown) {
-                this._harness.log("-- Running test teardown.");
-                try {
-                    let result = testcase.teardown.call(null);
-                    if (result === false) {
</del><ins>+                // Run the teardown action, if one was provided.
+                if (testcase.teardown) {
+                    this._harness.log("-- Running test teardown.");
+                    priorLogCount++;
+
+                    let teardownResult = testcase.teardown();
+                    if (teardownResult === false) {
</ins><span class="cx">                         this._harness.log("!! TEARDOWN FAILED");
</span><del>-                        return false;
</del><ins>+                        this.failCount++;
+                        continue;
</ins><span class="cx">                     }
</span><del>-                } catch (e) {
-                    this.logThrownObject(e);
-                    return false;
</del><span class="cx">                 }
</span><ins>+            } catch (e) {
+                this.failCount++;
+                this.logThrownObject(e);
</ins><span class="cx">             }
</span><span class="cx">         }
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>