blob: 5949c17ed8df7431d72e0922d1cfd27758684532 [file] [log] [blame]
// Copyright 2017 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/wifi_hotspot_connector.h"
#include <memory>
#include <sstream>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.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 "base/timer/mock_timer.h"
#include "base/values.h"
#include "chromeos/network/network_connect.h"
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h"
#include "chromeos/network/network_state_test_helper.h"
#include "chromeos/network/shill_property_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
namespace chromeos {
namespace tether {
namespace {
const char kSsid[] = "ssid";
const char kPassword[] = "password";
const char kOtherWifiServiceGuid[] = "otherWifiServiceGuid";
const char kTetherNetworkGuid[] = "tetherNetworkGuid";
const char kTetherNetworkGuid2[] = "tetherNetworkGuid2";
constexpr base::TimeDelta kConnectionToHotspotTime =
base::TimeDelta::FromSeconds(20);
std::string CreateConfigurationJsonString(const std::string& guid) {
std::stringstream ss;
ss << "{"
<< " \"GUID\": \"" << guid << "\","
<< " \"Type\": \"" << shill::kTypeWifi << "\","
<< " \"State\": \"" << shill::kStateIdle << "\""
<< "}";
return ss.str();
}
} // namespace
class WifiHotspotConnectorTest : public testing::Test {
public:
class TestNetworkConnect : public NetworkConnect {
public:
explicit TestNetworkConnect(NetworkStateTestHelper* helper)
: helper_(helper) {}
~TestNetworkConnect() override = default;
void set_is_running_in_test_task_runner(
bool is_running_in_test_task_runner) {
is_running_in_test_task_runner_ = is_running_in_test_task_runner;
}
base::DictionaryValue* last_configuration() {
return last_configuration_.get();
}
std::string last_service_path_created() {
return last_service_path_created_;
}
std::string network_id_to_connect() { return network_id_to_connect_; }
std::string network_id_to_disconnect() { return network_id_to_disconnect_; }
uint32_t num_connection_attempts() { return num_connection_attempts_; }
uint32_t num_disconnection_attempts() {
return num_disconnection_attempts_;
}
// Finish configuring the last specified Wi-Fi network config.
void ConfigureServiceWithLastNetworkConfig() {
std::string wifi_guid;
EXPECT_TRUE(
last_configuration_->GetString(shill::kGuidProperty, &wifi_guid));
last_service_path_created_ =
helper_->ConfigureService(CreateConfigurationJsonString(wifi_guid));
}
// NetworkConnect:
void SetTechnologyEnabled(const chromeos::NetworkTypePattern& technology,
bool enabled_state) override {}
void ShowMobileSetup(const std::string& network_id) override {}
void ConfigureNetworkIdAndConnect(
const std::string& network_id,
const base::DictionaryValue& shill_properties,
bool shared) override {}
void CreateConfigurationAndConnect(base::DictionaryValue* shill_properties,
bool shared) override {}
void CreateConfiguration(base::DictionaryValue* shill_properties,
bool shared) override {
EXPECT_FALSE(shared);
last_configuration_ = shill_properties->CreateDeepCopy();
// Prevent nested RunLoops when ConfigureServiceWithLastNetworkConfig()
// calls NetworkStateTestHelper::ConfigureService(); that causes threading
// issues. If |test_task_runner_| is causing this function to be run, the
// client which triggered this call can manually call
// ConfigureServiceWithLastNetworkConfig() once done.
if (!is_running_in_test_task_runner_)
ConfigureServiceWithLastNetworkConfig();
}
void ConnectToNetworkId(const std::string& network_id) override {
num_connection_attempts_++;
network_id_to_connect_ = network_id;
}
void DisconnectFromNetworkId(const std::string& network_id) override {
num_disconnection_attempts_++;
network_id_to_disconnect_ = network_id;
}
private:
NetworkStateTestHelper* helper_;
std::unique_ptr<base::DictionaryValue> last_configuration_;
std::string last_service_path_created_;
std::string network_id_to_connect_;
std::string network_id_to_disconnect_;
uint32_t num_connection_attempts_ = 0;
uint32_t num_disconnection_attempts_ = 0;
bool is_running_in_test_task_runner_ = false;
};
WifiHotspotConnectorTest() = default;
~WifiHotspotConnectorTest() override = default;
void SetUp() override {
other_wifi_service_path_.clear();
connection_callback_responses_.clear();
helper_.network_state_handler()->SetTetherTechnologyState(
NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED);
helper_.network_state_handler()->SetTechnologyEnabled(
NetworkTypePattern::WiFi(), true /* enabled */,
chromeos::network_handler::ErrorCallback());
base::RunLoop().RunUntilIdle();
SetUpShillState();
test_network_connect_ = base::WrapUnique(new TestNetworkConnect(&helper_));
helper_.network_state_handler()->AddTetherNetworkState(
kTetherNetworkGuid, "tetherNetworkName" /* name */,
"tetherNetworkCarrier" /* carrier */, 100 /* full battery */,
100 /* full signal strength */, false /* has_connected_to_host */);
helper_.network_state_handler()->AddTetherNetworkState(
kTetherNetworkGuid2, "tetherNetworkName2" /* name */,
"tetherNetworkCarrier2" /* carrier */, 100 /* full battery */,
100 /* full signal strength */, false /* has_connected_to_host */);
wifi_hotspot_connector_ = base::WrapUnique(new WifiHotspotConnector(
helper_.network_state_handler(), test_network_connect_.get()));
mock_timer_ = new base::MockOneShotTimer();
test_clock_.SetNow(base::Time::UnixEpoch());
test_task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>();
wifi_hotspot_connector_->SetTestDoubles(base::WrapUnique(mock_timer_),
&test_clock_, test_task_runner_);
}
void SetUpShillState() {
// Add a Wi-Fi service unrelated to the one under test. In some tests, a
// connection from another device is tested.
other_wifi_service_path_ = helper_.ConfigureService(
CreateConfigurationJsonString(std::string(kOtherWifiServiceGuid)));
}
void TearDown() override {
wifi_hotspot_connector_.reset();
}
void VerifyTimerSet() {
EXPECT_TRUE(mock_timer_->IsRunning());
EXPECT_EQ(base::TimeDelta::FromSeconds(
WifiHotspotConnector::kConnectionTimeoutSeconds),
mock_timer_->GetCurrentDelay());
}
void VerifyTimerStopped() { EXPECT_FALSE(mock_timer_->IsRunning()); }
void InvokeTimerTask() {
VerifyTimerSet();
mock_timer_->Fire();
}
void RunTestTaskRunner() {
test_network_connect_->set_is_running_in_test_task_runner(true);
test_task_runner_->RunUntilIdle();
test_network_connect_->set_is_running_in_test_task_runner(false);
}
void NotifyConnectable(const std::string& service_path) {
helper_.SetServiceProperty(service_path,
std::string(shill::kConnectableProperty),
base::Value(true));
RunTestTaskRunner();
}
void NotifyConnected(const std::string& service_path) {
helper_.SetServiceProperty(service_path, std::string(shill::kStateProperty),
base::Value(shill::kStateReady));
RunTestTaskRunner();
}
void VerifyConnectionToHotspotDurationRecorded(bool expected) {
if (expected) {
histogram_tester_.ExpectTimeBucketCount(
"InstantTethering.Performance.ConnectToHotspotDuration",
kConnectionToHotspotTime, 1);
} else {
histogram_tester_.ExpectTotalCount(
"InstantTethering.Performance.ConnectToHotspotDuration", 0);
}
}
// Verifies the last configuration has the expected SSID and password, and
// returns the Wi-Fi GUID for the configuration.
std::string VerifyLastConfiguration(const std::string& expected_ssid,
const std::string& expected_password) {
base::DictionaryValue* last_configuration =
test_network_connect_->last_configuration();
EXPECT_TRUE(last_configuration);
std::string ssid = shill_property_util::GetSSIDFromProperties(
*last_configuration, false, nullptr);
EXPECT_EQ(expected_ssid, ssid);
if (expected_password.empty()) {
std::string security;
EXPECT_TRUE(last_configuration->GetString(shill::kSecurityClassProperty,
&security));
EXPECT_EQ(std::string(shill::kSecurityNone), security);
} else {
std::string security;
EXPECT_TRUE(last_configuration->GetString(shill::kSecurityClassProperty,
&security));
EXPECT_EQ(std::string(shill::kSecurityPsk), security);
std::string password;
EXPECT_TRUE(
last_configuration->GetString(shill::kPassphraseProperty, &password));
EXPECT_EQ(expected_password, password);
}
std::string wifi_guid;
EXPECT_TRUE(
last_configuration->GetString(shill::kGuidProperty, &wifi_guid));
return wifi_guid;
}
void VerifyTetherAndWifiNetworkAssociation(
const std::string& wifi_guid,
const std::string& tether_guid,
uint32_t expected_num_connection_attempts) {
const NetworkState* wifi_network_state =
helper_.network_state_handler()->GetNetworkStateFromGuid(wifi_guid);
ASSERT_TRUE(wifi_network_state);
EXPECT_EQ(tether_guid, wifi_network_state->tether_guid());
const NetworkState* tether_network_state =
helper_.network_state_handler()->GetNetworkStateFromGuid(tether_guid);
ASSERT_TRUE(tether_network_state);
EXPECT_EQ(wifi_guid, tether_network_state->tether_guid());
EXPECT_EQ(expected_num_connection_attempts,
test_network_connect_->num_connection_attempts());
}
void VerifyNetworkNotAssociated(const std::string& guid) {
const NetworkState* network_state =
helper_.network_state_handler()->GetNetworkStateFromGuid(guid);
ASSERT_TRUE(network_state);
EXPECT_TRUE(network_state->tether_guid().empty());
}
void WifiConnectionCallback(const std::string& wifi_guid) {
connection_callback_responses_.push_back(wifi_guid);
}
NetworkStateHandler* network_state_handler() {
return helper_.network_state_handler();
}
base::test::ScopedTaskEnvironment scoped_task_environment_;
NetworkStateTestHelper helper_{true /* use_default_devices_and_services */};
std::string other_wifi_service_path_;
std::vector<std::string> connection_callback_responses_;
base::MockOneShotTimer* mock_timer_;
base::SimpleTestClock test_clock_;
scoped_refptr<base::TestSimpleTaskRunner> test_task_runner_;
std::unique_ptr<TestNetworkConnect> test_network_connect_;
std::unique_ptr<WifiHotspotConnector> wifi_hotspot_connector_;
base::HistogramTester histogram_tester_;
private:
DISALLOW_COPY_AND_ASSIGN(WifiHotspotConnectorTest);
};
TEST_F(WifiHotspotConnectorTest, TestConnect_NetworkDoesNotBecomeConnectable) {
wifi_hotspot_connector_->ConnectToWifiHotspot(
std::string(kSsid), std::string(kPassword), kTetherNetworkGuid,
base::Bind(&WifiHotspotConnectorTest::WifiConnectionCallback,
base::Unretained(this)));
std::string wifi_guid =
VerifyLastConfiguration(std::string(kSsid), std::string(kPassword));
EXPECT_FALSE(wifi_guid.empty());
// Network does not become connectable.
EXPECT_TRUE(test_network_connect_->network_id_to_connect().empty());
// Timeout timer fires.
EXPECT_EQ(0u, connection_callback_responses_.size());
InvokeTimerTask();
EXPECT_EQ(1u, connection_callback_responses_.size());
EXPECT_TRUE(connection_callback_responses_[0].empty());
VerifyConnectionToHotspotDurationRecorded(false /* expected */);
EXPECT_EQ(0u, test_network_connect_->num_disconnection_attempts());
}
TEST_F(WifiHotspotConnectorTest, TestConnect_AnotherNetworkBecomesConnectable) {
wifi_hotspot_connector_->ConnectToWifiHotspot(
std::string(kSsid), std::string(kPassword), kTetherNetworkGuid,
base::Bind(&WifiHotspotConnectorTest::WifiConnectionCallback,
base::Unretained(this)));
std::string wifi_guid =
VerifyLastConfiguration(std::string(kSsid), std::string(kPassword));
EXPECT_FALSE(wifi_guid.empty());
// Another network becomes connectable. This should not cause the connection
// to start.
NotifyConnectable(other_wifi_service_path_);
VerifyNetworkNotAssociated(wifi_guid);
std::string other_wifi_guid = network_state_handler()
->GetNetworkState(other_wifi_service_path_)
->guid();
VerifyNetworkNotAssociated(other_wifi_guid);
EXPECT_TRUE(test_network_connect_->network_id_to_connect().empty());
// Timeout timer fires.
EXPECT_EQ(0u, connection_callback_responses_.size());
InvokeTimerTask();
EXPECT_EQ(1u, connection_callback_responses_.size());
EXPECT_TRUE(connection_callback_responses_[0].empty());
VerifyConnectionToHotspotDurationRecorded(false /* expected */);
EXPECT_EQ(0u, test_network_connect_->num_disconnection_attempts());
}
TEST_F(WifiHotspotConnectorTest, TestConnect_CannotConnectToNetwork) {
wifi_hotspot_connector_->ConnectToWifiHotspot(
std::string(kSsid), std::string(kPassword), kTetherNetworkGuid,
base::Bind(&WifiHotspotConnectorTest::WifiConnectionCallback,
base::Unretained(this)));
std::string wifi_guid =
VerifyLastConfiguration(std::string(kSsid), std::string(kPassword));
EXPECT_FALSE(wifi_guid.empty());
// Network becomes connectable.
NotifyConnectable(test_network_connect_->last_service_path_created());
VerifyTetherAndWifiNetworkAssociation(
wifi_guid, kTetherNetworkGuid, 1u /* expected_num_connection_attempts */);
EXPECT_EQ(wifi_guid, test_network_connect_->network_id_to_connect());
// Network connection does not occur.
EXPECT_EQ(0u, connection_callback_responses_.size());
// Timeout timer fires.
InvokeTimerTask();
EXPECT_EQ(1u, connection_callback_responses_.size());
EXPECT_TRUE(connection_callback_responses_[0].empty());
EXPECT_EQ(1u, test_network_connect_->num_disconnection_attempts());
EXPECT_EQ(wifi_guid, test_network_connect_->network_id_to_disconnect());
VerifyConnectionToHotspotDurationRecorded(false /* expected */);
}
TEST_F(WifiHotspotConnectorTest, TestConnect_DeletedWhileConnectionPending) {
wifi_hotspot_connector_->ConnectToWifiHotspot(
std::string(kSsid), std::string(kPassword), kTetherNetworkGuid,
base::Bind(&WifiHotspotConnectorTest::WifiConnectionCallback,
base::Unretained(this)));
std::string wifi_guid =
VerifyLastConfiguration(std::string(kSsid), std::string(kPassword));
EXPECT_FALSE(wifi_guid.empty());
// Network becomes connectable.
NotifyConnectable(test_network_connect_->last_service_path_created());
VerifyTetherAndWifiNetworkAssociation(
wifi_guid, kTetherNetworkGuid, 1u /* expected_num_connection_attempts */);
EXPECT_EQ(wifi_guid, test_network_connect_->network_id_to_connect());
EXPECT_EQ(0u, connection_callback_responses_.size());
// Delete the connector; this should trigger a disconnection attempt.
wifi_hotspot_connector_.reset();
EXPECT_EQ(1u, test_network_connect_->num_disconnection_attempts());
EXPECT_EQ(wifi_guid, test_network_connect_->network_id_to_disconnect());
EXPECT_EQ(1u, connection_callback_responses_.size());
EXPECT_EQ(std::string(), connection_callback_responses_[0]);
VerifyConnectionToHotspotDurationRecorded(false /* expected */);
}
TEST_F(WifiHotspotConnectorTest, TestConnect_Success) {
wifi_hotspot_connector_->ConnectToWifiHotspot(
std::string(kSsid), std::string(kPassword), kTetherNetworkGuid,
base::Bind(&WifiHotspotConnectorTest::WifiConnectionCallback,
base::Unretained(this)));
std::string wifi_guid =
VerifyLastConfiguration(std::string(kSsid), std::string(kPassword));
EXPECT_FALSE(wifi_guid.empty());
// Network becomes connectable.
NotifyConnectable(test_network_connect_->last_service_path_created());
VerifyTetherAndWifiNetworkAssociation(
wifi_guid, kTetherNetworkGuid, 1u /* expected_num_connection_attempts */);
EXPECT_EQ(wifi_guid, test_network_connect_->network_id_to_connect());
EXPECT_EQ(0u, connection_callback_responses_.size());
test_clock_.Advance(kConnectionToHotspotTime);
// Connection to network successful.
NotifyConnected(test_network_connect_->last_service_path_created());
EXPECT_EQ(1u, connection_callback_responses_.size());
EXPECT_EQ(wifi_guid, connection_callback_responses_[0]);
VerifyTimerStopped();
VerifyConnectionToHotspotDurationRecorded(true /* expected */);
EXPECT_EQ(0u, test_network_connect_->num_disconnection_attempts());
}
TEST_F(WifiHotspotConnectorTest, TestConnect_Success_EmptyPassword) {
wifi_hotspot_connector_->ConnectToWifiHotspot(
std::string(kSsid), std::string() /* password */, kTetherNetworkGuid,
base::Bind(&WifiHotspotConnectorTest::WifiConnectionCallback,
base::Unretained(this)));
std::string wifi_guid = VerifyLastConfiguration(kSsid, std::string());
EXPECT_FALSE(wifi_guid.empty());
// Network becomes connectable.
NotifyConnectable(test_network_connect_->last_service_path_created());
VerifyTetherAndWifiNetworkAssociation(
wifi_guid, kTetherNetworkGuid, 1u /* expected_num_connection_attempts */);
EXPECT_EQ(wifi_guid, test_network_connect_->network_id_to_connect());
EXPECT_EQ(0u, connection_callback_responses_.size());
test_clock_.Advance(kConnectionToHotspotTime);
// Connection to network successful.
NotifyConnected(test_network_connect_->last_service_path_created());
EXPECT_EQ(1u, connection_callback_responses_.size());
EXPECT_EQ(wifi_guid, connection_callback_responses_[0]);
VerifyTimerStopped();
VerifyConnectionToHotspotDurationRecorded(true /* expected */);
EXPECT_EQ(0u, test_network_connect_->num_disconnection_attempts());
}
TEST_F(WifiHotspotConnectorTest,
TestConnect_SecondConnectionWhileWaitingForFirstToBecomeConnectable) {
wifi_hotspot_connector_->ConnectToWifiHotspot(
"ssid1", "password1", "tetherNetworkGuid1",
base::Bind(&WifiHotspotConnectorTest::WifiConnectionCallback,
base::Unretained(this)));
std::string wifi_guid1 = VerifyLastConfiguration("ssid1", "password1");
EXPECT_FALSE(wifi_guid1.empty());
std::string service_path1 =
test_network_connect_->last_service_path_created();
EXPECT_FALSE(service_path1.empty());
// Pass some arbitrary time -- this should not affect the
// recorded duration because the start time should be reset
// for a new network attempt.
test_clock_.Advance(base::TimeDelta::FromSeconds(13));
// Before network becomes connectable, start the new connection.
EXPECT_EQ(0u, connection_callback_responses_.size());
wifi_hotspot_connector_->ConnectToWifiHotspot(
"ssid2", "password2", kTetherNetworkGuid2,
base::Bind(&WifiHotspotConnectorTest::WifiConnectionCallback,
base::Unretained(this)));
std::string wifi_guid2 = VerifyLastConfiguration("ssid2", "password2");
EXPECT_FALSE(wifi_guid2.empty());
std::string service_path2 =
test_network_connect_->last_service_path_created();
EXPECT_FALSE(service_path2.empty());
VerifyNetworkNotAssociated(wifi_guid1);
EXPECT_NE(service_path1, service_path2);
// The original connection attempt should have gotten an empty response.
EXPECT_EQ(1u, connection_callback_responses_.size());
EXPECT_TRUE(connection_callback_responses_[0].empty());
// First network becomes connectable.
NotifyConnectable(service_path1);
// A connection should not have started to that GUID.
EXPECT_TRUE(test_network_connect_->network_id_to_connect().empty());
EXPECT_EQ(1u, connection_callback_responses_.size());
test_clock_.Advance(kConnectionToHotspotTime);
// Second network becomes connectable.
NotifyConnectable(service_path2);
VerifyTetherAndWifiNetworkAssociation(
wifi_guid2, kTetherNetworkGuid2,
1u /* expected_num_connection_attempts */);
EXPECT_EQ(wifi_guid2, test_network_connect_->network_id_to_connect());
EXPECT_EQ(1u, connection_callback_responses_.size());
// Connection to network successful.
NotifyConnected(service_path2);
EXPECT_EQ(2u, connection_callback_responses_.size());
EXPECT_EQ(wifi_guid2, connection_callback_responses_[1]);
VerifyTimerStopped();
VerifyConnectionToHotspotDurationRecorded(true /* expected */);
EXPECT_EQ(0u, test_network_connect_->num_disconnection_attempts());
}
TEST_F(WifiHotspotConnectorTest,
TestConnect_SecondConnectionWhileWaitingForFirstToConnect) {
wifi_hotspot_connector_->ConnectToWifiHotspot(
"ssid1", "password1", kTetherNetworkGuid,
base::Bind(&WifiHotspotConnectorTest::WifiConnectionCallback,
base::Unretained(this)));
// Pass some arbitrary time -- this should not affect the
// recorded duration because the start time should be reset
// for a new network attempt.
test_clock_.Advance(base::TimeDelta::FromSeconds(13));
std::string wifi_guid1 = VerifyLastConfiguration("ssid1", "password1");
EXPECT_FALSE(wifi_guid1.empty());
std::string service_path1 =
test_network_connect_->last_service_path_created();
EXPECT_FALSE(service_path1.empty());
// First network becomes connectable.
NotifyConnectable(service_path1);
VerifyTetherAndWifiNetworkAssociation(
wifi_guid1, kTetherNetworkGuid,
1u /* expected_num_connection_attempts */);
// After network becomes connectable, request a connection to second network.
wifi_hotspot_connector_->ConnectToWifiHotspot(
"ssid2", "password2", kTetherNetworkGuid2,
base::Bind(&WifiHotspotConnectorTest::WifiConnectionCallback,
base::Unretained(this)));
// The first Tether and Wi-Fi networks should no longer be associated.
VerifyNetworkNotAssociated(kTetherNetworkGuid);
VerifyNetworkNotAssociated(wifi_guid1);
// The original connection attempt should have gotten an empty response.
EXPECT_EQ(1u, connection_callback_responses_.size());
EXPECT_TRUE(connection_callback_responses_[0].empty());
// A disconnection attempt should have been initiated to the other network.
EXPECT_EQ(1u, test_network_connect_->num_disconnection_attempts());
EXPECT_EQ(wifi_guid1, test_network_connect_->network_id_to_disconnect());
std::string wifi_guid2 = VerifyLastConfiguration("ssid2", "password2");
EXPECT_FALSE(wifi_guid2.empty());
std::string service_path2 =
test_network_connect_->last_service_path_created();
EXPECT_FALSE(service_path2.empty());
EXPECT_NE(service_path1, service_path2);
test_clock_.Advance(kConnectionToHotspotTime);
// Second network becomes connectable.
NotifyConnectable(service_path2);
VerifyTetherAndWifiNetworkAssociation(
wifi_guid2, kTetherNetworkGuid2,
2u /* expected_num_connection_attempts */);
EXPECT_EQ(wifi_guid2, test_network_connect_->network_id_to_connect());
EXPECT_EQ(1u, connection_callback_responses_.size());
// Connection to network successful.
NotifyConnected(service_path2);
EXPECT_EQ(2u, connection_callback_responses_.size());
EXPECT_EQ(wifi_guid2, connection_callback_responses_[1]);
VerifyTimerStopped();
VerifyConnectionToHotspotDurationRecorded(true /* expected */);
}
TEST_F(WifiHotspotConnectorTest, TestConnect_WifiDisabled_Success) {
network_state_handler()->SetTechnologyEnabled(
NetworkTypePattern::WiFi(), false /* enabled */,
chromeos::network_handler::ErrorCallback());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(
network_state_handler()->IsTechnologyEnabled(NetworkTypePattern::WiFi()));
wifi_hotspot_connector_->ConnectToWifiHotspot(
std::string(kSsid), std::string(kPassword), kTetherNetworkGuid,
base::Bind(&WifiHotspotConnectorTest::WifiConnectionCallback,
base::Unretained(this)));
// Allow the asyncronous call to NetworkStateHandler::SetTechnologyEnabled()
// within WifiHotspotConnector::ConnectToWifiHotspot() to synchronously
// run. After this call, Wi-Fi should be enabled and WifiHotspotConnector
// will have called TestNetworkConnect::CreateConfiguration().
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(
network_state_handler()->IsTechnologyEnabled(NetworkTypePattern::WiFi()));
// Run the task and manually invoke ConfigureServiceWithLastNetworkConfig() to
// prevent nested RunLoop errors.
RunTestTaskRunner();
test_network_connect_->ConfigureServiceWithLastNetworkConfig();
std::string wifi_guid =
VerifyLastConfiguration(std::string(kSsid), std::string(kPassword));
EXPECT_FALSE(wifi_guid.empty());
test_clock_.Advance(kConnectionToHotspotTime);
// Network becomes connectable.
NotifyConnectable(test_network_connect_->last_service_path_created());
VerifyTetherAndWifiNetworkAssociation(
wifi_guid, kTetherNetworkGuid, 1u /* expected_num_connection_attempts */);
EXPECT_EQ(wifi_guid, test_network_connect_->network_id_to_connect());
EXPECT_EQ(0u, connection_callback_responses_.size());
// Connection to network successful.
NotifyConnected(test_network_connect_->last_service_path_created());
EXPECT_EQ(1u, connection_callback_responses_.size());
EXPECT_EQ(wifi_guid, connection_callback_responses_[0]);
VerifyTimerStopped();
VerifyConnectionToHotspotDurationRecorded(true /* expected */);
EXPECT_EQ(0u, test_network_connect_->num_disconnection_attempts());
}
TEST_F(WifiHotspotConnectorTest,
TestConnect_WifiDisabled_Success_OtherDeviceStatesChange) {
network_state_handler()->SetTechnologyEnabled(
NetworkTypePattern::WiFi(), false /* enabled */,
chromeos::network_handler::ErrorCallback());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(
network_state_handler()->IsTechnologyEnabled(NetworkTypePattern::WiFi()));
wifi_hotspot_connector_->ConnectToWifiHotspot(
std::string(kSsid), std::string(kPassword), kTetherNetworkGuid,
base::Bind(&WifiHotspotConnectorTest::WifiConnectionCallback,
base::Unretained(this)));
// Ensure that WifiHotspotConnector only begins configuring the Wi-Fi network
// once Wi-Fi is enabled.
wifi_hotspot_connector_->DeviceListChanged();
wifi_hotspot_connector_->DeviceListChanged();
EXPECT_FALSE(test_network_connect_->last_configuration());
// Allow the asyncronous call to NetworkStateHandler::SetTechnologyEnabled()
// within WifiHotspotConnector::ConnectToWifiHotspot() to synchronously
// run. After this call, Wi-Fi should be enabled and WifiHotspotConnector
// will have called TestNetworkConnect::CreateConfiguration().
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(
network_state_handler()->IsTechnologyEnabled(NetworkTypePattern::WiFi()));
// Run the task and manually invoke ConfigureServiceWithLastNetworkConfig() to
// prevent nested RunLoop errors.
RunTestTaskRunner();
test_network_connect_->ConfigureServiceWithLastNetworkConfig();
std::string wifi_guid =
VerifyLastConfiguration(std::string(kSsid), std::string(kPassword));
EXPECT_FALSE(wifi_guid.empty());
// Network becomes connectable.
NotifyConnectable(test_network_connect_->last_service_path_created());
VerifyTetherAndWifiNetworkAssociation(
wifi_guid, kTetherNetworkGuid, 1u /* expected_num_connection_attempts */);
EXPECT_EQ(wifi_guid, test_network_connect_->network_id_to_connect());
EXPECT_EQ(0u, connection_callback_responses_.size());
test_clock_.Advance(kConnectionToHotspotTime);
// Connection to network successful.
NotifyConnected(test_network_connect_->last_service_path_created());
EXPECT_EQ(1u, connection_callback_responses_.size());
EXPECT_EQ(wifi_guid, connection_callback_responses_[0]);
VerifyTimerStopped();
VerifyConnectionToHotspotDurationRecorded(true /* expected */);
EXPECT_EQ(0u, test_network_connect_->num_disconnection_attempts());
}
TEST_F(WifiHotspotConnectorTest, TestConnect_WifiDisabled_AttemptTimesOut) {
network_state_handler()->SetTechnologyEnabled(
NetworkTypePattern::WiFi(), false /* enabled */,
chromeos::network_handler::ErrorCallback());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(
network_state_handler()->IsTechnologyEnabled(NetworkTypePattern::WiFi()));
wifi_hotspot_connector_->ConnectToWifiHotspot(
std::string(kSsid), std::string(kPassword), kTetherNetworkGuid,
base::Bind(&WifiHotspotConnectorTest::WifiConnectionCallback,
base::Unretained(this)));
// Timeout timer fires.
InvokeTimerTask();
EXPECT_EQ(1u, connection_callback_responses_.size());
EXPECT_TRUE(connection_callback_responses_[0].empty());
// Allow the asyncronous call to NetworkStateHandler::SetTechnologyEnabled()
// within WifiHotspotConnector::ConnectToWifiHotspot() to synchronously
// run. After this call, Wi-Fi should be enabled, but the connection attempt
// has timed out and therefore a new Wi-Fi configuration should not exist.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(
network_state_handler()->IsTechnologyEnabled(NetworkTypePattern::WiFi()));
// No configuration should have been created since the connection attempt
// timed out before Wi-Fi was successfully enabled.
EXPECT_FALSE(test_network_connect_->last_configuration());
VerifyConnectionToHotspotDurationRecorded(false /* expected */);
EXPECT_EQ(0u, test_network_connect_->num_disconnection_attempts());
}
TEST_F(WifiHotspotConnectorTest,
TestConnect_WifiDisabled_SecondConnectionWhileWaitingForWifiEnabled) {
network_state_handler()->SetTechnologyEnabled(
NetworkTypePattern::WiFi(), false /* enabled */,
chromeos::network_handler::ErrorCallback());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(
network_state_handler()->IsTechnologyEnabled(NetworkTypePattern::WiFi()));
wifi_hotspot_connector_->ConnectToWifiHotspot(
"ssid1", "password1", kTetherNetworkGuid,
base::Bind(&WifiHotspotConnectorTest::WifiConnectionCallback,
base::Unretained(this)));
// Pass some arbitrary time -- this should not affect the
// recorded duration because the start time should be reset
// for a new network attempt.
test_clock_.Advance(base::TimeDelta::FromSeconds(13));
EXPECT_FALSE(test_network_connect_->last_configuration());
wifi_hotspot_connector_->ConnectToWifiHotspot(
"ssid2", "password2", kTetherNetworkGuid2,
base::Bind(&WifiHotspotConnectorTest::WifiConnectionCallback,
base::Unretained(this)));
// Allow the asyncronous call to NetworkStateHandler::SetTechnologyEnabled()
// within WifiHotspotConnector::ConnectToWifiHotspot() to synchronously
// run. After this call, Wi-Fi should be enabled and WifiHotspotConnector
// will have called TestNetworkConnect::CreateConfiguration().
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(
network_state_handler()->IsTechnologyEnabled(NetworkTypePattern::WiFi()));
// Run the task and manually invoke ConfigureServiceWithLastNetworkConfig() to
// prevent nested RunLoop errors.
RunTestTaskRunner();
test_network_connect_->ConfigureServiceWithLastNetworkConfig();
std::string wifi_guid2 = VerifyLastConfiguration("ssid2", "password2");
EXPECT_FALSE(wifi_guid2.empty());
std::string service_path2 =
test_network_connect_->last_service_path_created();
EXPECT_FALSE(service_path2.empty());
// Second network becomes connectable.
NotifyConnectable(service_path2);
VerifyTetherAndWifiNetworkAssociation(
wifi_guid2, kTetherNetworkGuid2,
1u /* expected_num_connection_attempts */);
EXPECT_EQ(wifi_guid2, test_network_connect_->network_id_to_connect());
EXPECT_EQ(1u, connection_callback_responses_.size());
test_clock_.Advance(kConnectionToHotspotTime);
// Connection to network successful.
NotifyConnected(service_path2);
EXPECT_EQ(2u, connection_callback_responses_.size());
EXPECT_EQ(wifi_guid2, connection_callback_responses_[1]);
VerifyTimerStopped();
VerifyConnectionToHotspotDurationRecorded(true /* expected */);
EXPECT_EQ(0u, test_network_connect_->num_disconnection_attempts());
}
} // namespace tether
} // namespace chromeos