blob: f1b194c8e06fef474de996b94e9a2e2ed6ed6d13 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/components/tether/connection_preserver_impl.h"
#include <memory>
#include "base/base64.h"
#include "base/memory/ptr_util.h"
#include "base/test/scoped_task_environment.h"
#include "base/timer/mock_timer.h"
#include "chromeos/components/multidevice/remote_device_test_util.h"
#include "chromeos/components/tether/fake_active_host.h"
#include "chromeos/components/tether/mock_tether_host_response_recorder.h"
#include "chromeos/components/tether/timer_factory.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h"
#include "chromeos/network/network_state_test.h"
#include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h"
#include "chromeos/services/secure_channel/public/cpp/client/fake_client_channel.h"
#include "chromeos/services/secure_channel/public/cpp/client/fake_connection_attempt.h"
#include "chromeos/services/secure_channel/public/cpp/client/fake_secure_channel_client.h"
#include "chromeos/services/secure_channel/public/cpp/shared/connection_priority.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
using testing::_;
using testing::NiceMock;
using testing::Return;
namespace chromeos {
namespace tether {
namespace {
const char kWifiNetworkGuid[] = "wifiNetworkGuid";
const char kTetherNetworkGuid[] = "tetherNetworkGuid";
std::string CreateConfigurationJsonString(const std::string& guid,
const std::string& type) {
std::stringstream ss;
ss << "{"
<< " \"GUID\": \"" << guid << "\","
<< " \"Type\": \"" << type << "\","
<< " \"State\": \"" << shill::kStateReady << "\""
<< "}";
return ss.str();
}
} // namespace
class ConnectionPreserverImplTest : public NetworkStateTest {
protected:
ConnectionPreserverImplTest()
: test_local_device_(multidevice::RemoteDeviceRefBuilder()
.SetPublicKey("local device")
.Build()),
test_remote_devices_(multidevice::CreateRemoteDeviceRefListForTest(3)) {
std::transform(
test_remote_devices_.begin(), test_remote_devices_.end(),
std::back_inserter(test_remote_device_ids_),
[](const auto& remote_device) { return remote_device.GetDeviceId(); });
}
void SetUp() override {
DBusThreadManager::Initialize();
NetworkStateTest::SetUp();
fake_device_sync_client_ =
std::make_unique<device_sync::FakeDeviceSyncClient>();
fake_device_sync_client_->set_local_device_metadata(test_local_device_);
fake_device_sync_client_->set_synced_devices(test_remote_devices_);
fake_secure_channel_client_ =
std::make_unique<secure_channel::FakeSecureChannelClient>();
fake_active_host_ = std::make_unique<FakeActiveHost>();
previously_connected_host_ids_.clear();
mock_tether_host_response_recorder_ =
std::make_unique<NiceMock<MockTetherHostResponseRecorder>>();
ON_CALL(*mock_tether_host_response_recorder_,
GetPreviouslyConnectedHostIds())
.WillByDefault(Invoke(
this, &ConnectionPreserverImplTest::GetPreviouslyConnectedHostIds));
connection_preserver_ = std::make_unique<ConnectionPreserverImpl>(
fake_device_sync_client_.get(), fake_secure_channel_client_.get(),
network_state_handler(), fake_active_host_.get(),
mock_tether_host_response_recorder_.get());
mock_timer_ = new base::MockOneShotTimer();
connection_preserver_->SetTimerForTesting(base::WrapUnique(mock_timer_));
}
void TearDown() override {
connection_preserver_.reset();
ShutdownNetworkState();
NetworkStateTest::TearDown();
DBusThreadManager::Shutdown();
}
void SimulateSuccessfulHostScan(multidevice::RemoteDeviceRef remote_device,
bool should_remain_registered) {
// |connection_preserver_| should only grab |fake_connection_attempt| if
// it is intended to keep the connection open.
auto fake_connection_attempt =
std::make_unique<secure_channel::FakeConnectionAttempt>();
auto* fake_connection_attempt_raw = fake_connection_attempt.get();
fake_secure_channel_client_->set_next_listen_connection_attempt(
remote_device, test_local_device_, std::move(fake_connection_attempt));
connection_preserver_->HandleSuccessfulTetherAvailabilityResponse(
remote_device.GetDeviceId());
// If |connection_preserver_| is not expected to preserve the connection, it
// should not create a ConnectionAttempt, i.e., |next_connection_attempt|
// should still be present.
secure_channel::ConnectionAttempt* next_connection_attempt =
fake_secure_channel_client_->peek_next_listen_connection_attempt(
remote_device, test_local_device_);
if (should_remain_registered) {
EXPECT_FALSE(next_connection_attempt);
} else {
// Expect that |connection_preserver_| did not grab the ConnectionAttempt.
EXPECT_TRUE(next_connection_attempt);
// Clean up |next_connection_attempt| or else
// |fake_secure_channel_client_| will fail a DCHECK when it's destroyed.
fake_secure_channel_client_->clear_next_listen_connection_attempt(
remote_device, test_local_device_);
return;
}
auto fake_client_channel =
std::make_unique<secure_channel::FakeClientChannel>();
auto* fake_client_channel_raw = fake_client_channel.get();
fake_client_channel_raw->set_destructor_callback(
base::BindOnce(&ConnectionPreserverImplTest::OnClientChannelDestroyed,
base::Unretained(this), remote_device));
fake_connection_attempt_raw->NotifyConnection(
std::move(fake_client_channel));
// Expect that |connection_preserver_| continues to hold on to the
// ClientChannel until it is destroyed or the active host becomes connected.
VerifyChannelForRemoteDeviceDestroyed(remote_device,
false /* expect_destroyed */);
}
void VerifyChannelForRemoteDeviceDestroyed(
multidevice::RemoteDeviceRef remote_device,
bool expect_destroyed) {
if (expect_destroyed) {
EXPECT_TRUE(remote_device_to_client_channel_destruction_count_map_
[remote_device]);
} else {
EXPECT_FALSE(remote_device_to_client_channel_destruction_count_map_
[remote_device]);
}
}
void ConnectToWifi() {
std::string wifi_service_path = ConfigureService(
CreateConfigurationJsonString(kWifiNetworkGuid, shill::kTypeWifi));
}
std::vector<std::string> GetPreviouslyConnectedHostIds() {
return previously_connected_host_ids_;
}
void OnClientChannelDestroyed(multidevice::RemoteDeviceRef remote_device) {
remote_device_to_client_channel_destruction_count_map_[remote_device]++;
}
base::test::ScopedTaskEnvironment scoped_task_environment_;
const multidevice::RemoteDeviceRef test_local_device_;
const multidevice::RemoteDeviceRefList test_remote_devices_;
std::vector<std::string> test_remote_device_ids_;
base::flat_map<multidevice::RemoteDeviceRef,
secure_channel::FakeClientChannel*>
remote_device_to_fake_client_channel_map_;
base::flat_map<multidevice::RemoteDeviceRef, int>
remote_device_to_client_channel_destruction_count_map_;
std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_;
std::unique_ptr<secure_channel::FakeSecureChannelClient>
fake_secure_channel_client_;
std::unique_ptr<FakeActiveHost> fake_active_host_;
std::unique_ptr<NiceMock<MockTetherHostResponseRecorder>>
mock_tether_host_response_recorder_;
base::MockOneShotTimer* mock_timer_;
std::unique_ptr<ConnectionPreserverImpl> connection_preserver_;
std::vector<std::string> previously_connected_host_ids_;
private:
DISALLOW_COPY_AND_ASSIGN(ConnectionPreserverImplTest);
};
TEST_F(ConnectionPreserverImplTest,
TestHandleSuccessfulTetherAvailabilityResponse_NoPreservedConnection) {
SimulateSuccessfulHostScan(test_remote_devices_[0],
true /* should_remain_registered */);
}
TEST_F(ConnectionPreserverImplTest,
TestHandleSuccessfulTetherAvailabilityResponse_HasInternet) {
ConnectToWifi();
SimulateSuccessfulHostScan(test_remote_devices_[0],
false /* should_remain_registered */);
}
TEST_F(
ConnectionPreserverImplTest,
TestHandleSuccessfulTetherAvailabilityResponse_PreservedConnectionExists_NoPreviouslyConnectedHosts) {
SimulateSuccessfulHostScan(test_remote_devices_[0],
true /* should_remain_registered */);
VerifyChannelForRemoteDeviceDestroyed(test_remote_devices_[0],
false /* expect_destroyed */);
SimulateSuccessfulHostScan(test_remote_devices_[1],
true /* should_remain_registered */);
VerifyChannelForRemoteDeviceDestroyed(test_remote_devices_[0],
true /* expect_destroyed */);
VerifyChannelForRemoteDeviceDestroyed(test_remote_devices_[1],
false /* expect_destroyed */);
}
TEST_F(ConnectionPreserverImplTest,
TestHandleSuccessfulTetherAvailabilityResponse_TimesOut) {
SimulateSuccessfulHostScan(test_remote_devices_[0],
true /* should_remain_registered */);
mock_timer_->Fire();
VerifyChannelForRemoteDeviceDestroyed(test_remote_devices_[0],
true /* expect_destroyed */);
}
TEST_F(ConnectionPreserverImplTest,
TestHandleSuccessfulTetherAvailabilityResponse_PreserverDestroyed) {
SimulateSuccessfulHostScan(test_remote_devices_[0],
true /* should_remain_registered */);
connection_preserver_.reset();
VerifyChannelForRemoteDeviceDestroyed(test_remote_devices_[0],
true /* expect_destroyed */);
}
TEST_F(
ConnectionPreserverImplTest,
TestHandleSuccessfulTetherAvailabilityResponse_ActiveHostBecomesConnected) {
SimulateSuccessfulHostScan(test_remote_devices_[0],
true /* should_remain_registered */);
fake_active_host_->SetActiveHostConnecting(test_remote_device_ids_[0],
kTetherNetworkGuid);
fake_active_host_->SetActiveHostConnected(
test_remote_device_ids_[0], kTetherNetworkGuid, kWifiNetworkGuid);
VerifyChannelForRemoteDeviceDestroyed(test_remote_devices_[0],
true /* expect_destroyed */);
}
TEST_F(
ConnectionPreserverImplTest,
TestHandleSuccessfulTetherAvailabilityResponse_PreviouslyConnectedHostsExist) {
// |test_remote_device_ids_[0]| is the most recently connected device, and
// should be preferred over any other device.
previously_connected_host_ids_.push_back(test_remote_device_ids_[0]);
previously_connected_host_ids_.push_back(test_remote_device_ids_[1]);
SimulateSuccessfulHostScan(test_remote_devices_[2],
true /* should_remain_registered */);
VerifyChannelForRemoteDeviceDestroyed(test_remote_devices_[2],
false /* expect_destroyed */);
SimulateSuccessfulHostScan(test_remote_devices_[1],
true /* should_remain_registered */);
VerifyChannelForRemoteDeviceDestroyed(test_remote_devices_[2],
true /* expect_destroyed */);
VerifyChannelForRemoteDeviceDestroyed(test_remote_devices_[1],
false /* expect_destroyed */);
SimulateSuccessfulHostScan(test_remote_devices_[0],
true /* should_remain_registered */);
VerifyChannelForRemoteDeviceDestroyed(test_remote_devices_[1],
true /* expect_destroyed */);
VerifyChannelForRemoteDeviceDestroyed(test_remote_devices_[0],
false /* expect_destroyed */);
SimulateSuccessfulHostScan(test_remote_devices_[1],
false /* should_remain_registered */);
VerifyChannelForRemoteDeviceDestroyed(test_remote_devices_[0],
false /* expect_destroyed */);
SimulateSuccessfulHostScan(test_remote_devices_[2],
false /* should_remain_registered */);
VerifyChannelForRemoteDeviceDestroyed(test_remote_devices_[0],
false /* expect_destroyed */);
}
} // namespace tether
} // namespace chromeos