Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(286)

Unified Diff: third_party/WebKit/Source/modules/fetch/BytesConsumerTest.cpp

Issue 2269953004: Implment BytesConsumer::tee (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/modules/fetch/BytesConsumerTest.cpp
diff --git a/third_party/WebKit/Source/modules/fetch/BytesConsumerTest.cpp b/third_party/WebKit/Source/modules/fetch/BytesConsumerTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a4ace6897ef72186e982f43b67afd7833047c659
--- /dev/null
+++ b/third_party/WebKit/Source/modules/fetch/BytesConsumerTest.cpp
@@ -0,0 +1,434 @@
+// 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/BytesConsumer.h"
+
+#include "core/testing/DummyPageHolder.h"
+#include "modules/fetch/BytesConsumerTestUtil.h"
+#include "platform/blob/BlobData.h"
+#include "platform/testing/UnitTestHelpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "wtf/RefPtr.h"
+
+namespace blink {
+
+namespace {
+
+using Command = BytesConsumerTestUtil::Command;
+using Result = BytesConsumer::Result;
+using ReplayingBytesConsumer = BytesConsumerTestUtil::ReplayingBytesConsumer;
+
+String toString(const Vector<char>& v)
+{
+ return String(v.data(), v.size());
+}
+
+class TestClient final : public GarbageCollectedFinalized<TestClient>, public BytesConsumer::Client {
+ USING_GARBAGE_COLLECTED_MIXIN(TestClient);
+public:
+ void onStateChange() override { ++m_numOnStateChangeCalled; }
+ int numOnStateChangeCalled() const { return m_numOnStateChangeCalled; }
+
+private:
+ int m_numOnStateChangeCalled = 0;
+};
+
+class BytesConsumerTeeTest : public ::testing::Test {
+public:
+ BytesConsumerTeeTest()
+ : m_page(DummyPageHolder::create())
+ {
+ }
+
+ Document* document() { return &m_page->document(); }
+
+private:
+ std::unique_ptr<DummyPageHolder> m_page;
+};
+
+class FakeBlobBytesConsumer : public BytesConsumer {
+public:
+ explicit FakeBlobBytesConsumer(PassRefPtr<BlobDataHandle> handle) : m_blobHandle(handle) {}
+ ~FakeBlobBytesConsumer() override {}
+
+ Result beginRead(const char** buffer, size_t* available) override
+ {
+ if (m_state == PublicState::Closed)
+ return Result::Done;
+ m_blobHandle = nullptr;
+ m_state = PublicState::Errored;
+ return Result::Error;
+ }
+ Result endRead(size_t readSize) override
+ {
+ if (m_state == PublicState::Closed)
+ return Result::Error;
+ m_blobHandle = nullptr;
+ m_state = PublicState::Errored;
+ return Result::Error;
+ }
+ PassRefPtr<BlobDataHandle> drainAsBlobDataHandle(BlobSizePolicy policy)
+ {
+ if (m_state != PublicState::ReadableOrWaiting)
+ return nullptr;
+ DCHECK(m_blobHandle);
+ if (policy == BlobSizePolicy::DisallowBlobWithInvalidSize && m_blobHandle->size() == UINT64_MAX)
+ return nullptr;
+ m_state = PublicState::Closed;
+ return m_blobHandle.release();
+ }
+
+ void setClient(Client*) override {}
+ void clearClient() override {}
+ void cancel() override {}
+ PublicState getPublicState() const override { return m_state; }
+ Error getError() const override { return Error(); }
+ String debugName() const override { return "FakeBlobBytesConsumer"; }
+
+private:
+ PublicState m_state = PublicState::ReadableOrWaiting;
+ RefPtr<BlobDataHandle> m_blobHandle;
+};
+
+TEST_F(BytesConsumerTeeTest, CreateDone)
+{
+ ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
+ src->add(Command(Command::Done));
+ EXPECT_FALSE(src->isCancelled());
+
+ BytesConsumer* dest1 = nullptr;
+ BytesConsumer* dest2 = nullptr;
+ BytesConsumer::tee(document(), src, &dest1, &dest2);
+
+ auto result1 = (new BytesConsumerTestUtil::Reader(dest1))->run();
+ auto result2 = (new BytesConsumerTestUtil::Reader(dest2))->run();
+
+ EXPECT_EQ(Result::Done, result1.first);
+ EXPECT_TRUE(result1.second.isEmpty());
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
+ EXPECT_EQ(Result::Done, result2.first);
+ EXPECT_TRUE(result2.second.isEmpty());
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest2->getPublicState());
+ EXPECT_FALSE(src->isCancelled());
+
+ // Cancelling does nothing when closed.
+ dest1->cancel();
+ dest2->cancel();
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest2->getPublicState());
+ EXPECT_FALSE(src->isCancelled());
+}
+
+TEST_F(BytesConsumerTeeTest, Read)
+{
+ ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
+
+ src->add(Command(Command::Wait));
+ src->add(Command(Command::Data, "hello, "));
+ src->add(Command(Command::Wait));
+ src->add(Command(Command::Data, "world"));
+ src->add(Command(Command::Wait));
+ src->add(Command(Command::Wait));
+ src->add(Command(Command::Done));
+
+ BytesConsumer* dest1 = nullptr;
+ BytesConsumer* dest2 = nullptr;
+ BytesConsumer::tee(document(), src, &dest1, &dest2);
+
+ EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting, dest1->getPublicState());
+ EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting, dest2->getPublicState());
+
+ auto result1 = (new BytesConsumerTestUtil::Reader(dest1))->run();
+ auto result2 = (new BytesConsumerTestUtil::Reader(dest2))->run();
+
+ EXPECT_EQ(Result::Done, result1.first);
+ EXPECT_EQ("hello, world", toString(result1.second));
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
+ EXPECT_EQ(Result::Done, result2.first);
+ EXPECT_EQ("hello, world", toString(result2.second));
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest2->getPublicState());
+ EXPECT_FALSE(src->isCancelled());
+}
+
+TEST_F(BytesConsumerTeeTest, TwoPhaseRead)
+{
+ ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
+
+ src->add(Command(Command::Wait));
+ src->add(Command(Command::Data, "hello, "));
+ src->add(Command(Command::Wait));
+ src->add(Command(Command::Data, "world"));
+ src->add(Command(Command::Wait));
+ src->add(Command(Command::Wait));
+ src->add(Command(Command::Done));
+
+ BytesConsumer* dest1 = nullptr;
+ BytesConsumer* dest2 = nullptr;
+ BytesConsumer::tee(document(), src, &dest1, &dest2);
+
+ EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting, dest1->getPublicState());
+ EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting, dest2->getPublicState());
+
+ auto result1 = (new BytesConsumerTestUtil::TwoPhaseReader(dest1))->run();
+ auto result2 = (new BytesConsumerTestUtil::TwoPhaseReader(dest2))->run();
+
+ EXPECT_EQ(Result::Done, result1.first);
+ EXPECT_EQ("hello, world", toString(result1.second));
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
+ EXPECT_EQ(Result::Done, result2.first);
+ EXPECT_EQ("hello, world", toString(result2.second));
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest2->getPublicState());
+ EXPECT_FALSE(src->isCancelled());
+}
+
+TEST_F(BytesConsumerTeeTest, Error)
+{
+ ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
+
+ src->add(Command(Command::Data, "hello, "));
+ src->add(Command(Command::Data, "world"));
+ src->add(Command(Command::Error));
+
+ BytesConsumer* dest1 = nullptr;
+ BytesConsumer* dest2 = nullptr;
+ BytesConsumer::tee(document(), src, &dest1, &dest2);
+
+ EXPECT_EQ(BytesConsumer::PublicState::Errored, dest1->getPublicState());
+ EXPECT_EQ(BytesConsumer::PublicState::Errored, dest2->getPublicState());
+
+ auto result1 = (new BytesConsumerTestUtil::TwoPhaseReader(dest1))->run();
+ auto result2 = (new BytesConsumerTestUtil::TwoPhaseReader(dest2))->run();
+
+ EXPECT_EQ(Result::Error, result1.first);
+ EXPECT_TRUE(result1.second.isEmpty());
+ EXPECT_EQ(BytesConsumer::PublicState::Errored, dest1->getPublicState());
+ EXPECT_EQ(Result::Error, result2.first);
+ EXPECT_TRUE(result2.second.isEmpty());
+ EXPECT_EQ(BytesConsumer::PublicState::Errored, dest2->getPublicState());
+ EXPECT_FALSE(src->isCancelled());
+
+ // Cancelling does nothing when errored.
+ dest1->cancel();
+ dest2->cancel();
+ EXPECT_EQ(BytesConsumer::PublicState::Errored, dest1->getPublicState());
+ EXPECT_EQ(BytesConsumer::PublicState::Errored, dest2->getPublicState());
+ EXPECT_FALSE(src->isCancelled());
+}
+
+TEST_F(BytesConsumerTeeTest, Cancel)
+{
+ ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
+
+ src->add(Command(Command::Data, "hello, "));
+ src->add(Command(Command::Wait));
+
+ BytesConsumer* dest1 = nullptr;
+ BytesConsumer* dest2 = nullptr;
+ BytesConsumer::tee(document(), src, &dest1, &dest2);
+
+ EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting, dest1->getPublicState());
+ EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting, dest2->getPublicState());
+
+ EXPECT_FALSE(src->isCancelled());
+ dest1->cancel();
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
+ EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting, dest2->getPublicState());
+ EXPECT_FALSE(src->isCancelled());
+ dest2->cancel();
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest2->getPublicState());
+ EXPECT_TRUE(src->isCancelled());
+}
+
+TEST_F(BytesConsumerTeeTest, CancelShouldNotAffectTheOtherDestination)
+{
+ ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
+
+ src->add(Command(Command::Data, "hello, "));
+ src->add(Command(Command::Wait));
+ src->add(Command(Command::Data, "world"));
+ src->add(Command(Command::Done));
+
+ BytesConsumer* dest1 = nullptr;
+ BytesConsumer* dest2 = nullptr;
+ BytesConsumer::tee(document(), src, &dest1, &dest2);
+
+ EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting, dest1->getPublicState());
+ EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting, dest2->getPublicState());
+
+ EXPECT_FALSE(src->isCancelled());
+ dest1->cancel();
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
+ EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting, dest2->getPublicState());
+ EXPECT_FALSE(src->isCancelled());
+
+ auto result2 = (new BytesConsumerTestUtil::TwoPhaseReader(dest2))->run();
+
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest2->getPublicState());
+ EXPECT_EQ(Result::Done, result2.first);
+ EXPECT_EQ("hello, world", toString(result2.second));
+ EXPECT_FALSE(src->isCancelled());
+}
+
+TEST_F(BytesConsumerTeeTest, CancelShouldNotAffectTheOtherDestination2)
+{
+ ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
+
+ src->add(Command(Command::Data, "hello, "));
+ src->add(Command(Command::Wait));
+ src->add(Command(Command::Data, "world"));
+ src->add(Command(Command::Error));
+
+ BytesConsumer* dest1 = nullptr;
+ BytesConsumer* dest2 = nullptr;
+ BytesConsumer::tee(document(), src, &dest1, &dest2);
+
+ EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting, dest1->getPublicState());
+ EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting, dest2->getPublicState());
+
+ EXPECT_FALSE(src->isCancelled());
+ dest1->cancel();
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
+ EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting, dest2->getPublicState());
+ EXPECT_FALSE(src->isCancelled());
+
+ auto result2 = (new BytesConsumerTestUtil::TwoPhaseReader(dest2))->run();
+
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
+ EXPECT_EQ(BytesConsumer::PublicState::Errored, dest2->getPublicState());
+ EXPECT_EQ(Result::Error, result2.first);
+ EXPECT_FALSE(src->isCancelled());
+}
+
+TEST_F(BytesConsumerTeeTest, BlobHandle)
+{
+ RefPtr<BlobDataHandle> blobDataHandle = BlobDataHandle::create(BlobData::create(), 12345);
+ BytesConsumer* src = new FakeBlobBytesConsumer(blobDataHandle);
+
+ BytesConsumer* dest1 = nullptr;
+ BytesConsumer* dest2 = nullptr;
+ BytesConsumer::tee(document(), src, &dest1, &dest2);
+
+ RefPtr<BlobDataHandle> destBlobDataHandle1 = dest1->drainAsBlobDataHandle(BytesConsumer::BlobSizePolicy::AllowBlobWithInvalidSize);
+ RefPtr<BlobDataHandle> destBlobDataHandle2 = dest2->drainAsBlobDataHandle(BytesConsumer::BlobSizePolicy::DisallowBlobWithInvalidSize);
+ ASSERT_TRUE(destBlobDataHandle1);
+ ASSERT_TRUE(destBlobDataHandle2);
+ EXPECT_EQ(12345u, destBlobDataHandle1->size());
+ EXPECT_EQ(12345u, destBlobDataHandle2->size());
+}
+
+TEST_F(BytesConsumerTeeTest, BlobHandleWithInvalidSize)
+{
+ RefPtr<BlobDataHandle> blobDataHandle = BlobDataHandle::create(BlobData::create(), -1);
+ BytesConsumer* src = new FakeBlobBytesConsumer(blobDataHandle);
+
+ BytesConsumer* dest1 = nullptr;
+ BytesConsumer* dest2 = nullptr;
+ BytesConsumer::tee(document(), src, &dest1, &dest2);
+
+ RefPtr<BlobDataHandle> destBlobDataHandle1 = dest1->drainAsBlobDataHandle(BytesConsumer::BlobSizePolicy::AllowBlobWithInvalidSize);
+ RefPtr<BlobDataHandle> destBlobDataHandle2 = dest2->drainAsBlobDataHandle(BytesConsumer::BlobSizePolicy::DisallowBlobWithInvalidSize);
+ ASSERT_TRUE(destBlobDataHandle1);
+ ASSERT_FALSE(destBlobDataHandle2);
+ EXPECT_EQ(UINT64_MAX, destBlobDataHandle1->size());
+}
+
+TEST_F(BytesConsumerTeeTest, ConsumerCanBeErroredInTwoPhaseRead)
+{
+ ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
+ src->add(Command(Command::Data, "a"));
+ src->add(Command(Command::Wait));
+ src->add(Command(Command::Error));
+
+ BytesConsumer* dest1 = nullptr;
+ BytesConsumer* dest2 = nullptr;
+ BytesConsumer::tee(document(), src, &dest1, &dest2);
+ TestClient* client = new TestClient();
+ dest1->setClient(client);
+
+ const char* buffer = nullptr;
+ size_t available = 0;
+ ASSERT_EQ(Result::Ok, dest1->beginRead(&buffer, &available));
+ ASSERT_EQ(1u, available);
+
+ EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting, dest1->getPublicState());
+ int numOnStateChangeCalled = client->numOnStateChangeCalled();
+ EXPECT_EQ(Result::Error, (new BytesConsumerTestUtil::Reader(dest2))->run().first);
+ EXPECT_EQ(BytesConsumer::PublicState::Errored, dest1->getPublicState());
+ EXPECT_EQ(numOnStateChangeCalled + 1, client->numOnStateChangeCalled());
+ EXPECT_EQ('a', buffer[0]);
+ EXPECT_EQ(Result::Ok, dest1->endRead(available));
+}
+
+TEST_F(BytesConsumerTeeTest, AsyncNotificationShouldBeDispatchedWhenAllDataIsConsumed)
+{
+ ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
+ src->add(Command(Command::Data, "a"));
+ src->add(Command(Command::Wait));
+ src->add(Command(Command::Done));
+ TestClient* client = new TestClient();
+
+ BytesConsumer* dest1 = nullptr;
+ BytesConsumer* dest2 = nullptr;
+ BytesConsumer::tee(document(), src, &dest1, &dest2);
+
+ dest1->setClient(client);
+
+ const char* buffer = nullptr;
+ size_t available = 0;
+ ASSERT_EQ(Result::Ok, dest1->beginRead(&buffer, &available));
+ ASSERT_EQ(1u, available);
+ EXPECT_EQ('a', buffer[0]);
+
+ EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting, src->getPublicState());
+ testing::runPendingTasks();
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, src->getPublicState());
+ // Just for checking UAF.
+ EXPECT_EQ('a', buffer[0]);
+ ASSERT_EQ(Result::Ok, dest1->endRead(1));
+
+ EXPECT_EQ(0, client->numOnStateChangeCalled());
+ EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting, dest1->getPublicState());
+ testing::runPendingTasks();
+ EXPECT_EQ(1, client->numOnStateChangeCalled());
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
+}
+
+TEST_F(BytesConsumerTeeTest, AsyncCloseNotificationShouldBeCancelledBySubsequentReadCall)
+{
+ ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
+ src->add(Command(Command::Data, "a"));
+ src->add(Command(Command::Done));
+ TestClient* client = new TestClient();
+
+ BytesConsumer* dest1 = nullptr;
+ BytesConsumer* dest2 = nullptr;
+ BytesConsumer::tee(document(), src, &dest1, &dest2);
+
+ dest1->setClient(client);
+
+ const char* buffer = nullptr;
+ size_t available = 0;
+ ASSERT_EQ(Result::Ok, dest1->beginRead(&buffer, &available));
+ ASSERT_EQ(1u, available);
+ EXPECT_EQ('a', buffer[0]);
+
+ testing::runPendingTasks();
+ // Just for checking UAF.
+ EXPECT_EQ('a', buffer[0]);
+ ASSERT_EQ(Result::Ok, dest1->endRead(1));
+ EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting, dest1->getPublicState());
+
+ EXPECT_EQ(Result::Done, dest1->beginRead(&buffer, &available));
+ EXPECT_EQ(0, client->numOnStateChangeCalled());
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
+ testing::runPendingTasks();
+ EXPECT_EQ(0, client->numOnStateChangeCalled());
+ EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
+}
+
+} // namespace
+
+} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698