chromium/chrome/browser/ash/file_system_provider/operation_request_manager_unittest.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/file_system_provider/operation_request_manager.h"

#include <stddef.h>

#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "chrome/browser/ash/file_system_provider/notification_manager_interface.h"
#include "chrome/browser/ash/file_system_provider/request_manager.h"
#include "chrome/browser/ash/file_system_provider/request_value.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash::file_system_provider {
namespace {

const RequestType kTestRequestType = RequestType::kGetMetadata;

std::optional<std::string> GetTestingParamFromResult(
    const RequestValue& result) {
  if (const std::string* testing_param = result.testing_params()) {
    return *testing_param;
  }
  return std::nullopt;
}

// Fake implementation for the notification manager. Simulates user action on
// a notification.
class FakeNotificationManager : public NotificationManagerInterface {
 public:
  FakeNotificationManager() = default;

  FakeNotificationManager(const FakeNotificationManager&) = delete;
  FakeNotificationManager& operator=(const FakeNotificationManager&) = delete;

  ~FakeNotificationManager() override = default;

  // NotificationManagerInterface overrides:
  void ShowUnresponsiveNotification(int id,
                                    NotificationCallback callback) override {
    callbacks_[id] = std::move(callback);
  }

  void HideUnresponsiveNotification(int id) override { callbacks_.erase(id); }

  // Aborts all of the virtually shown notifications.
  void Abort() { OnNotificationResult(ABORT); }

  // Discards all of the virtually shown notifications.
  void Continue() { OnNotificationResult(CONTINUE); }

  // Returns number of currently shown notifications.
  size_t size() { return callbacks_.size(); }

 private:
  typedef std::map<int, NotificationCallback> CallbackMap;

  // Handles a notification result by calling all registered callbacks and
  // clearing the list.
  void OnNotificationResult(NotificationResult result) {
    CallbackMap::iterator it = callbacks_.begin();
    while (it != callbacks_.end()) {
      CallbackMap::iterator current_it = it++;
      NotificationCallback callback = std::move(current_it->second);
      callbacks_.erase(current_it);
      std::move(callback).Run(result);
    }
  }

  CallbackMap callbacks_;
};

class TimeoutWaiter : public RequestManager::Observer {
 public:
  explicit TimeoutWaiter(RequestManager* request_manager) {
    scoped_observation_.Observe(request_manager);
  }
  void OnRequestCreated(int request_id, RequestType type) override {}
  void OnRequestDestroyed(int request_id,
                          OperationCompletion completion) override {}
  void OnRequestExecuted(int request_id) override {}
  void OnRequestFulfilled(int request_id,
                          const RequestValue& result,
                          bool has_more) override {}
  void OnRequestRejected(int request_id,
                         const RequestValue& result,
                         base::File::Error error) override {}
  void OnRequestTimedOut(int request_id) override { run_loop_.Quit(); }
  void Wait() { run_loop_.Run(); }

 private:
  base::RunLoop run_loop_;
  base::ScopedObservation<RequestManager, RequestManager::Observer>
      scoped_observation_{this};
};

// Logs calls of the success and error callbacks on requests.
class EventLogger {
 public:
  class ExecuteEvent {
   public:
    explicit ExecuteEvent(int request_id) : request_id_(request_id) {}
    virtual ~ExecuteEvent() = default;

    int request_id() { return request_id_; }

   private:
    int request_id_;
  };

  class SuccessEvent {
   public:
    SuccessEvent(int request_id, const RequestValue& result, bool has_more)
        : request_id_(request_id),
          testing_param_(GetTestingParamFromResult(result)),
          result_is_valid_(result.is_valid()),
          has_more_(has_more) {}
    virtual ~SuccessEvent() = default;

    int request_id() const { return request_id_; }
    const std::optional<std::string>& testing_param() const {
      return testing_param_;
    }
    bool has_more() const { return has_more_; }
    bool result_is_valid() const { return result_is_valid_; }

   private:
    int request_id_;
    std::optional<std::string> testing_param_;
    bool result_is_valid_;
    bool has_more_;
  };

  class ErrorEvent {
   public:
    ErrorEvent(int request_id, base::File::Error error)
        : request_id_(request_id), error_(error) {}
    virtual ~ErrorEvent() = default;

    int request_id() { return request_id_; }
    base::File::Error error() { return error_; }

   private:
    int request_id_;
    base::File::Error error_;
  };

  class AbortEvent {
   public:
    explicit AbortEvent(int request_id) : request_id_(request_id) {}
    virtual ~AbortEvent() = default;

    int request_id() { return request_id_; }

   private:
    int request_id_;
  };

  EventLogger() = default;

  EventLogger(const EventLogger&) = delete;
  EventLogger& operator=(const EventLogger&) = delete;

  virtual ~EventLogger() = default;

  void OnExecute(int request_id) {
    execute_events_.push_back(std::make_unique<ExecuteEvent>(request_id));
  }

  void OnSuccess(int request_id, const RequestValue& result, bool has_more) {
    success_events_.push_back(
        std::make_unique<SuccessEvent>(request_id, result, has_more));
  }

  void OnError(int request_id, base::File::Error error) {
    error_events_.push_back(std::make_unique<ErrorEvent>(request_id, error));
  }

  void OnAbort(int request_id) {
    abort_events_.push_back(std::make_unique<AbortEvent>(request_id));
  }

  std::vector<std::unique_ptr<ExecuteEvent>>& execute_events() {
    return execute_events_;
  }
  std::vector<std::unique_ptr<SuccessEvent>>& success_events() {
    return success_events_;
  }
  std::vector<std::unique_ptr<ErrorEvent>>& error_events() {
    return error_events_;
  }
  std::vector<std::unique_ptr<AbortEvent>>& abort_events() {
    return abort_events_;
  }

  base::WeakPtr<EventLogger> GetWeakPtr() {
    return weak_ptr_factory_.GetWeakPtr();
  }

 private:
  std::vector<std::unique_ptr<ExecuteEvent>> execute_events_;
  std::vector<std::unique_ptr<SuccessEvent>> success_events_;
  std::vector<std::unique_ptr<ErrorEvent>> error_events_;
  std::vector<std::unique_ptr<AbortEvent>> abort_events_;
  base::WeakPtrFactory<EventLogger> weak_ptr_factory_{this};
};

// Fake handler, which forwards callbacks to the logger. The handler is owned
// by a request manager, however the logger is owned by tests.
class FakeHandler : public RequestManager::HandlerInterface {
 public:
  // The handler can outlive the passed logger, so using a weak pointer. The
  // |execute_reply| value will be returned for the Execute() call.
  FakeHandler(base::WeakPtr<EventLogger> logger, bool execute_reply)
      : logger_(logger), execute_reply_(execute_reply) {}

  // RequestManager::Handler overrides.
  bool Execute(int request_id) override {
    if (logger_.get())
      logger_->OnExecute(request_id);

    return execute_reply_;
  }

  // RequestManager::Handler overrides.
  void OnSuccess(int request_id,
                 const RequestValue& result,
                 bool has_more) override {
    if (logger_.get())
      logger_->OnSuccess(request_id, std::move(result), has_more);
  }

  // RequestManager::Handler overrides.
  void OnError(int request_id,
               const RequestValue& result,
               base::File::Error error) override {
    if (logger_.get())
      logger_->OnError(request_id, error);
  }

  void OnAbort(int request_id) override {
    if (logger_.get()) {
      logger_->OnAbort(request_id);
    }
  }

  FakeHandler(const FakeHandler&) = delete;
  FakeHandler& operator=(const FakeHandler&) = delete;

  ~FakeHandler() override = default;

 private:
  base::WeakPtr<EventLogger> logger_;
  bool execute_reply_;
};

// Observer the request manager for request events.
class RequestObserver : public RequestManager::Observer {
 public:
  class Event {
   public:
    explicit Event(int request_id) : request_id_(request_id) {}
    virtual ~Event() = default;
    int request_id() const { return request_id_; }

   private:
    int request_id_;
  };

  class CreatedEvent : public Event {
   public:
    CreatedEvent(int request_id, RequestType type)
        : Event(request_id), type_(type) {}
    ~CreatedEvent() override = default;

    RequestType type() const { return type_; }

   private:
    RequestType type_;
  };

  class FulfilledEvent : public Event {
   public:
    FulfilledEvent(int request_id, bool has_more)
        : Event(request_id), has_more_(has_more) {}
    ~FulfilledEvent() override = default;

    bool has_more() const { return has_more_; }

   private:
    bool has_more_;
  };

  class RejectedEvent : public Event {
   public:
    RejectedEvent(int request_id, base::File::Error error)
        : Event(request_id), error_(error) {}
    ~RejectedEvent() override = default;

    base::File::Error error() const { return error_; }

   private:
    base::File::Error error_;
  };

  class DestroyedEvent : public Event {
   public:
    DestroyedEvent(int request_id, OperationCompletion completion)
        : Event(request_id), completion_(completion) {}

    OperationCompletion completion() const { return completion_; }

   private:
    OperationCompletion completion_;
  };

  RequestObserver() = default;

  RequestObserver(const RequestObserver&) = delete;
  RequestObserver& operator=(const RequestObserver&) = delete;

  ~RequestObserver() override = default;

  // RequestManager::Observer overrides.
  void OnRequestCreated(int request_id, RequestType type) override {
    created_.emplace_back(request_id, type);
  }

  // RequestManager::Observer overrides.
  void OnRequestDestroyed(int request_id,
                          OperationCompletion completion) override {
    destroyed_.emplace_back(request_id, completion);
  }

  // RequestManager::Observer overrides.
  void OnRequestExecuted(int request_id) override {
    executed_.emplace_back(request_id);
  }

  // RequestManager::Observer overrides.
  void OnRequestFulfilled(int request_id,
                          const RequestValue& result,
                          bool has_more) override {
    fulfilled_.emplace_back(request_id, has_more);
  }

  // RequestManager::Observer overrides.
  void OnRequestRejected(int request_id,
                         const RequestValue& result,
                         base::File::Error error) override {
    rejected_.emplace_back(request_id, error);
  }

  // RequestManager::Observer overrides.
  void OnRequestTimedOut(int request_id) override {
    timed_out_.emplace_back(request_id);
  }

  const std::vector<CreatedEvent>& created() const { return created_; }
  const std::vector<DestroyedEvent>& destroyed() const { return destroyed_; }
  const std::vector<Event>& executed() const { return executed_; }
  const std::vector<FulfilledEvent>& fulfilled() const { return fulfilled_; }
  const std::vector<RejectedEvent>& rejected() const { return rejected_; }
  const std::vector<Event>& timed_out() const { return timed_out_; }

 private:
  std::vector<CreatedEvent> created_;
  std::vector<DestroyedEvent> destroyed_;
  std::vector<Event> executed_;
  std::vector<FulfilledEvent> fulfilled_;
  std::vector<RejectedEvent> rejected_;
  std::vector<Event> timed_out_;
};

}  // namespace

class FileSystemProviderRequestManagerTest : public testing::Test {
 protected:
  FileSystemProviderRequestManagerTest() = default;
  ~FileSystemProviderRequestManagerTest() override = default;

  void SetUp() override {
    profile_ = std::make_unique<TestingProfile>();
    notification_manager_ = std::make_unique<FakeNotificationManager>();
    request_manager_ = std::make_unique<OperationRequestManager>(
        profile_.get(), /*provider_id=*/std::string(),
        notification_manager_.get(), /*timeout=*/base::Seconds(10));
  }

  content::BrowserTaskEnvironment task_environment_;
  std::unique_ptr<TestingProfile> profile_;
  std::unique_ptr<FakeNotificationManager> notification_manager_;
  std::unique_ptr<OperationRequestManager> request_manager_;
};

TEST_F(FileSystemProviderRequestManagerTest, CreateFailure) {
  EventLogger logger;
  RequestObserver observer;
  request_manager_->AddObserver(&observer);

  const int request_id = request_manager_->CreateRequest(
      kTestRequestType,
      base::WrapUnique<RequestManager::HandlerInterface>(
          new FakeHandler(logger.GetWeakPtr(), /*execute_reply=*/false)));

  EXPECT_EQ(0, request_id);
  EXPECT_EQ(0u, logger.success_events().size());
  EXPECT_EQ(0u, logger.error_events().size());

  EXPECT_EQ(1u, observer.created().size());
  EXPECT_EQ(kTestRequestType, observer.created()[0].type());
  EXPECT_EQ(1u, observer.destroyed().size());
  EXPECT_EQ(0u, observer.executed().size());

  const std::vector<int> active_request_ids =
      request_manager_->GetActiveRequestIds();
  EXPECT_EQ(0u, active_request_ids.size());

  request_manager_->RemoveObserver(&observer);
}

TEST_F(FileSystemProviderRequestManagerTest, CreateAndFulFill) {
  EventLogger logger;
  RequestObserver observer;
  request_manager_->AddObserver(&observer);

  const int request_id = request_manager_->CreateRequest(
      kTestRequestType,
      base::WrapUnique<RequestManager::HandlerInterface>(
          new FakeHandler(logger.GetWeakPtr(), /*execute_reply=*/true)));

  EXPECT_EQ(1, request_id);
  EXPECT_EQ(0u, logger.success_events().size());
  EXPECT_EQ(0u, logger.error_events().size());

  ASSERT_EQ(1u, observer.created().size());
  EXPECT_EQ(request_id, observer.created()[0].request_id());
  EXPECT_EQ(kTestRequestType, observer.created()[0].type());

  ASSERT_EQ(1u, observer.executed().size());
  EXPECT_EQ(request_id, observer.executed()[0].request_id());

  const std::vector<int> active_request_ids =
      request_manager_->GetActiveRequestIds();
  ASSERT_EQ(1u, active_request_ids.size());
  EXPECT_EQ(request_id, active_request_ids[0]);

  RequestValue response(RequestValue::CreateForTesting("i-like-vanilla"));
  const bool has_more = false;

  const base::File::Error result = request_manager_->FulfillRequest(
      request_id, std::move(response), has_more);
  EXPECT_EQ(base::File::FILE_OK, result);

  ASSERT_EQ(1u, observer.fulfilled().size());
  EXPECT_EQ(request_id, observer.fulfilled()[0].request_id());
  EXPECT_FALSE(observer.fulfilled()[0].has_more());

  // Validate if the callback has correct arguments.
  ASSERT_EQ(1u, logger.success_events().size());
  EXPECT_EQ(0u, logger.error_events().size());
  EventLogger::SuccessEvent* event = logger.success_events()[0].get();
  ASSERT_TRUE(event->testing_param());
  EXPECT_EQ("i-like-vanilla", *event->testing_param());
  EXPECT_FALSE(event->has_more());

  // Confirm, that the request is removed. Basically, fulfilling again for the
  // same request, should fail.
  {
    EXPECT_EQ(0u, request_manager_->GetActiveRequestIds().size());

    const base::File::Error retry =
        request_manager_->FulfillRequest(request_id, RequestValue(), has_more);
    EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, retry);
    EXPECT_EQ(1u, observer.fulfilled().size());
  }

  // Rejecting should also fail.
  {
    const base::File::Error retry = request_manager_->RejectRequest(
        request_id, RequestValue(), base::File::FILE_ERROR_FAILED);
    EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, retry);
    EXPECT_EQ(0u, observer.rejected().size());
  }

  ASSERT_EQ(1u, observer.destroyed().size());
  EXPECT_EQ(request_id, observer.destroyed()[0].request_id());
  EXPECT_EQ(OperationCompletion::kCompletedNormally,
            observer.destroyed()[0].completion());
  EXPECT_EQ(0u, observer.timed_out().size());

  request_manager_->RemoveObserver(&observer);
}

TEST_F(FileSystemProviderRequestManagerTest, CreateAndFulFill_WithHasNext) {
  EventLogger logger;
  RequestObserver observer;
  request_manager_->AddObserver(&observer);

  const int request_id = request_manager_->CreateRequest(
      kTestRequestType,
      base::WrapUnique<RequestManager::HandlerInterface>(
          new FakeHandler(logger.GetWeakPtr(), /*execute_reply=*/true)));

  EXPECT_EQ(1, request_id);
  EXPECT_EQ(0u, logger.success_events().size());
  EXPECT_EQ(0u, logger.error_events().size());

  ASSERT_EQ(1u, observer.created().size());
  EXPECT_EQ(request_id, observer.created()[0].request_id());
  EXPECT_EQ(kTestRequestType, observer.created()[0].type());

  ASSERT_EQ(1u, observer.executed().size());
  EXPECT_EQ(request_id, observer.executed()[0].request_id());

  const bool has_more = true;

  const base::File::Error result =
      request_manager_->FulfillRequest(request_id, RequestValue(), has_more);
  EXPECT_EQ(base::File::FILE_OK, result);

  // Validate if the callback has correct arguments.
  ASSERT_EQ(1u, logger.success_events().size());
  EXPECT_EQ(0u, logger.error_events().size());
  EventLogger::SuccessEvent* event = logger.success_events()[0].get();
  EXPECT_FALSE(event->result_is_valid());
  EXPECT_TRUE(event->has_more());

  ASSERT_EQ(1u, observer.fulfilled().size());
  EXPECT_EQ(request_id, observer.fulfilled()[0].request_id());
  EXPECT_TRUE(observer.fulfilled()[0].has_more());

  // Confirm, that the request is not removed (since it has has_more == true).
  // Basically, fulfilling again for the same request, should not fail.
  {
    const std::vector<int> active_request_ids =
        request_manager_->GetActiveRequestIds();
    ASSERT_EQ(1u, active_request_ids.size());
    EXPECT_EQ(request_id, active_request_ids[0]);

    const bool new_has_more = false;
    const base::File::Error retry = request_manager_->FulfillRequest(
        request_id, RequestValue(), new_has_more);
    EXPECT_EQ(base::File::FILE_OK, retry);

    ASSERT_EQ(2u, observer.fulfilled().size());
    EXPECT_EQ(request_id, observer.fulfilled()[1].request_id());
    EXPECT_FALSE(observer.fulfilled()[1].has_more());
  }

  // Since |new_has_more| is false, then the request should be removed. To check
  // it, try to fulfill again, what should fail.
  {
    const std::vector<int> active_request_ids =
        request_manager_->GetActiveRequestIds();
    EXPECT_EQ(0u, active_request_ids.size());

    const bool new_has_more = false;
    const base::File::Error retry = request_manager_->FulfillRequest(
        request_id, RequestValue(), new_has_more);
    EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, retry);
    EXPECT_EQ(0u, observer.rejected().size());
  }

  ASSERT_EQ(1u, observer.destroyed().size());
  EXPECT_EQ(request_id, observer.destroyed()[0].request_id());
  EXPECT_EQ(0u, observer.timed_out().size());

  request_manager_->RemoveObserver(&observer);
}

TEST_F(FileSystemProviderRequestManagerTest, CreateAndReject) {
  EventLogger logger;
  RequestObserver observer;
  request_manager_->AddObserver(&observer);

  const int request_id = request_manager_->CreateRequest(
      kTestRequestType,
      base::WrapUnique<RequestManager::HandlerInterface>(
          new FakeHandler(logger.GetWeakPtr(), /*execute_reply=*/true)));

  EXPECT_EQ(1, request_id);
  EXPECT_EQ(0u, logger.success_events().size());
  EXPECT_EQ(0u, logger.error_events().size());

  ASSERT_EQ(1u, observer.created().size());
  EXPECT_EQ(request_id, observer.created()[0].request_id());
  EXPECT_EQ(kTestRequestType, observer.created()[0].type());

  ASSERT_EQ(1u, observer.executed().size());
  EXPECT_EQ(request_id, observer.executed()[0].request_id());

  const base::File::Error error = base::File::FILE_ERROR_NO_MEMORY;
  const base::File::Error result =
      request_manager_->RejectRequest(request_id, RequestValue(), error);
  EXPECT_EQ(base::File::FILE_OK, result);

  // Validate if the callback has correct arguments.
  ASSERT_EQ(1u, logger.error_events().size());
  EXPECT_EQ(0u, logger.success_events().size());
  EventLogger::ErrorEvent* event = logger.error_events()[0].get();
  EXPECT_EQ(error, event->error());

  ASSERT_EQ(1u, observer.rejected().size());
  EXPECT_EQ(request_id, observer.rejected()[0].request_id());
  EXPECT_EQ(error, observer.rejected()[0].error());

  // Confirm, that the request is removed. Basically, fulfilling again for the
  // same request, should fail.
  {
    const bool has_more = false;
    const base::File::Error retry =
        request_manager_->FulfillRequest(request_id, RequestValue(), has_more);
    EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, retry);
    EXPECT_EQ(0u, observer.fulfilled().size());
  }

  // Rejecting should also fail.
  {
    const base::File::Error retry =
        request_manager_->RejectRequest(request_id, RequestValue(), error);
    EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, retry);
    EXPECT_EQ(1u, observer.rejected().size());
  }

  ASSERT_EQ(1u, observer.destroyed().size());
  EXPECT_EQ(request_id, observer.destroyed()[0].request_id());
  EXPECT_EQ(OperationCompletion::kCompletedNormally,
            observer.destroyed()[0].completion());
  EXPECT_EQ(0u, observer.timed_out().size());

  request_manager_->RemoveObserver(&observer);
}

TEST_F(FileSystemProviderRequestManagerTest,
       CreateAndFulfillWithWrongRequestId) {
  EventLogger logger;
  RequestObserver observer;
  request_manager_->AddObserver(&observer);

  const int request_id = request_manager_->CreateRequest(
      kTestRequestType,
      base::WrapUnique<RequestManager::HandlerInterface>(
          new FakeHandler(logger.GetWeakPtr(), /*execute_reply=*/true)));

  EXPECT_EQ(1, request_id);
  EXPECT_EQ(0u, logger.success_events().size());
  EXPECT_EQ(0u, logger.error_events().size());

  ASSERT_EQ(1u, observer.created().size());
  EXPECT_EQ(request_id, observer.created()[0].request_id());
  EXPECT_EQ(kTestRequestType, observer.created()[0].type());

  ASSERT_EQ(1u, observer.executed().size());
  EXPECT_EQ(request_id, observer.executed()[0].request_id());

  const bool has_more = true;

  const base::File::Error result = request_manager_->FulfillRequest(
      request_id + 1, RequestValue(), has_more);
  EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, result);

  // Callbacks should not be called.
  EXPECT_EQ(0u, logger.error_events().size());
  EXPECT_EQ(0u, logger.success_events().size());

  EXPECT_EQ(0u, observer.fulfilled().size());
  EXPECT_EQ(request_id, observer.executed()[0].request_id());

  // Confirm, that the request hasn't been removed, by fulfilling it correctly.
  {
    const base::File::Error retry =
        request_manager_->FulfillRequest(request_id, RequestValue(), has_more);
    EXPECT_EQ(base::File::FILE_OK, retry);
    EXPECT_EQ(1u, observer.fulfilled().size());
  }

  request_manager_->RemoveObserver(&observer);
}

TEST_F(FileSystemProviderRequestManagerTest,
       CreateAndRejectWithWrongRequestId) {
  EventLogger logger;
  RequestObserver observer;
  request_manager_->AddObserver(&observer);

  const int request_id = request_manager_->CreateRequest(
      kTestRequestType,
      base::WrapUnique<RequestManager::HandlerInterface>(
          new FakeHandler(logger.GetWeakPtr(), /*execute_reply=*/true)));

  EXPECT_EQ(1, request_id);
  EXPECT_EQ(0u, logger.success_events().size());
  EXPECT_EQ(0u, logger.error_events().size());

  ASSERT_EQ(1u, observer.created().size());
  EXPECT_EQ(request_id, observer.created()[0].request_id());
  EXPECT_EQ(kTestRequestType, observer.created()[0].type());

  ASSERT_EQ(1u, observer.executed().size());
  EXPECT_EQ(request_id, observer.executed()[0].request_id());

  const base::File::Error error = base::File::FILE_ERROR_NO_MEMORY;
  const base::File::Error result =
      request_manager_->RejectRequest(request_id + 1, RequestValue(), error);
  EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, result);

  // Callbacks should not be called.
  EXPECT_EQ(0u, logger.error_events().size());
  EXPECT_EQ(0u, logger.success_events().size());

  EXPECT_EQ(0u, observer.rejected().size());

  // Confirm, that the request hasn't been removed, by rejecting it correctly.
  {
    const base::File::Error retry =
        request_manager_->RejectRequest(request_id, RequestValue(), error);
    EXPECT_EQ(base::File::FILE_OK, retry);
    EXPECT_EQ(1u, observer.rejected().size());
  }

  request_manager_->RemoveObserver(&observer);
}

TEST_F(FileSystemProviderRequestManagerTest, UniqueIds) {
  EventLogger logger;

  const int first_request_id = request_manager_->CreateRequest(
      kTestRequestType,
      base::WrapUnique<RequestManager::HandlerInterface>(
          new FakeHandler(logger.GetWeakPtr(), /*execute_reply=*/true)));

  const int second_request_id = request_manager_->CreateRequest(
      kTestRequestType,
      base::WrapUnique<RequestManager::HandlerInterface>(
          new FakeHandler(logger.GetWeakPtr(), /*execute_reply=*/true)));

  EXPECT_EQ(1, first_request_id);
  EXPECT_EQ(2, second_request_id);
}

TEST_F(FileSystemProviderRequestManagerTest, AbortOnDestroy) {
  EventLogger logger;
  RequestObserver observer;
  int request_id;

  {
    OperationRequestManager request_manager(
        profile_.get(), /*provider_id=*/std::string(),
        /*notification_manager=*/nullptr, /*timeout=*/base::Seconds(10));
    request_manager.AddObserver(&observer);

    request_id = request_manager.CreateRequest(
        kTestRequestType,
        base::WrapUnique<RequestManager::HandlerInterface>(
            new FakeHandler(logger.GetWeakPtr(), /*execute_reply=*/true)));

    EXPECT_EQ(1, request_id);
    EXPECT_EQ(0u, logger.success_events().size());
    EXPECT_EQ(0u, logger.error_events().size());
    EXPECT_EQ(0u, logger.abort_events().size());

    ASSERT_EQ(1u, observer.created().size());
    EXPECT_EQ(request_id, observer.created()[0].request_id());
    EXPECT_EQ(kTestRequestType, observer.created()[0].type());

    ASSERT_EQ(1u, observer.executed().size());
    EXPECT_EQ(request_id, observer.executed()[0].request_id());

    EXPECT_EQ(0u, observer.fulfilled().size());
    EXPECT_EQ(0u, observer.rejected().size());
    EXPECT_EQ(0u, observer.destroyed().size());
    EXPECT_EQ(0u, observer.timed_out().size());

    // Do not remove the observer, to catch events while destructing.
  }

  // All active requests should be aborted in the destructor of RequestManager.
  ASSERT_EQ(1u, logger.error_events().size());
  EventLogger::ErrorEvent* event = logger.error_events()[0].get();
  EXPECT_EQ(base::File::FILE_ERROR_ABORT, event->error());
  ASSERT_EQ(1u, logger.abort_events().size());
  EXPECT_EQ(request_id, logger.abort_events()[0]->request_id());

  EXPECT_EQ(0u, logger.success_events().size());

  EXPECT_EQ(0u, observer.fulfilled().size());
  EXPECT_EQ(0u, observer.timed_out().size());
  ASSERT_EQ(1u, observer.rejected().size());
  EXPECT_EQ(request_id, observer.rejected()[0].request_id());
  EXPECT_EQ(base::File::FILE_ERROR_ABORT, observer.rejected()[0].error());
  ASSERT_EQ(1u, observer.destroyed().size());
  EXPECT_EQ(OperationCompletion::kAbortedInternally,
            observer.destroyed()[0].completion());
}

TEST_F(FileSystemProviderRequestManagerTest, AbortOnTimeout) {
  EventLogger logger;
  RequestObserver observer;
  request_manager_->AddObserver(&observer);

  request_manager_->SetTimeoutForTesting(base::Seconds(0));
  const int request_id = request_manager_->CreateRequest(
      kTestRequestType,
      base::WrapUnique<RequestManager::HandlerInterface>(
          new FakeHandler(logger.GetWeakPtr(), /*execute_reply=*/true)));
  EXPECT_EQ(1, request_id);
  EXPECT_EQ(0u, logger.success_events().size());
  EXPECT_EQ(0u, logger.error_events().size());
  EXPECT_EQ(0u, notification_manager_->size());

  ASSERT_EQ(1u, observer.created().size());
  EXPECT_EQ(request_id, observer.created()[0].request_id());
  EXPECT_EQ(kTestRequestType, observer.created()[0].type());

  ASSERT_EQ(1u, observer.executed().size());
  EXPECT_EQ(request_id, observer.executed()[0].request_id());

  // Wait until the request is timed out.
  base::RunLoop().RunUntilIdle();

  // Abort the request.
  EXPECT_EQ(1u, notification_manager_->size());
  notification_manager_->Abort();
  EXPECT_EQ(0u, notification_manager_->size());

  ASSERT_EQ(1u, logger.error_events().size());
  EventLogger::ErrorEvent* event = logger.error_events()[0].get();
  EXPECT_EQ(base::File::FILE_ERROR_ABORT, event->error());
  ASSERT_EQ(1u, logger.abort_events().size());
  EXPECT_EQ(request_id, logger.abort_events()[0]->request_id());

  ASSERT_EQ(1u, observer.rejected().size());
  EXPECT_EQ(request_id, observer.rejected()[0].request_id());
  EXPECT_EQ(base::File::FILE_ERROR_ABORT, observer.rejected()[0].error());
  ASSERT_EQ(1u, observer.timed_out().size());
  EXPECT_EQ(request_id, observer.timed_out()[0].request_id());
  ASSERT_EQ(1u, observer.destroyed().size());
  EXPECT_EQ(request_id, observer.destroyed()[0].request_id());
  EXPECT_EQ(OperationCompletion::kAbortedFromNotification,
            observer.destroyed()[0].completion());

  request_manager_->RemoveObserver(&observer);
}

TEST_F(FileSystemProviderRequestManagerTest, ContinueOnTimeout) {
  EventLogger logger;
  RequestObserver observer;
  request_manager_->AddObserver(&observer);

  request_manager_->SetTimeoutForTesting(base::Seconds(0));
  const int request_id = request_manager_->CreateRequest(
      kTestRequestType,
      base::WrapUnique<RequestManager::HandlerInterface>(
          new FakeHandler(logger.GetWeakPtr(), /*execute_reply=*/true)));
  EXPECT_EQ(1, request_id);
  EXPECT_EQ(0u, logger.success_events().size());
  EXPECT_EQ(0u, logger.error_events().size());
  EXPECT_EQ(0u, logger.abort_events().size());
  EXPECT_EQ(0u, notification_manager_->size());

  ASSERT_EQ(1u, observer.created().size());
  EXPECT_EQ(request_id, observer.created()[0].request_id());
  EXPECT_EQ(kTestRequestType, observer.created()[0].type());

  ASSERT_EQ(1u, observer.executed().size());
  EXPECT_EQ(request_id, observer.executed()[0].request_id());

  // Wait until the request is timed out.
  base::RunLoop().RunUntilIdle();

  // Let the provider have more time by closing the notification.
  EXPECT_EQ(1u, notification_manager_->size());
  notification_manager_->Continue();
  EXPECT_EQ(0u, notification_manager_->size());

  // The request is still active.
  EXPECT_EQ(0u, logger.success_events().size());
  EXPECT_EQ(0u, logger.error_events().size());
  EXPECT_EQ(0u, logger.abort_events().size());

  // Wait until the request is timed out again.
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(1u, notification_manager_->size());

  // Fulfill and check that operation completion mode changes.
  const base::File::Error result = request_manager_->FulfillRequest(
      request_id,
      RequestValue(RequestValue::CreateForTesting("i-like-vanilla")),
      /*has_more=*/false);
  EXPECT_EQ(base::File::FILE_OK, result);

  ASSERT_EQ(1u, observer.fulfilled().size());
  ASSERT_EQ(1u, observer.destroyed().size());
  EXPECT_EQ(OperationCompletion::kCompletedAfterWarning,
            observer.destroyed()[0].completion());

  request_manager_->RemoveObserver(&observer);
}

TEST_F(FileSystemProviderRequestManagerTest, NoNotificationWhileInteractive) {
  EventLogger logger;
  RequestObserver observer;
  base::ScopedObservation<RequestManager, RequestManager::Observer> observation(
      &observer);
  observation.Observe(request_manager_.get());

  request_manager_->StartUserInteraction();

  request_manager_->SetTimeoutForTesting(base::Seconds(0));
  int request_id = request_manager_->CreateRequest(
      kTestRequestType,
      base::WrapUnique<RequestManager::HandlerInterface>(
          new FakeHandler(logger.GetWeakPtr(), /*execute_reply=*/true)));
  EXPECT_EQ(1, request_id);
  EXPECT_EQ(0u, logger.success_events().size());
  EXPECT_EQ(0u, logger.error_events().size());
  EXPECT_EQ(0u, logger.abort_events().size());
  EXPECT_EQ(0u, notification_manager_->size());

  ASSERT_EQ(1u, observer.created().size());
  EXPECT_EQ(request_id, observer.created()[0].request_id());
  EXPECT_EQ(kTestRequestType, observer.created()[0].type());

  ASSERT_EQ(1u, observer.executed().size());
  EXPECT_EQ(request_id, observer.executed()[0].request_id());

  // Wait until the request is timed out.
  TimeoutWaiter(request_manager_.get()).Wait();

  // Should not have shown a notification.
  EXPECT_EQ(0u, notification_manager_->size());

  // The request is still active.
  EXPECT_EQ(0u, logger.success_events().size());
  EXPECT_EQ(0u, logger.error_events().size());
  EXPECT_EQ(0u, logger.abort_events().size());

  // No longer interacting with the user.
  request_manager_->EndUserInteraction();

  // Wait until the request is timed out again.
  TimeoutWaiter(request_manager_.get()).Wait();

  // Shown a notification.
  EXPECT_EQ(1u, notification_manager_->size());
}

}  // namespace ash::file_system_provider