// 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 "third_party/blink/renderer/core/fetch/bytes_consumer_test_util.h" #include "third_party/blink/public/platform/task_type.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/platform/heap/persistent.h" #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" #include "third_party/blink/renderer/platform/wtf/assertions.h" #include "third_party/blink/renderer/platform/wtf/functional.h" namespace blink { namespace { using Result = BytesConsumer::Result; using testing::_; using testing::ByMove; using testing::DoAll; using testing::Return; using testing::SetArgPointee; } // namespace BytesConsumerTestUtil::MockBytesConsumer::MockBytesConsumer() { ON_CALL(*this, BeginRead(_, _)) .WillByDefault(DoAll(SetArgPointee<0>(nullptr), SetArgPointee<1>(0), Return(Result::kError))); ON_CALL(*this, EndRead(_)).WillByDefault(Return(Result::kError)); ON_CALL(*this, GetPublicState()).WillByDefault(Return(PublicState::kErrored)); ON_CALL(*this, DrainAsBlobDataHandle(_)) .WillByDefault(Return(ByMove(nullptr))); ON_CALL(*this, DrainAsFormData()).WillByDefault(Return(ByMove(nullptr))); } BytesConsumerTestUtil::ReplayingBytesConsumer::ReplayingBytesConsumer( ExecutionContext* execution_context) : execution_context_(execution_context) {} BytesConsumerTestUtil::ReplayingBytesConsumer::~ReplayingBytesConsumer() {} Result BytesConsumerTestUtil::ReplayingBytesConsumer::BeginRead( const char** buffer, size_t* available) { ++notification_token_; if (commands_.IsEmpty()) { switch (state_) { case BytesConsumer::InternalState::kReadable: case BytesConsumer::InternalState::kWaiting: return Result::kShouldWait; case BytesConsumer::InternalState::kClosed: return Result::kDone; case BytesConsumer::InternalState::kErrored: return Result::kError; } } const Command& command = commands_[0]; switch (command.GetName()) { case Command::kData: DCHECK_LE(offset_, command.Body().size()); *buffer = command.Body().data() + offset_; *available = command.Body().size() - offset_; return Result::kOk; case Command::kDone: commands_.pop_front(); Close(); return Result::kDone; case Command::kError: { Error e(String::FromUTF8(command.Body().data(), command.Body().size())); commands_.pop_front(); MakeErrored(std::move(e)); return Result::kError; } case Command::kWait: commands_.pop_front(); state_ = InternalState::kWaiting; execution_context_->GetTaskRunner(TaskType::kNetworking) ->PostTask(FROM_HERE, WTF::Bind(&ReplayingBytesConsumer::NotifyAsReadable, WrapPersistent(this), notification_token_)); return Result::kShouldWait; } NOTREACHED(); return Result::kError; } Result BytesConsumerTestUtil::ReplayingBytesConsumer::EndRead(size_t read) { DCHECK(!commands_.IsEmpty()); const Command& command = commands_[0]; DCHECK_EQ(Command::kData, command.GetName()); offset_ += read; DCHECK_LE(offset_, command.Body().size()); if (offset_ < command.Body().size()) return Result::kOk; offset_ = 0; commands_.pop_front(); return Result::kOk; } void BytesConsumerTestUtil::ReplayingBytesConsumer::SetClient(Client* client) { DCHECK(!client_); DCHECK(client); client_ = client; ++notification_token_; } void BytesConsumerTestUtil::ReplayingBytesConsumer::ClearClient() { DCHECK(client_); client_ = nullptr; ++notification_token_; } void BytesConsumerTestUtil::ReplayingBytesConsumer::Cancel() { Close(); is_cancelled_ = true; } BytesConsumer::PublicState BytesConsumerTestUtil::ReplayingBytesConsumer::GetPublicState() const { return GetPublicStateFromInternalState(state_); } BytesConsumer::Error BytesConsumerTestUtil::ReplayingBytesConsumer::GetError() const { return error_; } void BytesConsumerTestUtil::ReplayingBytesConsumer::NotifyAsReadable( int notification_token) { if (notification_token_ != notification_token) { // The notification is cancelled. return; } DCHECK(client_); DCHECK_NE(InternalState::kClosed, state_); DCHECK_NE(InternalState::kErrored, state_); client_->OnStateChange(); } void BytesConsumerTestUtil::ReplayingBytesConsumer::Close() { commands_.clear(); offset_ = 0; state_ = InternalState::kClosed; ++notification_token_; } void BytesConsumerTestUtil::ReplayingBytesConsumer::MakeErrored( const Error& e) { commands_.clear(); offset_ = 0; error_ = e; state_ = InternalState::kErrored; ++notification_token_; } void BytesConsumerTestUtil::ReplayingBytesConsumer::Trace( blink::Visitor* visitor) { visitor->Trace(execution_context_); visitor->Trace(client_); BytesConsumer::Trace(visitor); } BytesConsumerTestUtil::TwoPhaseReader::TwoPhaseReader(BytesConsumer* consumer) : consumer_(consumer) { consumer_->SetClient(this); } void BytesConsumerTestUtil::TwoPhaseReader::OnStateChange() { while (true) { const char* buffer = nullptr; size_t available = 0; auto result = consumer_->BeginRead(&buffer, &available); if (result == BytesConsumer::Result::kShouldWait) return; if (result == BytesConsumer::Result::kOk) { // We don't use |available| as-is to test cases where endRead // is called with a number smaller than |available|. We choose 3 // because of the same reasons as Reader::onStateChange. wtf_size_t read = static_cast(std::min(static_cast(3), available)); data_.Append(buffer, read); result = consumer_->EndRead(read); } DCHECK_NE(result, BytesConsumer::Result::kShouldWait); if (result != BytesConsumer::Result::kOk) { result_ = result; return; } } } std::pair> BytesConsumerTestUtil::TwoPhaseReader::Run() { OnStateChange(); while (result_ != BytesConsumer::Result::kDone && result_ != BytesConsumer::Result::kError) test::RunPendingTasks(); test::RunPendingTasks(); return std::make_pair(result_, std::move(data_)); } String BytesConsumerTestUtil::CharVectorToString(const Vector& v) { return String(v.data(), v.size()); } } // namespace blink