blob: 6bbe395bb4fecd09e4aacfc5dce76182702dbd2e [file] [log] [blame]
// 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.
size_t read = std::min(static_cast<size_t>(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<BytesConsumer::Result, Vector<char>>
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<char>& v) {
return String(v.data(), v.size());
}
} // namespace blink