Enhance shouldNotThrow()/shouldThrow() to accept functions and a descriptive message
authorddkilzer@apple.com <ddkilzer@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 29 Jun 2016 04:17:36 +0000 (04:17 +0000)
committerddkilzer@apple.com <ddkilzer@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 29 Jun 2016 04:17:36 +0000 (04:17 +0000)
<https://webkit.org/b/159232>

Reviewed by Brent Fulgham.

Based on a Blink change (patch by <hongchan@chromium.org>):
<https://src.chromium.org/viewvc/blink?view=revision&revision=192204>

Currently, shouldNotThrow() and shouldThrow() take the following
arguments:

    shouldNotThrow(evalString)
    shouldThrow(evalString, expectedExceptionString)

The challenges with this design are:

    1) The 'evalString' must capture every variable that it
       needs, which means the code can be long, and concatenated
       into a single line.  It would be really nice to be able
       to use an (anonymous) function to capture variables
       instead.
    2) The 'evalString' is literally printed out in the test
       results, which isn't always the most descriptive.  A
       descriptive message would make it clearer what failed.
    3) When changing a shouldThrow() into a shouldNotThrow()
       or copying/pasting code, it's possible to forget to
       remove 'expectedExceptionString' from the function call.

This patch changes the methods to take the following arguments:

    shouldNotThrow(evalString|function [, message])
    shouldThrow(evalString|function, expectedExceptionString [, message])

If 'function' is passed in, then it is invoked instead of
evaluated, and 'message' replaces the literal code in the
pass/fail output.

This patch also adds the global 'didFailSomeTests' variable to
js-test.js, which already exists in js-test-pre.js.  This was
added to js-test-pre.js in r153203 by Oliver Hunt to
LayoutTests/fast/js/resources/js-test-pre.js.

* fast/canvas/webgl/canvas-supports-context-expected.txt:
* fast/canvas/webgl/gl-bind-attrib-location-before-compile-test-expected.txt:
* fast/css-grid-layout/grid-element-auto-repeat-get-set-expected.txt:
* fast/dom/getElementsByClassName/ASCII-case-insensitive-expected.txt:
* storage/indexeddb/cursor-basics-expected.txt:
* storage/indexeddb/cursor-basics-private-expected.txt:
- Update expected results to include "Some tests fail." since
  some subtests actually do fail during these tests.

* fast/css/parsing-css-lang.html:
* fast/css/parsing-css-matches-1.html:
* fast/css/parsing-css-matches-2.html:
* fast/css/parsing-css-matches-3.html:
* fast/css/parsing-css-matches-4.html:
* fast/css/parsing-css-not-1.html:
* fast/css/parsing-css-not-2.html:
* fast/css/parsing-css-not-3.html:
* fast/css/parsing-css-not-4.html:
* fast/css/parsing-css-nth-child-of-1.html:
* fast/css/parsing-css-nth-child-of-2.html:
* fast/css/parsing-css-nth-last-child-of-1.html:
* fast/css/parsing-css-nth-last-child-of-2.html:
* js/script-tests/arrowfunction-supercall.js:
- Remove expectedExceptionString from shouldNotThrow() calls
  after they were changed from shouldThrow() calls.

* resources/js-test-pre.js:
(shouldNotThrow): Change to invoke first argument if it is a
function, else use eval() as before.  Use second argurment in
place of first argument (if set) when printing results.  NOTE:
Care was taken not to add any lines of code to prevent changes
to test results.
(shouldThrow): Ditto.  Reformat code.
* resources/js-test.js: Declare 'didFailSomeTests'.
(testFailed): Set 'didFailSomeTests' to true when a test fails.
(shouldNotThrow): Same changes as js-test-pre.js.
(shouldThrow): Ditto.
(isSuccessfullyParsed): Output a message if 'didFailSomeTests'
is true.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@202609 268f45cc-cd09-0410-ab3c-d52691b4dbfc

23 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/canvas/webgl/canvas-supports-context-expected.txt
LayoutTests/fast/canvas/webgl/gl-bind-attrib-location-before-compile-test-expected.txt
LayoutTests/fast/css-grid-layout/grid-element-auto-repeat-get-set-expected.txt
LayoutTests/fast/css/parsing-css-lang.html
LayoutTests/fast/css/parsing-css-matches-1.html
LayoutTests/fast/css/parsing-css-matches-2.html
LayoutTests/fast/css/parsing-css-matches-3.html
LayoutTests/fast/css/parsing-css-matches-4.html
LayoutTests/fast/css/parsing-css-not-1.html
LayoutTests/fast/css/parsing-css-not-2.html
LayoutTests/fast/css/parsing-css-not-3.html
LayoutTests/fast/css/parsing-css-not-4.html
LayoutTests/fast/css/parsing-css-nth-child-of-1.html
LayoutTests/fast/css/parsing-css-nth-child-of-2.html
LayoutTests/fast/css/parsing-css-nth-last-child-of-1.html
LayoutTests/fast/css/parsing-css-nth-last-child-of-2.html
LayoutTests/fast/dom/getElementsByClassName/ASCII-case-insensitive-expected.txt
LayoutTests/js/script-tests/arrowfunction-supercall.js
LayoutTests/resources/js-test-pre.js
LayoutTests/resources/js-test.js
LayoutTests/storage/indexeddb/cursor-basics-expected.txt
LayoutTests/storage/indexeddb/cursor-basics-private-expected.txt

index 2ff2963..5f7bc2e 100644 (file)
@@ -1,3 +1,87 @@
+2016-06-28  David Kilzer  <ddkilzer@apple.com>
+
+        Enhance shouldNotThrow()/shouldThrow() to accept functions and a descriptive message
+        <https://webkit.org/b/159232>
+
+        Reviewed by Brent Fulgham.
+
+        Based on a Blink change (patch by <hongchan@chromium.org>):
+        <https://src.chromium.org/viewvc/blink?view=revision&revision=192204>
+
+        Currently, shouldNotThrow() and shouldThrow() take the following
+        arguments:
+
+            shouldNotThrow(evalString)
+            shouldThrow(evalString, expectedExceptionString)
+
+        The challenges with this design are:
+
+            1) The 'evalString' must capture every variable that it
+               needs, which means the code can be long, and concatenated
+               into a single line.  It would be really nice to be able
+               to use an (anonymous) function to capture variables
+               instead.
+            2) The 'evalString' is literally printed out in the test
+               results, which isn't always the most descriptive.  A
+               descriptive message would make it clearer what failed.
+            3) When changing a shouldThrow() into a shouldNotThrow()
+               or copying/pasting code, it's possible to forget to
+               remove 'expectedExceptionString' from the function call.
+
+        This patch changes the methods to take the following arguments:
+
+            shouldNotThrow(evalString|function [, message])
+            shouldThrow(evalString|function, expectedExceptionString [, message])
+
+        If 'function' is passed in, then it is invoked instead of
+        evaluated, and 'message' replaces the literal code in the
+        pass/fail output.
+
+        This patch also adds the global 'didFailSomeTests' variable to
+        js-test.js, which already exists in js-test-pre.js.  This was
+        added to js-test-pre.js in r153203 by Oliver Hunt to
+        LayoutTests/fast/js/resources/js-test-pre.js.
+
+        * fast/canvas/webgl/canvas-supports-context-expected.txt:
+        * fast/canvas/webgl/gl-bind-attrib-location-before-compile-test-expected.txt:
+        * fast/css-grid-layout/grid-element-auto-repeat-get-set-expected.txt:
+        * fast/dom/getElementsByClassName/ASCII-case-insensitive-expected.txt:
+        * storage/indexeddb/cursor-basics-expected.txt:
+        * storage/indexeddb/cursor-basics-private-expected.txt:
+        - Update expected results to include "Some tests fail." since
+          some subtests actually do fail during these tests.
+
+        * fast/css/parsing-css-lang.html:
+        * fast/css/parsing-css-matches-1.html:
+        * fast/css/parsing-css-matches-2.html:
+        * fast/css/parsing-css-matches-3.html:
+        * fast/css/parsing-css-matches-4.html:
+        * fast/css/parsing-css-not-1.html:
+        * fast/css/parsing-css-not-2.html:
+        * fast/css/parsing-css-not-3.html:
+        * fast/css/parsing-css-not-4.html:
+        * fast/css/parsing-css-nth-child-of-1.html:
+        * fast/css/parsing-css-nth-child-of-2.html:
+        * fast/css/parsing-css-nth-last-child-of-1.html:
+        * fast/css/parsing-css-nth-last-child-of-2.html:
+        * js/script-tests/arrowfunction-supercall.js:
+        - Remove expectedExceptionString from shouldNotThrow() calls
+          after they were changed from shouldThrow() calls.
+
+        * resources/js-test-pre.js:
+        (shouldNotThrow): Change to invoke first argument if it is a
+        function, else use eval() as before.  Use second argurment in
+        place of first argument (if set) when printing results.  NOTE:
+        Care was taken not to add any lines of code to prevent changes
+        to test results.
+        (shouldThrow): Ditto.  Reformat code.
+        * resources/js-test.js: Declare 'didFailSomeTests'.
+        (testFailed): Set 'didFailSomeTests' to true when a test fails.
+        (shouldNotThrow): Same changes as js-test-pre.js.
+        (shouldThrow): Ditto.
+        (isSuccessfullyParsed): Output a message if 'didFailSomeTests'
+        is true.
+
 2016-06-28  Jiewen Tan  <jiewen_tan@apple.com>
 
         Implement "replacement" codec
index e347531..69281d2 100644 (file)
@@ -115,6 +115,7 @@ Testing how probablySupportsContext handles no parameters
 PASS document.createElement('canvas').probablySupportsContext() is false
 
 PASS successfullyParsed is true
+Some tests failed.
 
 TEST COMPLETE
 
index 8bad1f9..96b57dd 100644 (file)
@@ -23,6 +23,7 @@ FAIL pixel at (20,15) is (0,255,0,255), should be (255,0,0,255)
 PASS getError was expected value: NO_ERROR : 
 
 PASS successfullyParsed is true
+Some tests failed.
 
 TEST COMPLETE
 
index 84abe5b..0abca91 100644 (file)
@@ -57,6 +57,7 @@ PASS window.getComputedStyle(element, '').getPropertyValue('grid-template-column
 PASS window.getComputedStyle(element, '').getPropertyValue('grid-template-columns') is "none"
 PASS window.getComputedStyle(element, '').getPropertyValue('grid-template-columns') is "none"
 PASS successfullyParsed is true
+Some tests failed.
 
 TEST COMPLETE
 
index 3e141e1..35c6028 100644 (file)
@@ -11,7 +11,7 @@
 description('Test the parsing of :lang(stringList) for querySelector and style.');
 
 function testValidLanguageRange(languageRangeString, expectedSerializedLanguageRange) {
-    shouldNotThrow('document.querySelector(\':lang(' + languageRangeString.replace(/\\/g, '\\\\') + ')\')', '"Error: SyntaxError: DOM Exception 12"');
+    shouldNotThrow('document.querySelector(\':lang(' + languageRangeString.replace(/\\/g, '\\\\') + ')\')');
 
     var styleContainer = document.getElementById('style-container');
     styleContainer.innerHTML = ':lang(' + languageRangeString + ') { }';
index fecd649..25781f1 100644 (file)
@@ -11,7 +11,7 @@
 description('Test the parsing of :matches(selectorList) for querySelector and style.');
 
 function testValidSelector(selectorString, expectedSerializedSelector) {
-    shouldNotThrow('document.querySelector(":matches(' + selectorString.replace(/\\/g, '\\\\') + ')")', '"Error: SyntaxError: DOM Exception 12"');
+    shouldNotThrow('document.querySelector(":matches(' + selectorString.replace(/\\/g, '\\\\') + ')")');
 
     var styleContainer = document.getElementById('style-container');
     styleContainer.innerHTML = ':matches(' + selectorString + ') { }';
index 3fa115c..ba0cbec 100644 (file)
@@ -11,7 +11,7 @@
 description('Test the parsing of :matches(selectorList) for querySelector and style.');
 
 function testValidSelector(selectorString, expectedSerializedSelector) {
-    shouldNotThrow('document.querySelector(":matches(' + selectorString.replace(/\\/g, '\\\\') + ')")', '"Error: SyntaxError: DOM Exception 12"');
+    shouldNotThrow('document.querySelector(":matches(' + selectorString.replace(/\\/g, '\\\\') + ')")');
 
     var styleContainer = document.getElementById('style-container');
     styleContainer.innerHTML = ':matches(' + selectorString + ') { }';
index f1d0ffb..c6f5690 100644 (file)
@@ -11,7 +11,7 @@
 description('Test the parsing of :matches(selectorList) for querySelector and style.');
 
 function testValidSelector(selectorString, expectedSerializedSelector) {
-    shouldNotThrow('document.querySelector(":matches(' + selectorString.replace(/\\/g, '\\\\') + ')")', '"Error: SyntaxError: DOM Exception 12"');
+    shouldNotThrow('document.querySelector(":matches(' + selectorString.replace(/\\/g, '\\\\') + ')")');
 
     var styleContainer = document.getElementById('style-container');
     styleContainer.innerHTML = ':matches(' + selectorString + ') { }';
index 8852838..2f08578 100644 (file)
@@ -11,7 +11,7 @@
 description('Test the parsing of :matches(selectorList) for querySelector and style.');
 
 function testValidSelector(selectorString, expectedSerializedSelector) {
-    shouldNotThrow('document.querySelector(":matches(' + selectorString.replace(/\\/g, '\\\\') + ')")', '"Error: SyntaxError: DOM Exception 12"');
+    shouldNotThrow('document.querySelector(":matches(' + selectorString.replace(/\\/g, '\\\\') + ')")');
 
     var styleContainer = document.getElementById('style-container');
     styleContainer.innerHTML = ':matches(' + selectorString + ') { }';
index 018533f..69bebbf 100644 (file)
@@ -11,7 +11,7 @@
 description('Test the parsing of :not(selectorList) for querySelector and style.');
 
 function testValidSelector(selectorString, expectedSerializedSelector) {
-    shouldNotThrow('document.querySelector(":not(' + selectorString.replace(/\\/g, '\\\\') + ')")', '"Error: SyntaxError: DOM Exception 12"');
+    shouldNotThrow('document.querySelector(":not(' + selectorString.replace(/\\/g, '\\\\') + ')")');
 
     var styleContainer = document.getElementById('style-container');
     styleContainer.innerHTML = ':not(' + selectorString + ') { }';
index b2f1d4c..46ac650 100644 (file)
@@ -11,7 +11,7 @@
 description('Test the parsing of :not(selectorList) for querySelector and style.');
 
 function testValidSelector(selectorString, expectedSerializedSelector) {
-    shouldNotThrow('document.querySelector(":not(' + selectorString.replace(/\\/g, '\\\\') + ')")', '"Error: SyntaxError: DOM Exception 12"');
+    shouldNotThrow('document.querySelector(":not(' + selectorString.replace(/\\/g, '\\\\') + ')")');
 
     var styleContainer = document.getElementById('style-container');
     styleContainer.innerHTML = ':not(' + selectorString + ') { }';
index 64bd4bc..ddb1fd7 100644 (file)
@@ -11,7 +11,7 @@
 description('Test the parsing of :not(selectorList) for querySelector and style.');
 
 function testValidSelector(selectorString, expectedSerializedSelector) {
-    shouldNotThrow('document.querySelector(":not(' + selectorString.replace(/\\/g, '\\\\') + ')")', '"Error: SyntaxError: DOM Exception 12"');
+    shouldNotThrow('document.querySelector(":not(' + selectorString.replace(/\\/g, '\\\\') + ')")');
 
     var styleContainer = document.getElementById('style-container');
     styleContainer.innerHTML = ':not(' + selectorString + ') { }';
index 22b602b..d7d2b10 100644 (file)
@@ -11,7 +11,7 @@
 description('Test the parsing of :not(selectorList) for querySelector and style.');
 
 function testValidSelector(selectorString, expectedSerializedSelector) {
-    shouldNotThrow('document.querySelector(":not(' + selectorString.replace(/\\/g, '\\\\') + ')")', '"Error: SyntaxError: DOM Exception 12"');
+    shouldNotThrow('document.querySelector(":not(' + selectorString.replace(/\\/g, '\\\\') + ')")');
 
     var styleContainer = document.getElementById('style-container');
     styleContainer.innerHTML = ':not(' + selectorString + ') { }';
index c6a95aa..3e60b08 100644 (file)
@@ -11,7 +11,7 @@
 description('Test the parsing of :nth-child(of) for querySelector and style.');
 
 function testValidSelector(selectorString, expectedSerializedSelector) {
-    shouldNotThrow('document.querySelector(":nth-child(' + selectorString.replace(/\\/g, '\\\\') + ')")', '"Error: SyntaxError: DOM Exception 12"');
+    shouldNotThrow('document.querySelector(":nth-child(' + selectorString.replace(/\\/g, '\\\\') + ')")');
 
     var styleContainer = document.getElementById('style-container');
     styleContainer.innerHTML = ':nth-child(' + selectorString + ') { }';
index 090f950..e2a3108 100644 (file)
@@ -11,7 +11,7 @@
 description('Test the parsing of :nth-child(of) for querySelector and style.');
 
 function testValidSelector(selectorString, expectedSerializedSelector) {
-    shouldNotThrow('document.querySelector(":nth-child(' + selectorString.replace(/\\/g, '\\\\') + ')")', '"Error: SyntaxError: DOM Exception 12"');
+    shouldNotThrow('document.querySelector(":nth-child(' + selectorString.replace(/\\/g, '\\\\') + ')")');
 
     var styleContainer = document.getElementById('style-container');
     styleContainer.innerHTML = ':nth-child(' + selectorString + ') { }';
index fc853e0..8d2e281 100644 (file)
@@ -11,7 +11,7 @@
 description('Test the parsing of :nth-last-child(of) for querySelector and style.');
 
 function testValidSelector(selectorString, expectedSerializedSelector) {
-    shouldNotThrow('document.querySelector(":nth-last-child(' + selectorString.replace(/\\/g, '\\\\') + ')")', '"Error: SyntaxError: DOM Exception 12"');
+    shouldNotThrow('document.querySelector(":nth-last-child(' + selectorString.replace(/\\/g, '\\\\') + ')")');
 
     var styleContainer = document.getElementById('style-container');
     styleContainer.innerHTML = ':nth-last-child(' + selectorString + ') { }';
index 9370e5a..5c8dc2b 100644 (file)
@@ -11,7 +11,7 @@
 description('Test the parsing of :nth-last-child(of) for querySelector and style.');
 
 function testValidSelector(selectorString, expectedSerializedSelector) {
-    shouldNotThrow('document.querySelector(":nth-last-child(' + selectorString.replace(/\\/g, '\\\\') + ')")', '"Error: SyntaxError: DOM Exception 12"');
+    shouldNotThrow('document.querySelector(":nth-last-child(' + selectorString.replace(/\\/g, '\\\\') + ')")');
 
     var styleContainer = document.getElementById('style-container');
     styleContainer.innerHTML = ':nth-last-child(' + selectorString + ') { }';
index 9f9307f..fe4a297 100644 (file)
@@ -3,6 +3,7 @@ FAIL getByClassName('A') should be a A. Was A.
 PASS getByClassName('ä') is "ä"
 PASS getByClassName('Ä') is "Ä"
 PASS successfullyParsed is true
+Some tests failed.
 
 TEST COMPLETE
 
index 23457a3..6970064 100644 (file)
@@ -151,7 +151,7 @@ shouldBeTrue("indexOfChildClassInStackError > -1 && errorStack.indexOf('ChildCla
 shouldBe("(new class extends A { constructor() { ((a = super())=>{})() } }).id", "value");
 shouldThrow('(new class extends A { constructor() { ((a = this)=>{ return a; })() } })', '"ReferenceError: Cannot access uninitialized variable."');
 shouldThrow('(new class extends A { constructor() { ((a = this, b=super())=>{ return a; })() } })', '"ReferenceError: Cannot access uninitialized variable."');
-shouldNotThrow('(new class extends A { constructor() { ((a = new.target)=>{ return a; })(); super(); } })', '"ReferenceError: Cannot access uninitialized variable."');
-shouldNotThrow('(new class extends A { constructor() { ((a = new.target, b=super())=>{ return a; })() } })', '"ReferenceError: Cannot access uninitialized variable."');
+shouldNotThrow('(new class extends A { constructor() { ((a = new.target)=>{ return a; })(); super(); } })');
+shouldNotThrow('(new class extends A { constructor() { ((a = new.target, b=super())=>{ return a; })() } })');
 
 var successfullyParsed = true;
index f11b4b5..00c5237 100644 (file)
@@ -583,38 +583,38 @@ function expectTrue(v, msg) {
   }
 }
 
-function shouldNotThrow(_a) {
+function shouldNotThrow(_a, _message) {
     try {
-        eval(_a);
-        testPassed(_a + " did not throw exception.");
+        typeof _a == "function" ? _a() : eval(_a);
+        testPassed((_message ? _message : _a) + " did not throw exception.");
     } catch (e) {
-        testFailed(_a + " should not throw exception. Threw exception " + e + ".");
+        testFailed((_message ? _message : _a) + " should not throw exception. Threw exception " + e + ".");
     }
 }
 
-function shouldThrow(_a, _e)
+function shouldThrow(_a, _e, _message)
 {
-  var exception;
-  var _av;
-  try {
-     _av = eval(_a);
-  } catch (e) {
-     exception = e;
-  }
+    var _exception;
+    var _av;
+    try {
+        _av = typeof _a == "function" ? _a() : eval(_a);
+    } catch (e) {
+        _exception = e;
+    }
 
-  var _ev;
-  if (_e)
-      _ev =  eval(_e);
+    var _ev;
+    if (_e)
+        _ev = eval(_e);
 
-  if (exception) {
-    if (typeof _e == "undefined" || exception == _ev)
-      testPassed(_a + " threw exception " + exception + ".");
+    if (_exception) {
+        if (typeof _e == "undefined" || _exception == _ev)
+            testPassed((_message ? _message : _a) + " threw exception " + _exception + ".");
+        else
+            testFailed((_message ? _message : _a) + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Threw exception " + _exception + ".");
+    } else if (typeof _av == "undefined")
+        testFailed((_message ? _message : _a) + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was undefined.");
     else
-      testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Threw exception " + exception + ".");
-  } else if (typeof _av == "undefined")
-    testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was undefined.");
-  else
-    testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was " + _av + ".");
+        testFailed((_message ? _message : _a) + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was " + _av + ".");
 }
 
 function shouldHaveHadError(message)
index 955998e..4512a42 100644 (file)
@@ -10,7 +10,9 @@ if (self.testRunner) {
         testRunner.setPrivateBrowsingEnabled(true);
 }
 
-var description, debug, successfullyParsed;
+var description, debug, didFailSomeTests, successfullyParsed;
+
+didFailSomeTests = false;
 
 var expectingError; // set by shouldHaveError()
 var expectedErrorMessage; // set by onerror when expectingError is true
@@ -153,6 +155,7 @@ function testPassed(msg)
 
 function testFailed(msg)
 {
+    didFailSomeTests = true;
     debug('<span><span class="fail">FAIL</span> ' + escapeHTML(msg) + '</span>');
 }
 
@@ -559,38 +562,38 @@ function expectTrue(v, msg) {
   }
 }
 
-function shouldNotThrow(_a) {
+function shouldNotThrow(_a, _message) {
     try {
-        eval(_a);
-        testPassed(_a + " did not throw exception.");
+        typeof _a == "function" ?  _a() : eval(_a);
+        testPassed((_message ? _message : _a) + " did not throw exception.");
     } catch (e) {
-        testFailed(_a + " should not throw exception. Threw exception " + e + ".");
+        testFailed((_message ? _message : _a) + " should not throw exception. Threw exception " + e + ".");
     }
 }
 
-function shouldThrow(_a, _e)
+function shouldThrow(_a, _e, _message)
 {
-  var _exception;
-  var _av;
-  try {
-     _av = eval(_a);
-  } catch (e) {
-     _exception = e;
-  }
+    var _exception;
+    var _av;
+    try {
+        _av = typeof _a == "function" ? _a() : eval(_a);
+    } catch (e) {
+        _exception = e;
+    }
 
-  var _ev;
-  if (_e)
-      _ev = eval(_e);
+    var _ev;
+    if (_e)
+        _ev = eval(_e);
 
-  if (_exception) {
-    if (typeof _e == "undefined" || _exception == _ev)
-      testPassed(_a + " threw exception " + _exception + ".");
+    if (_exception) {
+        if (typeof _e == "undefined" || _exception == _ev)
+            testPassed((_message ? _message : _a) + " threw exception " + _exception + ".");
+        else
+            testFailed((_message ? _message : _a) + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Threw exception " + _exception + ".");
+    } else if (typeof _av == "undefined")
+        testFailed((_message ? _message : _a) + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was undefined.");
     else
-      testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Threw exception " + _exception + ".");
-  } else if (typeof _av == "undefined")
-    testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was undefined.");
-  else
-    testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was " + _av + ".");
+        testFailed((_message ? _message : _a) + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was " + _av + ".");
 }
 
 function shouldBeNow(a, delta)
@@ -691,6 +694,8 @@ function isSuccessfullyParsed()
     // FIXME: Remove this and only report unexpected syntax errors.
     successfullyParsed = !unexpectedErrorMessage;
     shouldBeTrue("successfullyParsed");
+    if (didFailSomeTests)
+        debug("Some tests failed.");
     debug('<br /><span class="pass">TEST COMPLETE</span>');
 }
 
index 379f57f..33d9cb4 100644 (file)
@@ -82,6 +82,7 @@ PASS cursor instanceof IDBCursorWithValue is false
 PASS cursor.primaryKey is 0
 PASS 'value' in cursor is false
 PASS successfullyParsed is true
+Some tests failed.
 
 TEST COMPLETE
 
index 379f57f..33d9cb4 100644 (file)
@@ -82,6 +82,7 @@ PASS cursor instanceof IDBCursorWithValue is false
 PASS cursor.primaryKey is 0
 PASS 'value' in cursor is false
 PASS successfullyParsed is true
+Some tests failed.
 
 TEST COMPLETE