blob: 19d12ac49cc390fc64ae6f36b4ff426e40d0db24 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/streams/transferable_streams.h"
#include "base/types/strong_alias.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/bindings/core/v8/readable_stream_default_reader_or_readable_stream_byob_reader.h"
#include "third_party/blink/renderer/bindings/core/v8/script_function.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_readable_stream_default_reader.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_writable_stream_default_writer.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/messaging/message_channel.h"
#include "third_party/blink/renderer/core/streams/readable_stream.h"
#include "third_party/blink/renderer/core/streams/readable_stream_default_controller_with_script_scope.h"
#include "third_party/blink/renderer/core/streams/readable_stream_default_reader.h"
#include "third_party/blink/renderer/core/streams/readable_stream_transferring_optimizer.h"
#include "third_party/blink/renderer/core/streams/underlying_source_base.h"
#include "third_party/blink/renderer/core/streams/writable_stream.h"
#include "third_party/blink/renderer/core/streams/writable_stream_default_writer.h"
#include "third_party/blink/renderer/core/streams/writable_stream_transferring_optimizer.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "v8/include/v8.h"
namespace blink {
namespace {
enum class SourceType { kPush, kPull };
class TestUnderlyingSource final : public UnderlyingSourceBase {
public:
TestUnderlyingSource(SourceType source_type,
ScriptState* script_state,
Vector<int> sequence,
ScriptPromise start_promise)
: UnderlyingSourceBase(script_state),
type_(source_type),
sequence_(std::move(sequence)),
start_promise_(start_promise) {}
TestUnderlyingSource(SourceType source_type,
ScriptState* script_state,
Vector<int> sequence)
: TestUnderlyingSource(source_type,
script_state,
std::move(sequence),
ScriptPromise::CastUndefined(script_state)) {}
~TestUnderlyingSource() override = default;
ScriptPromise Start(ScriptState* script_state) override {
started_ = true;
if (type_ == SourceType::kPush) {
for (int element : sequence_) {
EnqueueOrError(script_state, element);
}
index_ = sequence_.size();
Controller()->Close();
}
return start_promise_;
}
ScriptPromise pull(ScriptState* script_state) override {
if (type_ == SourceType::kPush) {
return ScriptPromise::CastUndefined(script_state);
}
if (index_ == sequence_.size()) {
Controller()->Close();
return ScriptPromise::CastUndefined(script_state);
}
EnqueueOrError(script_state, sequence_[index_]);
++index_;
return ScriptPromise::CastUndefined(script_state);
}
ScriptPromise Cancel(ScriptState* script_state, ScriptValue reason) override {
cancelled_ = true;
cancel_reason_ = reason;
return ScriptPromise::CastUndefined(script_state);
}
bool IsStarted() const { return started_; }
bool IsCancelled() const { return cancelled_; }
ScriptValue CancelReason() const { return cancel_reason_; }
void Trace(Visitor* visitor) const override {
visitor->Trace(start_promise_);
visitor->Trace(cancel_reason_);
UnderlyingSourceBase::Trace(visitor);
}
private:
void EnqueueOrError(ScriptState* script_state, int num) {
if (num < 0) {
Controller()->Error(V8ThrowException::CreateRangeError(
script_state->GetIsolate(), "foo"));
return;
}
Controller()->Enqueue(num);
}
const SourceType type_;
const Vector<int> sequence_;
wtf_size_t index_ = 0;
const ScriptPromise start_promise_;
bool started_ = false;
bool cancelled_ = false;
ScriptValue cancel_reason_;
};
void ExpectValue(int line,
ScriptState* script_state,
v8::Local<v8::Value> result,
int32_t expectation) {
SCOPED_TRACE(testing::Message() << "__LINE__ = " << line);
if (!result->IsObject()) {
ADD_FAILURE() << "The result is not an Object.";
return;
}
v8::Local<v8::Value> value;
bool done = false;
if (!V8UnpackIteratorResult(script_state, result.As<v8::Object>(), &done)
.ToLocal(&value)) {
ADD_FAILURE() << "Failed to unpack the iterator result.";
return;
}
EXPECT_FALSE(done);
if (!value->IsInt32()) {
ADD_FAILURE() << "The value is not an int32.";
return;
}
EXPECT_EQ(value.As<v8::Number>()->Value(), expectation);
}
void ExpectDone(int line,
ScriptState* script_state,
v8::Local<v8::Value> result) {
SCOPED_TRACE(testing::Message() << "__LINE__ = " << line);
v8::Local<v8::Value> value;
bool done = false;
if (!V8UnpackIteratorResult(script_state, result.As<v8::Object>(), &done)
.ToLocal(&value)) {
ADD_FAILURE() << "Failed to unpack the iterator result.";
return;
}
EXPECT_TRUE(done);
}
// We only do minimal testing here. The functionality of transferable streams is
// tested in the layout tests.
TEST(TransferableStreamsTest, SmokeTest) {
V8TestingScope scope;
auto* channel =
MakeGarbageCollected<MessageChannel>(scope.GetExecutionContext());
auto* script_state = scope.GetScriptState();
auto* writable = CreateCrossRealmTransformWritable(
script_state, channel->port1(), /*optimizer=*/nullptr,
ASSERT_NO_EXCEPTION);
ASSERT_TRUE(writable);
auto* readable = CreateCrossRealmTransformReadable(
script_state, channel->port2(), /*optimizer=*/nullptr,
ASSERT_NO_EXCEPTION);
ASSERT_TRUE(readable);
auto* writer = writable->getWriter(script_state, ASSERT_NO_EXCEPTION);
auto* reader =
readable->GetDefaultReaderForTesting(script_state, ASSERT_NO_EXCEPTION);
writer->write(script_state, ScriptValue::CreateNull(scope.GetIsolate()),
ASSERT_NO_EXCEPTION);
class ExpectNullResponse : public ScriptFunction {
public:
static v8::Local<v8::Function> Create(ScriptState* script_state,
bool* got_response) {
auto* self =
MakeGarbageCollected<ExpectNullResponse>(script_state, got_response);
return self->BindToV8Function();
}
ExpectNullResponse(ScriptState* script_state, bool* got_response)
: ScriptFunction(script_state), got_response_(got_response) {}
private:
ScriptValue Call(ScriptValue value) override {
*got_response_ = true;
if (!value.IsObject()) {
ADD_FAILURE() << "iterator must be an object";
return ScriptValue();
}
bool done = false;
auto* script_state = GetScriptState();
auto chunk_maybe =
V8UnpackIteratorResult(script_state,
value.V8Value()
->ToObject(script_state->GetContext())
.ToLocalChecked(),
&done);
EXPECT_FALSE(done);
v8::Local<v8::Value> chunk;
if (!chunk_maybe.ToLocal(&chunk)) {
ADD_FAILURE() << "V8UnpackIteratorResult failed";
return ScriptValue();
}
EXPECT_TRUE(chunk->IsNull());
return ScriptValue();
}
bool* got_response_;
};
// TODO(ricea): This is copy-and-pasted from transform_stream_test.cc. Put it
// in a shared location.
class ExpectNotReached : public ScriptFunction {
public:
static v8::Local<v8::Function> Create(ScriptState* script_state) {
auto* self = MakeGarbageCollected<ExpectNotReached>(script_state);
return self->BindToV8Function();
}
explicit ExpectNotReached(ScriptState* script_state)
: ScriptFunction(script_state) {}
private:
ScriptValue Call(ScriptValue) override {
ADD_FAILURE() << "ExpectNotReached was reached";
return ScriptValue();
}
};
bool got_response = false;
reader->read(script_state, ASSERT_NO_EXCEPTION)
.Then(ExpectNullResponse::Create(script_state, &got_response),
ExpectNotReached::Create(script_state));
// Need to run the event loop to pass messages through the MessagePort.
test::RunPendingTasks();
// Resolve promises.
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
EXPECT_TRUE(got_response);
}
TEST(ConcatenatedReadableStreamTest, Empty) {
V8TestingScope scope;
auto* script_state = scope.GetScriptState();
TestUnderlyingSource* source1 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPull, script_state, Vector<int>({}));
TestUnderlyingSource* source2 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPull, script_state, Vector<int>({}));
ReadableStream* stream =
CreateConcatenatedReadableStream(script_state, source1, source2);
ASSERT_TRUE(stream);
auto* reader =
stream->GetDefaultReaderForTesting(script_state, ASSERT_NO_EXCEPTION);
ASSERT_TRUE(reader);
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kFulfilled);
ExpectDone(__LINE__, script_state, read_promise->Result());
}
EXPECT_TRUE(source1->IsStarted());
EXPECT_TRUE(source2->IsStarted());
EXPECT_FALSE(source1->IsCancelled());
EXPECT_FALSE(source2->IsCancelled());
}
TEST(ConcatenatedReadableStreamTest, SuccessfulRead) {
V8TestingScope scope;
auto* script_state = scope.GetScriptState();
TestUnderlyingSource* source1 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPull, script_state, Vector<int>({1}));
TestUnderlyingSource* source2 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPull, script_state, Vector<int>({5, 6}));
ReadableStream* stream =
CreateConcatenatedReadableStream(script_state, source1, source2);
ASSERT_TRUE(stream);
auto* reader =
stream->GetDefaultReaderForTesting(script_state, ASSERT_NO_EXCEPTION);
ASSERT_TRUE(reader);
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kFulfilled);
ExpectValue(__LINE__, script_state, read_promise->Result(), 1);
EXPECT_TRUE(source1->IsStarted());
EXPECT_FALSE(source2->IsStarted());
}
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kFulfilled);
ExpectValue(__LINE__, script_state, read_promise->Result(), 5);
EXPECT_TRUE(source2->IsStarted());
}
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kFulfilled);
ExpectValue(__LINE__, script_state, read_promise->Result(), 6);
}
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kFulfilled);
ExpectDone(__LINE__, script_state, read_promise->Result());
}
EXPECT_FALSE(source1->IsCancelled());
EXPECT_FALSE(source2->IsCancelled());
}
TEST(ConcatenatedReadableStreamTest, SuccessfulReadForPushSources) {
V8TestingScope scope;
auto* script_state = scope.GetScriptState();
TestUnderlyingSource* source1 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPush, script_state, Vector<int>({1}));
TestUnderlyingSource* source2 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPush, script_state, Vector<int>({5, 6}));
ReadableStream* stream =
CreateConcatenatedReadableStream(script_state, source1, source2);
ASSERT_TRUE(stream);
auto* reader =
stream->GetDefaultReaderForTesting(script_state, ASSERT_NO_EXCEPTION);
ASSERT_TRUE(reader);
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kFulfilled);
ExpectValue(__LINE__, script_state, read_promise->Result(), 1);
EXPECT_TRUE(source1->IsStarted());
EXPECT_FALSE(source2->IsStarted());
}
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kFulfilled);
ExpectValue(__LINE__, script_state, read_promise->Result(), 5);
EXPECT_TRUE(source2->IsStarted());
}
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kFulfilled);
ExpectValue(__LINE__, script_state, read_promise->Result(), 6);
}
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kFulfilled);
ExpectDone(__LINE__, script_state, read_promise->Result());
}
EXPECT_FALSE(source1->IsCancelled());
EXPECT_FALSE(source2->IsCancelled());
}
TEST(ConcatenatedReadableStreamTest, ErrorInSource1) {
V8TestingScope scope;
auto* script_state = scope.GetScriptState();
TestUnderlyingSource* source1 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPull, script_state, Vector<int>({1, -2}));
TestUnderlyingSource* source2 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPull, script_state, Vector<int>({5, 6}));
ReadableStream* stream =
CreateConcatenatedReadableStream(script_state, source1, source2);
ASSERT_TRUE(stream);
auto* reader =
stream->GetDefaultReaderForTesting(script_state, ASSERT_NO_EXCEPTION);
ASSERT_TRUE(reader);
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kFulfilled);
ExpectValue(__LINE__, script_state, read_promise->Result(), 1);
}
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kRejected);
}
EXPECT_TRUE(source1->IsStarted());
EXPECT_FALSE(source1->IsCancelled());
EXPECT_TRUE(source2->IsStarted());
EXPECT_TRUE(source2->IsCancelled());
}
TEST(ConcatenatedReadableStreamTest, ErrorInSource2) {
V8TestingScope scope;
auto* script_state = scope.GetScriptState();
TestUnderlyingSource* source1 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPull, script_state, Vector<int>({1}));
TestUnderlyingSource* source2 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPull, script_state, Vector<int>({-2}));
ReadableStream* stream =
CreateConcatenatedReadableStream(script_state, source1, source2);
ASSERT_TRUE(stream);
auto* reader =
stream->GetDefaultReaderForTesting(script_state, ASSERT_NO_EXCEPTION);
ASSERT_TRUE(reader);
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kFulfilled);
ExpectValue(__LINE__, script_state, read_promise->Result(), 1);
}
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kRejected);
}
EXPECT_TRUE(source1->IsStarted());
EXPECT_FALSE(source1->IsCancelled());
EXPECT_TRUE(source2->IsStarted());
EXPECT_FALSE(source2->IsCancelled());
}
TEST(ConcatenatedReadableStreamTest, Cancel1) {
V8TestingScope scope;
auto* script_state = scope.GetScriptState();
TestUnderlyingSource* source1 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPull, script_state, Vector<int>({1, 2}));
TestUnderlyingSource* source2 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPull, script_state, Vector<int>({5, 6}));
ScriptValue reason = ScriptValue::From(script_state, "hello");
ReadableStream* stream =
CreateConcatenatedReadableStream(script_state, source1, source2);
ASSERT_TRUE(stream);
auto* reader =
stream->GetDefaultReaderForTesting(script_state, ASSERT_NO_EXCEPTION);
ASSERT_TRUE(reader);
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kFulfilled);
ExpectValue(__LINE__, script_state, read_promise->Result(), 1);
}
EXPECT_TRUE(source1->IsStarted());
EXPECT_FALSE(source1->IsCancelled());
EXPECT_FALSE(source2->IsStarted());
EXPECT_FALSE(source2->IsCancelled());
{
reader->cancel(script_state, reason, ASSERT_NO_EXCEPTION);
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
}
EXPECT_TRUE(source1->IsStarted());
EXPECT_TRUE(source1->IsCancelled());
EXPECT_EQ(reason, source1->CancelReason());
EXPECT_TRUE(source2->IsStarted());
EXPECT_TRUE(source2->IsCancelled());
EXPECT_EQ(reason, source2->CancelReason());
}
TEST(ConcatenatedReadableStreamTest, Cancel2) {
V8TestingScope scope;
auto* script_state = scope.GetScriptState();
TestUnderlyingSource* source1 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPull, script_state, Vector<int>({}));
TestUnderlyingSource* source2 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPull, script_state, Vector<int>({5}));
ScriptValue reason = ScriptValue::From(script_state, "hello");
ReadableStream* stream =
CreateConcatenatedReadableStream(script_state, source1, source2);
ASSERT_TRUE(stream);
auto* reader =
stream->GetDefaultReaderForTesting(script_state, ASSERT_NO_EXCEPTION);
ASSERT_TRUE(reader);
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kFulfilled);
ExpectValue(__LINE__, script_state, read_promise->Result(), 5);
}
{
reader->cancel(script_state, reason, ASSERT_NO_EXCEPTION);
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
}
EXPECT_TRUE(source1->IsStarted());
EXPECT_FALSE(source1->IsCancelled());
EXPECT_TRUE(source2->IsStarted());
EXPECT_TRUE(source2->IsCancelled());
EXPECT_EQ(reason, source2->CancelReason());
}
TEST(ConcatenatedReadableStreamTest, PendingStart1) {
V8TestingScope scope;
auto* script_state = scope.GetScriptState();
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
TestUnderlyingSource* source1 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPull, script_state, Vector<int>({1, 2}),
resolver->Promise());
TestUnderlyingSource* source2 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPull, script_state, Vector<int>({5, 6}));
ReadableStream* stream =
CreateConcatenatedReadableStream(script_state, source1, source2);
ASSERT_TRUE(stream);
auto* reader =
stream->GetDefaultReaderForTesting(script_state, ASSERT_NO_EXCEPTION);
ASSERT_TRUE(reader);
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kPending);
resolver->Resolve();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kFulfilled);
ExpectValue(__LINE__, script_state, read_promise->Result(), 1);
}
EXPECT_TRUE(source1->IsStarted());
EXPECT_FALSE(source2->IsStarted());
}
TEST(ConcatenatedReadableStreamTest, PendingStart2) {
V8TestingScope scope;
auto* script_state = scope.GetScriptState();
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
TestUnderlyingSource* source1 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPull, script_state, Vector<int>({1}));
TestUnderlyingSource* source2 = MakeGarbageCollected<TestUnderlyingSource>(
SourceType::kPull, script_state, Vector<int>({5, 6}),
resolver->Promise());
ReadableStream* stream =
CreateConcatenatedReadableStream(script_state, source1, source2);
ASSERT_TRUE(stream);
auto* reader =
stream->GetDefaultReaderForTesting(script_state, ASSERT_NO_EXCEPTION);
ASSERT_TRUE(reader);
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kFulfilled);
ExpectValue(__LINE__, script_state, read_promise->Result(), 1);
}
{
v8::Local<v8::Promise> read_promise =
reader->read(script_state, ASSERT_NO_EXCEPTION).V8Promise();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kPending);
resolver->Resolve();
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
ASSERT_EQ(read_promise->State(), v8::Promise::kFulfilled);
ExpectValue(__LINE__, script_state, read_promise->Result(), 5);
}
EXPECT_TRUE(source1->IsStarted());
EXPECT_TRUE(source2->IsStarted());
}
} // namespace
} // namespace blink