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

#include "device/bluetooth/floss/floss_manager_client.h"

#include <utility>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/task_environment.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/mock_bus.h"
#include "dbus/mock_exported_object.h"
#include "dbus/mock_object_proxy.h"
#include "dbus/object_manager.h"
#include "dbus/object_path.h"
#include "device/bluetooth/floss/floss_dbus_client.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace floss {
namespace {

using testing::DoAll;

const std::vector<std::pair<int, bool>> kMockAdaptersAvailable = {{0, false},
                                                                  {5, true}};

void FakeExportMethod(
    const std::string& interface_name,
    const std::string& method_name,
    const dbus::ExportedObject::MethodCallCallback& method_call_callback,
    dbus::ExportedObject::OnExportedCallback on_exported_callback) {
  std::move(on_exported_callback)
      .Run(interface_name, method_name, /*success=*/true);
}

class TestManagerObserver : public FlossManagerClient::Observer {
 public:
  explicit TestManagerObserver(FlossManagerClient* client) : client_(client) {
    client_->AddObserver(this);
  }

  ~TestManagerObserver() override { client_->RemoveObserver(this); }

  void ManagerPresent(bool present) override {
    manager_present_count_++;
    manager_present_ = present;
  }

  void AdapterPresent(int adapter, bool present) override {
    adapter_present_count_++;
    adapter_present_[adapter] = present;
  }

  void AdapterEnabledChanged(int adapter, bool enabled) override {
    adapter_enabled_changed_count_++;
    adapter_enabled_[adapter] = enabled;
  }

  int manager_present_count_ = 0;
  int adapter_present_count_ = 0;
  int adapter_enabled_changed_count_ = 0;

  bool manager_present_ = false;
  std::map<int, bool> adapter_present_;
  std::map<int, bool> adapter_enabled_;

 private:
  raw_ptr<FlossManagerClient> client_ = nullptr;
};

}  // namespace

class FlossManagerClientTest : public testing::Test {
 public:
  FlossManagerClientTest() = default;

  base::Version GetCurrVersion() {
    return floss::version::GetMaximalSupportedVersion();
  }

  void SetUpMocks() {
    auto obj_mgr_path =
        ::dbus::ObjectPath(FlossManagerClient::kObjectManagerPath);

    manager_object_proxy_ = base::MakeRefCounted<::dbus::MockObjectProxy>(
        bus_.get(), kManagerInterface, ::dbus::ObjectPath(kManagerObject));

    exported_callbacks_ = base::MakeRefCounted<::dbus::MockExportedObject>(
        bus_.get(),
        ::dbus::ObjectPath(FlossManagerClient::kExportedCallbacksPath));

    // Make sure a valid object manager is returned
    EXPECT_CALL(*bus_.get(), GetObjectProxy(kManagerInterface, obj_mgr_path))
        .WillOnce(::testing::Return(manager_object_proxy_.get()));
    EXPECT_CALL(*bus_.get(), GetDBusTaskRunner())
        .WillOnce(::testing::Return(
            ::base::SequencedTaskRunner::GetCurrentDefault().get()));
    object_manager_ = ::dbus::ObjectManager::Create(
        bus_.get(), kManagerInterface, obj_mgr_path);
    EXPECT_CALL(*bus_.get(),
                GetObjectManager(kManagerInterface, dbus::ObjectPath("/")))
        .WillOnce(::testing::Return(object_manager_.get()));

    // Set up expects for remaining interfaces
    EXPECT_CALL(*bus_.get(), GetObjectProxy(kManagerInterface,
                                            ::dbus::ObjectPath(kManagerObject)))
        .WillRepeatedly(::testing::Return(manager_object_proxy_.get()));
    EXPECT_CALL(*bus_.get(), GetExportedObject)
        .WillRepeatedly(::testing::Return(exported_callbacks_.get()));

    // Exported callback methods that we don't need to invoke.
    EXPECT_CALL(*exported_callbacks_.get(), ExportMethod)
        .Times(1)
        .WillRepeatedly(&FakeExportMethod);
    // Save method handlers of exported callbacks that we need to invoke here.
    EXPECT_CALL(
        *exported_callbacks_.get(),
        ExportMethod(manager::kCallbackInterface, manager::kOnHciDeviceChanged,
                     testing::_, testing::_))
        .WillOnce(DoAll(testing::SaveArg<2>(&on_hci_device_changed_),
                        &FakeExportMethod));
    EXPECT_CALL(
        *exported_callbacks_.get(),
        ExportMethod(manager::kCallbackInterface, manager::kOnHciEnabledChanged,
                     testing::_, testing::_))
        .WillOnce(DoAll(testing::SaveArg<2>(&on_hci_enabled_changed_),
                        &FakeExportMethod));

    // Handle method calls on the object proxy
    ON_CALL(*manager_object_proxy_.get(), CallMethodWithErrorResponse)
        .WillByDefault([this](::dbus::MethodCall* method_call, int timeout_ms,
                              ::dbus::ObjectProxy::ResponseOrErrorCallback cb) {
          if (method_call->GetMember() == manager::kGetAvailableAdapters) {
            HandleGetAvailableAdapters(method_call, timeout_ms, std::move(cb));
          } else if (method_call->GetMember() == manager::kGetAdapterEnabled) {
            HandleGetAdapterEnabled(method_call, timeout_ms, std::move(cb));
          } else if (method_call->GetMember() == manager::kSetFlossEnabled) {
            HandleSetFlossEnabled(method_call, timeout_ms, std::move(cb));
          } else if (method_call->GetMember() == manager::kGetFlossEnabled) {
            HandleGetFlossEnabled(method_call, timeout_ms, std::move(cb));
          } else if (method_call->GetMember() == manager::kGetFlossApiVersion) {
            HandleGetFlossApiVersion(method_call, timeout_ms, std::move(cb));
          }

          method_called_[method_call->GetMember()]++;
        });
  }

  void SendHciDeviceCallback(int adapter,
                             bool present,
                             dbus::ExportedObject::ResponseSender response) {
    dbus::MethodCall method_call(manager::kCallbackInterface,
                                 manager::kOnHciDeviceChanged);
    method_call.SetSerial(serial_++);
    dbus::MessageWriter writer(&method_call);
    writer.AppendInt32(adapter);
    writer.AppendBool(present);

    on_hci_device_changed_.Run(&method_call, std::move(response));
  }

  void SendInvalidHciDeviceCallback(dbus::ExportedObject::ResponseSender rsp) {
    dbus::MethodCall method_call(manager::kCallbackInterface,
                                 manager::kOnHciDeviceChanged);
    method_call.SetSerial(serial_++);
    on_hci_device_changed_.Run(&method_call, std::move(rsp));
  }

  void SendHciEnabledCallback(int adapter,
                              bool enabled,
                              dbus::ExportedObject::ResponseSender response) {
    dbus::MethodCall method_call(manager::kCallbackInterface,
                                 manager::kOnHciEnabledChanged);
    method_call.SetSerial(serial_++);
    dbus::MessageWriter writer(&method_call);
    writer.AppendInt32(adapter);
    writer.AppendBool(enabled);

    on_hci_enabled_changed_.Run(&method_call, std::move(response));
  }

  void SendInvalidHciEnabledCallback(dbus::ExportedObject::ResponseSender rsp) {
    dbus::MethodCall method_call(manager::kCallbackInterface,
                                 manager::kOnHciEnabledChanged);
    method_call.SetSerial(serial_++);
    on_hci_enabled_changed_.Run(&method_call, std::move(rsp));
  }

  void SetUp() override {
    ::dbus::Bus::Options options;
    options.bus_type = ::dbus::Bus::BusType::SYSTEM;
    bus_ = base::MakeRefCounted<::dbus::MockBus>(std::move(options));
    client_ = FlossManagerClient::Create();

    SetUpMocks();
  }

  void TearDown() override {
    // Clean up the client first so it gets rid of all its references to the
    // various buses, object proxies, etc.
    client_.reset();
    method_called_.clear();
  }

  void HandleGetAvailableAdapters(
      ::dbus::MethodCall* method_call,
      int timeout_ms,
      ::dbus::ObjectProxy::ResponseOrErrorCallback cb) {
    // Return that there are 2 adapter objects
    auto response = ::dbus::Response::CreateEmpty();

    ::dbus::MessageWriter msg(response.get());
    ::dbus::MessageWriter outer(nullptr);
    msg.OpenArray("a{sv}", &outer);
    for (auto kv : kMockAdaptersAvailable) {
      ::dbus::MessageWriter inner(nullptr);
      ::dbus::MessageWriter dict(nullptr);
      outer.OpenArray("{sv}", &inner);

      inner.OpenDictEntry(&dict);
      dict.AppendString("hci_interface");
      dict.AppendVariantOfInt32(kv.first);
      inner.CloseContainer(&dict);

      inner.OpenDictEntry(&dict);
      dict.AppendString("enabled");
      dict.AppendVariantOfBool(kv.second);
      inner.CloseContainer(&dict);

      outer.CloseContainer(&inner);
    }
    msg.CloseContainer(&outer);

    std::move(cb).Run(response.get(), nullptr);
  }

  void HandleGetAdapterEnabled(
      ::dbus::MethodCall* method_call,
      int timeout_ms,
      ::dbus::ObjectProxy::ResponseOrErrorCallback cb) {
    auto response = ::dbus::Response::CreateEmpty();
    ::dbus::MessageWriter writer(response.get());
    writer.AppendBool(get_adapter_enabled_return_);
    std::move(cb).Run(response.get(), nullptr);
  }

  void HandleSetFlossEnabled(::dbus::MethodCall* method_call,
                             int timeout_ms,
                             ::dbus::ObjectProxy::ResponseOrErrorCallback cb) {
    method_call->SetSerial(serial_++);
    if (fail_setfloss_count_ > 0) {
      fail_setfloss_count_--;

      std::string error_name("org.foo.bar");
      std::string error_message("SetFlossEnabled failed");
      auto error = ::dbus::ErrorResponse::FromMethodCall(
          method_call, error_name, error_message);
      std::move(cb).Run(nullptr, error.get());
    } else {
      auto response = ::dbus::Response::CreateEmpty();
      std::move(cb).Run(response.get(), nullptr);
    }
  }

  void HandleGetFlossEnabled(::dbus::MethodCall* method_call,
                             int timeout_ms,
                             ::dbus::ObjectProxy::ResponseOrErrorCallback cb) {
    method_call->SetSerial(serial_++);
    if (fail_getfloss_count_ > 0) {
      fail_getfloss_count_--;

      std::string error_name("org.foo.bar");
      std::string error_message("GetFlossEnabled failed");
      auto error = ::dbus::ErrorResponse::FromMethodCall(
          method_call, error_name, error_message);
      std::move(cb).Run(nullptr, error.get());
    } else {
      auto response = ::dbus::Response::CreateEmpty();
      ::dbus::MessageWriter writer(response.get());
      writer.AppendBool(floss_enabled_target_);
      std::move(cb).Run(response.get(), nullptr);
    }
  }

  void HandleGetFlossApiVersion(
      ::dbus::MethodCall* method_call,
      int timeout_ms,
      ::dbus::ObjectProxy::ResponseOrErrorCallback cb) {
    auto response = ::dbus::Response::CreateEmpty();
    ::dbus::MessageWriter writer(response.get());
    writer.AppendUint32(floss_api_version_);
    std::move(cb).Run(response.get(), nullptr);
  }

  void ExpectErrorResponse(std::unique_ptr<dbus::Response> response) {
    EXPECT_EQ(response->GetMessageType(),
              dbus::Message::MessageType::MESSAGE_ERROR);
  }

  void ExpectNormalResponse(std::unique_ptr<dbus::Response> response) {
    EXPECT_NE(response->GetMessageType(),
              dbus::Message::MessageType::MESSAGE_ERROR);
  }

  void TriggerObjectAdded(dbus::ObjectPath& path,
                          const std::string& interface) {
    client_->ObjectAdded(path, interface);
  }

  void TriggerObjectRemoved(dbus::ObjectPath& path,
                            const std::string& interface) {
    client_->ObjectRemoved(path, interface);
  }

  bool IsInterfaceRegisteredOnObjectManager(const std::string& interface) {
    return client_->object_manager_ != nullptr &&
           client_->object_manager_->IsInterfaceRegisteredForTesting(interface);
  }

 protected:
  void SetFlossEnabled(bool enable,
                       int retry_count,
                       int retry_ms,
                       base::RepeatingClosure quitloop) {
    client_->SetFlossEnabled(enable, retry_count, retry_ms,
                             GetQuitLoopCallback(quitloop));
  }

  void DoGetFlossApiVersion() { client_->DoGetFlossApiVersion(); }

  bool IsCompatibleFlossApi() { return client_->IsCompatibleFlossApi(); }

  void EndRunLoopCallback(base::RepeatingClosure quit, DBusResult<bool> ret) {
    std::move(quit).Run();
  }

  ResponseCallback<bool> GetQuitLoopCallback(base::RepeatingClosure quit) {
    return base::BindOnce(&FlossManagerClientTest::EndRunLoopCallback,
                          weak_ptr_factory_.GetWeakPtr(), std::move(quit));
  }

  // DBus messages require an increasing serial number or the dbus libraries
  // assert.
  int serial_ = 1;
  std::unique_ptr<FlossManagerClient> client_;
  scoped_refptr<::dbus::MockBus> bus_;
  scoped_refptr<::dbus::MockExportedObject> exported_callbacks_;
  scoped_refptr<::dbus::MockObjectProxy> manager_object_proxy_;
  scoped_refptr<::dbus::ObjectManager> object_manager_;
  std::map<std::string, int> method_called_;

  // Testing the |SetFlossEnabled| retry with a target of enabling Floss.
  int fail_setfloss_count_ = 0;
  int fail_getfloss_count_ = 0;
  bool floss_enabled_target_ = true;
  uint32_t floss_api_version_ = 0x1234abcd;

  bool get_adapter_enabled_return_ = false;

  dbus::ExportedObject::MethodCallCallback on_hci_device_changed_;
  dbus::ExportedObject::MethodCallCallback on_hci_enabled_changed_;

  base::test::SingleThreadTaskEnvironment task_environment_;
  base::WeakPtrFactory<FlossManagerClientTest> weak_ptr_factory_{this};
};

// Make sure adapter presence is updated on init
TEST_F(FlossManagerClientTest, QueriesAdapterPresenceOnInit) {
  TestManagerObserver observer(client_.get());
  client_->Init(bus_.get(), kManagerInterface, /*adapter_index=*/-1,
                GetCurrVersion(), base::DoNothing());
  EXPECT_EQ(observer.manager_present_count_, 1);
  EXPECT_TRUE(observer.manager_present_);

  EXPECT_THAT(client_->GetAdapters(), ::testing::ElementsAre(0, 5));
  EXPECT_TRUE(client_->GetAdapterPresent(0));
  EXPECT_TRUE(client_->GetAdapterPresent(5));
  EXPECT_FALSE(client_->GetAdapterPresent(1));

  EXPECT_EQ(observer.adapter_present_count_, 2);
  EXPECT_TRUE(observer.adapter_present_[0]);
  EXPECT_TRUE(observer.adapter_present_[5]);
}

// Make sure adapter presence is plumbed through callbacks
TEST_F(FlossManagerClientTest, VerifyAdapterPresent) {
  TestManagerObserver observer(client_.get());
  client_->Init(bus_.get(), kManagerInterface, /*adapter_index=*/-1,
                GetCurrVersion(), base::DoNothing());
  EXPECT_EQ(observer.adapter_present_count_, 2);
  EXPECT_EQ(observer.adapter_enabled_changed_count_, 2);
  EXPECT_TRUE(observer.adapter_present_[0]);

  SendInvalidHciDeviceCallback(
      base::BindOnce(&FlossManagerClientTest::ExpectErrorResponse,
                     weak_ptr_factory_.GetWeakPtr()));

  SendHciDeviceCallback(
      0, false,
      base::BindOnce(&FlossManagerClientTest::ExpectNormalResponse,
                     weak_ptr_factory_.GetWeakPtr()));

  EXPECT_THAT(client_->GetAdapters(), ::testing::ElementsAre(5));
  EXPECT_TRUE(client_->GetAdapterPresent(5));
  EXPECT_FALSE(client_->GetAdapterPresent(0));
  EXPECT_FALSE(client_->GetAdapterEnabled(0));

  // 2 to start and 1 to remove "0"
  EXPECT_EQ(observer.adapter_present_count_, 3);
  EXPECT_FALSE(observer.adapter_present_[0]);

  // On present = false, the client may not be sent an additional enabled
  // = false. It is implied and the client must act accordingly.
  EXPECT_EQ(observer.adapter_enabled_changed_count_, 2);
}

// Make sure we query the enabled state when adapter presents
TEST_F(FlossManagerClientTest, VerifyAdapterPresentEnabled) {
  TestManagerObserver observer(client_.get());
  client_->Init(bus_.get(), kManagerInterface, /*adapter_index=*/-1,
                GetCurrVersion(), base::DoNothing());

  EXPECT_EQ(method_called_[manager::kGetAdapterEnabled], 0);
  EXPECT_EQ(observer.adapter_present_count_, 2);

  // A disabled adapter presents
  SendHciDeviceCallback(
      1, true,
      base::BindOnce(&FlossManagerClientTest::ExpectNormalResponse,
                     weak_ptr_factory_.GetWeakPtr()));

  EXPECT_EQ(method_called_[manager::kGetAdapterEnabled], 1);
  EXPECT_EQ(observer.adapter_present_count_, 3);
  EXPECT_FALSE(client_->GetAdapterEnabled(1));

  // An enabled adapter presents
  get_adapter_enabled_return_ = true;
  SendHciDeviceCallback(
      2, true,
      base::BindOnce(&FlossManagerClientTest::ExpectNormalResponse,
                     weak_ptr_factory_.GetWeakPtr()));

  EXPECT_EQ(method_called_[manager::kGetAdapterEnabled], 2);
  EXPECT_EQ(observer.adapter_present_count_, 4);
  EXPECT_TRUE(client_->GetAdapterEnabled(2));

  // Presenting twice should be no-op
  SendHciDeviceCallback(
      2, true,
      base::BindOnce(&FlossManagerClientTest::ExpectNormalResponse,
                     weak_ptr_factory_.GetWeakPtr()));

  EXPECT_EQ(method_called_[manager::kGetAdapterEnabled], 2);
  EXPECT_EQ(observer.adapter_present_count_, 4);
  EXPECT_TRUE(client_->GetAdapterEnabled(2));
}

// Make sure adapter powered is plumbed through callbacks
TEST_F(FlossManagerClientTest, VerifyAdapterEnabled) {
  TestManagerObserver observer(client_.get());
  client_->Init(bus_.get(), kManagerInterface, /*adapter_index=*/-1,
                GetCurrVersion(), base::DoNothing());
  // Pre-conditions
  EXPECT_FALSE(client_->GetAdapterEnabled(0));
  EXPECT_TRUE(client_->GetAdapterEnabled(5));
  EXPECT_EQ(observer.adapter_enabled_changed_count_, 2);

  // Adapter 1 is not present at the beginning
  EXPECT_FALSE(client_->GetAdapterEnabled(1));
  EXPECT_FALSE(client_->GetAdapterPresent(1));

  SendInvalidHciEnabledCallback(
      base::BindOnce(&FlossManagerClientTest::ExpectErrorResponse,
                     weak_ptr_factory_.GetWeakPtr()));

  SendHciEnabledCallback(
      0, true,
      base::BindOnce(&FlossManagerClientTest::ExpectNormalResponse,
                     weak_ptr_factory_.GetWeakPtr()));

  EXPECT_TRUE(client_->GetAdapterEnabled(0));
  EXPECT_EQ(observer.adapter_enabled_changed_count_, 3);
  EXPECT_TRUE(observer.adapter_enabled_[0]);

  // Enabled implies presence too
  SendHciEnabledCallback(
      1, true,
      base::BindOnce(&FlossManagerClientTest::ExpectNormalResponse,
                     weak_ptr_factory_.GetWeakPtr()));
  EXPECT_TRUE(client_->GetAdapterPresent(1));
  EXPECT_TRUE(client_->GetAdapterEnabled(1));

  EXPECT_EQ(observer.adapter_enabled_changed_count_, 4);
  EXPECT_TRUE(observer.adapter_enabled_[1]);
  // On enabled = true, present = true is implied. The platform should emit both
  // but the client shouldn't depend on it.
  EXPECT_FALSE(observer.adapter_present_[1]);

  // 5 was unchanged
  EXPECT_TRUE(client_->GetAdapterEnabled(5));
  EXPECT_TRUE(observer.adapter_enabled_[5]);
}

// Make sure manager presence is correctly detected
TEST_F(FlossManagerClientTest, HandleManagerPresence) {
  TestManagerObserver observer(client_.get());
  client_->Init(bus_.get(), kManagerInterface, /*adapter_index=*/-1,
                GetCurrVersion(), base::DoNothing());
  dbus::ObjectPath opath = dbus::ObjectPath(kManagerObject);
  EXPECT_EQ(observer.manager_present_count_, 1);

  // Make sure we registered against ObjectManager
  EXPECT_TRUE(IsInterfaceRegisteredOnObjectManager(kManagerInterface));

  // By default, the manager should be available
  EXPECT_TRUE(observer.manager_present_);

  // Triggering an ObjectRemoved should clear all present adapters
  TriggerObjectRemoved(opath, kManagerInterface);
  EXPECT_THAT(client_->GetAdapters(), ::testing::ElementsAre());
  EXPECT_FALSE(observer.manager_present_);

  // Triggering ObjectRemoved while already unavailable should do nothing
  TriggerObjectRemoved(opath, kManagerInterface);
  EXPECT_THAT(client_->GetAdapters(), ::testing::ElementsAre());
  EXPECT_FALSE(observer.manager_present_);

  method_called_.clear();
  // Triggering ObjectAdded should refill available hci devices and register
  // callbacks
  TriggerObjectAdded(opath, kManagerInterface);
  EXPECT_TRUE(method_called_[manager::kGetAvailableAdapters] > 0);
  EXPECT_TRUE(method_called_[manager::kRegisterCallback] > 0);
  EXPECT_TRUE(observer.manager_present_);

  // Triggering ObjectAdded on an already added object should do nothing
  observer.manager_present_count_ = 0;
  method_called_.clear();
  SendHciDeviceCallback(
      1, true,
      base::BindOnce(&FlossManagerClientTest::ExpectNormalResponse,
                     weak_ptr_factory_.GetWeakPtr()));
  EXPECT_TRUE(method_called_[manager::kGetAdapterEnabled] > 0);
  EXPECT_TRUE(client_->GetAdapterPresent(1));
  TriggerObjectAdded(opath, kManagerInterface);
  EXPECT_EQ(observer.manager_present_count_, 0);
  EXPECT_TRUE(client_->GetAdapterPresent(1));
  EXPECT_TRUE(method_called_[manager::kGetAvailableAdapters] == 0);
  EXPECT_TRUE(method_called_[manager::kRegisterCallback] == 0);
}

#if BUILDFLAG(IS_CHROMEOS)
TEST_F(FlossManagerClientTest, SetFlossEnabledRetries) {
  base::RunLoop loop;

  TestManagerObserver observer(client_.get());
  floss_enabled_target_ = false;
  client_->Init(bus_.get(), kManagerInterface, /*adapter_index=*/-1,
                GetCurrVersion(), base::DoNothing());

  // First confirm we had it set to False
  EXPECT_EQ(method_called_[manager::kSetFlossEnabled], 1);
  EXPECT_EQ(method_called_[manager::kGetFlossEnabled], 1);

  method_called_.clear();

  // Retries up to 3 times across both Get and Set.
  fail_setfloss_count_ = 1;
  fail_getfloss_count_ = 1;
  floss_enabled_target_ = true;
  SetFlossEnabled(true, 3, 0, loop.QuitClosure());
  loop.Run();

  EXPECT_EQ(method_called_[manager::kSetFlossEnabled], 2);
  EXPECT_EQ(method_called_[manager::kGetFlossEnabled], 2);
}
#endif  // BUILDFLAG(IS_CHROMEOS)

TEST_F(FlossManagerClientTest, GetFlossApiVersion) {
  base::Version version = floss::version::IntoVersion(floss_api_version_);

  TestManagerObserver observer(client_.get());
  client_->Init(bus_.get(), kManagerInterface, /*adapter_index=*/-1,
                GetCurrVersion(), base::DoNothing());

  method_called_.clear();
  DoGetFlossApiVersion();

  EXPECT_EQ(method_called_[manager::kGetFlossApiVersion], 1);
  EXPECT_EQ(client_->GetFlossApiVersion(), version);
}

TEST_F(FlossManagerClientTest, NewFlossDaemonIsNotCompatible) {
  // Given Floss daemon's Floss API version is a newer one.
  floss_api_version_ = 0xffffffff;

  // When FlossManagerClient gets the Floss API version at initialized.
  TestManagerObserver observer(client_.get());
  client_->Init(bus_.get(), kManagerInterface, /*adapter_index=*/-1,
                GetCurrVersion(), base::DoNothing());

  // Then, the Floss API exported by Floss daemon is not compatible.
  EXPECT_FALSE(IsCompatibleFlossApi());
}
}  // namespace floss
