Index: third_party/WebKit/Source/bindings/core/v8/ReadableStreamOperationsTest.cpp |
diff --git a/third_party/WebKit/Source/bindings/core/v8/ReadableStreamOperationsTest.cpp b/third_party/WebKit/Source/bindings/core/v8/ReadableStreamOperationsTest.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..db2fcb48a0bd63bc07c8a384faf3a73fb48a1402 |
--- /dev/null |
+++ b/third_party/WebKit/Source/bindings/core/v8/ReadableStreamOperationsTest.cpp |
@@ -0,0 +1,307 @@ |
+// 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 "bindings/core/v8/ReadableStreamOperations.h" |
+ |
+#include "bindings/core/v8/ExceptionState.h" |
+#include "bindings/core/v8/ScriptFunction.h" |
+#include "bindings/core/v8/ScriptState.h" |
+#include "bindings/core/v8/V8Binding.h" |
+#include "bindings/core/v8/V8IteratorResultValue.h" |
+#include "core/testing/DummyPageHolder.h" |
+#include "platform/heap/Handle.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include <v8.h> |
+ |
+namespace blink { |
+ |
+namespace { |
+ |
+class NotReached : public ScriptFunction { |
+public: |
+ static v8::Local<v8::Function> createFunction(ScriptState* scriptState) |
+ { |
+ NotReached* self = new NotReached(scriptState); |
+ return self->bindToV8Function(); |
+ } |
+ |
+private: |
+ explicit NotReached(ScriptState* scriptState) |
+ : ScriptFunction(scriptState) |
+ { |
+ } |
+ |
+ ScriptValue call(ScriptValue) override; |
+}; |
+ |
+ScriptValue NotReached::call(ScriptValue) |
+{ |
+ EXPECT_TRUE(false) << "'Unreachable' code was reached"; |
+ return ScriptValue(); |
+} |
+ |
+class Iteration final : public GarbageCollectedFinalized<Iteration> { |
+public: |
+ Iteration() |
+ : m_isSet(false) |
+ , m_isDone(false) |
+ , m_isValid(true) {} |
+ |
+ void set(ScriptValue v) |
+ { |
+ ASSERT(!v.isEmpty()); |
+ m_isSet = true; |
+ v8::Local<v8::Value> value; |
+ if (!v8IteratorUnpack(v.scriptState(), v.v8Value(), &m_isDone).ToLocal(&value)) { |
+ m_isValid = false; |
+ return; |
+ } |
+ m_value = toCoreString(value->ToString()); |
+ } |
+ |
+ bool isSet() const { return m_isSet; } |
+ bool isDone() const { return m_isDone; } |
+ bool isValid() const { return m_isValid; } |
+ const String& value() const { return m_value; } |
+ |
+ DEFINE_INLINE_TRACE() {} |
+ |
+private: |
+ bool m_isSet; |
+ bool m_isDone; |
+ bool m_isValid; |
+ String m_value; |
+}; |
+ |
+class Function : public ScriptFunction { |
+public: |
+ static v8::Local<v8::Function> createFunction(ScriptState* scriptState, Iteration* iteration) |
+ { |
+ Function* self = new Function(scriptState, iteration); |
+ return self->bindToV8Function(); |
+ } |
+ |
+ DEFINE_INLINE_VIRTUAL_TRACE() |
+ { |
+ visitor->trace(m_iteration); |
+ ScriptFunction::trace(visitor); |
+ } |
+ |
+private: |
+ Function(ScriptState* scriptState, Iteration* iteration) |
+ : ScriptFunction(scriptState) |
+ , m_iteration(iteration) |
+ { |
+ } |
+ |
+ ScriptValue call(ScriptValue value) override |
+ { |
+ m_iteration->set(value); |
+ return value; |
+ } |
+ |
+ Member<Iteration> m_iteration; |
+}; |
+ |
+class ReadableStreamOperationsTest : public ::testing::Test { |
+public: |
+ ReadableStreamOperationsTest() |
+ : m_pageHolder(DummyPageHolder::create()) |
+ { |
+ } |
+ |
+ ~ReadableStreamOperationsTest() override |
+ { |
+ ScriptState::Scope scope(scriptState()); |
+ // Execute all pending microtasks |
+ isolate()->RunMicrotasks(); |
+ } |
+ |
+ OwnPtr<DummyPageHolder> m_pageHolder; |
+ ScriptState* scriptState() const { return ScriptState::forMainWorld(&m_pageHolder->frame()); } |
+ v8::Isolate* isolate() const { return scriptState()->isolate(); } |
+ |
+ v8::MaybeLocal<v8::Value> eval(const char* s) |
+ { |
+ v8::MaybeLocal<v8::String> source = v8::String::NewFromUtf8(isolate(), s, v8::String::kNormalString); |
bashi
2015/12/04 06:48:53
nit: It would be nice to combine empty check and v
yhirano
2015/12/04 06:51:21
I did essentially the same in PS9. Is it OK?
|
+ if (source.IsEmpty()) { |
+ ADD_FAILURE(); |
+ return v8::MaybeLocal<v8::Value>(); |
+ } |
+ v8::MaybeLocal<v8::Script> script = v8::Script::Compile(scriptState()->context(), source.ToLocalChecked()); |
+ if (script.IsEmpty()) { |
+ ADD_FAILURE() << "Compilation fails"; |
+ return v8::MaybeLocal<v8::Value>(); |
+ } |
+ return script.ToLocalChecked()->Run(scriptState()->context()); |
+ } |
+ v8::MaybeLocal<v8::Value> evalNoThrow(const char* s) |
+ { |
+ v8::TryCatch block(isolate()); |
+ v8::MaybeLocal<v8::Value> r = eval(s); |
+ if (block.HasCaught()) |
+ ADD_FAILURE() << toCoreString(block.Exception()->ToString(isolate())).utf8().data(); |
+ return r; |
+ } |
+}; |
+ |
+TEST_F(ReadableStreamOperationsTest, IsReadableStream) |
+{ |
+ ScriptState::Scope scope(scriptState()); |
+ |
+ { |
+ v8::TryCatch block(isolate()); |
+ EXPECT_FALSE(ReadableStreamOperations::isReadableStream(scriptState(), v8::Undefined(isolate()))); |
+ EXPECT_FALSE(block.HasCaught()); |
+ } |
+ { |
+ v8::TryCatch block(isolate()); |
+ EXPECT_FALSE(ReadableStreamOperations::isReadableStream(scriptState(), v8::Null(isolate()))); |
+ EXPECT_FALSE(block.HasCaught()); |
+ } |
+ { |
+ v8::TryCatch block(isolate()); |
+ EXPECT_FALSE(ReadableStreamOperations::isReadableStream(scriptState(), v8::Object::New(isolate()))); |
+ EXPECT_FALSE(block.HasCaught()); |
+ } |
+ { |
+ v8::Local<v8::Value> stream; |
+ ASSERT_TRUE(evalNoThrow("new ReadableStream()").ToLocal(&stream)); |
+ |
+ v8::TryCatch block(isolate()); |
+ EXPECT_TRUE(ReadableStreamOperations::isReadableStream(scriptState(), stream)); |
+ EXPECT_FALSE(block.HasCaught()); |
+ } |
+} |
+ |
+TEST_F(ReadableStreamOperationsTest, IsReadableStreamReaderInvalid) |
+{ |
+ ScriptState::Scope scope(scriptState()); |
+ |
+ { |
+ v8::TryCatch block(isolate()); |
+ EXPECT_FALSE(ReadableStreamOperations::isReadableStreamReader(scriptState(), v8::Undefined(isolate()))); |
+ EXPECT_FALSE(block.HasCaught()); |
+ } |
+ { |
+ v8::TryCatch block(isolate()); |
+ EXPECT_FALSE(ReadableStreamOperations::isReadableStreamReader(scriptState(), v8::Null(isolate()))); |
+ EXPECT_FALSE(block.HasCaught()); |
+ } |
+ { |
+ v8::TryCatch block(isolate()); |
+ EXPECT_FALSE(ReadableStreamOperations::isReadableStreamReader(scriptState(), v8::Object::New(isolate()))); |
+ EXPECT_FALSE(block.HasCaught()); |
+ } |
+ { |
+ v8::Local<v8::Value> stream; |
+ ASSERT_TRUE(evalNoThrow("new ReadableStream()").ToLocal(&stream)); |
+ |
+ v8::TryCatch block(isolate()); |
+ EXPECT_FALSE(ReadableStreamOperations::isReadableStreamReader(scriptState(), stream)); |
+ EXPECT_FALSE(block.HasCaught()); |
+ } |
+} |
+ |
+TEST_F(ReadableStreamOperationsTest, GetReader) |
+{ |
+ ScriptState::Scope scope(scriptState()); |
+ v8::Local<v8::Value> stream; |
+ ASSERT_TRUE(evalNoThrow("new ReadableStream()").ToLocal(&stream)); |
+ |
+ { |
+ v8::TryCatch block(isolate()); |
+ EXPECT_FALSE(ReadableStreamOperations::isLocked(scriptState(), stream)); |
+ ScriptValue reader; |
+ { |
+ TrackExceptionState es; |
+ reader = ReadableStreamOperations::getReader(scriptState(), stream, es); |
+ ASSERT_FALSE(es.hadException()); |
+ } |
+ EXPECT_TRUE(ReadableStreamOperations::isLocked(scriptState(), stream)); |
+ ASSERT_FALSE(reader.isEmpty()); |
+ EXPECT_FALSE(block.HasCaught()); |
+ |
+ EXPECT_FALSE(ReadableStreamOperations::isReadableStream(scriptState(), reader.v8Value())); |
+ EXPECT_TRUE(ReadableStreamOperations::isReadableStreamReader(scriptState(), reader.v8Value())); |
+ EXPECT_FALSE(block.HasCaught()); |
+ } |
+ |
+ { |
+ // Already locked! |
+ v8::TryCatch block(isolate()); |
+ ScriptValue reader; |
+ { |
+ TrackExceptionState es; |
+ reader = ReadableStreamOperations::getReader(scriptState(), stream, es); |
+ ASSERT_TRUE(es.hadException()); |
+ } |
+ ASSERT_TRUE(reader.isEmpty()); |
+ EXPECT_FALSE(block.HasCaught()); |
+ } |
+} |
+ |
+TEST_F(ReadableStreamOperationsTest, IsDisturbed) |
+{ |
+ ScriptState::Scope scope(scriptState()); |
+ v8::Local<v8::Value> stream; |
+ ASSERT_TRUE(evalNoThrow("stream = new ReadableStream()").ToLocal(&stream)); |
+ |
+ v8::TryCatch block(isolate()); |
+ EXPECT_FALSE(ReadableStreamOperations::isDisturbed(scriptState(), stream)); |
+ |
+ ASSERT_FALSE(evalNoThrow("stream.cancel()").IsEmpty()); |
+ |
+ EXPECT_TRUE(ReadableStreamOperations::isDisturbed(scriptState(), stream)); |
+ EXPECT_FALSE(block.HasCaught()); |
+} |
+ |
+TEST_F(ReadableStreamOperationsTest, Read) |
+{ |
+ ScriptState::Scope scope(scriptState()); |
+ v8::Local<v8::Value> reader; |
+ ASSERT_TRUE(evalNoThrow( |
+ "var controller;" |
+ "function start(c) { controller = c; }" |
+ "new ReadableStream({start}).getReader()").ToLocal(&reader)); |
+ ASSERT_TRUE(ReadableStreamOperations::isReadableStreamReader(scriptState(), reader)); |
+ |
+ Iteration* it1 = new Iteration(); |
+ Iteration* it2 = new Iteration(); |
+ v8::TryCatch block(isolate()); |
+ ReadableStreamOperations::read(scriptState(), reader).then( |
+ Function::createFunction(scriptState(), it1), |
+ NotReached::createFunction(scriptState())); |
+ ReadableStreamOperations::read(scriptState(), reader).then( |
+ Function::createFunction(scriptState(), it2), |
+ NotReached::createFunction(scriptState())); |
+ |
+ isolate()->RunMicrotasks(); |
+ EXPECT_FALSE(it1->isSet()); |
+ EXPECT_FALSE(it2->isSet()); |
+ |
+ ASSERT_FALSE(evalNoThrow("controller.enqueue('hello')").IsEmpty()); |
+ isolate()->RunMicrotasks(); |
+ EXPECT_TRUE(it1->isSet()); |
+ EXPECT_TRUE(it1->isValid()); |
+ EXPECT_FALSE(it1->isDone()); |
+ EXPECT_EQ("hello", it1->value()); |
+ EXPECT_FALSE(it2->isSet()); |
+ |
+ ASSERT_FALSE(evalNoThrow("controller.close()").IsEmpty()); |
+ isolate()->RunMicrotasks(); |
+ EXPECT_TRUE(it1->isSet()); |
+ EXPECT_TRUE(it1->isValid()); |
+ EXPECT_FALSE(it1->isDone()); |
+ EXPECT_EQ("hello", it1->value()); |
+ EXPECT_TRUE(it2->isSet()); |
+ EXPECT_TRUE(it2->isValid()); |
+ EXPECT_TRUE(it2->isDone()); |
+} |
+ |
+} // namespace |
+ |
+} // namespace blink |
+ |