<!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>[187046] trunk/LayoutTests</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/187046">187046</a></dd>
<dt>Author</dt> <dd>burg@cs.washington.edu</dd>
<dt>Date</dt> <dd>2015-07-20 16:35:51 -0700 (Mon, 20 Jul 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Web Inspector: start using Promises to handle asynchronous steps in protocol tests
https://bugs.webkit.org/show_bug.cgi?id=147096

Reviewed by Joseph Pecoraro.

Existing protocol tests use lots of reinvented wheels to run
multiple asynchronous tests. This patch begins consolidation of
these redundant wheels, using a tiny test framework based on
promises.

The first change is to introduce InspectorTest.awaitCommand,
which returns a promise that is fulfilled or rejected if the command
succeeds or fails. This is the building block for writing protocol
tests with promises.

The second change is the introduction of InspectorTest.AsyncTestSuite.
This properly chains together multiple asynchronous test methods
using the promise interface. A synchronous version is also added.

To demonstrate how async tests can be written using this new approach,
this patch converts existing protocol test. Test coverage for the
new frameworks also demonstrates their use.

* http/tests/inspector-protocol/resources/InspectorTest.js:
(InspectorTest.sendCommand):
(InspectorTest.awaitCommand):
(InspectorTest.sendMessage):
(InspectorFrontendAPI.dispatchMessageAsync):
(InspectorTest.AsyncTestSuite): Add tests for this micro framework.
(InspectorTest.AsyncTestSuite.prototype.get passCount):
(InspectorTest.AsyncTestSuite.prototype.get skipCount):
(InspectorTest.AsyncTestSuite.prototype.addTestCase):
(InspectorTest.AsyncTestSuite.prototype.runTestCasesAndFinish.finish):
(InspectorTest.AsyncTestSuite.prototype.runTestCasesAndFinish):
(InspectorTest.AsyncTestSuite.prototype.runTestCases):
(InspectorTest.SyncTestSuite): Add tests for this micro framework.
(InspectorTest.SyncTestSuite.prototype.get passCount):
(InspectorTest.SyncTestSuite.prototype.get skipCount):
(InspectorTest.SyncTestSuite.prototype.addTestCase):
(InspectorTest.SyncTestSuite.prototype.runTestCasesAndFinish):
(InspectorTest.SyncTestSuite.prototype.runTestCases):
(InspectorTest.log):
(InspectorTest.assert):
* inspector-protocol/runtime/getProperties-expected.txt:
* inspector-protocol/runtime/getProperties.html: Rewrite.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestshttptestsinspectorprotocolresourcesInspectorTestjs">trunk/LayoutTests/http/tests/inspector-protocol/resources/InspectorTest.js</a></li>
<li><a href="#trunkLayoutTestsinspectorprotocolruntimegetPropertiesexpectedtxt">trunk/LayoutTests/inspector-protocol/runtime/getProperties-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorprotocolruntimegetPropertieshtml">trunk/LayoutTests/inspector-protocol/runtime/getProperties.html</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsinspectorprotocolasynctestsuiteexpectedtxt">trunk/LayoutTests/inspector-protocol/async-test-suite-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorprotocolasynctestsuitehtml">trunk/LayoutTests/inspector-protocol/async-test-suite.html</a></li>
<li><a href="#trunkLayoutTestsinspectorprotocolsynctestsuiteexpectedtxt">trunk/LayoutTests/inspector-protocol/sync-test-suite-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorprotocolsynctestsuitehtml">trunk/LayoutTests/inspector-protocol/sync-test-suite.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (187045 => 187046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2015-07-20 23:31:55 UTC (rev 187045)
+++ trunk/LayoutTests/ChangeLog        2015-07-20 23:35:51 UTC (rev 187046)
</span><span class="lines">@@ -1,3 +1,51 @@
</span><ins>+2015-07-20  Brian J. Burg  &lt;burg@cs.washington.edu&gt;
+
+        Web Inspector: start using Promises to handle asynchronous steps in protocol tests
+        https://bugs.webkit.org/show_bug.cgi?id=147096
+
+        Reviewed by Joseph Pecoraro.
+
+        Existing protocol tests use lots of reinvented wheels to run
+        multiple asynchronous tests. This patch begins consolidation of
+        these redundant wheels, using a tiny test framework based on
+        promises.
+
+        The first change is to introduce InspectorTest.awaitCommand,
+        which returns a promise that is fulfilled or rejected if the command
+        succeeds or fails. This is the building block for writing protocol
+        tests with promises.
+
+        The second change is the introduction of InspectorTest.AsyncTestSuite.
+        This properly chains together multiple asynchronous test methods
+        using the promise interface. A synchronous version is also added.
+
+        To demonstrate how async tests can be written using this new approach,
+        this patch converts existing protocol test. Test coverage for the
+        new frameworks also demonstrates their use.
+
+        * http/tests/inspector-protocol/resources/InspectorTest.js:
+        (InspectorTest.sendCommand):
+        (InspectorTest.awaitCommand):
+        (InspectorTest.sendMessage):
+        (InspectorFrontendAPI.dispatchMessageAsync):
+        (InspectorTest.AsyncTestSuite): Add tests for this micro framework.
+        (InspectorTest.AsyncTestSuite.prototype.get passCount):
+        (InspectorTest.AsyncTestSuite.prototype.get skipCount):
+        (InspectorTest.AsyncTestSuite.prototype.addTestCase):
+        (InspectorTest.AsyncTestSuite.prototype.runTestCasesAndFinish.finish):
+        (InspectorTest.AsyncTestSuite.prototype.runTestCasesAndFinish):
+        (InspectorTest.AsyncTestSuite.prototype.runTestCases):
+        (InspectorTest.SyncTestSuite): Add tests for this micro framework.
+        (InspectorTest.SyncTestSuite.prototype.get passCount):
+        (InspectorTest.SyncTestSuite.prototype.get skipCount):
+        (InspectorTest.SyncTestSuite.prototype.addTestCase):
+        (InspectorTest.SyncTestSuite.prototype.runTestCasesAndFinish):
+        (InspectorTest.SyncTestSuite.prototype.runTestCases):
+        (InspectorTest.log):
+        (InspectorTest.assert):
+        * inspector-protocol/runtime/getProperties-expected.txt:
+        * inspector-protocol/runtime/getProperties.html: Rewrite.
+
</ins><span class="cx"> 2015-07-20  Andreas Kling  &lt;akling@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Improve behavior of media elements in page cache.
</span></span></pre></div>
<a id="trunkLayoutTestshttptestsinspectorprotocolresourcesInspectorTestjs"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/inspector-protocol/resources/InspectorTest.js (187045 => 187046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/inspector-protocol/resources/InspectorTest.js        2015-07-20 23:31:55 UTC (rev 187045)
+++ trunk/LayoutTests/http/tests/inspector-protocol/resources/InspectorTest.js        2015-07-20 23:35:51 UTC (rev 187046)
</span><span class="lines">@@ -1,6 +1,6 @@
</span><span class="cx"> /*
</span><span class="cx">  * Copyright (C) 2012 Samsung Electronics. All rights reserved.
</span><del>- * Copyright (C) 2014 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="cx">  * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -26,51 +26,76 @@
</span><span class="cx"> InspectorFrontendAPI = {};
</span><span class="cx"> 
</span><span class="cx"> InspectorTest = {};
</span><del>-InspectorTest.dumpInspectorProtocolMessages = false;
</del><span class="cx"> InspectorTest._dispatchTable = [];
</span><span class="cx"> InspectorTest._requestId = -1;
</span><span class="cx"> InspectorTest.eventHandler = {};
</span><span class="cx"> 
</span><del>-/**
- * @param {string} method
- * @param {object} params
- * @param {function({object} messageObject)=} handler
- */
</del><ins>+InspectorTest.dumpInspectorProtocolMessages = false;
+InspectorTest.forceSyncDebugLogging = false;
+
</ins><span class="cx"> InspectorTest.sendCommand = function(method, params, handler)
</span><span class="cx"> {
</span><span class="cx">     this._dispatchTable[++this._requestId] = handler;
</span><ins>+    var messageObject = {method, params, &quot;id&quot;: this._requestId};
+    this.sendMessage(messageObject);
</ins><span class="cx"> 
</span><del>-    var messageObject = { &quot;method&quot;: method,
-                          &quot;params&quot;: params,
-                          &quot;id&quot;: this._requestId };
</del><ins>+    return this._requestId;
+}
</ins><span class="cx"> 
</span><ins>+InspectorTest.awaitCommand = function(args)
+{
+    var {method, params} = args;
+    return new Promise(function(resolve, reject) {
+        this._dispatchTable[++this._requestId] = {resolve, reject};
+        var messageObject = {method, params, &quot;id&quot;: this._requestId};
+        this.sendMessage(messageObject);
+    }.bind(this));
+}
+
+InspectorTest.sendMessage = function(messageObject)
+{
</ins><span class="cx">     // This matches the debug dumping in InspectorBackend, which is bypassed
</span><span class="cx">     // by InspectorTest. Return messages should be dumped by InspectorBackend.
</span><span class="cx">     if (this.dumpInspectorProtocolMessages)
</span><span class="cx">         console.log(&quot;frontend: &quot; + JSON.stringify(messageObject));
</span><span class="cx"> 
</span><span class="cx">     InspectorFrontendHost.sendMessageToBackend(JSON.stringify(messageObject));
</span><del>-
-    return this._requestId;
</del><span class="cx"> }
</span><span class="cx"> 
</span><del>-/**
- * @param {object} messageObject
- */
</del><span class="cx"> InspectorFrontendAPI.dispatchMessageAsync = function(messageObject)
</span><span class="cx"> {
</span><ins>+    // If the message has an id, then it is a reply to a command.
</ins><span class="cx">     var messageId = messageObject[&quot;id&quot;];
</span><span class="cx">     if (typeof messageId === &quot;number&quot;) {
</span><span class="cx">         var handler = InspectorTest._dispatchTable[messageId];
</span><del>-        if (handler &amp;&amp; typeof handler === &quot;function&quot;)
</del><ins>+        if (!handler)
+            return;
+
+        if (typeof handler === &quot;function&quot;)
</ins><span class="cx">             handler(messageObject);
</span><ins>+        else if (typeof handler === &quot;object&quot;) {
+            var {resolve, reject} = handler;
+            if (&quot;error&quot; in messageObject)
+                reject(messageObject.error.message);
+            else
+                resolve(messageObject.result);
+        }
+    // Otherwise, it is an event.
</ins><span class="cx">     } else {
</span><span class="cx">         var eventName = messageObject[&quot;method&quot;];
</span><del>-        var eventHandler = InspectorTest.eventHandler[eventName];
-        if (eventHandler)
-            eventHandler(messageObject);
-        else if (InspectorTest.defaultEventHandler)
-            InspectorTest.defaultEventHandler(messageObject);
</del><ins>+        var handler = InspectorTest.eventHandler[eventName];
+        if (!handler)
+            return;
+
+        if (typeof handler == &quot;function&quot;)
+            handler(messageObject);
+        else if (typeof handler === &quot;object&quot;) {
+            var {resolve, reject} = handler;
+            if (&quot;error&quot; in messageObject)
+                reject(messageObject.error.message);
+            else
+                resolve(messageObject.result);
+        }
</ins><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -100,31 +125,200 @@
</span><span class="cx">     };
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-/**
-* Logs message to document.
-* @param {string} message
-*/
</del><ins>+InspectorTest.AsyncTestSuite = class AsyncTestSuite {
+    constructor(name) {
+        if (!name || typeof name !== &quot;string&quot;)
+            throw new Error(&quot;Tried to create AsyncTestSuite without string suite name.&quot;);
+
+        this.name = name;
+
+        this.testcases = [];
+        this.runCount = 0;
+        this.failCount = 0;
+    }
+
+    get passCount()
+    {
+        return this.runCount - this.failCount;
+    }
+
+    get skipCount()
+    {
+        if (this.failCount)
+            return this.testcases.length - this.runCount;
+        else
+            return 0;
+    }
+
+    addTestCase(testcase)
+    {
+        if (!testcase || !(testcase instanceof Object))
+            throw new Error(&quot;Tried to add non-object test case.&quot;);
+
+        if (typeof testcase.name !== &quot;string&quot;)
+            throw new Error(&quot;Tried to add test case without a name.&quot;);
+
+        if (typeof testcase.test !== &quot;function&quot;)
+            throw new Error(&quot;Tried to add test case without `test` function.&quot;);
+
+        this.testcases.push(testcase);
+    }
+
+    // Use this if the test file only has one suite, and no handling
+    // of the promise returned by runTestCases() is needed.
+    runTestCasesAndFinish()
+    {
+        function finish() {
+            InspectorTest.completeTest();
+        }
+
+        this.runTestCases()
+            .then(finish)
+            .catch(finish);
+    }
+
+    runTestCases()
+    {
+        if (!this.testcases.length)
+            throw new Error(&quot;Tried to call runTestCases() for suite with no test cases&quot;);
+        if (this._startedRunning)
+            throw new Error(&quot;Tried to call runTestCases() more than once.&quot;);
+
+        this._startedRunning = true;
+
+        InspectorTest.log(&quot;Running test suite: &quot; + this.name);
+
+        var suite = this;
+        var result = this.testcases.reduce(function(chain, testcase) {
+            return chain.then(function() {
+                InspectorTest.log(&quot;Running test case: &quot; + testcase.name);
+                suite.runCount++;
+                return new Promise(testcase.test);
+            });
+        }, Promise.resolve());
+
+        return result.catch(function(e) {
+            suite.failCount++;
+            var message = e;
+            if (e instanceof Error)
+                message = e.message;
+
+            if (typeof message !== &quot;string&quot;)
+                message = JSON.stringify(message);
+
+            InspectorTest.log(&quot;EXCEPTION: &quot; + message);
+            throw e; // Reject this promise by re-throwing the error.
+        });
+    }
+}
+
+InspectorTest.SyncTestSuite = class SyncTestSuite {
+    constructor(name) {
+        if (!name || typeof name !== &quot;string&quot;)
+            throw new Error(&quot;Tried to create SyncTestSuite without string suite name.&quot;);
+
+        this.name = name;
+
+        this.testcases = [];
+        this.runCount = 0;
+        this.failCount = 0;
+    }
+
+    get passCount()
+    {
+        return this.runCount - this.failCount;
+    }
+
+    get skipCount()
+    {
+        if (this.failCount)
+            return this.testcases.length - this.runCount;
+        else
+            return 0;
+    }
+
+    addTestCase(testcase)
+    {
+        if (!testcase || !(testcase instanceof Object))
+            throw new Error(&quot;Tried to add non-object test case.&quot;);
+
+        if (typeof testcase.name !== &quot;string&quot;)
+            throw new Error(&quot;Tried to add test case without a name.&quot;);
+
+        if (typeof testcase.test !== &quot;function&quot;)
+            throw new Error(&quot;Tried to add test case without `test` function.&quot;);
+
+        this.testcases.push(testcase);
+    }
+
+    // Use this if the test file only has one suite.
+    runTestCasesAndFinish()
+    {
+        this.runTestCases();
+        InspectorTest.completeTest();
+    }
+
+    runTestCases()
+    {
+        if (!this.testcases.length)
+            throw new Error(&quot;Tried to call runTestCases() for suite with no test cases&quot;);
+        if (this._startedRunning)
+            throw new Error(&quot;Tried to call runTestCases() more than once.&quot;);
+
+        this._startedRunning = true;
+
+        InspectorTest.log(&quot;Running test suite: &quot; + this.name);
+
+        var suite = this;
+        for (var testcase of this.testcases) {
+            InspectorTest.log(&quot;Running test case: &quot; + testcase.name);
+            suite.runCount++;
+            try {
+                var result = testcase.test.call(null);
+                if (result === false) {
+                    suite.failCount++;
+                    return false;
+                }
+            } catch (e) {
+                suite.failCount++;
+                var message = e;
+                if (e instanceof Error)
+                    message = e.message;
+                else
+                    e = new Error(e);
+
+                if (typeof message !== &quot;string&quot;)
+                    message = JSON.stringify(message);
+
+                InspectorTest.log(&quot;EXCEPTION: &quot; + message);
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
+
+// Logs a message to test document.
</ins><span class="cx"> InspectorTest.log = function(message)
</span><span class="cx"> {
</span><del>-    this.sendCommand(&quot;Runtime.evaluate&quot;, { &quot;expression&quot;: &quot;log(&quot; + JSON.stringify(message) + &quot;)&quot; } );
</del><ins>+    if (this.forceSyncDebugLogging)
+        this.debugLog(message);
+    else
+        this.sendCommand(&quot;Runtime.evaluate&quot;, { &quot;expression&quot;: &quot;log(&quot; + JSON.stringify(message) + &quot;)&quot; } );
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-/**
-* Logs an assert message to document.
-* @param {boolean} condition
-* @param {string} message
-*/
</del><ins>+// Logs an assertion result to the test document.
</ins><span class="cx"> InspectorTest.assert = function(condition, message)
</span><span class="cx"> {
</span><span class="cx">     var status = condition ? &quot;PASS&quot; : &quot;FAIL&quot;;
</span><del>-    this.sendCommand(&quot;Runtime.evaluate&quot;, { &quot;expression&quot;: &quot;log(&quot; + JSON.stringify(status + &quot;: &quot; + message) + &quot;)&quot; } );
</del><ins>+    var message = typeof message !== &quot;string&quot; ? JSON.stringify(message) : message;
+    var formattedMessage = status + &quot;: &quot; + message;
+    this.log(formattedMessage);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-/**
-* Logs message directly to process stdout via alert function (hopefully followed by flush call).
-* This message should survive process crash or kill by timeout.
-* @param {string} message
-*/
</del><ins>+// Logs message a directly to stdout of the test process via alert function.
+// This message should survive process crash or kill by timeout.
</ins><span class="cx"> InspectorTest.debugLog = function(message)
</span><span class="cx"> {
</span><span class="cx">     this.sendCommand(&quot;Runtime.evaluate&quot;, { &quot;expression&quot;: &quot;debugLog(&quot; + JSON.stringify(message) + &quot;)&quot; } );
</span><span class="lines">@@ -144,9 +338,6 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-/**
- * @param {string} scriptName
- */
</del><span class="cx"> InspectorTest.importScript = function(scriptName)
</span><span class="cx"> {
</span><span class="cx">     var xhr = new XMLHttpRequest();
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorprotocolasynctestsuiteexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector-protocol/async-test-suite-expected.txt (0 => 187046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector-protocol/async-test-suite-expected.txt                                (rev 0)
+++ trunk/LayoutTests/inspector-protocol/async-test-suite-expected.txt        2015-07-20 23:35:51 UTC (rev 187046)
</span><span class="lines">@@ -0,0 +1,34 @@
</span><ins>+PASS: instantiating AsyncTestSuite requires name argument.
+PASS: instantiating AsyncTestSuite requires string name 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 test function.
+PASS: should not be able to run empty test suite.
+Running test suite: AsyncTestSuite.RunTwiceSuite
+PASS: should not be able to run a test suite twice.
+Running test case: DummyTest0
+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]
+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.
+Running test suite: AsyncTestSuite.AbortOnFailure
+Running test case: PassingTest5
+Running test case: FailingTest6
+EXCEPTION: {&quot;token&quot;:666}
+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.
+
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorprotocolasynctestsuitehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector-protocol/async-test-suite.html (0 => 187046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector-protocol/async-test-suite.html                                (rev 0)
+++ trunk/LayoutTests/inspector-protocol/async-test-suite.html        2015-07-20 23:35:51 UTC (rev 187046)
</span><span class="lines">@@ -0,0 +1,178 @@
</span><ins>+&lt;html&gt;
+&lt;head&gt;
+&lt;script type=&quot;text/javascript&quot; src=&quot;../http/tests/inspector-protocol/resources/protocol-test.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+function test()
+{
+    try {
+        new InspectorTest.AsyncTestSuite();
+        InspectorTest.log(&quot;FAIL: instantiating AsyncTestSuite requires name argument.&quot;);
+    } catch (e) {
+        InspectorTest.log(&quot;PASS: instantiating AsyncTestSuite requires name argument.&quot;);
+    }
+
+    try {
+        new InspectorTest.AsyncTestSuite({});
+        InspectorTest.log(&quot;FAIL: instantiating AsyncTestSuite requires string name argument.&quot;);
+    } catch (e) {
+        InspectorTest.log(&quot;PASS: instantiating AsyncTestSuite requires string name argument.&quot;);
+    }
+
+    var badArgsSuite = new InspectorTest.AsyncTestSuite(&quot;dummy&quot;);
+    try {
+        badArgsSuite.addTestCase();
+        InspectorTest.log(&quot;FAIL: should not be able to add empty test case.&quot;);
+    } catch (e) {
+        InspectorTest.log(&quot;PASS: should not be able to add empty test case.&quot;);
+    }
+    try {
+        badArgsSuite.addTestCase(&quot;string&quot;);
+        InspectorTest.log(&quot;FAIL: should not be able to add non-object test case.&quot;);
+    } catch (e) {
+        InspectorTest.log(&quot;PASS: should not be able to add non-object test case.&quot;);
+    }
+    try {
+        badArgsSuite.addTestCase({
+            name: {},
+            test: function() {},
+        });
+        InspectorTest.log(&quot;FAIL: test case should require string name.&quot;);
+    } catch (e) {
+        InspectorTest.log(&quot;PASS: test case should require string name.&quot;);
+    }
+    try {
+        badArgsSuite.addTestCase({
+            name: &quot;foo&quot;,
+            test: null,
+        });
+        InspectorTest.log(&quot;FAIL: test case should require test function.&quot;);
+    } catch (e) {
+        InspectorTest.log(&quot;PASS: test case should require test function.&quot;);
+    }
+
+    var runEmptySuite = new InspectorTest.AsyncTestSuite(&quot;AsyncTestSuite.RunEmptySuite&quot;);
+    try {
+        runEmptySuite.runTestCases();
+        InspectorTest.log(&quot;FAIL: should not be able to run empty test suite.&quot;);
+    } catch (e) {
+        InspectorTest.log(&quot;PASS: should not be able to run empty test suite.&quot;);
+    }
+
+    var runTwiceSuite = new InspectorTest.AsyncTestSuite(&quot;AsyncTestSuite.RunTwiceSuite&quot;);
+    runTwiceSuite.addTestCase({
+        name: &quot;DummyTest0&quot;,
+        description: &quot;Check that a suite can't run more than once.&quot;,
+        test: function(resolve, reject) {
+            resolve();
+        }
+    });
+
+    var result = runTwiceSuite.runTestCases();
+    try {
+        // Test cases won't run in this event loop; this call should still throw.
+        // Later tests are chained to this suite to avoid nondeterminism.
+        runTwiceSuite.runTestCases();
+        InspectorTest.log(&quot;FAIL: should not be able to run a test suite twice.&quot;);
+    } catch (e) {
+        InspectorTest.log(&quot;PASS: should not be able to run a test suite twice.&quot;);
+    }
+
+    var rejectToken = {&quot;token&quot;: 666};
+    var thrownError = new Error(rejectToken);
+
+    var sequentialExecutionSuite = new InspectorTest.AsyncTestSuite(&quot;AsyncTestSuite.SequentialExecution&quot;);
+    sequentialExecutionSuite.addTestCase({
+        name: &quot;DummyTest1&quot;,
+        description: &quot;Check test case execution order.&quot;,
+        test: function(resolve, reject) {
+            resolve();
+        }
+    });
+    sequentialExecutionSuite.addTestCase({
+        name: &quot;DummyTest2&quot;,
+        description: &quot;Check test case execution order.&quot;,
+        test: function(resolve, reject) {
+            resolve();
+        }
+    });
+    sequentialExecutionSuite.addTestCase({
+        name: &quot;DummyTest3&quot;,
+        description: &quot;Check test case execution order.&quot;,
+        test: function(resolve, reject) {
+            resolve();
+        }
+    });
+    sequentialExecutionSuite.addTestCase({
+        name: &quot;FailingTest4&quot;,
+        description: &quot;Check that test fails by throwing an Error instance.&quot;,
+        test: function(resolve, reject) {
+            throw thrownError;
+        }
+    });
+
+    var abortOnFailureSuite = new InspectorTest.AsyncTestSuite(&quot;AsyncTestSuite.AbortOnFailure&quot;);
+    abortOnFailureSuite.addTestCase({
+        name: &quot;PassingTest5&quot;,
+        description: &quot;This test is a dummy.&quot;,
+        test: function(resolve, reject) {
+            resolve();
+        }
+    });
+    abortOnFailureSuite.addTestCase({
+        name: &quot;FailingTest6&quot;,
+        description: &quot;This test should fail by explicitly calling the `reject` callback.&quot;,
+        test: function(resolve, reject) {
+            reject(rejectToken);
+        }
+    });
+    abortOnFailureSuite.addTestCase({
+        name: &quot;PassingTest7&quot;,
+        description: &quot;This test should not executed when the preceding test fails.&quot;,
+        test: function(resolve, reject) {
+            resolve();
+        }
+    });
+
+    result = result.then(function() {
+        var promise = sequentialExecutionSuite.runTestCases();
+        InspectorTest.assert(result instanceof Promise, &quot;AsyncTestSuite.RunTestCases() should return a Promise.&quot;);
+        return promise;
+    });
+    result = result.then(function resolved() {
+        InspectorTest.log(&quot;FAIL: Promise from sequentialExecutionSuite.runTestCases() should reject when a test case fails.&quot;);
+        return Promise.resolve(); // Continue this test.
+    }, function rejected(e) {
+        InspectorTest.log(&quot;PASS: Promise from sequentialExecutionSuite.runTestCases() should reject when a test case fails.&quot;);
+        InspectorTest.assert(e === thrownError, &quot;Promise from sequentialExecutionSuite.runTestCases() should reject without altering its result value.&quot;);
+
+        InspectorTest.assert(sequentialExecutionSuite.runCount === 4, &quot;sequentialExecutionSuite should have executed four tests.&quot;);
+        InspectorTest.assert(sequentialExecutionSuite.passCount === 3, &quot;sequentialExecutionSuite should have passed three tests.&quot;);
+        InspectorTest.assert(sequentialExecutionSuite.failCount === 1, &quot;sequentialExecutionSuite should have failed 1 test.&quot;);
+        InspectorTest.assert(sequentialExecutionSuite.skipCount === 0, &quot;sequentialExecutionSuite should have skipped zero tests.&quot;);
+        return Promise.resolve(); // Continue this test.
+    });
+
+    result = result.then(function() {
+        return abortOnFailureSuite.runTestCases();
+    }).then(function resolved() {
+        InspectorTest.log(&quot;FAIL: Promise from abortOnFailureSuite.runTestCases() should reject when a test case fails.&quot;);
+        return Promise.resolve(); // Continue this test.
+    }, function rejected(e) {
+        InspectorTest.log(&quot;PASS: Promise from abortOnFailureSuite.runTestCases() should reject when a test case fails.&quot;);
+        InspectorTest.assert(e === rejectToken, &quot;Promise from abortOnFailureSuite.runTestCases() should reject without altering its result value.&quot;);
+        InspectorTest.assert(abortOnFailureSuite.runCount === 2, &quot;abortOnFailureSuite should have executed two tests.&quot;);
+        InspectorTest.assert(abortOnFailureSuite.passCount === 1, &quot;abortOnFailureSuite should have passed one test.&quot;);
+        InspectorTest.assert(abortOnFailureSuite.failCount === 1, &quot;abortOnFailureSuite should have failed one test.&quot;);
+        InspectorTest.assert(abortOnFailureSuite.skipCount === 1, &quot;abortOnFailureSuite should have skipped one test.&quot;);
+
+        return Promise.resolve(); // Continue this test.
+    });
+
+    // This will finish the test whether the chain was resolved or rejected.
+    result = result.then(function() { InspectorTest.completeTest(); });
+}
+&lt;/script&gt;
+&lt;/head&gt;
+&lt;body onLoad=&quot;runTest()&quot;&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorprotocolruntimegetPropertiesexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector-protocol/runtime/getProperties-expected.txt (187045 => 187046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector-protocol/runtime/getProperties-expected.txt        2015-07-20 23:31:55 UTC (rev 187045)
+++ trunk/LayoutTests/inspector-protocol/runtime/getProperties-expected.txt        2015-07-20 23:35:51 UTC (rev 187046)
</span><span class="lines">@@ -1,13 +1,20 @@
</span><del>-Properties of Object(5)
</del><ins>+Running test suite: Runtime.getProperties
+Running test case: CheckPropertiesOfWrapperObject
+Evaluating expression: (function(){var r = Object(5); r.foo = 'cat';return r;})()
+Properties:
</ins><span class="cx">   __proto__ object Number
</span><span class="cx">   foo string cat
</span><del>-Properties of array
</del><ins>+Running test case: CheckPropertiesOfArray
+Evaluating expression: ['red', 'green', 'blue']
+Properties:
</ins><span class="cx">   __proto__ object Array
</span><span class="cx">   0 string red
</span><span class="cx">   1 string green
</span><span class="cx">   2 string blue
</span><span class="cx">   length number 3
</span><del>-Properties of Bound function
</del><ins>+Running test case: CheckPropertiesOfBoundConstructor
+Evaluating expression: Number.bind({}, 5)
+Properties:
</ins><span class="cx">   __proto__ function function () {
</span><span class="cx">     [native code]
</span><span class="cx"> }
</span><span class="lines">@@ -15,7 +22,7 @@
</span><span class="cx">   caller
</span><span class="cx">   length number 0
</span><span class="cx">   name string Number
</span><del>-Internal properties
</del><ins>+Internal properties:
</ins><span class="cx">   boundArgs object Array
</span><span class="cx">   boundThis object Object
</span><span class="cx">   targetFunction function function Number() {
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorprotocolruntimegetPropertieshtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector-protocol/runtime/getProperties.html (187045 => 187046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector-protocol/runtime/getProperties.html        2015-07-20 23:31:55 UTC (rev 187045)
+++ trunk/LayoutTests/inspector-protocol/runtime/getProperties.html        2015-07-20 23:35:51 UTC (rev 187046)
</span><span class="lines">@@ -4,143 +4,82 @@
</span><span class="cx"> &lt;script&gt;
</span><span class="cx"> function test()
</span><span class="cx"> {
</span><del>-    // A general-purpose engine for sending a sequence of protocol commands.
-    // The clients provide requests and response handlers, while the engine catches
-    // errors and makes sure that once there's nothing to do completeTest() is called.
-    // @param step is an object with command, params and callback fields
-    function runRequestSeries(step) {
-        processStep(step);
</del><ins>+    var suite = new InspectorTest.AsyncTestSuite(&quot;Runtime.getProperties&quot;);
</ins><span class="cx"> 
</span><del>-        function processStep(s) {
-            try {
-                processStepOrFail(s);
-            } catch (e) {
-                InspectorTest.log(e.stack);
-                InspectorTest.completeTest();
-            }
-        }
</del><ins>+    addGetPropertiesTestCase({
+        name: &quot;CheckPropertiesOfWrapperObject&quot;,
+        description: &quot;Check properties of `Object(5)`.&quot;,
+        expression: &quot;(function(){var r = Object(5); r.foo = 'cat';return r;})()&quot;,
+    });
</ins><span class="cx"> 
</span><del>-        function processStepOrFail(s) {
-            if (!s) {
-                InspectorTest.completeTest();
-                return;
-            }
-            if (!s.command) {
-                // A simple loopback step.
-                var next = s.callback();
-                processStep(next);
-                return;
-            }
</del><ins>+    addGetPropertiesTestCase({
+        name: &quot;CheckPropertiesOfArray&quot;,
+        description: &quot;Check properties of `['red', 'green', 'blue']`.&quot;,
+        expression: &quot;['red', 'green', 'blue']&quot;,
+    });
</ins><span class="cx"> 
</span><del>-            var innerCallback = function(response) {
-                if (&quot;error&quot; in response) {
-                    InspectorTest.log(response.error.message);
-                    InspectorTest.completeTest();
-                    return;
-                }
-                var next;
-                try {
-                    next = s.callback(response.result);
-                } catch (e) {
-                    InspectorTest.log(e.stack);
-                    InspectorTest.completeTest();
-                    return;
-                }
-                processStep(next);
-            }
-            InspectorTest.sendCommand(s.command, s.params, innerCallback);
-        }
-    }
</del><ins>+    addGetPropertiesTestCase({
+        name: &quot;CheckPropertiesOfBoundConstructor&quot;,
+        description: &quot;Check properties of a bound function (has a bunch of internal properties).&quot;,
+        expression: &quot;Number.bind({}, 5)&quot;,
+    });
</ins><span class="cx"> 
</span><del>-    var firstStep = { callback: callbackStart5 };
</del><ins>+    suite.runTestCasesAndFinish();
</ins><span class="cx"> 
</span><del>-    runRequestSeries(firstStep);
</del><ins>+    function addGetPropertiesTestCase(args) {
+        var {name, description, expression} = args;
</ins><span class="cx"> 
</span><del>-    // 'Object5' section -- check properties of '5' wrapped as object  (has an internal property).
</del><ins>+        suite.addTestCase({
+            name,
+            description,
+            test: function(resolve, reject) {
+                InspectorTest.log(&quot;Evaluating expression: &quot; + expression);
+                InspectorTest.awaitCommand({
+                    method: &quot;Runtime.evaluate&quot;,
+                    params: {expression}
+                })
+                .then(function(reply) {
+                    var objectId = reply.result.objectId;
+                    if (objectId === undefined)
+                        throw new Error(&quot;objectId is expected&quot;);
</ins><span class="cx"> 
</span><del>-    function callbackStart5() {
-        // Create an wrapper object with additional property.
-        var expression = &quot;(function(){var r = Object(5); r.foo = 'cat';return r;})()&quot;;
-
-        return { command: &quot;Runtime.evaluate&quot;, params: {expression: expression}, callback: callbackEval5 };
</del><ins>+                    return InspectorTest.awaitCommand({
+                        method: &quot;Runtime.getProperties&quot;,
+                        params: {objectId, ownProperties: true}
+                    });
+                })
+                .then(function(reply) {
+                    dumpGetPropertiesResult(reply);
+                    resolve();
+                })
+                .catch(reject);
+            }
+        });
</ins><span class="cx">     }
</span><del>-    function callbackEval5(result) {
-        var id = result.result.objectId;
-        if (id === undefined)
-            throw new Error(&quot;objectId is expected&quot;);
-        return {
-            command: &quot;Runtime.getProperties&quot;, params: {objectId: id, ownProperties: true}, callback: callbackProperties5
-        };
-    }
-    function callbackProperties5(result) {
-        logGetPropertiesResult(&quot;Object(5)&quot;, result);
-        return { callback: callbackStartArray };
-    }
</del><span class="cx"> 
</span><ins>+    // A helper function that dumps object properties and internal properties in sorted order.
+    function dumpGetPropertiesResult(protocolResult) {
+        var properties = protocolResult.result;
+        if (properties) {
+            InspectorTest.log(&quot;Properties:&quot;);
+            properties.sort(NamedThingComparator);
+            properties.map(dumpSingleProperty);
+        }
</ins><span class="cx"> 
</span><del>-    // 'Array' section -- check properties of an array.
</del><ins>+        var internalProperties = protocolResult.internalProperties;
+        if (internalProperties) {
+            InspectorTest.log(&quot;Internal properties:&quot;);
+            internalProperties.sort(NamedThingComparator);
+            internalProperties.map(dumpSingleProperty);
+        }
</ins><span class="cx"> 
</span><del>-    function callbackStartArray() {
-        var expression = &quot;['red', 'green', 'blue']&quot;;
-        return { command: &quot;Runtime.evaluate&quot;, params: {expression: expression}, callback: callbackEvalArray };
-    }
-    function callbackEvalArray(result) {
-        var id = result.result.objectId;
-        if (id === undefined)
-            throw new Error(&quot;objectId is expected&quot;);
-        return {
-            command: &quot;Runtime.getProperties&quot;, params: {objectId: id, ownProperties: true}, callback: callbackPropertiesArray
-        };
-    }
-    function callbackPropertiesArray(result) {
-        logGetPropertiesResult(&quot;array&quot;, result);
-        return { callback: callbackStartBound };
-    }
-
-
-    // 'Bound' section -- check properties of a bound function (has a bunch of internal properties).
-
-    function callbackStartBound() {
-        var expression = &quot;Number.bind({}, 5)&quot;;
-        return { command: &quot;Runtime.evaluate&quot;, params: {expression: expression}, callback: callbackEvalBound };
-    }
-    function callbackEvalBound(result) {
-        var id = result.result.objectId;
-        if (id === undefined)
-            throw new Error(&quot;objectId is expected&quot;);
-        return {
-            command: &quot;Runtime.getProperties&quot;, params: {objectId: id, ownProperties: true}, callback: callbackPropertiesBound
-        };
-    }
-    function callbackPropertiesBound(result) {
-        logGetPropertiesResult(&quot;Bound function&quot;, result);
-        return; // End of test
-    }
-
-
-    // A helper function that dumps object properties and internal properties in sorted order.
-    function logGetPropertiesResult(title, protocolResult) {
-        InspectorTest.log(&quot;Properties of &quot; + title);
-        var propertyArray = protocolResult.result;
-        propertyArray.sort(NamedThingComparator);
-        for (var i = 0; i &lt; propertyArray.length; i++) {
-            var p = propertyArray[i];
-            var v = p.value;
-            if (v)
-                InspectorTest.log(&quot;  &quot; + p.name + &quot; &quot; + v.type + &quot; &quot; + (v.value || v.description));
</del><ins>+        function dumpSingleProperty(property) {
+            var {name, value} = property;
+            if (value)
+                InspectorTest.log(&quot;  &quot; + name + &quot; &quot; + value.type + &quot; &quot; + (value.value || value.description));
</ins><span class="cx">             else
</span><del>-                InspectorTest.log(&quot;  &quot; + p.name);
</del><ins>+                InspectorTest.log(&quot;  &quot; + name);
</ins><span class="cx">         }
</span><del>-        var internalPropertyArray = protocolResult.internalProperties;
-        if (internalPropertyArray) {
-            InspectorTest.log(&quot;Internal properties&quot;);
-            internalPropertyArray.sort(NamedThingComparator);
-            for (var i = 0; i &lt; internalPropertyArray.length; i++) {
-                var p = internalPropertyArray[i];
-                var v = p.value;
-                InspectorTest.log(&quot;  &quot; + p.name + &quot; &quot; + v.type + &quot; &quot; + (v.value || v.description));
-            }
-        }
</del><span class="cx"> 
</span><span class="cx">         function NamedThingComparator(o1, o2) {
</span><span class="cx">             return o1.name.localeCompare(o2.name);
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorprotocolsynctestsuiteexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector-protocol/sync-test-suite-expected.txt (0 => 187046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector-protocol/sync-test-suite-expected.txt                                (rev 0)
+++ trunk/LayoutTests/inspector-protocol/sync-test-suite-expected.txt        2015-07-20 23:35:51 UTC (rev 187046)
</span><span class="lines">@@ -0,0 +1,31 @@
</span><ins>+PASS: instantiating SyncTestSuite requires name argument.
+PASS: instantiating SyncTestSuite requires string name 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 test function.
+PASS: should not be able to run empty test suite.
+Running test suite: SyncTestSuite.RunTwiceSuite
+Running test case: DummyTest0
+PASS: Return value of runTwiceSuite.runTestCases() should be true when all tests pass.
+PASS: should not be able to run a test suite twice.
+Running test suite: SyncTestSuite.SequentialExecution
+Running test case: DummyTest1
+Running test case: DummyTest2
+Running test case: DummyTest3
+Running test case: FailingTest4
+EXCEPTION: [object Object]
+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.
+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.
+
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorprotocolsynctestsuitehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector-protocol/sync-test-suite.html (0 => 187046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector-protocol/sync-test-suite.html                                (rev 0)
+++ trunk/LayoutTests/inspector-protocol/sync-test-suite.html        2015-07-20 23:35:51 UTC (rev 187046)
</span><span class="lines">@@ -0,0 +1,139 @@
</span><ins>+&lt;html&gt;
+&lt;head&gt;
+&lt;script type=&quot;text/javascript&quot; src=&quot;../http/tests/inspector-protocol/resources/protocol-test.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+function test()
+{
+    try {
+        new InspectorTest.SyncTestSuite();
+        InspectorTest.log(&quot;FAIL: instantiating SyncTestSuite requires name argument.&quot;);
+    } catch (e) {
+        InspectorTest.log(&quot;PASS: instantiating SyncTestSuite requires name argument.&quot;);
+    }
+
+    try {
+        new InspectorTest.SyncTestSuite({});
+        InspectorTest.log(&quot;FAIL: instantiating SyncTestSuite requires string name argument.&quot;);
+    } catch (e) {
+        InspectorTest.log(&quot;PASS: instantiating SyncTestSuite requires string name argument.&quot;);
+    }
+
+    var badArgsSuite = new InspectorTest.SyncTestSuite(&quot;dummy&quot;);
+    try {
+        badArgsSuite.addTestCase();
+        InspectorTest.log(&quot;FAIL: should not be able to add empty test case.&quot;);
+    } catch (e) {
+        InspectorTest.log(&quot;PASS: should not be able to add empty test case.&quot;);
+    }
+    try {
+        badArgsSuite.addTestCase(&quot;string&quot;);
+        InspectorTest.log(&quot;FAIL: should not be able to add non-object test case.&quot;);
+    } catch (e) {
+        InspectorTest.log(&quot;PASS: should not be able to add non-object test case.&quot;);
+    }
+    try {
+        badArgsSuite.addTestCase({
+            name: {},
+            test: function() { return true; },
+        });
+        InspectorTest.log(&quot;FAIL: test case should require string name.&quot;);
+    } catch (e) {
+        InspectorTest.log(&quot;PASS: test case should require string name.&quot;);
+    }
+    try {
+        badArgsSuite.addTestCase({
+            name: &quot;foo&quot;,
+            test: null,
+        });
+        InspectorTest.log(&quot;FAIL: test case should require test function.&quot;);
+    } catch (e) {
+        InspectorTest.log(&quot;PASS: test case should require test function.&quot;);
+    }
+
+    var runEmptySuite = new InspectorTest.SyncTestSuite(&quot;SyncTestSuite.RunEmptySuite&quot;);
+    try {
+        runEmptySuite.runTestCases();
+        InspectorTest.log(&quot;FAIL: should not be able to run empty test suite.&quot;);
+    } catch (e) {
+        InspectorTest.log(&quot;PASS: should not be able to run empty test suite.&quot;);
+    }
+
+    var runTwiceSuite = new InspectorTest.SyncTestSuite(&quot;SyncTestSuite.RunTwiceSuite&quot;);
+    runTwiceSuite.addTestCase({
+        name: &quot;DummyTest0&quot;,
+        description: &quot;Check that a suite can't run more than once.&quot;,
+        test: function() { return true; }
+    });
+
+    try {
+        var result = runTwiceSuite.runTestCases();
+        InspectorTest.assert(result === true, &quot;Return value of runTwiceSuite.runTestCases() should be true when all tests pass.&quot;);
+
+        runTwiceSuite.runTestCases(); // Try to trigger an error.
+        InspectorTest.log(&quot;FAIL: should not be able to run a test suite twice.&quot;);
+    } catch (e) {
+        InspectorTest.log(&quot;PASS: should not be able to run a test suite twice.&quot;);
+    }
+
+    var thrownError = new Error({&quot;token&quot;: 666});
+
+    var sequentialExecutionSuite = new InspectorTest.SyncTestSuite(&quot;SyncTestSuite.SequentialExecution&quot;);
+    sequentialExecutionSuite.addTestCase({
+        name: &quot;DummyTest1&quot;,
+        description: &quot;Check test case execution order.&quot;,
+        test: function() { return true; }
+    });
+    sequentialExecutionSuite.addTestCase({
+        name: &quot;DummyTest2&quot;,
+        description: &quot;Check test case execution order.&quot;,
+        test: function() { return true; }
+    });
+    sequentialExecutionSuite.addTestCase({
+        name: &quot;DummyTest3&quot;,
+        description: &quot;Check test case execution order.&quot;,
+        test: function() { return true; }
+    });
+    sequentialExecutionSuite.addTestCase({
+        name: &quot;FailingTest4&quot;,
+        description: &quot;Check that test fails by throwing an Error instance.&quot;,
+        test: function() { throw thrownError; }
+    });
+
+    var result = sequentialExecutionSuite.runTestCases();
+    InspectorTest.assert(result === false, &quot;Return value of sequentialExecutionSuite.runTestCases() should be false when a test case fails.&quot;);
+    InspectorTest.assert(sequentialExecutionSuite.runCount === 4, &quot;sequentialExecutionSuite should have executed four tests.&quot;);
+    InspectorTest.assert(sequentialExecutionSuite.passCount === 3, &quot;sequentialExecutionSuite should have passed three tests.&quot;);
+    InspectorTest.assert(sequentialExecutionSuite.failCount === 1, &quot;sequentialExecutionSuite should have failed 1 test.&quot;);
+    InspectorTest.assert(sequentialExecutionSuite.skipCount === 0, &quot;sequentialExecutionSuite should have skipped zero tests.&quot;);
+
+    var abortOnFailureSuite = new InspectorTest.SyncTestSuite(&quot;SyncTestSuite.AbortOnFailure&quot;);
+    abortOnFailureSuite.addTestCase({
+        name: &quot;PassingTest5&quot;,
+        description: &quot;This test is a dummy.&quot;,
+        test: function() { return true; }
+    });
+    abortOnFailureSuite.addTestCase({
+        name: &quot;FailingTest6&quot;,
+        description: &quot;This test should fail by explicitly returning `false`.&quot;,
+        test: function() { return false; }
+    });
+    abortOnFailureSuite.addTestCase({
+        name: &quot;PassingTest7&quot;,
+        description: &quot;This test should not executed when the preceding test fails.&quot;,
+        test: function() { return true; }
+    });
+
+    abortOnFailureSuite.runTestCases();
+    InspectorTest.assert(result === false, &quot;Return value of abortOnFailureSuite.runTestCases() should be false when a test case fails.&quot;);
+    InspectorTest.assert(abortOnFailureSuite.runCount === 2, &quot;abortOnFailureSuite should have executed two tests.&quot;);
+    InspectorTest.assert(abortOnFailureSuite.passCount === 1, &quot;abortOnFailureSuite should have passed one test.&quot;);
+    InspectorTest.assert(abortOnFailureSuite.failCount === 1, &quot;abortOnFailureSuite should have failed one test.&quot;);
+    InspectorTest.assert(abortOnFailureSuite.skipCount === 1, &quot;abortOnFailureSuite should have skipped one test.&quot;);
+
+    InspectorTest.completeTest();
+}
+&lt;/script&gt;
+&lt;/head&gt;
+&lt;body onLoad=&quot;runTest()&quot;&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre>
</div>
</div>

</body>
</html>