Chromium Code Reviews| 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..e489d64202661ac22916cd8c42bbfe7df2eb4724 |
| --- /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/ScriptFunction.h" |
| +#include "bindings/core/v8/ScriptState.h" |
| +#include "bindings/core/v8/V8Binding.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) |
| + { |
| + m_isSet = true; |
| + if (v.isEmpty() || !v.v8Value()->IsObject()) { |
| + m_isValid = false; |
| + return; |
| + } |
| + v8::Local<v8::Object> object = v.v8Value().As<v8::Object>(); |
| + v8::Local<v8::Value> done; |
| + if (!object->Get(v.scriptState()->context(), v8String(v.isolate(), "done")).ToLocal(&done) |
| + || done->IsUndefined()) { |
| + m_isValid = false; |
| + return; |
| + } |
| + m_isDone = done->ToBoolean()->Value(); |
| + |
| + v8::Local<v8::Value> value; |
| + if (!object->Get(v.scriptState()->context(), v8String(v.isolate(), "value")).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); |
| + 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 = ReadableStreamOperations::getReader(scriptState(), stream); |
| + 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 = ReadableStreamOperations::getReader(scriptState(), stream); |
| + ASSERT_TRUE(reader.isEmpty()); |
| + EXPECT_TRUE(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( |
|
domenic
2015/12/02 17:53:03
This is workable but kind of takes a lot of suppor
yhirano
2015/12/03 11:18:29
Unfortunately, we cannot use lambda for the purpos
|
| + 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 |
| + |