chromium/chromeos/ash/components/data_migration/testing/fake_nearby_connections.h

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

#ifndef CHROMEOS_ASH_COMPONENTS_DATA_MIGRATION_TESTING_FAKE_NEARBY_CONNECTIONS_H_
#define CHROMEOS_ASH_COMPONENTS_DATA_MIGRATION_TESTING_FAKE_NEARBY_CONNECTIONS_H_

#include <cstdint>
#include <string>
#include <string_view>
#include <vector>

#include "base/component_export.h"
#include "chromeos/ash/services/nearby/public/mojom/nearby_connections.mojom.h"
#include "chromeos/ash/services/nearby/public/mojom/nearby_connections_types.mojom.h"
#include "mojo/public/cpp/bindings/remote.h"

namespace data_migration {

// Purpose-built for data migration. Acts as the remote device (the one
// transferring data to the ChromeOS device) in tests.
class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_DATA_MIGRATION)
    FakeNearbyConnections
    : public ::nearby::connections::mojom::NearbyConnections {
 public:
  using PayloadStatus = ::nearby::connections::mojom::PayloadStatus;
  using Status = ::nearby::connections::mojom::Status;

  // `remote_endpoint_id` is the id of the simulated remote device from whom
  // data will be transferred.
  explicit FakeNearbyConnections(std::string_view remote_endpoint_id);
  FakeNearbyConnections(const FakeNearbyConnections&) = delete;
  FakeNearbyConnections& operator=(const FakeNearbyConnections&) = delete;
  ~FakeNearbyConnections() override;

  // Simulates a file being sent from the remote device (played by
  // `FakeNearbyConnections`) to the local device. The contents of the file
  // are randomly generated. All transferred file bytes are returned to the
  // caller via `transferred_bytes` if it's non-null.
  //
  // Returns true if file transmission was successfully simulated, false if any
  // error prevents the simulation.
  bool SendFile(int64_t payload_id,
                std::vector<uint8_t>* transferred_bytes = nullptr);

  // Sets the final payload status for all future `SendFile()` calls with
  // matching `payload_id`. Can be used to simulate file transfer failures.
  //
  // All file payloads with id not matching `payload_id` will return `kSuccess`.
  void SetFinalFilePayloadStatus(PayloadStatus status, int64_t payload_id);

  // The `register_payload_file_result_generator` is invoked for each call to
  // `RegisterPayloadFile()` and returns the `Status` of the operation.
  // By default, the generator is null and `RegisterPayloadFile()` succeeds.
  void set_register_payload_file_result_generator(
      base::RepeatingCallback<Status()>
          register_payload_file_result_generator) {
    register_payload_file_result_generator_ =
        std::move(register_payload_file_result_generator);
  }

  // Invoked every time a payload is sent from the local device to the remote
  // device.
  void set_local_to_remote_payload_listener(
      base::RepeatingCallback<void(::nearby::connections::mojom::PayloadPtr)>
          local_to_remote_payload_listener) {
    local_to_remote_payload_listener_ =
        std::move(local_to_remote_payload_listener);
  }

  // Invoked when both sides of the connection have been accepted, and payloads
  // can be exchanged.
  void set_connection_established_listener(
      base::RepeatingClosure connection_established_listener) {
    connection_established_listener_ =
        std::move(connection_established_listener);
  }

  // Size of all test files transmitted via `SendFile()`. Defaults to 1000.
  void set_test_file_size_in_bytes(size_t test_file_size_in_bytes) {
    test_file_size_in_bytes_ = test_file_size_in_bytes;
  }
  int test_file_size_in_bytes() const { return test_file_size_in_bytes_; }

  // Number of chunks into which files transmitted via `SendFile()` are divided.
  // Each chunk has the same size:
  // `test_file_size_in_bytes_` / `test_file_num_chunks_`.
  //
  // Defaults to 4.
  void set_test_file_num_chunks(size_t test_file_num_chunks) {
    test_file_num_chunks_ = test_file_num_chunks;
  }

  // Simulates a bytes payload being sent from the remote device to the local
  // device.
  //
  // Returns true if file transmission was successfully simulated, false if any
  // error prevents the simulation.
  [[nodiscard]] bool SendBytesPayload(int64_t payload_id,
                                      const std::string& bytes);

  // Simulates remote device disconnecting from the local device. Returns true
  // if simulation was successful; false if the remote device is not connected
  // to begin with.
  [[nodiscard]] bool SimulateRemoteDisconnect();

 private:
  // See `RegisterPayloadFile()` method.
  struct RegisteredFilePayload {
    RegisteredFilePayload();
    RegisteredFilePayload(base::File input_file_in, base::File output_file_in);
    RegisteredFilePayload(RegisteredFilePayload&&);
    RegisteredFilePayload& operator=(RegisteredFilePayload&&);
    ~RegisteredFilePayload();

    base::File input_file;
    base::File output_file;
  };

  // ::nearby::connections::mojom::NearbyConnections:
  void StartAdvertising(
      const std::string& service_id,
      const std::vector<uint8_t>& endpoint_info,
      ::nearby::connections::mojom::AdvertisingOptionsPtr options,
      mojo::PendingRemote<
          ::nearby::connections::mojom::ConnectionLifecycleListener> listener,
      StartAdvertisingCallback callback) override;
  void StopAdvertising(const std::string& service_id,
                       StopAdvertisingCallback callback) override;
  void StartDiscovery(
      const std::string& service_id,
      ::nearby::connections::mojom::DiscoveryOptionsPtr options,
      mojo::PendingRemote<
          ::nearby::connections::mojom::EndpointDiscoveryListener> listener,
      StartDiscoveryCallback callback) override;
  void StopDiscovery(const std::string& service_id,
                     StopDiscoveryCallback callback) override;
  void InjectBluetoothEndpoint(
      const std::string& service_id,
      const std::string& endpoint_id,
      const std::vector<uint8_t>& endpoint_info,
      const std::vector<uint8_t>& remote_bluetooth_mac_address,
      InjectBluetoothEndpointCallback callback) override;
  void RequestConnection(
      const std::string& service_id,
      const std::vector<uint8_t>& endpoint_info,
      const std::string& endpoint_id,
      ::nearby::connections::mojom::ConnectionOptionsPtr options,
      mojo::PendingRemote<
          ::nearby::connections::mojom::ConnectionLifecycleListener> listener,
      RequestConnectionCallback callback) override;
  void DisconnectFromEndpoint(const std::string& service_id,
                              const std::string& endpoint_id,
                              DisconnectFromEndpointCallback callback) override;
  void AcceptConnection(
      const std::string& service_id,
      const std::string& endpoint_id,
      mojo::PendingRemote<::nearby::connections::mojom::PayloadListener>
          listener,
      AcceptConnectionCallback callback) override;
  void RejectConnection(const std::string& service_id,
                        const std::string& endpoint_id,
                        RejectConnectionCallback callback) override;
  void SendPayload(const std::string& service_id,
                   const std::vector<std::string>& endpoint_ids,
                   ::nearby::connections::mojom::PayloadPtr payload,
                   SendPayloadCallback callback) override;
  void CancelPayload(const std::string& service_id,
                     int64_t payload_id,
                     CancelPayloadCallback callback) override;
  void StopAllEndpoints(const std::string& service_id,
                        StopAllEndpointsCallback callback) override;
  void InitiateBandwidthUpgrade(
      const std::string& service_id,
      const std::string& endpoint_id,
      InitiateBandwidthUpgradeCallback callback) override;
  void RegisterPayloadFile(const std::string& service_id,
                           int64_t payload_id,
                           base::File input_file,
                           base::File output_file,
                           RegisterPayloadFileCallback callback) override;
  void RequestConnectionV3(
      const std::string& service_id,
      ash::nearby::presence::mojom::PresenceDevicePtr remote_device,
      ::nearby::connections::mojom::ConnectionOptionsPtr connection_options,
      mojo::PendingRemote<::nearby::connections::mojom::ConnectionListenerV3>
          listener,
      RequestConnectionV3Callback callback) override;
  void AcceptConnectionV3(
      const std::string& service_id,
      ash::nearby::presence::mojom::PresenceDevicePtr remote_device,
      mojo::PendingRemote<::nearby::connections::mojom::PayloadListenerV3>
          listener,
      AcceptConnectionV3Callback callback) override;
  void RejectConnectionV3(
      const std::string& service_id,
      ash::nearby::presence::mojom::PresenceDevicePtr remote_device,
      RejectConnectionV3Callback callback) override;
  void DisconnectFromDeviceV3(
      const std::string& service_id,
      ash::nearby::presence::mojom::PresenceDevicePtr remote_device,
      DisconnectFromDeviceV3Callback callback) override;
  void RegisterServiceWithPresenceDeviceProvider(
      const std::string& service_id) override;

  const std::string remote_endpoint_id_;
  bool is_advertising_ = false;

  // Conceptually, both the `connection_listener_` and the
  // `remote_to_local_payload_listener_` are the target ChromeOS device that is
  // receiving data.
  //
  // Set during the discovery/advertising process.
  mojo::Remote<::nearby::connections::mojom::ConnectionLifecycleListener>
      connection_listener_;
  // Set during the payload transfer process (after connection is established).
  mojo::Remote<::nearby::connections::mojom::PayloadListener>
      remote_to_local_payload_listener_;

  base::flat_map</*payload_id*/ int64_t, RegisteredFilePayload>
      registered_files_;
  base::flat_map</*payload_id*/ int64_t, PayloadStatus>
      final_file_payload_status_;
  base::RepeatingCallback<Status()> register_payload_file_result_generator_;
  base::RepeatingCallback<void(::nearby::connections::mojom::PayloadPtr)>
      local_to_remote_payload_listener_;
  base::RepeatingClosure connection_established_listener_;

  size_t test_file_size_in_bytes_ = 1000;
  size_t test_file_num_chunks_ = 4;
};

}  // namespace data_migration

#endif  // CHROMEOS_ASH_COMPONENTS_DATA_MIGRATION_TESTING_FAKE_NEARBY_CONNECTIONS_H_