blob: 60694226eb99c777ee6a8e89e8a8a35cb7badca1 [file] [log] [blame]
// Copyright 2016 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/host_scanner_operation.h"
#include <algorithm>
#include <memory>
#include <vector>
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/simple_test_clock.h"
#include "base/test/test_simple_task_runner.h"
#include "chromeos/components/multidevice/remote_device_test_util.h"
#include "chromeos/components/tether/fake_connection_preserver.h"
#include "chromeos/components/tether/host_scan_device_prioritizer.h"
#include "chromeos/components/tether/message_wrapper.h"
#include "chromeos/components/tether/mock_tether_host_response_recorder.h"
#include "chromeos/components/tether/proto/tether.pb.h"
#include "chromeos/components/tether/proto_test_util.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 "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::StrictMock;
namespace chromeos {
namespace tether {
namespace {
class FakeHostScanDevicePrioritizer : public HostScanDevicePrioritizer {
public:
FakeHostScanDevicePrioritizer() : HostScanDevicePrioritizer() {}
~FakeHostScanDevicePrioritizer() override = default;
// Simply leave |remote_devices| as-is.
void SortByHostScanOrder(
multidevice::RemoteDeviceRefList* remote_devices) const override {}
};
// Used to verify the HostScannerOperation notifies the observer when
// appropriate.
class MockOperationObserver : public HostScannerOperation::Observer {
public:
MockOperationObserver() = default;
~MockOperationObserver() = default;
MOCK_METHOD3(OnTetherAvailabilityResponse,
void(const std::vector<HostScannerOperation::ScannedDeviceInfo>&,
const multidevice::RemoteDeviceRefList&,
bool));
private:
DISALLOW_COPY_AND_ASSIGN(MockOperationObserver);
};
DeviceStatus CreateFakeDeviceStatus() {
return CreateTestDeviceStatus("Google Fi", 75 /* battery_percentage */,
4 /* connection_strength */);
}
} // namespace
class HostScannerOperationTest : public testing::Test {
protected:
HostScannerOperationTest()
: local_device_(multidevice::RemoteDeviceRefBuilder()
.SetPublicKey("local device")
.Build()),
remote_device_(multidevice::CreateRemoteDeviceRefListForTest(1)[0]) {}
void SetUp() override {
fake_device_sync_client_.set_local_device_metadata(local_device_);
operation_ = ConstructOperation();
operation_->Initialize();
ConnectAuthenticatedChannelForDevice(remote_device_);
}
multidevice::RemoteDeviceRefList GetOperationRemoteDevices(
HostScannerOperation* operation) const {
return operation->remote_devices();
}
std::unique_ptr<HostScannerOperation> ConstructOperation() {
EXPECT_CALL(mock_observer_,
OnTetherAvailabilityResponse(
std::vector<HostScannerOperation::ScannedDeviceInfo>(),
multidevice::RemoteDeviceRefList(), false));
auto connection_attempt =
std::make_unique<secure_channel::FakeConnectionAttempt>();
connection_attempt_ = connection_attempt.get();
fake_secure_channel_client_.set_next_listen_connection_attempt(
remote_device_, local_device_, std::move(connection_attempt));
auto operation = base::WrapUnique(new HostScannerOperation(
multidevice::RemoteDeviceRefList({remote_device_}),
&fake_device_sync_client_, &fake_secure_channel_client_,
&fake_device_prioritizer_, &mock_tether_host_response_recorder_,
&fake_connection_preserver_));
operation->AddObserver(&mock_observer_);
test_clock_.SetNow(base::Time::UnixEpoch());
test_task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>();
operation->SetTestDoubles(&test_clock_, test_task_runner_);
return operation;
}
void ConnectAuthenticatedChannelForDevice(
multidevice::RemoteDeviceRef remote_device) {
auto fake_client_channel =
std::make_unique<secure_channel::FakeClientChannel>();
connection_attempt_->NotifyConnection(std::move(fake_client_channel));
}
const multidevice::RemoteDeviceRef local_device_;
const multidevice::RemoteDeviceRef remote_device_;
secure_channel::FakeConnectionAttempt* connection_attempt_;
device_sync::FakeDeviceSyncClient fake_device_sync_client_;
secure_channel::FakeSecureChannelClient fake_secure_channel_client_;
FakeHostScanDevicePrioritizer fake_device_prioritizer_;
StrictMock<MockTetherHostResponseRecorder>
mock_tether_host_response_recorder_;
FakeConnectionPreserver fake_connection_preserver_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
base::SimpleTestClock test_clock_;
scoped_refptr<base::TestSimpleTaskRunner> test_task_runner_;
MockOperationObserver mock_observer_;
base::HistogramTester histogram_tester_;
std::unique_ptr<HostScannerOperation> operation_;
private:
DISALLOW_COPY_AND_ASSIGN(HostScannerOperationTest);
};
TEST_F(HostScannerOperationTest,
SendsTetherAvailabilityRequestOnceAuthenticated) {
std::unique_ptr<HostScannerOperation> operation = ConstructOperation();
operation->Initialize();
// Create the client channel to the remote device.
auto fake_client_channel =
std::make_unique<secure_channel::FakeClientChannel>();
// No requests as a result of creating the client channel.
auto& sent_messages = fake_client_channel->sent_messages();
EXPECT_EQ(0u, sent_messages.size());
// Connect and authenticate the client channel.
connection_attempt_->NotifyConnection(std::move(fake_client_channel));
// Verify the TetherAvailabilityRequest message is sent.
auto message_wrapper =
std::make_unique<MessageWrapper>(TetherAvailabilityRequest());
std::string expected_payload = message_wrapper->ToRawMessage();
EXPECT_EQ(1u, sent_messages.size());
EXPECT_EQ(expected_payload, sent_messages[0].first);
}
TEST_F(HostScannerOperationTest, RecordsResponseDuration) {
static constexpr base::TimeDelta kTetherAvailabilityResponseTime =
base::TimeDelta::FromSeconds(3);
// Advance the clock in order to verify a non-zero response duration is
// recorded and verified (below).
test_clock_.Advance(kTetherAvailabilityResponseTime);
std::unique_ptr<MessageWrapper> message(
new MessageWrapper(TetherAvailabilityResponse()));
operation_->OnMessageReceived(std::move(message), remote_device_);
histogram_tester_.ExpectTimeBucketCount(
"InstantTethering.Performance.TetherAvailabilityResponseDuration",
kTetherAvailabilityResponseTime, 1);
}
// Tests that the HostScannerOperation does not record a potential tether
// connection after receiving an error response.
TEST_F(HostScannerOperationTest, ErrorResponses) {
const TetherAvailabilityResponse_ResponseCode kErrorResponseCodes[] = {
TetherAvailabilityResponse_ResponseCode_UNKNOWN_ERROR,
TetherAvailabilityResponse_ResponseCode_NO_RECEPTION,
TetherAvailabilityResponse_ResponseCode_NO_SIM_CARD};
for (auto response_code : kErrorResponseCodes) {
// No response should be recorded.
EXPECT_CALL(mock_tether_host_response_recorder_,
RecordSuccessfulTetherAvailabilityResponse(_))
.Times(0);
// Observers should not be notified.
EXPECT_CALL(mock_observer_, OnTetherAvailabilityResponse(
testing::_, testing::_, testing::_))
.Times(0);
// Respond with the error code.
TetherAvailabilityResponse response;
response.set_response_code(response_code);
std::unique_ptr<MessageWrapper> message(new MessageWrapper(response));
operation_->OnMessageReceived(std::move(message), remote_device_);
// Connection is not preserved.
bool connection_preserved =
!fake_connection_preserver_
.last_requested_preserved_connection_device_id()
.empty();
EXPECT_FALSE(connection_preserved);
}
}
// Tests that the observer is notified of the list of devices whose
// notifications are disabled each time a new response is received.
TEST_F(HostScannerOperationTest, NotificationsDisabled) {
std::vector<TetherAvailabilityResponse_ResponseCode>
kNotificationsDisabledResponseCodes = {
TetherAvailabilityResponse_ResponseCode_NOTIFICATIONS_DISABLED_LEGACY,
TetherAvailabilityResponse_ResponseCode_NOTIFICATIONS_DISABLED_WITH_NOTIFICATION_CHANNEL};
multidevice::RemoteDeviceRefList devices_notifications_disabled;
for (auto response_code : kNotificationsDisabledResponseCodes) {
// No response should be recorded.
EXPECT_CALL(mock_tether_host_response_recorder_,
RecordSuccessfulTetherAvailabilityResponse(_))
.Times(0);
// Because the operation is ongoing, each device contained in the response
// is added to the list of devices whose notifications are disabled.
devices_notifications_disabled.push_back(remote_device_);
// The observer is notified of the list of devices whose notificaitons are
// disabled.
EXPECT_CALL(
mock_observer_,
OnTetherAvailabilityResponse(
std::vector<HostScannerOperation::ScannedDeviceInfo>(),
devices_notifications_disabled, false /* is_final_scan_result */));
// Respond with the error code.
TetherAvailabilityResponse response;
response.set_response_code(response_code);
std::unique_ptr<MessageWrapper> message(new MessageWrapper(response));
operation_->OnMessageReceived(std::move(message), remote_device_);
// Connection is not preserved.
bool connection_preserved =
!fake_connection_preserver_
.last_requested_preserved_connection_device_id()
.empty();
EXPECT_FALSE(connection_preserved);
}
}
TEST_F(HostScannerOperationTest, TetherAvailable) {
// The scanned device is recorded.
EXPECT_CALL(mock_tether_host_response_recorder_,
RecordSuccessfulTetherAvailabilityResponse(remote_device_));
// The observer is notified of the scanned device.
DeviceStatus device_status = CreateFakeDeviceStatus();
HostScannerOperation::ScannedDeviceInfo scanned_device(
remote_device_, device_status, false /* setup_required */);
std::vector<HostScannerOperation::ScannedDeviceInfo> scanned_devices(
{scanned_device});
EXPECT_CALL(mock_observer_,
OnTetherAvailabilityResponse(scanned_devices,
multidevice::RemoteDeviceRefList(),
false /* is_final_scan_result */));
// Respond with TETHER_AVAILABLE response code and the device info and status.
TetherAvailabilityResponse response;
response.set_response_code(
TetherAvailabilityResponse_ResponseCode_TETHER_AVAILABLE);
response.mutable_device_status()->CopyFrom(device_status);
std::unique_ptr<MessageWrapper> message(new MessageWrapper(response));
operation_->OnMessageReceived(std::move(message), remote_device_);
// Connection is preserved.
EXPECT_EQ(remote_device_.GetDeviceId(),
fake_connection_preserver_
.last_requested_preserved_connection_device_id());
}
TEST_F(HostScannerOperationTest, LastProvisioningFailed) {
// The scanned device is recorded.
EXPECT_CALL(mock_tether_host_response_recorder_,
RecordSuccessfulTetherAvailabilityResponse(remote_device_));
// The observer is notified of the scanned device.
DeviceStatus device_status = CreateFakeDeviceStatus();
HostScannerOperation::ScannedDeviceInfo scanned_device(
remote_device_, device_status, false /* setup_required */);
std::vector<HostScannerOperation::ScannedDeviceInfo> scanned_devices(
{scanned_device});
EXPECT_CALL(mock_observer_,
OnTetherAvailabilityResponse(scanned_devices,
multidevice::RemoteDeviceRefList(),
false /* is_final_scan_result */));
// Respond with TETHER_AVAILABLE response code and the device info and status.
TetherAvailabilityResponse response;
response.set_response_code(
TetherAvailabilityResponse_ResponseCode_LAST_PROVISIONING_FAILED);
response.mutable_device_status()->CopyFrom(device_status);
std::unique_ptr<MessageWrapper> message(new MessageWrapper(response));
operation_->OnMessageReceived(std::move(message), remote_device_);
// Connection is preserved.
EXPECT_EQ(remote_device_.GetDeviceId(),
fake_connection_preserver_
.last_requested_preserved_connection_device_id());
}
TEST_F(HostScannerOperationTest, SetupRequired) {
// The scanned device is recorded.
EXPECT_CALL(mock_tether_host_response_recorder_,
RecordSuccessfulTetherAvailabilityResponse(remote_device_));
// The observer is notified that the scanned device has the |setup_required|
// flag set.
DeviceStatus device_status = CreateFakeDeviceStatus();
HostScannerOperation::ScannedDeviceInfo scanned_device(
remote_device_, device_status, true /* setup_required */);
std::vector<HostScannerOperation::ScannedDeviceInfo> scanned_devices(
{scanned_device});
EXPECT_CALL(mock_observer_,
OnTetherAvailabilityResponse(scanned_devices,
multidevice::RemoteDeviceRefList(),
false /* is_final_scan_result */));
// Respond with SETUP_NEEDED response code and the device info and status.
TetherAvailabilityResponse response;
response.set_response_code(
TetherAvailabilityResponse_ResponseCode_SETUP_NEEDED);
response.mutable_device_status()->CopyFrom(device_status);
std::unique_ptr<MessageWrapper> message(new MessageWrapper(response));
operation_->OnMessageReceived(std::move(message), remote_device_);
// Connection is preserved.
EXPECT_EQ(remote_device_.GetDeviceId(),
fake_connection_preserver_
.last_requested_preserved_connection_device_id());
}
} // namespace tether
} // namespace chromeos