blob: c33eab2e180fb5864bb92f9a4bb843606814f2c8 [file]
// 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 "modules/fetch/BytesConsumerTestUtil.h"
#include "core/dom/ExecutionContext.h"
#include "core/dom/TaskRunnerHelper.h"
#include "platform/WebTaskRunner.h"
#include "platform/testing/UnitTestHelpers.h"
#include "wtf/Assertions.h"
#include "wtf/Functional.h"
namespace blink {
using Result = BytesConsumer::Result;
using ::testing::_;
using ::testing::ByMove;
using ::testing::DoAll;
using ::testing::Return;
using ::testing::SetArgPointee;
BytesConsumerTestUtil::MockBytesConsumer::MockBytesConsumer() {
ON_CALL(*this, beginRead(_, _))
.WillByDefault(DoAll(SetArgPointee<0>(nullptr), SetArgPointee<1>(0),
Return(Result::Error)));
ON_CALL(*this, endRead(_)).WillByDefault(Return(Result::Error));
ON_CALL(*this, getPublicState()).WillByDefault(Return(PublicState::Errored));
ON_CALL(*this, drainAsBlobDataHandle(_))
.WillByDefault(Return(ByMove(nullptr)));
ON_CALL(*this, drainAsFormData()).WillByDefault(Return(ByMove(nullptr)));
}
BytesConsumerTestUtil::ReplayingBytesConsumer::ReplayingBytesConsumer(
ExecutionContext* executionContext)
: m_executionContext(executionContext) {}
BytesConsumerTestUtil::ReplayingBytesConsumer::~ReplayingBytesConsumer() {}
Result BytesConsumerTestUtil::ReplayingBytesConsumer::beginRead(
const char** buffer,
size_t* available) {
++m_notificationToken;
if (m_commands.isEmpty()) {
switch (m_state) {
case BytesConsumer::InternalState::Readable:
case BytesConsumer::InternalState::Waiting:
return Result::ShouldWait;
case BytesConsumer::InternalState::Closed:
return Result::Done;
case BytesConsumer::InternalState::Errored:
return Result::Error;
}
}
const Command& command = m_commands[0];
switch (command.getName()) {
case Command::Data:
DCHECK_LE(m_offset, command.body().size());
*buffer = command.body().data() + m_offset;
*available = command.body().size() - m_offset;
return Result::Ok;
case Command::Done:
m_commands.removeFirst();
close();
return Result::Done;
case Command::Error: {
Error e(String::fromUTF8(command.body().data(), command.body().size()));
m_commands.removeFirst();
error(std::move(e));
return Result::Error;
}
case Command::Wait:
m_commands.removeFirst();
m_state = InternalState::Waiting;
TaskRunnerHelper::get(TaskType::Networking, m_executionContext)
->postTask(BLINK_FROM_HERE,
WTF::bind(&ReplayingBytesConsumer::notifyAsReadable,
wrapPersistent(this), m_notificationToken));
return Result::ShouldWait;
}
NOTREACHED();
return Result::Error;
}
Result BytesConsumerTestUtil::ReplayingBytesConsumer::endRead(size_t read) {
DCHECK(!m_commands.isEmpty());
const Command& command = m_commands[0];
DCHECK_EQ(Command::Data, command.getName());
m_offset += read;
DCHECK_LE(m_offset, command.body().size());
if (m_offset < command.body().size())
return Result::Ok;
m_offset = 0;
m_commands.removeFirst();
return Result::Ok;
}
void BytesConsumerTestUtil::ReplayingBytesConsumer::setClient(Client* client) {
DCHECK(!m_client);
DCHECK(client);
m_client = client;
++m_notificationToken;
}
void BytesConsumerTestUtil::ReplayingBytesConsumer::clearClient() {
DCHECK(m_client);
m_client = nullptr;
++m_notificationToken;
}
void BytesConsumerTestUtil::ReplayingBytesConsumer::cancel() {
close();
m_isCancelled = true;
}
BytesConsumer::PublicState
BytesConsumerTestUtil::ReplayingBytesConsumer::getPublicState() const {
return getPublicStateFromInternalState(m_state);
}
BytesConsumer::Error BytesConsumerTestUtil::ReplayingBytesConsumer::getError()
const {
return m_error;
}
void BytesConsumerTestUtil::ReplayingBytesConsumer::notifyAsReadable(
int notificationToken) {
if (m_notificationToken != notificationToken) {
// The notification is cancelled.
return;
}
DCHECK(m_client);
DCHECK_NE(InternalState::Closed, m_state);
DCHECK_NE(InternalState::Errored, m_state);
m_client->onStateChange();
}
void BytesConsumerTestUtil::ReplayingBytesConsumer::close() {
m_commands.clear();
m_offset = 0;
m_state = InternalState::Closed;
++m_notificationToken;
}
void BytesConsumerTestUtil::ReplayingBytesConsumer::error(const Error& e) {
m_commands.clear();
m_offset = 0;
m_error = e;
m_state = InternalState::Errored;
++m_notificationToken;
}
DEFINE_TRACE(BytesConsumerTestUtil::ReplayingBytesConsumer) {
visitor->trace(m_executionContext);
visitor->trace(m_client);
BytesConsumer::trace(visitor);
}
BytesConsumerTestUtil::TwoPhaseReader::TwoPhaseReader(BytesConsumer* consumer)
: m_consumer(consumer) {
m_consumer->setClient(this);
}
void BytesConsumerTestUtil::TwoPhaseReader::onStateChange() {
while (true) {
const char* buffer = nullptr;
size_t available = 0;
auto result = m_consumer->beginRead(&buffer, &available);
if (result == BytesConsumer::Result::ShouldWait)
return;
if (result == BytesConsumer::Result::Ok) {
// 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);
m_data.append(buffer, read);
result = m_consumer->endRead(read);
}
DCHECK_NE(result, BytesConsumer::Result::ShouldWait);
if (result != BytesConsumer::Result::Ok) {
m_result = result;
return;
}
}
}
std::pair<BytesConsumer::Result, Vector<char>>
BytesConsumerTestUtil::TwoPhaseReader::run() {
onStateChange();
while (m_result != BytesConsumer::Result::Done &&
m_result != BytesConsumer::Result::Error)
testing::runPendingTasks();
testing::runPendingTasks();
return std::make_pair(m_result, std::move(m_data));
}
} // namespace blink