Index: Source/core/streams/ExclusiveStreamReaderTest.cpp |
diff --git a/Source/core/streams/ExclusiveStreamReaderTest.cpp b/Source/core/streams/ExclusiveStreamReaderTest.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..491baf8e99bcc30130bd2a949a483e8cd6a87513 |
--- /dev/null |
+++ b/Source/core/streams/ExclusiveStreamReaderTest.cpp |
@@ -0,0 +1,434 @@ |
+// Copyright 2015 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 "config.h" |
+#include "core/streams/ExclusiveStreamReader.h" |
+ |
+#include "bindings/core/v8/ExceptionState.h" |
+#include "bindings/core/v8/ScriptState.h" |
+#include "bindings/core/v8/ToV8.h" |
+#include "bindings/core/v8/V8ThrowException.h" |
+#include "core/dom/DOMException.h" |
+#include "core/dom/Document.h" |
+#include "core/dom/ExceptionCode.h" |
+#include "core/streams/ReadableStream.h" |
+#include "core/streams/ReadableStreamImpl.h" |
+#include "core/streams/UnderlyingSource.h" |
+#include "core/testing/DummyPageHolder.h" |
+#include <gtest/gtest.h> |
+ |
+namespace blink { |
+ |
+using StringStream = ReadableStreamImpl<ReadableStreamChunkTypeTraits<String>>; |
+ |
+namespace { |
+ |
+class StringCapturingFunction final : public ScriptFunction { |
+public: |
+ static v8::Handle<v8::Function> createFunction(ScriptState* scriptState, String* value) |
+ { |
+ StringCapturingFunction* self = new StringCapturingFunction(scriptState, value); |
+ return self->bindToV8Function(); |
+ } |
+ |
+private: |
+ StringCapturingFunction(ScriptState* scriptState, String* value) |
+ : ScriptFunction(scriptState) |
+ , m_value(value) |
+ { |
+ } |
+ |
+ ScriptValue call(ScriptValue value) override |
+ { |
+ ASSERT(!value.isEmpty()); |
+ *m_value = toCoreString(value.v8Value()->ToString(scriptState()->isolate())); |
+ return value; |
+ } |
+ |
+ String* m_value; |
+}; |
+ |
+class NoopUnderlyingSource final : public GarbageCollectedFinalized<NoopUnderlyingSource>, public UnderlyingSource { |
+ USING_GARBAGE_COLLECTED_MIXIN(NoopUnderlyingSource); |
+public: |
+ ~NoopUnderlyingSource() override { } |
+ |
+ void pullSource() override { } |
+ ScriptPromise cancelSource(ScriptState* scriptState, ScriptValue reason) { return ScriptPromise::cast(scriptState, reason); } |
+ void trace(Visitor* visitor) override { UnderlyingSource::trace(visitor); } |
+}; |
+ |
+class PermissiveStrategy final : public StringStream::Strategy { |
+public: |
+ bool shouldApplyBackpressure(size_t, ReadableStream*) override { return false; } |
+}; |
+ |
+class ExclusiveStreamReaderTest : public ::testing::Test { |
+public: |
+ ExclusiveStreamReaderTest() |
+ : m_page(DummyPageHolder::create(IntSize(1, 1))) |
+ , m_scope(scriptState()) |
+ , m_exceptionState(ExceptionState::ConstructionContext, "property", "interface", scriptState()->context()->Global(), isolate()) |
+ , m_stream(new StringStream(scriptState()->executionContext(), new NoopUnderlyingSource, new PermissiveStrategy)) |
+ { |
+ m_stream->didSourceStart(); |
+ } |
+ |
+ ~ExclusiveStreamReaderTest() |
+ { |
+ // We need to call |error| in order to make |
+ // ActiveDOMObject::hasPendingActivity return false. |
+ m_stream->error(DOMException::create(AbortError, "done")); |
+ } |
+ |
+ ScriptState* scriptState() { return ScriptState::forMainWorld(m_page->document().frame()); } |
+ v8::Isolate* isolate() { return scriptState()->isolate(); } |
+ |
+ v8::Handle<v8::Function> createCaptor(String* value) |
+ { |
+ return StringCapturingFunction::createFunction(scriptState(), value); |
+ } |
+ |
+ OwnPtr<DummyPageHolder> m_page; |
+ ScriptState::Scope m_scope; |
+ ExceptionState m_exceptionState; |
+ Persistent<StringStream> m_stream; |
+}; |
+ |
+TEST_F(ExclusiveStreamReaderTest, Construct) |
+{ |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ EXPECT_TRUE(reader->isActive()); |
+} |
+ |
+TEST_F(ExclusiveStreamReaderTest, Release) |
+{ |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ EXPECT_TRUE(reader->isActive()); |
+ reader->releaseLock(); |
+ EXPECT_FALSE(reader->isActive()); |
+ |
+ ExclusiveStreamReader* another = new ExclusiveStreamReader(m_stream); |
+ EXPECT_TRUE(another->isActive()); |
+ EXPECT_FALSE(reader->isActive()); |
+ reader->releaseLock(); |
+ EXPECT_TRUE(another->isActive()); |
+ EXPECT_FALSE(reader->isActive()); |
+} |
+ |
+TEST_F(ExclusiveStreamReaderTest, MaskState) |
+{ |
+ m_stream->enqueue("hello"); |
+ EXPECT_EQ("readable", m_stream->stateString()); |
+ |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ EXPECT_EQ("waiting", m_stream->stateString()); |
+ EXPECT_EQ("readable", reader->state()); |
+ |
+ reader->releaseLock(); |
+ EXPECT_EQ("readable", m_stream->stateString()); |
+ EXPECT_EQ("closed", reader->state()); |
+ |
+ ExclusiveStreamReader* another = new ExclusiveStreamReader(m_stream); |
+ EXPECT_EQ("waiting", m_stream->stateString()); |
+ EXPECT_EQ("closed", reader->state()); |
+ EXPECT_EQ("readable", another->state()); |
+} |
+ |
+TEST_F(ExclusiveStreamReaderTest, MaskReady) |
+{ |
+ m_stream->enqueue("hello"); |
+ isolate()->RunMicrotasks(); |
+ |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ { |
+ String s1, s2; |
+ reader->ready(scriptState()).then(createCaptor(&s1)); |
+ m_stream->ready(scriptState()).then(createCaptor(&s2)); |
+ isolate()->RunMicrotasks(); |
+ EXPECT_EQ("undefined", s1); |
+ EXPECT_TRUE(s2.isNull()); |
+ |
+ reader->releaseLock(); |
+ isolate()->RunMicrotasks(); |
+ EXPECT_EQ("undefined", s2); |
+ } |
+ |
+ { |
+ String s1, s2; |
+ reader->ready(scriptState()).then(createCaptor(&s1)); |
+ m_stream->ready(scriptState()).then(createCaptor(&s2)); |
+ isolate()->RunMicrotasks(); |
+ EXPECT_EQ("undefined", s1); |
+ EXPECT_EQ("undefined", s2); |
+ } |
+ |
+ ExclusiveStreamReader* another = new ExclusiveStreamReader(m_stream); |
+ { |
+ String s1, s2, s3; |
+ reader->ready(scriptState()).then(createCaptor(&s1)); |
+ m_stream->ready(scriptState()).then(createCaptor(&s2)); |
+ another->ready(scriptState()).then(createCaptor(&s3)); |
+ isolate()->RunMicrotasks(); |
+ EXPECT_EQ("undefined", s1); |
+ EXPECT_TRUE(s2.isNull()); |
+ EXPECT_EQ("undefined", s3); |
+ |
+ // We need to call here to ensure all promises having captors are |
+ // resolved or rejected. |
+ m_stream->error(DOMException::create(AbortError, "done")); |
+ isolate()->RunMicrotasks(); |
+ } |
+} |
+ |
+TEST_F(ExclusiveStreamReaderTest, ReaderRead) |
+{ |
+ m_stream->enqueue("hello"); |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ |
+ EXPECT_EQ(ReadableStream::Readable, m_stream->stateInternal()); |
+ ScriptValue value = reader->read(scriptState(), m_exceptionState); |
+ |
+ EXPECT_FALSE(m_exceptionState.hadException()); |
+ String stringValue; |
+ EXPECT_TRUE(value.toString(stringValue)); |
+ EXPECT_EQ("hello", stringValue); |
+} |
+ |
+TEST_F(ExclusiveStreamReaderTest, StreamReadShouldFailWhenLocked) |
+{ |
+ m_stream->enqueue("hello"); |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ EXPECT_TRUE(reader->isActive()); |
+ |
+ EXPECT_EQ(ReadableStream::Readable, m_stream->stateInternal()); |
+ m_stream->read(scriptState(), m_exceptionState); |
+ |
+ EXPECT_TRUE(m_exceptionState.hadException()); |
+ EXPECT_EQ(ReadableStream::Readable, m_stream->stateInternal()); |
+} |
+ |
+TEST_F(ExclusiveStreamReaderTest, ReaderReadShouldFailWhenNotLocked) |
+{ |
+ m_stream->enqueue("hello"); |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ reader->releaseLock(); |
+ EXPECT_FALSE(reader->isActive()); |
+ |
+ EXPECT_EQ(ReadableStream::Readable, m_stream->stateInternal()); |
+ reader->read(scriptState(), m_exceptionState); |
+ |
+ EXPECT_TRUE(m_exceptionState.hadException()); |
+ EXPECT_EQ(ReadableStream::Readable, m_stream->stateInternal()); |
+} |
+ |
+TEST_F(ExclusiveStreamReaderTest, ClosedReader) |
+{ |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ |
+ m_stream->close(); |
+ |
+ EXPECT_EQ("closed", m_stream->stateString()); |
+ EXPECT_EQ("closed", reader->state()); |
+ EXPECT_FALSE(reader->isActive()); |
+ |
+ String onClosedFulfilled, onClosedRejected; |
+ String onReadyFulfilled, onReadyRejected; |
+ isolate()->RunMicrotasks(); |
+ reader->closed(scriptState()).then(createCaptor(&onClosedFulfilled), createCaptor(&onClosedRejected)); |
+ reader->ready(scriptState()).then(createCaptor(&onReadyFulfilled), createCaptor(&onReadyRejected)); |
+ EXPECT_TRUE(onClosedFulfilled.isNull()); |
+ EXPECT_TRUE(onClosedRejected.isNull()); |
+ EXPECT_TRUE(onReadyFulfilled.isNull()); |
+ EXPECT_TRUE(onReadyRejected.isNull()); |
+ |
+ isolate()->RunMicrotasks(); |
+ EXPECT_EQ("undefined", onClosedFulfilled); |
+ EXPECT_TRUE(onClosedRejected.isNull()); |
+ EXPECT_EQ("undefined", onReadyFulfilled); |
+ EXPECT_TRUE(onReadyRejected.isNull()); |
+} |
+ |
+TEST_F(ExclusiveStreamReaderTest, ErroredReader) |
+{ |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ |
+ m_stream->error(DOMException::create(SyntaxError, "some error")); |
+ |
+ EXPECT_EQ("errored", m_stream->stateString()); |
+ EXPECT_EQ("errored", reader->state()); |
+ EXPECT_FALSE(reader->isActive()); |
+ |
+ String onClosedFulfilled, onClosedRejected; |
+ String onReadyFulfilled, onReadyRejected; |
+ reader->closed(scriptState()).then(createCaptor(&onClosedFulfilled), createCaptor(&onClosedRejected)); |
+ reader->ready(scriptState()).then(createCaptor(&onReadyFulfilled), createCaptor(&onReadyRejected)); |
+ EXPECT_TRUE(onClosedFulfilled.isNull()); |
+ EXPECT_TRUE(onClosedRejected.isNull()); |
+ EXPECT_TRUE(onReadyFulfilled.isNull()); |
+ EXPECT_TRUE(onReadyRejected.isNull()); |
+ |
+ isolate()->RunMicrotasks(); |
+ EXPECT_TRUE(onClosedFulfilled.isNull()); |
+ EXPECT_EQ("SyntaxError: some error", onClosedRejected); |
+ EXPECT_EQ("undefined", onReadyFulfilled); |
+ EXPECT_TRUE(onReadyRejected.isNull()); |
+} |
+ |
+TEST_F(ExclusiveStreamReaderTest, ReadyPromiseShouldNotBeResolvedWhenLocked) |
+{ |
+ String s; |
+ ScriptPromise ready = m_stream->ready(scriptState()); |
+ ready.then(createCaptor(&s)); |
+ isolate()->RunMicrotasks(); |
+ EXPECT_TRUE(s.isNull()); |
+ |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ EXPECT_TRUE(reader->isActive()); |
+ EXPECT_NE(ready, m_stream->ready(scriptState())); |
+ |
+ isolate()->RunMicrotasks(); |
+ EXPECT_TRUE(s.isNull()); |
+ |
+ // We need to call here to ensure all promises having captors are resolved |
+ // or rejected. |
+ m_stream->error(DOMException::create(AbortError, "done")); |
+ isolate()->RunMicrotasks(); |
+} |
+ |
+TEST_F(ExclusiveStreamReaderTest, ReaderShouldBeReleasedWhenClosed) |
+{ |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ EXPECT_TRUE(reader->isActive()); |
+ m_stream->close(); |
+ EXPECT_FALSE(reader->isActive()); |
+} |
+ |
+TEST_F(ExclusiveStreamReaderTest, ReaderShouldBeReleasedWhenCanceled) |
+{ |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ EXPECT_TRUE(reader->isActive()); |
+ reader->cancel(scriptState(), ScriptValue(scriptState(), v8::Undefined(isolate()))); |
+ EXPECT_FALSE(reader->isActive()); |
+} |
+ |
+TEST_F(ExclusiveStreamReaderTest, ReaderShouldBeReleasedWhenErrored) |
+{ |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ EXPECT_TRUE(reader->isActive()); |
+ m_stream->error(DOMException::create(SyntaxError, "some error")); |
+ EXPECT_FALSE(reader->isActive()); |
+} |
+ |
+TEST_F(ExclusiveStreamReaderTest, StreamCancelShouldFailWhenLocked) |
+{ |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ EXPECT_TRUE(reader->isActive()); |
+ ScriptPromise p = m_stream->cancel(scriptState(), ScriptValue(scriptState(), v8::Undefined(isolate()))); |
+ EXPECT_EQ(ReadableStream::Waiting, m_stream->stateInternal()); |
+ String onFulfilled, onRejected; |
+ p.then(createCaptor(&onFulfilled), createCaptor(&onRejected)); |
+ |
+ EXPECT_TRUE(onFulfilled.isNull()); |
+ EXPECT_TRUE(onRejected.isNull()); |
+ isolate()->RunMicrotasks(); |
+ EXPECT_TRUE(onFulfilled.isNull()); |
+ EXPECT_EQ("TypeError: this stream is locked to an ExclusiveStreamReader", onRejected); |
+} |
+ |
+TEST_F(ExclusiveStreamReaderTest, ReaderCancelShouldNotWorkWhenNotActive) |
+{ |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ reader->releaseLock(); |
+ EXPECT_FALSE(reader->isActive()); |
+ |
+ ScriptPromise p = reader->cancel(scriptState(), ScriptValue(scriptState(), v8::Undefined(isolate()))); |
+ EXPECT_EQ(ReadableStream::Waiting, m_stream->stateInternal()); |
+ String onFulfilled, onRejected; |
+ p.then(createCaptor(&onFulfilled), createCaptor(&onRejected)); |
+ |
+ EXPECT_TRUE(onFulfilled.isNull()); |
+ EXPECT_TRUE(onRejected.isNull()); |
+ isolate()->RunMicrotasks(); |
+ EXPECT_EQ("undefined", onFulfilled); |
+ EXPECT_TRUE(onRejected.isNull()); |
+} |
+ |
+TEST_F(ExclusiveStreamReaderTest, ReadyShouldNotBeResolvedWhileLocked) |
+{ |
+ String onFulfilled; |
+ m_stream->ready(scriptState()).then(createCaptor(&onFulfilled)); |
+ |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ |
+ m_stream->enqueue("hello"); |
+ m_stream->enqueue("world"); |
+ |
+ ASSERT_EQ("readable", reader->state()); |
+ reader->read(scriptState(), m_exceptionState); |
+ ASSERT_EQ("readable", reader->state()); |
+ reader->read(scriptState(), m_exceptionState); |
+ ASSERT_EQ("waiting", reader->state()); |
+ |
+ isolate()->RunMicrotasks(); |
+ EXPECT_TRUE(onFulfilled.isNull()); |
+ |
+ // We need to call here to ensure all promises having captors are resolved |
+ // or rejected. |
+ m_stream->error(DOMException::create(AbortError, "done")); |
+ isolate()->RunMicrotasks(); |
+} |
+ |
+TEST_F(ExclusiveStreamReaderTest, ReadyShouldNotBeResolvedWhenReleasedIfNotReady) |
+{ |
+ String onFulfilled; |
+ m_stream->ready(scriptState()).then(createCaptor(&onFulfilled)); |
+ |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ |
+ m_stream->enqueue("hello"); |
+ m_stream->enqueue("world"); |
+ |
+ ASSERT_EQ("readable", reader->state()); |
+ reader->read(scriptState(), m_exceptionState); |
+ ASSERT_EQ("readable", reader->state()); |
+ reader->read(scriptState(), m_exceptionState); |
+ ASSERT_EQ("waiting", reader->state()); |
+ |
+ reader->releaseLock(); |
+ |
+ isolate()->RunMicrotasks(); |
+ EXPECT_TRUE(onFulfilled.isNull()); |
+ |
+ // We need to call here to ensure all promises having captors are resolved |
+ // or rejected. |
+ m_stream->error(DOMException::create(AbortError, "done")); |
+ isolate()->RunMicrotasks(); |
+} |
+ |
+TEST_F(ExclusiveStreamReaderTest, ReadyShouldBeResolvedWhenReleasedIfReady) |
+{ |
+ String onFulfilled; |
+ m_stream->ready(scriptState()).then(createCaptor(&onFulfilled)); |
+ |
+ ExclusiveStreamReader* reader = new ExclusiveStreamReader(m_stream); |
+ |
+ m_stream->enqueue("hello"); |
+ m_stream->enqueue("world"); |
+ |
+ ASSERT_EQ("readable", reader->state()); |
+ reader->read(scriptState(), m_exceptionState); |
+ ASSERT_EQ("readable", reader->state()); |
+ |
+ isolate()->RunMicrotasks(); |
+ reader->releaseLock(); |
+ EXPECT_TRUE(onFulfilled.isNull()); |
+ |
+ isolate()->RunMicrotasks(); |
+ EXPECT_EQ("undefined", onFulfilled); |
+} |
+ |
+} // namespace |
+ |
+} // namespace blink |