TEST_F(ReadableStreamTest, CancelWhenReadable) { ScriptState::Scope scope(scriptState()); ExceptionState exceptionState(ExceptionState::ConstructionContext, "property", "interface", scriptState()->context()->Global(), isolate()); StringStream* stream = construct(); String onFulfilled, onRejected; String onCancelFulfilled, onCancelRejected; ScriptValue reason(scriptState(), v8String(scriptState()->isolate(), "reason")); ScriptPromise promise = ScriptPromise::cast(scriptState(), v8String(scriptState()->isolate(), "hello")); { InSequence s; EXPECT_CALL(*m_underlyingSource, cancelSource(scriptState(), reason)).WillOnce(ReturnPointee(&promise)); } stream->enqueue("hello"); EXPECT_EQ(ReadableStream::Readable, stream->stateInternal()); EXPECT_FALSE(stream->isDisturbed()); ScriptPromise cancelResult = stream->cancel(scriptState(), reason); EXPECT_TRUE(stream->isDisturbed()); cancelResult.then(createCaptor(&onCancelFulfilled), createCaptor(&onCancelRejected)); EXPECT_NE(promise, cancelResult); EXPECT_EQ(ReadableStream::Closed, stream->stateInternal()); EXPECT_TRUE(onCancelFulfilled.isNull()); EXPECT_TRUE(onCancelRejected.isNull()); isolate()->RunMicrotasks(); EXPECT_EQ("undefined", onCancelFulfilled); EXPECT_TRUE(onCancelRejected.isNull()); }
TEST_F(ReadableStreamTest, CancelWhenLocked) { ScriptState::Scope scope(scriptState()); ExceptionState exceptionState(ExceptionState::ConstructionContext, "property", "interface", scriptState()->context()->Global(), isolate()); String onFulfilled, onRejected; StringStream* stream = construct(); ReadableStreamReader* reader = stream->getReader(scriptState()->executionContext(), exceptionState); EXPECT_TRUE(reader->isActive()); EXPECT_FALSE(exceptionState.hadException()); EXPECT_EQ(ReadableStream::Readable, stream->stateInternal()); EXPECT_FALSE(stream->isDisturbed()); stream->cancel(scriptState(), ScriptValue(scriptState(), v8::Undefined(isolate()))).then(createCaptor(&onFulfilled), createCaptor(&onRejected)); EXPECT_FALSE(stream->isDisturbed()); EXPECT_TRUE(onFulfilled.isNull()); EXPECT_TRUE(onRejected.isNull()); isolate()->RunMicrotasks(); EXPECT_TRUE(onFulfilled.isNull()); EXPECT_EQ("TypeError: this stream is locked to a ReadableStreamReader", onRejected); EXPECT_TRUE(reader->isActive()); EXPECT_EQ(ReadableStream::Readable, stream->stateInternal()); }
TEST_F(ReadableStreamTest, CloseWhenReadable) { ScriptState::Scope scope(scriptState()); ExceptionState exceptionState(ExceptionState::ConstructionContext, "property", "interface", scriptState()->context()->Global(), isolate()); StringStream* stream = construct(); EXPECT_TRUE(stream->enqueue("hello")); EXPECT_TRUE(stream->enqueue("bye")); stream->close(); EXPECT_FALSE(stream->enqueue("should be ignored")); EXPECT_EQ(ReadableStream::Readable, stream->stateInternal()); EXPECT_FALSE(stream->isPulling()); EXPECT_TRUE(stream->isDraining()); stream->read(scriptState()); isolate()->RunMicrotasks(); EXPECT_EQ(ReadableStream::Readable, stream->stateInternal()); EXPECT_FALSE(stream->isPulling()); EXPECT_TRUE(stream->isDraining()); stream->read(scriptState()); EXPECT_EQ(ReadableStream::Closed, stream->stateInternal()); EXPECT_FALSE(stream->isPulling()); EXPECT_TRUE(stream->isDraining()); }
TEST_F(ReadableStreamTest, ReadQueue) { ScriptState::Scope scope(scriptState()); ExceptionState exceptionState(ExceptionState::ConstructionContext, "property", "interface", scriptState()->context()->Global(), isolate()); StringStream* stream = construct(); Checkpoint checkpoint; { InSequence s; EXPECT_CALL(checkpoint, Call(0)); EXPECT_CALL(*m_underlyingSource, pullSource()).Times(1); EXPECT_CALL(checkpoint, Call(1)); } Deque<std::pair<String, size_t>> queue; EXPECT_TRUE(stream->enqueue("hello")); EXPECT_TRUE(stream->enqueue("bye")); EXPECT_EQ(ReadableStream::Readable, stream->stateInternal()); EXPECT_FALSE(stream->isPulling()); checkpoint.Call(0); EXPECT_FALSE(stream->isDisturbed()); stream->readInternal(queue); EXPECT_TRUE(stream->isDisturbed()); checkpoint.Call(1); ASSERT_EQ(2u, queue.size()); EXPECT_EQ(std::make_pair(String("hello"), static_cast<size_t>(5)), queue[0]); EXPECT_EQ(std::make_pair(String("bye"), static_cast<size_t>(3)), queue[1]); EXPECT_EQ(ReadableStream::Readable, stream->stateInternal()); EXPECT_TRUE(stream->isPulling()); EXPECT_FALSE(stream->isDraining()); }
TEST_F(ReadableStreamTest, Start) { ScriptState::Scope scope(scriptState()); ExceptionState exceptionState(ExceptionState::ConstructionContext, "property", "interface", scriptState()->context()->Global(), isolate()); Checkpoint checkpoint; { InSequence s; EXPECT_CALL(checkpoint, Call(0)); EXPECT_CALL(*m_underlyingSource, pullSource()).Times(1); EXPECT_CALL(checkpoint, Call(1)); } StringStream* stream = new StringStream(m_underlyingSource); EXPECT_FALSE(exceptionState.hadException()); EXPECT_FALSE(stream->isStarted()); EXPECT_FALSE(stream->isDraining()); EXPECT_FALSE(stream->isPulling()); EXPECT_FALSE(stream->isDisturbed()); EXPECT_EQ(stream->stateInternal(), ReadableStream::Readable); checkpoint.Call(0); stream->didSourceStart(); checkpoint.Call(1); EXPECT_TRUE(stream->isStarted()); EXPECT_FALSE(stream->isDraining()); EXPECT_TRUE(stream->isPulling()); EXPECT_EQ(stream->stateInternal(), ReadableStream::Readable); // We need to call |error| in order to make // ActiveDOMObject::hasPendingActivity return false. stream->error(DOMException::create(AbortError, "done")); }
TEST_F(ReadableStreamTest, CloseWhenErrored) { ScriptState::Scope scope(scriptState()); ExceptionState exceptionState(ExceptionState::ConstructionContext, "property", "interface", scriptState()->context()->Global(), isolate()); StringStream* stream = construct(); EXPECT_EQ(ReadableStream::Readable, stream->stateInternal()); stream->error(DOMException::create(NotFoundError, "error")); stream->close(); EXPECT_EQ(ReadableStream::Errored, stream->stateInternal()); }
TEST_F(ReadableStreamTest, CloseAndEnqueue) { ScriptState::Scope scope(scriptState()); ExceptionState exceptionState(ExceptionState::ConstructionContext, "property", "interface", scriptState()->context()->Global(), isolate()); StringStream* stream = construct(); stream->close(); EXPECT_EQ(ReadableStream::Closed, stream->stateInternal()); bool result = stream->enqueue("hello"); EXPECT_FALSE(result); EXPECT_EQ(ReadableStream::Closed, stream->stateInternal()); }
TEST_F(ReadableStreamTest, BackpressureOnEnqueueing) { ScriptState::Scope scope(scriptState()); ExceptionState exceptionState(ExceptionState::ConstructionContext, "property", "interface", scriptState()->context()->Global(), isolate()); auto strategy = MockStrategy::create(); Checkpoint checkpoint; StringStream* stream = construct(strategy); EXPECT_EQ(ReadableStream::Readable, stream->stateInternal()); { InSequence s; EXPECT_CALL(checkpoint, Call(0)); EXPECT_CALL(*strategy, size(String("hello"), stream)).WillOnce(Return(1)); EXPECT_CALL(*strategy, shouldApplyBackpressure(1, stream)).WillOnce(Return(false)); EXPECT_CALL(checkpoint, Call(1)); EXPECT_CALL(checkpoint, Call(2)); EXPECT_CALL(*strategy, size(String("world"), stream)).WillOnce(Return(2)); EXPECT_CALL(*strategy, shouldApplyBackpressure(3, stream)).WillOnce(Return(true)); EXPECT_CALL(checkpoint, Call(3)); } checkpoint.Call(0); bool result = stream->enqueue("hello"); checkpoint.Call(1); EXPECT_TRUE(result); checkpoint.Call(2); result = stream->enqueue("world"); checkpoint.Call(3); EXPECT_FALSE(result); stream->error(DOMException::create(AbortError, "done")); }
TEST_F(ReadableStreamTest, StartFail) { ScriptState::Scope scope(scriptState()); ExceptionState exceptionState(ExceptionState::ConstructionContext, "property", "interface", scriptState()->context()->Global(), isolate()); StringStream* stream = new StringStream(m_underlyingSource); EXPECT_FALSE(exceptionState.hadException()); EXPECT_FALSE(stream->isStarted()); EXPECT_FALSE(stream->isDraining()); EXPECT_FALSE(stream->isPulling()); EXPECT_EQ(stream->stateInternal(), ReadableStream::Readable); stream->error(DOMException::create(NotFoundError)); EXPECT_FALSE(stream->isStarted()); EXPECT_FALSE(stream->isDraining()); EXPECT_FALSE(stream->isPulling()); EXPECT_EQ(stream->stateInternal(), ReadableStream::Errored); }
TEST_F(ReadableStreamTest, CancelWhenErrored) { ScriptState::Scope scope(scriptState()); ExceptionState exceptionState(ExceptionState::ConstructionContext, "property", "interface", scriptState()->context()->Global(), isolate()); StringStream* stream = construct(); String onFulfilled, onRejected; stream->error(DOMException::create(NotFoundError, "error")); EXPECT_EQ(ReadableStream::Errored, stream->stateInternal()); EXPECT_FALSE(stream->isDisturbed()); ScriptPromise promise = stream->cancel(scriptState(), ScriptValue()); EXPECT_TRUE(stream->isDisturbed()); EXPECT_EQ(ReadableStream::Errored, stream->stateInternal()); promise.then(createCaptor(&onFulfilled), createCaptor(&onRejected)); EXPECT_TRUE(onFulfilled.isNull()); EXPECT_TRUE(onRejected.isNull()); isolate()->RunMicrotasks(); EXPECT_TRUE(onFulfilled.isNull()); EXPECT_EQ("NotFoundError: error", onRejected); }
TEST_F(ReadableStreamTest, BackpressureOnReading) { ScriptState::Scope scope(scriptState()); ExceptionState exceptionState(ExceptionState::ConstructionContext, "property", "interface", scriptState()->context()->Global(), isolate()); auto strategy = MockStrategy::create(); Checkpoint checkpoint; StringStream* stream = construct(strategy); EXPECT_EQ(ReadableStream::Readable, stream->stateInternal()); { InSequence s; EXPECT_CALL(*strategy, size(String("hello"), stream)).WillOnce(Return(2)); EXPECT_CALL(*strategy, shouldApplyBackpressure(2, stream)).WillOnce(Return(false)); EXPECT_CALL(*strategy, size(String("world"), stream)).WillOnce(Return(3)); EXPECT_CALL(*strategy, shouldApplyBackpressure(5, stream)).WillOnce(Return(false)); EXPECT_CALL(checkpoint, Call(0)); EXPECT_CALL(*strategy, shouldApplyBackpressure(3, stream)).WillOnce(Return(false)); EXPECT_CALL(*m_underlyingSource, pullSource()).Times(1); EXPECT_CALL(checkpoint, Call(1)); // shouldApplyBackpressure and pullSource are not called because the // stream is pulling. EXPECT_CALL(checkpoint, Call(2)); EXPECT_CALL(*strategy, size(String("foo"), stream)).WillOnce(Return(4)); EXPECT_CALL(*strategy, shouldApplyBackpressure(4, stream)).WillOnce(Return(true)); EXPECT_CALL(*strategy, size(String("bar"), stream)).WillOnce(Return(5)); EXPECT_CALL(*strategy, shouldApplyBackpressure(9, stream)).WillOnce(Return(true)); EXPECT_CALL(checkpoint, Call(3)); EXPECT_CALL(*strategy, shouldApplyBackpressure(5, stream)).WillOnce(Return(true)); EXPECT_CALL(checkpoint, Call(4)); } stream->enqueue("hello"); stream->enqueue("world"); checkpoint.Call(0); stream->read(scriptState()); checkpoint.Call(1); stream->read(scriptState()); checkpoint.Call(2); stream->enqueue("foo"); stream->enqueue("bar"); checkpoint.Call(3); stream->read(scriptState()); checkpoint.Call(4); stream->error(DOMException::create(AbortError, "done")); }