blob: dbe986b437e34f3dc66e3e6c0f1d6833daa50f03 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/cronet/native/test/test_upload_data_provider.h"
#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// Helper class that runs base::OnceClosure.
class TestRunnable {
public:
// Creates Cronet runnable that runs |task| once and destroys itself.
static Cronet_RunnablePtr CreateRunnable(base::OnceClosure task);
TestRunnable(const TestRunnable&) = delete;
TestRunnable& operator=(const TestRunnable&) = delete;
private:
explicit TestRunnable(base::OnceClosure task);
~TestRunnable();
// Runs |self| and destroys it.
static void Run(Cronet_RunnablePtr self);
// Closure to run.
base::OnceClosure task_;
};
TestRunnable::TestRunnable(base::OnceClosure task) : task_(std::move(task)) {}
TestRunnable::~TestRunnable() = default;
// static
Cronet_RunnablePtr TestRunnable::CreateRunnable(base::OnceClosure task) {
Cronet_RunnablePtr runnable = Cronet_Runnable_CreateWith(TestRunnable::Run);
Cronet_Runnable_SetClientContext(runnable, new TestRunnable(std::move(task)));
return runnable;
}
// static
void TestRunnable::Run(Cronet_RunnablePtr self) {
CHECK(self);
Cronet_ClientContext context = Cronet_Runnable_GetClientContext(self);
TestRunnable* runnable = static_cast<TestRunnable*>(context);
CHECK(runnable);
std::move(runnable->task_).Run();
delete runnable;
}
} // namespace
namespace cronet {
// Various test utility functions for testing Cronet.
namespace test {
TestUploadDataProvider::TestUploadDataProvider(
SuccessCallbackMode success_callback_mode,
Cronet_ExecutorPtr executor)
: success_callback_mode_(success_callback_mode), executor_(executor) {}
TestUploadDataProvider::~TestUploadDataProvider() = default;
Cronet_UploadDataProviderPtr
TestUploadDataProvider::CreateUploadDataProvider() {
Cronet_UploadDataProviderPtr upload_data_provider =
Cronet_UploadDataProvider_CreateWith(
TestUploadDataProvider::GetLength, TestUploadDataProvider::Read,
TestUploadDataProvider::Rewind, TestUploadDataProvider::Close);
Cronet_UploadDataProvider_SetClientContext(upload_data_provider, this);
return upload_data_provider;
}
void TestUploadDataProvider::AddRead(std::string read) {
EXPECT_TRUE(!started_) << "Adding bytes after read";
reads_.push_back(read);
}
void TestUploadDataProvider::SetReadFailure(int read_fail_index,
FailMode read_fail_mode) {
read_fail_index_ = read_fail_index;
read_fail_mode_ = read_fail_mode;
}
void TestUploadDataProvider::SetRewindFailure(FailMode rewind_fail_mode) {
rewind_fail_mode_ = rewind_fail_mode;
}
void TestUploadDataProvider::SetReadCancel(int read_cancel_index,
CancelMode read_cancel_mode) {
read_cancel_index_ = read_cancel_index;
read_cancel_mode_ = read_cancel_mode;
}
void TestUploadDataProvider::SetRewindCancel(CancelMode rewind_cancel_mode) {
rewind_cancel_mode_ = rewind_cancel_mode;
}
int64_t TestUploadDataProvider::GetLength() const {
EXPECT_TRUE(!closed_.IsSet()) << "Data Provider is closed";
if (bad_length_ != -1)
return bad_length_;
return GetUploadedLength();
}
int64_t TestUploadDataProvider::GetUploadedLength() const {
if (chunked_)
return -1ll;
int64_t length = 0ll;
for (const auto& read : reads_)
length += read.size();
return length;
}
void TestUploadDataProvider::Read(Cronet_UploadDataSinkPtr upload_data_sink,
Cronet_BufferPtr buffer) {
int current_read_call = num_read_calls_;
++num_read_calls_;
EXPECT_TRUE(!closed_.IsSet()) << "Data Provider is closed";
AssertIdle();
if (current_read_call == read_cancel_index_)
MaybeCancelRequest(read_cancel_mode_);
if (MaybeFailRead(current_read_call, upload_data_sink)) {
failed_ = true;
return;
}
read_pending_ = true;
started_ = true;
bool final_chunk = (chunked_ && next_read_ == reads_.size() - 1);
EXPECT_TRUE(next_read_ < reads_.size()) << "Too many reads: " << next_read_;
const auto& read = reads_[next_read_];
EXPECT_TRUE(read.size() <= Cronet_Buffer_GetSize(buffer))
<< "Read buffer smaller than expected.";
UNSAFE_TODO(memcpy(Cronet_Buffer_GetData(buffer), read.data(), read.size()));
++next_read_;
auto complete_closure = base::BindOnce(
[](TestUploadDataProvider* upload_data_provider,
Cronet_UploadDataSink* upload_data_sink, uint64_t bytes_read,
bool final_chunk) {
upload_data_provider->read_pending_ = false;
Cronet_UploadDataSink_OnReadSucceeded(upload_data_sink, bytes_read,
final_chunk);
},
this, upload_data_sink, read.size(), final_chunk);
if (success_callback_mode_ == SYNC) {
std::move(complete_closure).Run();
} else {
PostTaskToExecutor(std::move(complete_closure));
}
}
void TestUploadDataProvider::Rewind(Cronet_UploadDataSinkPtr upload_data_sink) {
++num_rewind_calls_;
EXPECT_TRUE(!closed_.IsSet()) << "Data Provider is closed";
AssertIdle();
MaybeCancelRequest(rewind_cancel_mode_);
if (MaybeFailRewind(upload_data_sink)) {
failed_ = true;
return;
}
// Should never try and rewind when rewinding does nothing.
EXPECT_TRUE(next_read_ != 0) << "Unexpected rewind when already at beginning";
rewind_pending_ = true;
next_read_ = 0;
auto complete_closure = base::BindOnce(
[](TestUploadDataProvider* upload_data_provider,
Cronet_UploadDataSink* upload_data_sink) {
upload_data_provider->rewind_pending_ = false;
Cronet_UploadDataSink_OnRewindSucceeded(upload_data_sink);
},
this, upload_data_sink);
if (success_callback_mode_ == SYNC) {
std::move(complete_closure).Run();
} else {
PostTaskToExecutor(std::move(complete_closure));
}
}
void TestUploadDataProvider::PostTaskToExecutor(base::OnceClosure task) {
EXPECT_TRUE(executor_);
// |runnable| is passed to executor, which destroys it after execution.
Cronet_Executor_Execute(executor_,
TestRunnable::CreateRunnable(std::move(task)));
}
void TestUploadDataProvider::AssertIdle() const {
EXPECT_TRUE(!read_pending_) << "Unexpected operation during read";
EXPECT_TRUE(!rewind_pending_) << "Unexpected operation during rewind";
EXPECT_TRUE(!failed_) << "Unexpected operation after failure";
}
bool TestUploadDataProvider::MaybeFailRead(
int read_index,
Cronet_UploadDataSinkPtr upload_data_sink) {
if (read_fail_mode_ == NONE)
return false;
if (read_index != read_fail_index_)
return false;
if (read_fail_mode_ == CALLBACK_SYNC) {
Cronet_UploadDataSink_OnReadError(upload_data_sink, "Sync read failure");
return true;
}
EXPECT_EQ(read_fail_mode_, CALLBACK_ASYNC);
PostTaskToExecutor(base::BindOnce(
[](Cronet_UploadDataSink* upload_data_sink) {
Cronet_UploadDataSink_OnReadError(upload_data_sink,
"Async read failure");
},
upload_data_sink));
return true;
}
bool TestUploadDataProvider::MaybeFailRewind(
Cronet_UploadDataSinkPtr upload_data_sink) {
if (rewind_fail_mode_ == NONE)
return false;
if (rewind_fail_mode_ == CALLBACK_SYNC) {
Cronet_UploadDataSink_OnRewindError(upload_data_sink,
"Sync rewind failure");
return true;
}
EXPECT_EQ(rewind_fail_mode_, CALLBACK_ASYNC);
PostTaskToExecutor(base::BindOnce(
[](Cronet_UploadDataSink* upload_data_sink) {
Cronet_UploadDataSink_OnRewindError(upload_data_sink,
"Async rewind failure");
},
upload_data_sink));
return true;
}
void TestUploadDataProvider::MaybeCancelRequest(CancelMode cancel_mode) {
if (cancel_mode == CANCEL_NONE)
return;
CHECK(url_request_);
if (cancel_mode == CANCEL_SYNC) {
Cronet_UrlRequest_Cancel(url_request_);
return;
}
EXPECT_EQ(cancel_mode, CANCEL_ASYNC);
PostTaskToExecutor(base::BindOnce(
[](Cronet_UrlRequestPtr url_request) {
Cronet_UrlRequest_Cancel(url_request);
},
url_request_));
}
void TestUploadDataProvider::Close() {
EXPECT_TRUE(!closed_.IsSet()) << "Closed twice";
closed_.Set();
awaiting_close_.Signal();
}
void TestUploadDataProvider::AssertClosed() {
awaiting_close_.TimedWait(base::Milliseconds(5000));
EXPECT_TRUE(closed_.IsSet()) << "Was not closed";
}
/* static */
TestUploadDataProvider* TestUploadDataProvider::GetThis(
Cronet_UploadDataProviderPtr self) {
return static_cast<TestUploadDataProvider*>(
Cronet_UploadDataProvider_GetClientContext(self));
}
/* static */
int64_t TestUploadDataProvider::GetLength(Cronet_UploadDataProviderPtr self) {
return GetThis(self)->GetLength();
}
/* static */
void TestUploadDataProvider::Read(Cronet_UploadDataProviderPtr self,
Cronet_UploadDataSinkPtr upload_data_sink,
Cronet_BufferPtr buffer) {
return GetThis(self)->Read(upload_data_sink, buffer);
}
/* static */
void TestUploadDataProvider::Rewind(Cronet_UploadDataProviderPtr self,
Cronet_UploadDataSinkPtr upload_data_sink) {
return GetThis(self)->Rewind(upload_data_sink);
}
/* static */
void TestUploadDataProvider::Close(Cronet_UploadDataProviderPtr self) {
return GetThis(self)->Close();
}
} // namespace test
} // namespace cronet