910e62b5创建于 1月15日历史提交
// 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