blob: 74ec4f465bb26e259d985f0756f016b1b1dea4c2 [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_impl.h"
#include <algorithm>
#include <memory>
#include <vector>
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
#include "chromeos/chromeos_features.h"
#include "chromeos/components/tether/device_id_tether_network_guid_map.h"
#include "chromeos/components/tether/fake_ble_connection_manager.h"
#include "chromeos/components/tether/fake_connection_preserver.h"
#include "chromeos/components/tether/fake_host_scan_cache.h"
#include "chromeos/components/tether/fake_notification_presenter.h"
#include "chromeos/components/tether/fake_tether_host_fetcher.h"
#include "chromeos/components/tether/gms_core_notifications_state_tracker_impl.h"
#include "chromeos/components/tether/host_scan_device_prioritizer.h"
#include "chromeos/components/tether/host_scanner.h"
#include "chromeos/components/tether/master_host_scan_cache.h"
#include "chromeos/components/tether/mock_tether_host_response_recorder.h"
#include "chromeos/components/tether/proto_test_util.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_shill_service_client.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_secure_channel_client.h"
#include "components/cryptauth/remote_device_test_util.h"
#include "components/session_manager/core/session_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
namespace chromeos {
namespace tether {
namespace {
class TestObserver final : public HostScanner::Observer {
public:
void ScanFinished() override { scan_finished_count_++; }
int scan_finished_count() { return scan_finished_count_; }
private:
int scan_finished_count_ = 0;
};
class FakeHostScanDevicePrioritizer : public HostScanDevicePrioritizer {
public:
FakeHostScanDevicePrioritizer() : HostScanDevicePrioritizer() {}
~FakeHostScanDevicePrioritizer() override = default;
// Simply leave |remote_devices| as-is.
void SortByHostScanOrder(
cryptauth::RemoteDeviceRefList* remote_devices) const override {}
};
class FakeHostScannerOperation : public HostScannerOperation {
public:
FakeHostScannerOperation(
const cryptauth::RemoteDeviceRefList& devices_to_connect,
device_sync::DeviceSyncClient* device_sync_client,
secure_channel::SecureChannelClient* secure_channel_client,
BleConnectionManager* connection_manager,
HostScanDevicePrioritizer* host_scan_device_prioritizer,
TetherHostResponseRecorder* tether_host_response_recorder,
ConnectionPreserver* connection_preserver)
: HostScannerOperation(devices_to_connect,
device_sync_client,
secure_channel_client,
connection_manager,
host_scan_device_prioritizer,
tether_host_response_recorder,
connection_preserver) {}
~FakeHostScannerOperation() override = default;
void SendScannedDeviceListUpdate(
const std::vector<HostScannerOperation::ScannedDeviceInfo>&
scanned_device_list_so_far,
bool is_final_scan_result) {
scanned_device_list_so_far_ = scanned_device_list_so_far;
NotifyObserversOfScannedDeviceList(is_final_scan_result);
}
};
class FakeHostScannerOperationFactory : public HostScannerOperation::Factory {
public:
FakeHostScannerOperationFactory(
const cryptauth::RemoteDeviceRefList& test_devices)
: expected_devices_(test_devices) {}
virtual ~FakeHostScannerOperationFactory() = default;
std::vector<FakeHostScannerOperation*>& created_operations() {
return created_operations_;
}
protected:
// HostScannerOperation::Factory:
std::unique_ptr<HostScannerOperation> BuildInstance(
const cryptauth::RemoteDeviceRefList& devices_to_connect,
device_sync::DeviceSyncClient* device_sync_client,
secure_channel::SecureChannelClient* secure_channel_client,
BleConnectionManager* connection_manager,
HostScanDevicePrioritizer* host_scan_device_prioritizer,
TetherHostResponseRecorder* tether_host_response_recorder,
ConnectionPreserver* connection_preserver) override {
EXPECT_EQ(expected_devices_, devices_to_connect);
FakeHostScannerOperation* operation = new FakeHostScannerOperation(
devices_to_connect, device_sync_client, secure_channel_client,
connection_manager, host_scan_device_prioritizer,
tether_host_response_recorder, connection_preserver);
created_operations_.push_back(operation);
return base::WrapUnique(operation);
}
private:
const cryptauth::RemoteDeviceRefList& expected_devices_;
std::vector<FakeHostScannerOperation*> created_operations_;
};
std::string GenerateCellProviderForDevice(
cryptauth::RemoteDeviceRef remote_device) {
// Return a string unique to |remote_device|.
return "cellProvider" + remote_device.GetTruncatedDeviceIdForLogs();
}
std::vector<HostScannerOperation::ScannedDeviceInfo>
CreateFakeScannedDeviceInfos(
const cryptauth::RemoteDeviceRefList& remote_devices) {
// At least 4 ScannedDeviceInfos should be created to ensure that all 4 cases
// described below are tested.
EXPECT_GT(remote_devices.size(), 3u);
std::vector<HostScannerOperation::ScannedDeviceInfo> scanned_device_infos;
for (size_t i = 0; i < remote_devices.size(); ++i) {
// Four field possibilities:
// i % 4 == 0: Field is not supplied.
// i % 4 == 1: Field is below the minimum value (int fields only).
// i % 4 == 2: Field is within the valid range (int fields only).
// i % 4 == 3: Field is above the maximium value (int fields only).
std::string cell_provider_name;
int battery_percentage;
int connection_strength;
switch (i % 4) {
case 0:
cell_provider_name = proto_test_util::kDoNotSetStringField;
battery_percentage = proto_test_util::kDoNotSetIntField;
connection_strength = proto_test_util::kDoNotSetIntField;
break;
case 1:
cell_provider_name = GenerateCellProviderForDevice(remote_devices[i]);
battery_percentage = -1 - i;
connection_strength = -1 - i;
break;
case 2:
cell_provider_name = GenerateCellProviderForDevice(remote_devices[i]);
battery_percentage = (50 + i) % 100; // Valid range is [0, 100].
connection_strength = (1 + i) % 4; // Valid range is [0, 4].
break;
case 3:
cell_provider_name = GenerateCellProviderForDevice(remote_devices[i]);
battery_percentage = 101 + i;
connection_strength = 101 + i;
break;
default:
NOTREACHED();
// Set values for |battery_percentage| and |connection_strength| here to
// prevent a compiler warning which says that they may be unset at this
// point.
battery_percentage = 0;
connection_strength = 0;
break;
}
DeviceStatus device_status = CreateTestDeviceStatus(
cell_provider_name, battery_percentage, connection_strength);
// Require set-up for odd-numbered device indices.
bool setup_required = i % 2 == 0;
scanned_device_infos.push_back(HostScannerOperation::ScannedDeviceInfo(
remote_devices[i], device_status, setup_required));
}
return scanned_device_infos;
}
} // namespace
class HostScannerImplTest : public NetworkStateTest {
protected:
HostScannerImplTest()
: test_devices_(cryptauth::CreateRemoteDeviceRefListForTest(4)),
test_scanned_device_infos(CreateFakeScannedDeviceInfos(test_devices_)) {
}
void SetUp() override {
scoped_feature_list_.InitAndDisableFeature(features::kMultiDeviceApi);
DBusThreadManager::Initialize();
NetworkHandler::Initialize();
NetworkStateTest::SetUp();
scanned_device_infos_from_current_scan_.clear();
fake_device_sync_client_ =
std::make_unique<device_sync::FakeDeviceSyncClient>();
fake_secure_channel_client_ =
std::make_unique<secure_channel::FakeSecureChannelClient>();
session_manager_ = std::make_unique<session_manager::SessionManager>();
fake_tether_host_fetcher_ =
std::make_unique<FakeTetherHostFetcher>(test_devices_);
fake_ble_connection_manager_ = std::make_unique<FakeBleConnectionManager>();
fake_host_scan_device_prioritizer_ =
std::make_unique<FakeHostScanDevicePrioritizer>();
mock_tether_host_response_recorder_ =
std::make_unique<MockTetherHostResponseRecorder>();
gms_core_notifications_state_tracker_ =
std::make_unique<GmsCoreNotificationsStateTrackerImpl>();
fake_notification_presenter_ =
std::make_unique<FakeNotificationPresenter>();
device_id_tether_network_guid_map_ =
std::make_unique<DeviceIdTetherNetworkGuidMap>();
fake_host_scan_cache_ = std::make_unique<FakeHostScanCache>();
fake_host_scanner_operation_factory_ =
base::WrapUnique(new FakeHostScannerOperationFactory(test_devices_));
HostScannerOperation::Factory::SetInstanceForTesting(
fake_host_scanner_operation_factory_.get());
fake_connection_preserver_ = std::make_unique<FakeConnectionPreserver>();
test_clock_ = std::make_unique<base::SimpleTestClock>();
host_scanner_ = base::WrapUnique(new HostScannerImpl(
fake_device_sync_client_.get(), fake_secure_channel_client_.get(),
network_state_handler(), session_manager_.get(),
fake_tether_host_fetcher_.get(), fake_ble_connection_manager_.get(),
fake_host_scan_device_prioritizer_.get(),
mock_tether_host_response_recorder_.get(),
gms_core_notifications_state_tracker_.get(),
fake_notification_presenter_.get(),
device_id_tether_network_guid_map_.get(), fake_host_scan_cache_.get(),
fake_connection_preserver_.get(), test_clock_.get()));
test_observer_ = std::make_unique<TestObserver>();
host_scanner_->AddObserver(test_observer_.get());
}
void TearDown() override {
host_scanner_->RemoveObserver(test_observer_.get());
HostScannerOperation::Factory::SetInstanceForTesting(nullptr);
NetworkStateTest::TearDown();
NetworkHandler::Shutdown();
DBusThreadManager::Shutdown();
}
// Causes |fake_operation| to receive the scan result in
// |test_scanned_device_infos| vector at the index |test_device_index| with
// the "final result" value of |is_final_scan_result|.
void ReceiveScanResultAndVerifySuccess(
FakeHostScannerOperation* fake_operation,
size_t test_device_index,
bool is_final_scan_result,
NotificationPresenter::PotentialHotspotNotificationState
expected_notification_state,
int num_expected_host_scan_histogram_samples = 1) {
bool already_in_list = false;
for (auto& scanned_device_info : scanned_device_infos_from_current_scan_) {
if (scanned_device_info.remote_device.GetDeviceId() ==
test_devices_[test_device_index].GetDeviceId()) {
already_in_list = true;
break;
}
}
if (!already_in_list) {
scanned_device_infos_from_current_scan_.push_back(
test_scanned_device_infos[test_device_index]);
}
int previous_scan_finished_count = test_observer_->scan_finished_count();
fake_operation->SendScannedDeviceListUpdate(
scanned_device_infos_from_current_scan_, is_final_scan_result);
VerifyScanResultsMatchCache();
EXPECT_EQ(previous_scan_finished_count + (is_final_scan_result ? 1 : 0),
test_observer_->scan_finished_count());
EXPECT_EQ(
expected_notification_state,
fake_notification_presenter_->GetPotentialHotspotNotificationState());
if (is_final_scan_result) {
HostScannerImpl::HostScanResultEventType expected_event_type =
HostScannerImpl::HostScanResultEventType::NO_HOSTS_FOUND;
if (!scanned_device_infos_from_current_scan_.empty() &&
expected_notification_state ==
NotificationPresenter::PotentialHotspotNotificationState::
NO_HOTSPOT_NOTIFICATION_SHOWN) {
expected_event_type = HostScannerImpl::HostScanResultEventType::
HOSTS_FOUND_BUT_NO_NOTIFICATION_SHOWN;
} else if (scanned_device_infos_from_current_scan_.size() == 1) {
expected_event_type = HostScannerImpl::HostScanResultEventType::
NOTIFICATION_SHOWN_SINGLE_HOST;
} else if (scanned_device_infos_from_current_scan_.size() > 1) {
expected_event_type = HostScannerImpl::HostScanResultEventType::
NOTIFICATION_SHOWN_MULTIPLE_HOSTS;
}
histogram_tester_.ExpectUniqueSample(
"InstantTethering.HostScanResult", expected_event_type,
num_expected_host_scan_histogram_samples);
}
}
void VerifyScanResultsMatchCache() {
std::vector<HostScannerOperation::ScannedDeviceInfo> combined_device_infos =
scanned_device_infos_from_current_scan_;
for (const auto& previous_scan_result :
scanned_device_infos_from_previous_scans_) {
bool already_in_combined = false;
for (const auto& combined_device_info : combined_device_infos) {
if (previous_scan_result.remote_device.GetDeviceId() ==
combined_device_info.remote_device.GetDeviceId()) {
already_in_combined = true;
break;
}
}
if (!already_in_combined)
combined_device_infos.push_back(previous_scan_result);
}
ASSERT_EQ(combined_device_infos.size(), fake_host_scan_cache_->size());
for (auto& scanned_device_info : combined_device_infos) {
std::string tether_network_guid =
device_id_tether_network_guid_map_->GetTetherNetworkGuidForDeviceId(
scanned_device_info.remote_device.GetDeviceId());
const HostScanCacheEntry* entry =
fake_host_scan_cache_->GetCacheEntry(tether_network_guid);
ASSERT_TRUE(entry);
VerifyScannedDeviceInfoAndCacheEntryAreEquivalent(scanned_device_info,
*entry);
}
}
void VerifyScannedDeviceInfoAndCacheEntryAreEquivalent(
const HostScannerOperation::ScannedDeviceInfo& scanned_device_info,
const HostScanCacheEntry& entry) {
EXPECT_EQ(scanned_device_info.remote_device.name(), entry.device_name);
const DeviceStatus& status = scanned_device_info.device_status;
if (!status.has_cell_provider() || status.cell_provider().empty())
EXPECT_EQ("unknown-carrier", entry.carrier);
else
EXPECT_EQ(status.cell_provider(), entry.carrier);
if (!status.has_battery_percentage() || status.battery_percentage() > 100)
EXPECT_EQ(100, entry.battery_percentage);
else if (status.battery_percentage() < 0)
EXPECT_EQ(0, entry.battery_percentage);
else
EXPECT_EQ(status.battery_percentage(), entry.battery_percentage);
if (!status.has_connection_strength() || status.connection_strength() > 4)
EXPECT_EQ(100, entry.signal_strength);
else if (status.connection_strength() < 0)
EXPECT_EQ(0, entry.signal_strength);
else
EXPECT_EQ(status.connection_strength() * 25, entry.signal_strength);
EXPECT_EQ(scanned_device_info.setup_required, entry.setup_required);
}
// Clears scan results from |scanned_device_infos_from_current_scan_| and
// transfers them to |scanned_device_infos_from_previous_scans_|.
void ClearCurrentScanResults() {
scanned_device_infos_from_previous_scans_ =
scanned_device_infos_from_current_scan_;
scanned_device_infos_from_current_scan_.clear();
}
void StartConnectingToWifiNetwork() {
std::stringstream ss;
ss << "{"
<< " \"GUID\": \"wifiNetworkGuid\","
<< " \"Type\": \"" << shill::kTypeWifi << "\","
<< " \"State\": \"" << shill::kStateConfiguration << "\""
<< "}";
ConfigureService(ss.str());
}
void SetScreenLockedState(bool is_locked) {
session_manager_->SetSessionState(
is_locked ? session_manager::SessionState::LOCKED
: session_manager::SessionState::LOGIN_PRIMARY);
}
const base::test::ScopedTaskEnvironment scoped_task_environment_;
const cryptauth::RemoteDeviceRefList test_devices_;
const std::vector<HostScannerOperation::ScannedDeviceInfo>
test_scanned_device_infos;
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_;
std::unique_ptr<secure_channel::SecureChannelClient>
fake_secure_channel_client_;
std::unique_ptr<session_manager::SessionManager> session_manager_;
std::unique_ptr<FakeTetherHostFetcher> fake_tether_host_fetcher_;
std::unique_ptr<FakeBleConnectionManager> fake_ble_connection_manager_;
std::unique_ptr<HostScanDevicePrioritizer> fake_host_scan_device_prioritizer_;
std::unique_ptr<MockTetherHostResponseRecorder>
mock_tether_host_response_recorder_;
std::unique_ptr<GmsCoreNotificationsStateTrackerImpl>
gms_core_notifications_state_tracker_;
std::unique_ptr<FakeNotificationPresenter> fake_notification_presenter_;
// TODO(hansberry): Use a fake for this when a real mapping scheme is created.
std::unique_ptr<DeviceIdTetherNetworkGuidMap>
device_id_tether_network_guid_map_;
std::unique_ptr<FakeHostScanCache> fake_host_scan_cache_;
std::unique_ptr<FakeConnectionPreserver> fake_connection_preserver_;
std::unique_ptr<base::SimpleTestClock> test_clock_;
std::unique_ptr<TestObserver> test_observer_;
std::unique_ptr<FakeHostScannerOperationFactory>
fake_host_scanner_operation_factory_;
std::vector<HostScannerOperation::ScannedDeviceInfo>
scanned_device_infos_from_current_scan_;
std::vector<HostScannerOperation::ScannedDeviceInfo>
scanned_device_infos_from_previous_scans_;
std::unique_ptr<HostScanner> host_scanner_;
base::HistogramTester histogram_tester_;
private:
DISALLOW_COPY_AND_ASSIGN(HostScannerImplTest);
};
TEST_F(HostScannerImplTest, TestScan_ConnectingToExistingNetwork) {
StartConnectingToWifiNetwork();
EXPECT_TRUE(network_state_handler()->DefaultNetwork());
EXPECT_FALSE(host_scanner_->IsScanActive());
host_scanner_->StartScan();
EXPECT_TRUE(host_scanner_->IsScanActive());
ASSERT_EQ(1u,
fake_host_scanner_operation_factory_->created_operations().size());
EXPECT_TRUE(host_scanner_->IsScanActive());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
0u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
NO_HOTSPOT_NOTIFICATION_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
1u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
NO_HOTSPOT_NOTIFICATION_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
2u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
NO_HOTSPOT_NOTIFICATION_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
3u /* test_device_index */, true /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
NO_HOTSPOT_NOTIFICATION_SHOWN);
EXPECT_FALSE(host_scanner_->IsScanActive());
}
TEST_F(HostScannerImplTest, TestNotificationNotDisplayedMultipleTimes) {
StartConnectingToWifiNetwork();
EXPECT_TRUE(network_state_handler()->DefaultNetwork());
EXPECT_FALSE(host_scanner_->IsScanActive());
host_scanner_->StartScan();
EXPECT_TRUE(host_scanner_->IsScanActive());
ASSERT_EQ(1u,
fake_host_scanner_operation_factory_->created_operations().size());
EXPECT_TRUE(host_scanner_->IsScanActive());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
0u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
NO_HOTSPOT_NOTIFICATION_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
1u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
NO_HOTSPOT_NOTIFICATION_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
2u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
NO_HOTSPOT_NOTIFICATION_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
3u /* test_device_index */, true /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
NO_HOTSPOT_NOTIFICATION_SHOWN);
EXPECT_FALSE(host_scanner_->IsScanActive());
}
TEST_F(HostScannerImplTest, TestNotificationDisplaysMultipleTimesWhenUnlocked) {
// Start a scan and receive a result.
host_scanner_->StartScan();
ASSERT_EQ(1u,
fake_host_scanner_operation_factory_->created_operations().size());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
0u /* test_device_index */, true /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
SINGLE_HOTSPOT_NEARBY_SHOWN);
EXPECT_FALSE(host_scanner_->IsScanActive());
// The scan has completed, and the "single hotspot nearby" notification should
// be shown. Remove it.
EXPECT_EQ(
NotificationPresenter::PotentialHotspotNotificationState::
SINGLE_HOTSPOT_NEARBY_SHOWN,
fake_notification_presenter_->GetPotentialHotspotNotificationState());
fake_notification_presenter_->RemovePotentialHotspotNotification();
// Now, simulate locking then unlocking the device.
SetScreenLockedState(true /* is_locked */);
SetScreenLockedState(false /* is_locked */);
// Start another scan and receive a result.
host_scanner_->StartScan();
ASSERT_EQ(2u,
fake_host_scanner_operation_factory_->created_operations().size());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[1],
0u /* test_device_index */, true /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
SINGLE_HOTSPOT_NEARBY_SHOWN,
2 /* num_expected_host_scan_histogram_samples */);
EXPECT_FALSE(host_scanner_->IsScanActive());
// The notification should be visible since the device was locked and unlocked
// since the last time it was shown.
EXPECT_EQ(
NotificationPresenter::PotentialHotspotNotificationState::
SINGLE_HOTSPOT_NEARBY_SHOWN,
fake_notification_presenter_->GetPotentialHotspotNotificationState());
}
TEST_F(HostScannerImplTest, TestScan_ResultsFromAllDevices) {
EXPECT_FALSE(host_scanner_->IsScanActive());
host_scanner_->StartScan();
EXPECT_TRUE(host_scanner_->IsScanActive());
ASSERT_EQ(1u,
fake_host_scanner_operation_factory_->created_operations().size());
EXPECT_TRUE(host_scanner_->IsScanActive());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
0u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
SINGLE_HOTSPOT_NEARBY_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
1u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
MULTIPLE_HOTSPOTS_NEARBY_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
2u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
MULTIPLE_HOTSPOTS_NEARBY_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
3u /* test_device_index */, true /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
MULTIPLE_HOTSPOTS_NEARBY_SHOWN);
EXPECT_FALSE(host_scanner_->IsScanActive());
}
TEST_F(HostScannerImplTest, TestScan_ResultsFromNoDevices) {
EXPECT_FALSE(host_scanner_->IsScanActive());
host_scanner_->StartScan();
EXPECT_TRUE(host_scanner_->IsScanActive());
ASSERT_EQ(1u,
fake_host_scanner_operation_factory_->created_operations().size());
EXPECT_TRUE(host_scanner_->IsScanActive());
fake_host_scanner_operation_factory_->created_operations()[0]
->SendScannedDeviceListUpdate(
std::vector<HostScannerOperation::ScannedDeviceInfo>(),
true /* is_final_scan_result */);
EXPECT_EQ(0u, fake_host_scan_cache_->size());
EXPECT_FALSE(host_scanner_->IsScanActive());
histogram_tester_.ExpectUniqueSample(
"InstantTethering.HostScanResult",
HostScannerImpl::HostScanResultEventType::NO_HOSTS_FOUND, 1);
}
TEST_F(HostScannerImplTest, StopScan) {
host_scanner_->StartScan();
EXPECT_TRUE(host_scanner_->IsScanActive());
ASSERT_EQ(1u,
fake_host_scanner_operation_factory_->created_operations().size());
EXPECT_TRUE(host_scanner_->IsScanActive());
host_scanner_->StopScan();
EXPECT_FALSE(host_scanner_->IsScanActive());
}
TEST_F(HostScannerImplTest, TestScan_ResultsFromSomeDevices) {
EXPECT_FALSE(host_scanner_->IsScanActive());
host_scanner_->StartScan();
EXPECT_TRUE(host_scanner_->IsScanActive());
ASSERT_EQ(1u,
fake_host_scanner_operation_factory_->created_operations().size());
EXPECT_TRUE(host_scanner_->IsScanActive());
// Only receive updates from the 0th and 1st device.
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
0u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
SINGLE_HOTSPOT_NEARBY_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
1u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
MULTIPLE_HOTSPOTS_NEARBY_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
fake_host_scanner_operation_factory_->created_operations()[0]
->SendScannedDeviceListUpdate(scanned_device_infos_from_current_scan_,
true /* is_final_scan_result */);
EXPECT_EQ(scanned_device_infos_from_current_scan_.size(),
fake_host_scan_cache_->size());
EXPECT_FALSE(host_scanner_->IsScanActive());
}
TEST_F(HostScannerImplTest, TestScan_MultipleScanCallsDuringOperation) {
EXPECT_FALSE(host_scanner_->IsScanActive());
host_scanner_->StartScan();
EXPECT_TRUE(host_scanner_->IsScanActive());
ASSERT_EQ(1u,
fake_host_scanner_operation_factory_->created_operations().size());
EXPECT_TRUE(host_scanner_->IsScanActive());
// Call StartScan again before the final scan result has been received. This
// should be a no-op.
host_scanner_->StartScan();
EXPECT_TRUE(host_scanner_->IsScanActive());
// No devices should have been received yet.
EXPECT_EQ(0u, fake_host_scan_cache_->size());
// Receive updates from the 0th device.
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
0u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
SINGLE_HOTSPOT_NEARBY_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
// Call StartScan again after a scan result has been received but before
// the final scan result ha been received. This should be a no-op.
host_scanner_->StartScan();
EXPECT_TRUE(host_scanner_->IsScanActive());
// The scanned devices so far should be the same (i.e., they should not have
// been affected by the extra call to StartScan()).
EXPECT_EQ(scanned_device_infos_from_current_scan_.size(),
fake_host_scan_cache_->size());
// Finally, finish the scan.
fake_host_scanner_operation_factory_->created_operations()[0]
->SendScannedDeviceListUpdate(scanned_device_infos_from_current_scan_,
true /* is_final_scan_result */);
EXPECT_EQ(scanned_device_infos_from_current_scan_.size(),
fake_host_scan_cache_->size());
EXPECT_FALSE(host_scanner_->IsScanActive());
}
TEST_F(HostScannerImplTest, TestScan_MultipleCompleteScanSessions) {
// Start the first scan session.
EXPECT_FALSE(host_scanner_->IsScanActive());
host_scanner_->StartScan();
EXPECT_TRUE(host_scanner_->IsScanActive());
ASSERT_EQ(1u,
fake_host_scanner_operation_factory_->created_operations().size());
EXPECT_TRUE(host_scanner_->IsScanActive());
// Receive updates from devices 0-2.
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
0u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
SINGLE_HOTSPOT_NEARBY_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
1u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
MULTIPLE_HOTSPOTS_NEARBY_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[0],
2u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
MULTIPLE_HOTSPOTS_NEARBY_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
// Finish the first scan.
fake_host_scanner_operation_factory_->created_operations()[0]
->SendScannedDeviceListUpdate(scanned_device_infos_from_current_scan_,
true /* is_final_scan_result */);
EXPECT_EQ(scanned_device_infos_from_current_scan_.size(),
fake_host_scan_cache_->size());
EXPECT_FALSE(host_scanner_->IsScanActive());
// The notification should still be visible.
EXPECT_EQ(
NotificationPresenter::PotentialHotspotNotificationState::
MULTIPLE_HOTSPOTS_NEARBY_SHOWN,
fake_notification_presenter_->GetPotentialHotspotNotificationState());
// Now, start the second scan session. Since the notification was still
// visible from the first scan session, it should still be able to be shown
// for the second scan session.
ClearCurrentScanResults();
EXPECT_FALSE(host_scanner_->IsScanActive());
host_scanner_->StartScan();
EXPECT_TRUE(host_scanner_->IsScanActive());
ASSERT_EQ(2u,
fake_host_scanner_operation_factory_->created_operations().size());
EXPECT_TRUE(host_scanner_->IsScanActive());
// The cache should be unaffected by the start of a new scan.
VerifyScanResultsMatchCache();
// Receive results from devices 0 only. Results from devices 1 and 2 should
// still be present in the cache even though no results have been received
// from that device during this scan session.
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[1],
0u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
MULTIPLE_HOTSPOTS_NEARBY_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
VerifyScanResultsMatchCache();
// Finish the second scan. Since results were not received from devices 1 or
// 2, previous results from those devices should now be removed from the
// cache.
fake_host_scanner_operation_factory_->created_operations()[1]
->SendScannedDeviceListUpdate(scanned_device_infos_from_current_scan_,
true /* is_final_scan_result */);
EXPECT_FALSE(host_scanner_->IsScanActive());
// The notification should have been changed to a single hotspot. Remove it
// before starting the third scan session.
EXPECT_EQ(
NotificationPresenter::PotentialHotspotNotificationState::
SINGLE_HOTSPOT_NEARBY_SHOWN,
fake_notification_presenter_->GetPotentialHotspotNotificationState());
fake_notification_presenter_->RemovePotentialHotspotNotification();
// Now, start the third scan session. Since the notification was hidden before
// the session started, it should not be shown for this session.
ClearCurrentScanResults();
EXPECT_FALSE(host_scanner_->IsScanActive());
host_scanner_->StartScan();
EXPECT_TRUE(host_scanner_->IsScanActive());
ASSERT_EQ(3u,
fake_host_scanner_operation_factory_->created_operations().size());
EXPECT_TRUE(host_scanner_->IsScanActive());
// The cache should be unaffected by the start of a new scan.
VerifyScanResultsMatchCache();
// Receive results from devices 0, 2 and 3. Results from device 1 should still
// be present in the cache even though no results have been received from that
// device during this scan session.
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[2],
0u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
NO_HOTSPOT_NOTIFICATION_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[2],
2u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
NO_HOTSPOT_NOTIFICATION_SHOWN);
ReceiveScanResultAndVerifySuccess(
fake_host_scanner_operation_factory_->created_operations()[2],
3u /* test_device_index */, false /* is_final_scan_result */,
NotificationPresenter::PotentialHotspotNotificationState::
NO_HOTSPOT_NOTIFICATION_SHOWN);
EXPECT_TRUE(host_scanner_->IsScanActive());
VerifyScanResultsMatchCache();
// Finish the second scan. Since results were not received from device 1,
// previous results from device 1 should now be removed from the cache.
fake_host_scanner_operation_factory_->created_operations()[2]
->SendScannedDeviceListUpdate(scanned_device_infos_from_current_scan_,
true /* is_final_scan_result */);
EXPECT_FALSE(host_scanner_->IsScanActive());
ClearCurrentScanResults();
VerifyScanResultsMatchCache();
}
} // namespace tether
} // namespace chromeos