chromium/chromeos/ash/components/tether/tether_component_impl_unittest.cc

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

#include "chromeos/ash/components/tether/tether_component_impl.h"

#include <memory>

#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/test/task_environment.h"
#include "base/test/test_simple_task_runner.h"
#include "chromeos/ash/components/network/network_handler_test_helper.h"
#include "chromeos/ash/components/tether/asynchronous_shutdown_object_container_impl.h"
#include "chromeos/ash/components/tether/crash_recovery_manager_impl.h"
#include "chromeos/ash/components/tether/fake_active_host.h"
#include "chromeos/ash/components/tether/fake_asynchronous_shutdown_object_container.h"
#include "chromeos/ash/components/tether/fake_crash_recovery_manager.h"
#include "chromeos/ash/components/tether/fake_host_scan_scheduler.h"
#include "chromeos/ash/components/tether/fake_synchronous_shutdown_object_container.h"
#include "chromeos/ash/components/tether/fake_tether_disconnector.h"
#include "chromeos/ash/components/tether/synchronous_shutdown_object_container_impl.h"
#include "chromeos/ash/components/tether/tether_session_completion_logger.h"
#include "chromeos/ash/services/secure_channel/public/cpp/client/secure_channel_client.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash {

namespace tether {

namespace {

class TestTetherComponentObserver : public TetherComponent::Observer {
 public:
  TestTetherComponentObserver() = default;
  ~TestTetherComponentObserver() override = default;

  bool shutdown_complete() { return shutdown_complete_; }

  // TetherComponent::Observer:
  void OnShutdownComplete() override { shutdown_complete_ = true; }

 private:
  bool shutdown_complete_ = false;
};

class FakeAsynchronousShutdownObjectContainerFactory
    : public AsynchronousShutdownObjectContainerImpl::Factory {
 public:
  explicit FakeAsynchronousShutdownObjectContainerFactory(
      FakeAsynchronousShutdownObjectContainer* fake_asynchronous_container)
      : fake_asynchronous_container_(fake_asynchronous_container) {}

  ~FakeAsynchronousShutdownObjectContainerFactory() override = default;

  // AsynchronousShutdownObjectContainerImpl::Factory:
  std::unique_ptr<AsynchronousShutdownObjectContainer> CreateInstance(
      device_sync::DeviceSyncClient* device_sync_client,
      secure_channel::SecureChannelClient* secure_channel_client,
      TetherHostFetcher* tether_host_fetcher,
      NetworkStateHandler* network_state_handler,
      ManagedNetworkConfigurationHandler* managed_network_configuration_handler,
      NetworkConnectionHandler* network_connection_handler,
      PrefService* pref_service) override {
    return base::WrapUnique(fake_asynchronous_container_.get());
  }

 private:
  raw_ptr<FakeAsynchronousShutdownObjectContainer, DanglingUntriaged>
      fake_asynchronous_container_;
};

class FakeSynchronousShutdownObjectContainerFactory
    : public SynchronousShutdownObjectContainerImpl::Factory {
 public:
  explicit FakeSynchronousShutdownObjectContainerFactory(
      FakeSynchronousShutdownObjectContainer* fake_synchronous_container)
      : fake_synchronous_container_(fake_synchronous_container) {}

  ~FakeSynchronousShutdownObjectContainerFactory() override = default;

  // SynchronousShutdownObjectContainerImpl::Factory:
  std::unique_ptr<SynchronousShutdownObjectContainer> CreateInstance(
      AsynchronousShutdownObjectContainer* asychronous_container,
      NotificationPresenter* notification_presenter,
      GmsCoreNotificationsStateTrackerImpl*
          gms_core_notifications_state_tracker,
      PrefService* pref_service,
      NetworkHandler* network_handler,
      NetworkConnect* network_connect,
      session_manager::SessionManager* session_manager,
      device_sync::DeviceSyncClient* device_sync_client,
      secure_channel::SecureChannelClient* secure_channel_client) override {
    return base::WrapUnique(fake_synchronous_container_.get());
  }

 private:
  raw_ptr<FakeSynchronousShutdownObjectContainer, DanglingUntriaged>
      fake_synchronous_container_;
};

class FakeCrashRecoveryManagerFactory
    : public CrashRecoveryManagerImpl::Factory {
 public:
  explicit FakeCrashRecoveryManagerFactory(
      FakeCrashRecoveryManager* fake_crash_recovery_manager)
      : fake_crash_recovery_manager_(fake_crash_recovery_manager) {}

  ~FakeCrashRecoveryManagerFactory() override = default;

  // CrashRecoveryManagerImpl::Factory:
  std::unique_ptr<CrashRecoveryManager> CreateInstance(
      NetworkStateHandler* network_state_handler,
      ActiveHost* active_host,
      HostScanCache* host_scan_cache) override {
    return base::WrapUnique(fake_crash_recovery_manager_.get());
  }

 private:
  raw_ptr<FakeCrashRecoveryManager, DanglingUntriaged>
      fake_crash_recovery_manager_;
};

}  // namespace

class TetherComponentImplTest : public testing::Test {
 public:
  TetherComponentImplTest(const TetherComponentImplTest&) = delete;
  TetherComponentImplTest& operator=(const TetherComponentImplTest&) = delete;

 protected:
  TetherComponentImplTest() = default;
  ~TetherComponentImplTest() override = default;

  void SetUp() override {
    was_synchronous_container_deleted_ = false;
    was_asynchronous_container_deleted_ = false;

    fake_active_host_ = std::make_unique<FakeActiveHost>();
    fake_host_scan_scheduler_ = std::make_unique<FakeHostScanScheduler>();
    fake_tether_disconnector_ = std::make_unique<FakeTetherDisconnector>();

    fake_synchronous_container_ = new FakeSynchronousShutdownObjectContainer(
        base::BindOnce(&TetherComponentImplTest::OnSynchronousContainerDeleted,
                       base::Unretained(this)));
    fake_synchronous_container_->set_active_host(fake_active_host_.get());
    fake_synchronous_container_->set_host_scan_scheduler(
        fake_host_scan_scheduler_.get());
    fake_synchronous_container_->set_tether_disconnector(
        fake_tether_disconnector_.get());
    fake_synchronous_container_factory_ =
        base::WrapUnique(new FakeSynchronousShutdownObjectContainerFactory(
            fake_synchronous_container_));
    SynchronousShutdownObjectContainerImpl::Factory::SetFactoryForTesting(
        fake_synchronous_container_factory_.get());

    fake_asynchronous_container_ = new FakeAsynchronousShutdownObjectContainer(
        base::BindOnce(&TetherComponentImplTest::OnAsynchronousContainerDeleted,
                       base::Unretained(this)));
    fake_asynchronous_container_factory_ =
        base::WrapUnique(new FakeAsynchronousShutdownObjectContainerFactory(
            fake_asynchronous_container_));
    AsynchronousShutdownObjectContainerImpl::Factory::SetFactoryForTesting(
        fake_asynchronous_container_factory_.get());

    fake_crash_recovery_manager_ = new FakeCrashRecoveryManager();
    fake_crash_recovery_manager_factory_ = base::WrapUnique(
        new FakeCrashRecoveryManagerFactory(fake_crash_recovery_manager_));
    CrashRecoveryManagerImpl::Factory::SetFactoryForTesting(
        fake_crash_recovery_manager_factory_.get());

    component_ = TetherComponentImpl::Factory::Create(
        nullptr /* device_sync_client */, nullptr /* secure_channel_client */,
        nullptr /* tether_host_fetcher */, nullptr /* notification_presenter */,
        nullptr /* gms_core_notifications_state_trackerstate_ */,
        nullptr /* pref_service */, NetworkHandler::Get() /* network_handler */,
        nullptr /* network_connect */, nullptr /* adapter */,
        nullptr /* session_manager */);

    test_observer_ = std::make_unique<TestTetherComponentObserver>();
    component_->AddObserver(test_observer_.get());
  }

  void InvokeCrashRecoveryCallback() {
    base::OnceClosure on_restoration_finished_callback =
        fake_crash_recovery_manager_->TakeOnRestorationFinishedCallback();
    EXPECT_FALSE(on_restoration_finished_callback.is_null());
    std::move(on_restoration_finished_callback).Run();
  }

  void InvokeAsynchronousShutdownCallback() {
    base::OnceClosure shutdown_complete_callback =
        fake_asynchronous_container_->TakeShutdownCompleteCallback();
    EXPECT_FALSE(shutdown_complete_callback.is_null());
    std::move(shutdown_complete_callback).Run();
  }

  void OnSynchronousContainerDeleted() {
    was_synchronous_container_deleted_ = true;
  }

  void OnAsynchronousContainerDeleted() {
    was_asynchronous_container_deleted_ = true;
  }

  bool was_synchronous_container_deleted_;
  bool was_asynchronous_container_deleted_;

  std::unique_ptr<FakeActiveHost> fake_active_host_;
  std::unique_ptr<FakeHostScanScheduler> fake_host_scan_scheduler_;
  std::unique_ptr<FakeTetherDisconnector> fake_tether_disconnector_;

  base::test::SingleThreadTaskEnvironment task_environment_;
  NetworkHandlerTestHelper helper_;

  raw_ptr<FakeSynchronousShutdownObjectContainer, DanglingUntriaged>
      fake_synchronous_container_;
  std::unique_ptr<FakeSynchronousShutdownObjectContainerFactory>
      fake_synchronous_container_factory_;

  raw_ptr<FakeAsynchronousShutdownObjectContainer, DanglingUntriaged>
      fake_asynchronous_container_;
  std::unique_ptr<FakeAsynchronousShutdownObjectContainerFactory>
      fake_asynchronous_container_factory_;

  raw_ptr<FakeCrashRecoveryManager, DanglingUntriaged>
      fake_crash_recovery_manager_;
  std::unique_ptr<FakeCrashRecoveryManagerFactory>
      fake_crash_recovery_manager_factory_;

  std::unique_ptr<TetherComponent> component_;

  std::unique_ptr<TestTetherComponentObserver> test_observer_;
};

TEST_F(TetherComponentImplTest, TestShutdown_Disconnected) {
  InvokeCrashRecoveryCallback();
  EXPECT_FALSE(test_observer_->shutdown_complete());

  component_->RequestShutdown(TetherComponent::ShutdownReason::USER_CLOSED_LID);
  EXPECT_TRUE(was_synchronous_container_deleted_);
  EXPECT_FALSE(was_asynchronous_container_deleted_);
  EXPECT_FALSE(test_observer_->shutdown_complete());

  // No disconnection attempt should have occurred since the active host was
  // disconnected.
  EXPECT_TRUE(fake_tether_disconnector_->last_disconnected_tether_network_guid()
                  .empty());

  InvokeAsynchronousShutdownCallback();
  EXPECT_TRUE(was_asynchronous_container_deleted_);
  EXPECT_TRUE(test_observer_->shutdown_complete());
}

TEST_F(TetherComponentImplTest, TestShutdown_Connecting) {
  InvokeCrashRecoveryCallback();
  EXPECT_FALSE(test_observer_->shutdown_complete());

  fake_active_host_->SetActiveHostConnecting("deviceId", "tetherNetworkGuid");
  component_->RequestShutdown(TetherComponent::ShutdownReason::USER_CLOSED_LID);
  EXPECT_TRUE(was_synchronous_container_deleted_);
  EXPECT_FALSE(was_asynchronous_container_deleted_);
  EXPECT_FALSE(test_observer_->shutdown_complete());

  // A disconnection attempt should have occurred.
  EXPECT_EQ("tetherNetworkGuid",
            fake_tether_disconnector_->last_disconnected_tether_network_guid());
  EXPECT_EQ(
      TetherSessionCompletionLogger::SessionCompletionReason::USER_CLOSED_LID,
      *fake_tether_disconnector_->last_session_completion_reason());

  InvokeAsynchronousShutdownCallback();
  EXPECT_TRUE(was_asynchronous_container_deleted_);
  EXPECT_TRUE(test_observer_->shutdown_complete());
}

TEST_F(TetherComponentImplTest, TestShutdown_Connected) {
  InvokeCrashRecoveryCallback();
  EXPECT_FALSE(test_observer_->shutdown_complete());

  fake_active_host_->SetActiveHostConnected("deviceId", "tetherNetworkGuid",
                                            "wifiNetworkGuid");
  component_->RequestShutdown(TetherComponent::ShutdownReason::USER_CLOSED_LID);
  EXPECT_TRUE(was_synchronous_container_deleted_);
  EXPECT_FALSE(was_asynchronous_container_deleted_);
  EXPECT_FALSE(test_observer_->shutdown_complete());

  // A disconnection attempt should have occurred.
  EXPECT_EQ("tetherNetworkGuid",
            fake_tether_disconnector_->last_disconnected_tether_network_guid());
  EXPECT_EQ(
      TetherSessionCompletionLogger::SessionCompletionReason::USER_CLOSED_LID,
      *fake_tether_disconnector_->last_session_completion_reason());

  InvokeAsynchronousShutdownCallback();
  EXPECT_TRUE(was_asynchronous_container_deleted_);
  EXPECT_TRUE(test_observer_->shutdown_complete());
}

TEST_F(TetherComponentImplTest, TestShutdown_BeforeCrashRecoveryComplete) {
  component_->RequestShutdown(TetherComponent::ShutdownReason::USER_CLOSED_LID);
  EXPECT_FALSE(test_observer_->shutdown_complete());

  // A shutdown attempt should not have occurred since crash recovery has
  // not completed.
  EXPECT_FALSE(was_synchronous_container_deleted_);
  EXPECT_FALSE(was_asynchronous_container_deleted_);
  EXPECT_TRUE(
      fake_asynchronous_container_->TakeShutdownCompleteCallback().is_null());

  InvokeCrashRecoveryCallback();
  EXPECT_TRUE(was_synchronous_container_deleted_);
  EXPECT_FALSE(test_observer_->shutdown_complete());

  // Now that crash recovery is complete, a shutdown attempt should have been
  // started.
  InvokeAsynchronousShutdownCallback();
  EXPECT_TRUE(was_asynchronous_container_deleted_);
  EXPECT_TRUE(test_observer_->shutdown_complete());
}

}  // namespace tether

}  // namespace ash