Commit 3bbd81af authored by titzer's avatar titzer Committed by Commit bot

[testing] Add the notion of a wait count to allow tests to robustly wait on asynchronous tasks.

Note that this also modifies mjsunit.js to allow the {failWithMessage} method to be monkey-patched by a test. This is necessary because assertions which fail in a promise's then-clause would normally only throw an exception, which is swallowed by the promise, causing the test to silently pass. Instead, patching this {failWithMessage} functionality allows then clauses to use the full assertion machinery of mjsunit.js.

R=ulan@chromium.org, gsathya@chromium.org

BUG=

Review-Url: https://codereview.chromium.org/2752043002
Cr-Commit-Position: refs/heads/master@{#43875}
parent 4c3217e1
......@@ -2585,12 +2585,23 @@ void Shell::CollectGarbage(Isolate* isolate) {
}
}
void Shell::EmptyMessageQueues(Isolate* isolate) {
if (!i::FLAG_verify_predictable) {
while (v8::platform::PumpMessageLoop(g_platform, isolate)) continue;
if (i::FLAG_verify_predictable) return;
while (true) {
// Pump the message loop until it is empty.
while (v8::platform::PumpMessageLoop(g_platform, isolate)) {
isolate->RunMicrotasks();
}
// Run the idle tasks.
v8::platform::RunIdleTasks(g_platform, isolate,
50.0 / base::Time::kMillisecondsPerSecond);
// If there are still outstanding waiters, sleep a little (to wait for
// background tasks) and then try everything again.
if (reinterpret_cast<i::Isolate*>(isolate)->GetWaitCountForTesting() > 0) {
base::OS::Sleep(base::TimeDelta::FromMilliseconds(1));
} else {
break;
}
}
}
......
......@@ -1243,6 +1243,11 @@ class Isolate {
// reset to nullptr.
void UnregisterFromReleaseAtTeardown(ManagedObjectFinalizer** finalizer_ptr);
// Used by mjsunit tests to force d8 to wait for certain things to run.
inline void IncrementWaitCountForTesting() { wait_count_++; }
inline void DecrementWaitCountForTesting() { wait_count_--; }
inline int GetWaitCountForTesting() { return wait_count_; }
protected:
explicit Isolate(bool enable_serializer);
bool IsArrayOrObjectPrototype(Object* object);
......@@ -1524,6 +1529,8 @@ class Isolate {
size_t total_regexp_code_generated_;
int wait_count_ = 0;
friend class ExecutionAccess;
friend class HandleScopeImplementer;
friend class HeapTester;
......
......@@ -948,5 +948,14 @@ RUNTIME_FUNCTION(Runtime_WasmNumInterpretedCalls) {
return *isolate->factory()->NewNumberFromSize(static_cast<size_t>(num));
}
RUNTIME_FUNCTION(Runtime_IncrementWaitCount) {
isolate->IncrementWaitCountForTesting();
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_DecrementWaitCount) {
isolate->DecrementWaitCountForTesting();
return isolate->heap()->undefined_value();
}
} // namespace internal
} // namespace v8
......@@ -457,7 +457,9 @@ namespace internal {
F(PromiseRevokeReject, 1, 1) \
F(PromiseResult, 1, 1) \
F(PromiseStatus, 1, 1) \
F(ReportPromiseReject, 2, 1)
F(ReportPromiseReject, 2, 1) \
F(IncrementWaitCount, 0, 1) \
F(DecrementWaitCount, 0, 1)
#define FOR_EACH_INTRINSIC_PROXY(F) \
F(IsJSProxy, 1, 1) \
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
// We have to patch mjsunit because normal assertion failures just throw
// exceptions which are swallowed in a then clause.
failWithMessage = (msg) => %AbortJS(msg);
let decrement = () => { %DecrementWaitCount(); }
let increment = () => { %IncrementWaitCount(); }
function WaitForPromise(p) {
increment();
p.then(decrement, decrement);
}
function newPromise() {
var outerResolve;
var outerReject;
let promise = new Promise((resolve, reject) => {
outerResolve = resolve;
outerReject = reject;
});
WaitForPromise(promise); // explicitly wait for promise to resolve.
return {
resolve: outerResolve,
reject: outerReject,
then: (f, g) => promise.then(f, g)
};
}
(function ResolveOK() {
let promise = newPromise();
promise.then(msg => {print("resolved: " + msg); assertEquals("ok", msg); },
ex => {print("rejected: " + ex); %AbortJS("" + ex); });
promise.resolve("ok");
promise.reject(11); // ignored
})();
(function RejectOK() {
let promise = newPromise();
promise.then(msg => {print("resolved: " + msg); %AbortJS("fail"); },
ex => {print("rejected: " + ex); assertEquals(42, ex); });
promise.reject(42);
promise.resolve("fail"); // ignored
})();
......@@ -149,6 +149,9 @@ var isCrankshafted;
// Returns true if given function is compiled by TurboFan.
var isTurboFanned;
// Monkey-patchable all-purpose failure handler.
var failWithMessage;
(function () { // Scope for utility functions.
......@@ -233,7 +236,8 @@ var isTurboFanned;
}
function failWithMessage(message) {
failWithMessage = function failWithMessage(message) {
print("oh, we failed: " + message);
throw new MjsUnitAssertionError(message);
}
......@@ -251,7 +255,7 @@ var isTurboFanned;
} else {
message += ":\nexpected:\n" + expectedText + "\nfound:\n" + foundText;
}
throw new MjsUnitAssertionError(message);
return failWithMessage(message);
}
......
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