|  | // Copyright 2016 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 "modules/fetch/BytesConsumerForDataConsumerHandle.h" | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "core/testing/DummyPageHolder.h" | 
|  | #include "modules/fetch/BytesConsumer.h" | 
|  | #include "modules/fetch/DataConsumerHandleTestUtil.h" | 
|  | #include "platform/testing/UnitTestHelpers.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace blink { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | using DataConsumerCommand = DataConsumerHandleTestUtil::Command; | 
|  | using Checkpoint = ::testing::StrictMock<::testing::MockFunction<void(int)>>; | 
|  | using ReplayingHandle = DataConsumerHandleTestUtil::ReplayingHandle; | 
|  | using Result = BytesConsumer::Result; | 
|  | using ::testing::ByMove; | 
|  | using ::testing::InSequence; | 
|  | using ::testing::Return; | 
|  |  | 
|  | class BytesConsumerForDataConsumerHandleTest : public ::testing::Test { | 
|  | public: | 
|  | Document* GetDocument() { return &page_->GetDocument(); } | 
|  |  | 
|  | protected: | 
|  | BytesConsumerForDataConsumerHandleTest() : page_(DummyPageHolder::Create()) {} | 
|  | ~BytesConsumerForDataConsumerHandleTest() override { | 
|  | ThreadState::Current()->CollectAllGarbage(); | 
|  | } | 
|  | std::unique_ptr<DummyPageHolder> page_; | 
|  | }; | 
|  |  | 
|  | class MockBytesConsumerClient | 
|  | : public GarbageCollectedFinalized<MockBytesConsumerClient>, | 
|  | public BytesConsumer::Client { | 
|  | USING_GARBAGE_COLLECTED_MIXIN(MockBytesConsumerClient); | 
|  |  | 
|  | public: | 
|  | static MockBytesConsumerClient* Create() { | 
|  | return new ::testing::StrictMock<MockBytesConsumerClient>(); | 
|  | } | 
|  | MOCK_METHOD0(OnStateChange, void()); | 
|  | String DebugName() const override { return "MockBytesConsumerClient"; } | 
|  |  | 
|  | protected: | 
|  | MockBytesConsumerClient() {} | 
|  | }; | 
|  |  | 
|  | class MockDataConsumerHandle final : public WebDataConsumerHandle { | 
|  | public: | 
|  | class MockReaderProxy : public GarbageCollectedFinalized<MockReaderProxy> { | 
|  | public: | 
|  | MOCK_METHOD3(BeginRead, | 
|  | WebDataConsumerHandle::Result(const void**, | 
|  | WebDataConsumerHandle::Flags, | 
|  | size_t*)); | 
|  | MOCK_METHOD1(EndRead, WebDataConsumerHandle::Result(size_t)); | 
|  |  | 
|  | void Trace(blink::Visitor* visitor) {} | 
|  | }; | 
|  |  | 
|  | MockDataConsumerHandle() : proxy_(new MockReaderProxy) {} | 
|  | MockReaderProxy* Proxy() { return proxy_; } | 
|  | const char* DebugName() const override { return "MockDataConsumerHandle"; } | 
|  |  | 
|  | private: | 
|  | class Reader final : public WebDataConsumerHandle::Reader { | 
|  | public: | 
|  | explicit Reader(MockReaderProxy* proxy) : proxy_(proxy) {} | 
|  | Result BeginRead(const void** buffer, | 
|  | Flags flags, | 
|  | size_t* available) override { | 
|  | return proxy_->BeginRead(buffer, flags, available); | 
|  | } | 
|  | Result EndRead(size_t read_size) override { | 
|  | return proxy_->EndRead(read_size); | 
|  | } | 
|  |  | 
|  | private: | 
|  | Persistent<MockReaderProxy> proxy_; | 
|  | }; | 
|  |  | 
|  | std::unique_ptr<WebDataConsumerHandle::Reader> ObtainReader( | 
|  | Client*) override { | 
|  | return std::make_unique<Reader>(proxy_); | 
|  | } | 
|  | Persistent<MockReaderProxy> proxy_; | 
|  | }; | 
|  |  | 
|  | TEST_F(BytesConsumerForDataConsumerHandleTest, Create) { | 
|  | std::unique_ptr<ReplayingHandle> handle = ReplayingHandle::Create(); | 
|  | handle->Add(DataConsumerCommand(DataConsumerCommand::kData, "hello")); | 
|  | handle->Add(DataConsumerCommand(DataConsumerCommand::kDone)); | 
|  | Persistent<BytesConsumer> consumer = | 
|  | new BytesConsumerForDataConsumerHandle(GetDocument(), std::move(handle)); | 
|  | } | 
|  |  | 
|  | TEST_F(BytesConsumerForDataConsumerHandleTest, BecomeReadable) { | 
|  | Checkpoint checkpoint; | 
|  | Persistent<MockBytesConsumerClient> client = | 
|  | MockBytesConsumerClient::Create(); | 
|  |  | 
|  | InSequence s; | 
|  | EXPECT_CALL(checkpoint, Call(1)); | 
|  | EXPECT_CALL(*client, OnStateChange()); | 
|  | EXPECT_CALL(checkpoint, Call(2)); | 
|  |  | 
|  | std::unique_ptr<ReplayingHandle> handle = ReplayingHandle::Create(); | 
|  | handle->Add(DataConsumerCommand(DataConsumerCommand::kData, "hello")); | 
|  | Persistent<BytesConsumer> consumer = | 
|  | new BytesConsumerForDataConsumerHandle(GetDocument(), std::move(handle)); | 
|  | consumer->SetClient(client); | 
|  | EXPECT_EQ(BytesConsumer::PublicState::kReadableOrWaiting, | 
|  | consumer->GetPublicState()); | 
|  |  | 
|  | checkpoint.Call(1); | 
|  | testing::RunPendingTasks(); | 
|  | checkpoint.Call(2); | 
|  | EXPECT_EQ(BytesConsumer::PublicState::kReadableOrWaiting, | 
|  | consumer->GetPublicState()); | 
|  | } | 
|  |  | 
|  | TEST_F(BytesConsumerForDataConsumerHandleTest, BecomeClosed) { | 
|  | Checkpoint checkpoint; | 
|  | Persistent<MockBytesConsumerClient> client = | 
|  | MockBytesConsumerClient::Create(); | 
|  |  | 
|  | InSequence s; | 
|  | EXPECT_CALL(checkpoint, Call(1)); | 
|  | EXPECT_CALL(*client, OnStateChange()); | 
|  | EXPECT_CALL(checkpoint, Call(2)); | 
|  |  | 
|  | std::unique_ptr<ReplayingHandle> handle = ReplayingHandle::Create(); | 
|  | handle->Add(DataConsumerCommand(DataConsumerCommand::kDone)); | 
|  | Persistent<BytesConsumer> consumer = | 
|  | new BytesConsumerForDataConsumerHandle(GetDocument(), std::move(handle)); | 
|  | consumer->SetClient(client); | 
|  | EXPECT_EQ(BytesConsumer::PublicState::kReadableOrWaiting, | 
|  | consumer->GetPublicState()); | 
|  |  | 
|  | checkpoint.Call(1); | 
|  | testing::RunPendingTasks(); | 
|  | checkpoint.Call(2); | 
|  | EXPECT_EQ(BytesConsumer::PublicState::kClosed, consumer->GetPublicState()); | 
|  | } | 
|  |  | 
|  | TEST_F(BytesConsumerForDataConsumerHandleTest, BecomeErrored) { | 
|  | Checkpoint checkpoint; | 
|  | Persistent<MockBytesConsumerClient> client = | 
|  | MockBytesConsumerClient::Create(); | 
|  |  | 
|  | InSequence s; | 
|  | EXPECT_CALL(checkpoint, Call(1)); | 
|  | EXPECT_CALL(*client, OnStateChange()); | 
|  | EXPECT_CALL(checkpoint, Call(2)); | 
|  |  | 
|  | std::unique_ptr<ReplayingHandle> handle = ReplayingHandle::Create(); | 
|  | handle->Add(DataConsumerCommand(DataConsumerCommand::kError)); | 
|  | Persistent<BytesConsumer> consumer = | 
|  | new BytesConsumerForDataConsumerHandle(GetDocument(), std::move(handle)); | 
|  | consumer->SetClient(client); | 
|  | EXPECT_EQ(BytesConsumer::PublicState::kReadableOrWaiting, | 
|  | consumer->GetPublicState()); | 
|  |  | 
|  | checkpoint.Call(1); | 
|  | testing::RunPendingTasks(); | 
|  | checkpoint.Call(2); | 
|  | EXPECT_EQ(BytesConsumer::PublicState::kErrored, consumer->GetPublicState()); | 
|  | } | 
|  |  | 
|  | TEST_F(BytesConsumerForDataConsumerHandleTest, ClearClient) { | 
|  | Checkpoint checkpoint; | 
|  | Persistent<MockBytesConsumerClient> client = | 
|  | MockBytesConsumerClient::Create(); | 
|  |  | 
|  | InSequence s; | 
|  | EXPECT_CALL(checkpoint, Call(1)); | 
|  | EXPECT_CALL(checkpoint, Call(2)); | 
|  |  | 
|  | std::unique_ptr<ReplayingHandle> handle = ReplayingHandle::Create(); | 
|  | handle->Add(DataConsumerCommand(DataConsumerCommand::kError)); | 
|  | Persistent<BytesConsumer> consumer = | 
|  | new BytesConsumerForDataConsumerHandle(GetDocument(), std::move(handle)); | 
|  | consumer->SetClient(client); | 
|  | consumer->ClearClient(); | 
|  |  | 
|  | checkpoint.Call(1); | 
|  | testing::RunPendingTasks(); | 
|  | checkpoint.Call(2); | 
|  | } | 
|  |  | 
|  | TEST_F(BytesConsumerForDataConsumerHandleTest, TwoPhaseReadWhenReadable) { | 
|  | std::unique_ptr<ReplayingHandle> handle = ReplayingHandle::Create(); | 
|  | handle->Add(DataConsumerCommand(DataConsumerCommand::kData, "hello")); | 
|  | Persistent<BytesConsumer> consumer = | 
|  | new BytesConsumerForDataConsumerHandle(GetDocument(), std::move(handle)); | 
|  | consumer->SetClient(MockBytesConsumerClient::Create()); | 
|  |  | 
|  | const char* buffer = nullptr; | 
|  | size_t available = 0; | 
|  | ASSERT_EQ(Result::kOk, consumer->BeginRead(&buffer, &available)); | 
|  | EXPECT_EQ("hello", String(buffer, available)); | 
|  |  | 
|  | ASSERT_EQ(Result::kOk, consumer->EndRead(1)); | 
|  | ASSERT_EQ(Result::kOk, consumer->BeginRead(&buffer, &available)); | 
|  | EXPECT_EQ("ello", String(buffer, available)); | 
|  |  | 
|  | ASSERT_EQ(Result::kOk, consumer->EndRead(4)); | 
|  | ASSERT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available)); | 
|  | } | 
|  |  | 
|  | TEST_F(BytesConsumerForDataConsumerHandleTest, TwoPhaseReadWhenWaiting) { | 
|  | std::unique_ptr<ReplayingHandle> handle = ReplayingHandle::Create(); | 
|  | Persistent<BytesConsumer> consumer = | 
|  | new BytesConsumerForDataConsumerHandle(GetDocument(), std::move(handle)); | 
|  | consumer->SetClient(MockBytesConsumerClient::Create()); | 
|  | const char* buffer = nullptr; | 
|  | size_t available = 0; | 
|  | ASSERT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available)); | 
|  | } | 
|  |  | 
|  | TEST_F(BytesConsumerForDataConsumerHandleTest, TwoPhaseReadWhenClosed) { | 
|  | std::unique_ptr<ReplayingHandle> handle = ReplayingHandle::Create(); | 
|  | handle->Add(DataConsumerCommand(DataConsumerCommand::kDone)); | 
|  | Persistent<BytesConsumer> consumer = | 
|  | new BytesConsumerForDataConsumerHandle(GetDocument(), std::move(handle)); | 
|  | consumer->SetClient(MockBytesConsumerClient::Create()); | 
|  | const char* buffer = nullptr; | 
|  | size_t available = 0; | 
|  | ASSERT_EQ(Result::kDone, consumer->BeginRead(&buffer, &available)); | 
|  | } | 
|  |  | 
|  | TEST_F(BytesConsumerForDataConsumerHandleTest, TwoPhaseReadWhenErrored) { | 
|  | std::unique_ptr<ReplayingHandle> handle = ReplayingHandle::Create(); | 
|  | handle->Add(DataConsumerCommand(DataConsumerCommand::kError)); | 
|  | Persistent<BytesConsumer> consumer = | 
|  | new BytesConsumerForDataConsumerHandle(GetDocument(), std::move(handle)); | 
|  | consumer->SetClient(MockBytesConsumerClient::Create()); | 
|  | const char* buffer = nullptr; | 
|  | size_t available = 0; | 
|  | ASSERT_EQ(Result::kError, consumer->BeginRead(&buffer, &available)); | 
|  | EXPECT_EQ(BytesConsumer::Error("error"), consumer->GetError()); | 
|  | } | 
|  |  | 
|  | TEST_F(BytesConsumerForDataConsumerHandleTest, Cancel) { | 
|  | std::unique_ptr<ReplayingHandle> handle = ReplayingHandle::Create(); | 
|  | Persistent<BytesConsumer> consumer = | 
|  | new BytesConsumerForDataConsumerHandle(GetDocument(), std::move(handle)); | 
|  | consumer->SetClient(MockBytesConsumerClient::Create()); | 
|  | consumer->Cancel(); | 
|  | const char* buffer = nullptr; | 
|  | size_t available = 0; | 
|  | ASSERT_EQ(Result::kDone, consumer->BeginRead(&buffer, &available)); | 
|  | } | 
|  |  | 
|  | TEST_F(BytesConsumerForDataConsumerHandleTest, drainAsBlobDataHandle) { | 
|  | // WebDataConsumerHandle::Reader::drainAsBlobDataHandle should return | 
|  | // nullptr from the second time, but we don't care that here. | 
|  | std::unique_ptr<MockDataConsumerHandle> handle = | 
|  | WTF::WrapUnique(new MockDataConsumerHandle); | 
|  | Persistent<MockDataConsumerHandle::MockReaderProxy> proxy = handle->Proxy(); | 
|  | Persistent<BytesConsumer> consumer = | 
|  | new BytesConsumerForDataConsumerHandle(GetDocument(), std::move(handle)); | 
|  | consumer->SetClient(MockBytesConsumerClient::Create()); | 
|  |  | 
|  | Checkpoint checkpoint; | 
|  | InSequence s; | 
|  |  | 
|  | EXPECT_FALSE(consumer->DrainAsBlobDataHandle( | 
|  | BytesConsumer::BlobSizePolicy::kDisallowBlobWithInvalidSize)); | 
|  | EXPECT_FALSE(consumer->DrainAsBlobDataHandle( | 
|  | BytesConsumer::BlobSizePolicy::kAllowBlobWithInvalidSize)); | 
|  | EXPECT_EQ(BytesConsumer::PublicState::kReadableOrWaiting, | 
|  | consumer->GetPublicState()); | 
|  | } | 
|  |  | 
|  | TEST_F(BytesConsumerForDataConsumerHandleTest, drainAsFormData) { | 
|  | std::unique_ptr<MockDataConsumerHandle> handle = | 
|  | WTF::WrapUnique(new MockDataConsumerHandle); | 
|  | Persistent<MockDataConsumerHandle::MockReaderProxy> proxy = handle->Proxy(); | 
|  | Persistent<BytesConsumer> consumer = | 
|  | new BytesConsumerForDataConsumerHandle(GetDocument(), std::move(handle)); | 
|  | consumer->SetClient(MockBytesConsumerClient::Create()); | 
|  |  | 
|  | Checkpoint checkpoint; | 
|  | InSequence s; | 
|  |  | 
|  | EXPECT_FALSE(consumer->DrainAsFormData()); | 
|  | EXPECT_EQ(BytesConsumer::PublicState::kReadableOrWaiting, | 
|  | consumer->GetPublicState()); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | }  // namespace blink |