Commit 61ed6a00 authored by Sathya Gunasekaran's avatar Sathya Gunasekaran Committed by Commit Bot

[tests] Async test framework

- No need for multiple assertAsyncRan() calls, just do t.plan(count)

- Previously, if you forget to call assertAsyncRan(), the test will still
  pass, which is no longer true.

- No longer hold global state (with
  asyncAssertsExpected). Previously if one assert wasn't hit then
  there's no way to find out which test failed. You'd have to
  comment each test and try again.

- Each test runs independently in the microtask queue.

- Better failure reporting by printing the entire function.

Example error :
=== mjsunit/harmony/promise-prototype-finally ===
abort: Expected asserts: 2, Actual asserts: 1
in test: reject/finally/then
assert => {
  assert.plan(2);
  Promise.reject(3).finally().then(
    assert.unreachable,
    x => {
      assert.equals(3, x);
    });
}


Change-Id: Ic3f6272e1e87b8b0121b8c8c7cce19cf90d1f1be
Reviewed-on: https://chromium-review.googlesource.com/455555
Commit-Queue: Sathya Gunasekaran <gsathya@chromium.org>
Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Reviewed-by: 's avatarDaniel Ehrenberg <littledan@chromium.org>
Reviewed-by: 's avatarCaitlin Potter <caitp@igalia.com>
Cr-Commit-Position: refs/heads/master@{#46910}
parent 00a8ab2e
...@@ -234,7 +234,7 @@ function testProtoSetter1_2() { ...@@ -234,7 +234,7 @@ function testProtoSetter1_2() {
for (var n in this) { for (var n in this) {
if (n.substr(0, 4) != 'test' || if (n.substr(0, 4) != 'test' ||
n == 'testRunner') { n == 'testRunner' || n == 'testAsync') {
continue; continue;
} }
state = 1; state = 1;
......
...@@ -134,7 +134,7 @@ function testCall_RuntimeVariable3() { ...@@ -134,7 +134,7 @@ function testCall_RuntimeVariable3() {
var functionsCalled = 0; var functionsCalled = 0;
for (var n in this) { for (var n in this) {
if (n.substr(0, 4) != 'test' || typeof this[n] !== "function") { if (n.substr(0, 4) != 'test' || typeof this[n] !== "function" || n == 'testAsync') {
continue; continue;
} }
state = 1; state = 1;
......
...@@ -167,6 +167,9 @@ var isCrankshafted; ...@@ -167,6 +167,9 @@ var isCrankshafted;
// Returns true if given function is compiled by TurboFan. // Returns true if given function is compiled by TurboFan.
var isTurboFanned; var isTurboFanned;
// Used for async tests. See definition below for more documentation.
var testAsync;
// Monkey-patchable all-purpose failure handler. // Monkey-patchable all-purpose failure handler.
var failWithMessage; var failWithMessage;
...@@ -270,8 +273,7 @@ var failWithMessage; ...@@ -270,8 +273,7 @@ var failWithMessage;
throw new MjsUnitAssertionError(message); throw new MjsUnitAssertionError(message);
} }
function formatFailureText(expectedText, found, name_opt) {
function fail(expectedText, found, name_opt) {
var message = "Fail" + "ure"; var message = "Fail" + "ure";
if (name_opt) { if (name_opt) {
// Fix this when we ditch the old test runner. // Fix this when we ditch the old test runner.
...@@ -284,7 +286,10 @@ var failWithMessage; ...@@ -284,7 +286,10 @@ var failWithMessage;
} else { } else {
message += ":\nexpected:\n" + expectedText + "\nfound:\n" + foundText; message += ":\nexpected:\n" + expectedText + "\nfound:\n" + foundText;
} }
return failWithMessage(message); }
function fail(expectedText, found, name_opt) {
return failWithMessage(formatFailureText(expectedText, found, name_opt));
} }
...@@ -706,4 +711,91 @@ var failWithMessage; ...@@ -706,4 +711,91 @@ var failWithMessage;
return error.stack; return error.stack;
} }
/**
* This is to be used through the testAsync helper function defined
* below.
*
* This requires the --allow-natives-syntax flag to allow calling
* runtime functions.
*
* There must be at least one assertion in an async test. A test
* with no assertions will fail.
*
* @example
* testAsync(assert => {
* assert.plan(1) // There should be one assertion in this test.
* Promise.resolve(1)
* .then(val => assert.equals(1, val),
* assert.unreachable);
* })
*/
class AsyncAssertion {
constructor(test, name) {
this.expectedAsserts_ = -1;
this.actualAsserts_ = 0;
this.test_ = test;
this.name_ = name || '';
}
/**
* Sets the number of expected asserts in the test. The test fails
* if the number of asserts computed after running the test is not
* equal to this specified value.
* @param {number} expectedAsserts
*/
plan(expectedAsserts) {
this.expectedAsserts_ = expectedAsserts;
}
fail(expectedText, found) {
message = formatFailureText(expectedText, found);
message += "\nin test:" + this.name_
message += "\n" + Function.prototype.toString.apply(this.test_);
eval("%AbortJS(message)");
}
equals(expected, found, name_opt) {
this.actualAsserts_++;
if (!deepEquals(expected, found)) {
this.fail(PrettyPrint(expected), found, name_opt);
}
}
unreachable() {
let message = "Failure: unreachable in test: " + this.name_;
message += "\n" + Function.prototype.toString.apply(this.test_);
eval("%AbortJS(message)");
}
done_() {
if (this.expectedAsserts_ === -1) {
let message = "Please call t.plan(count) to initialize test harness " +
"with correct assert count (Note: count > 0)";
eval("%AbortJS(message)");
}
if (this.expectedAsserts_ !== this.actualAsserts_) {
let message = "Expected asserts: " + this.expectedAsserts_;
message += ", Actual asserts: " + this.actualAsserts_;
message += "\nin test: " + this.name_;
message += "\n" + Function.prototype.toString.apply(this.test_);
eval("%AbortJS(message)");
}
}
}
/** This is used to test async functions and promises.
* @param {testCallback} test - test function
* @param {string} [name] - optional name of the test
*
*
* @callback testCallback
* @param {AsyncAssertion} assert
*/
testAsync = function(test, name) {
let assert = new AsyncAssertion(test, name);
test(assert);
eval("%RunMicrotasks()");
assert.done_();
}
})(); })();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment