blob: 31182d5f1cef1d4f9ac3f4818a73ca7fb7e6e95b [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 "net/nqe/network_qualities_prefs_manager.h"
#include <algorithm>
#include <map>
#include <memory>
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/threading/thread_checker.h"
#include "base/values.h"
#include "net/base/network_change_notifier.h"
#include "net/nqe/effective_connection_type.h"
#include "net/nqe/network_id.h"
#include "net/nqe/network_quality_estimator_test_util.h"
#include "net/nqe/network_quality_store.h"
#include "net/test/test_with_scoped_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace {
class TestPrefDelegate : public NetworkQualitiesPrefsManager::PrefDelegate {
public:
TestPrefDelegate()
: write_count_(0), read_count_(0), value_(new base::DictionaryValue) {}
~TestPrefDelegate() override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
value_->Clear();
EXPECT_EQ(0U, value_->size());
}
void SetDictionaryValue(const base::DictionaryValue& value) override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
write_count_++;
value_.reset(value.DeepCopy());
ASSERT_EQ(value.size(), value_->size());
}
std::unique_ptr<base::DictionaryValue> GetDictionaryValue() override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
read_count_++;
return value_->CreateDeepCopy();
}
size_t write_count() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return write_count_;
}
size_t read_count() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return read_count_;
}
private:
// Number of times prefs were written and read, respectively..
size_t write_count_;
size_t read_count_;
// Current value of the prefs.
std::unique_ptr<base::DictionaryValue> value_;
base::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN(TestPrefDelegate);
};
using NetworkQualitiesPrefManager = TestWithScopedTaskEnvironment;
TEST_F(NetworkQualitiesPrefManager, Write) {
// Force set the ECT to Slow 2G so that the ECT does not match the default
// ECT for the current connection type. This forces the prefs to be written
// for the current connection.
std::map<std::string, std::string> variation_params;
variation_params["force_effective_connection_type"] = "Slow-2G";
TestNetworkQualityEstimator estimator(variation_params);
std::unique_ptr<TestPrefDelegate> prefs_delegate(new TestPrefDelegate());
TestPrefDelegate* prefs_delegate_ptr = prefs_delegate.get();
NetworkQualitiesPrefsManager manager(std::move(prefs_delegate));
manager.InitializeOnNetworkThread(&estimator);
base::RunLoop().RunUntilIdle();
// Prefs must be read at when NetworkQualitiesPrefsManager is constructed.
EXPECT_EQ(1u, prefs_delegate_ptr->read_count());
estimator.SimulateNetworkChange(
NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, "test");
EXPECT_EQ(1u, prefs_delegate_ptr->write_count());
// Network quality generated from the default observation must be written.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(3u, prefs_delegate_ptr->write_count());
estimator.set_recent_effective_connection_type(EFFECTIVE_CONNECTION_TYPE_2G);
// Run a request so that effective connection type is recomputed, and
// observers are notified of change in the network quality.
estimator.RunOneRequest();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(4u, prefs_delegate_ptr->write_count());
estimator.set_recent_effective_connection_type(EFFECTIVE_CONNECTION_TYPE_3G);
// Run a request so that effective connection type is recomputed, and
// observers are notified of change in the network quality..
estimator.RunOneRequest();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(5u, prefs_delegate_ptr->write_count());
// Prefs should not be read again.
EXPECT_EQ(1u, prefs_delegate_ptr->read_count());
manager.ShutdownOnPrefSequence();
}
TEST_F(NetworkQualitiesPrefManager, WriteWhenMatchingExpectedECT) {
// Force set the ECT to Slow 2G so that the ECT does not match the default
// ECT for the current connection type. This forces the prefs to be written
// for the current connection.
std::map<std::string, std::string> variation_params;
variation_params["force_effective_connection_type"] = "Slow-2G";
TestNetworkQualityEstimator estimator(variation_params);
std::unique_ptr<TestPrefDelegate> prefs_delegate(new TestPrefDelegate());
TestPrefDelegate* prefs_delegate_ptr = prefs_delegate.get();
NetworkQualitiesPrefsManager manager(std::move(prefs_delegate));
manager.InitializeOnNetworkThread(&estimator);
base::RunLoop().RunUntilIdle();
// Prefs must be read at when NetworkQualitiesPrefsManager is constructed.
EXPECT_EQ(1u, prefs_delegate_ptr->read_count());
const nqe::internal::NetworkID network_id(
NetworkChangeNotifier::ConnectionType::CONNECTION_4G, "test", INT32_MIN);
estimator.SimulateNetworkChange(network_id.type, network_id.id);
EXPECT_EQ(1u, prefs_delegate_ptr->write_count());
// Network quality generated from the default observation must be written.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(3u, prefs_delegate_ptr->write_count());
estimator.set_recent_effective_connection_type(EFFECTIVE_CONNECTION_TYPE_2G);
// Run a request so that effective connection type is recomputed, and
// observers are notified of change in the network quality.
estimator.RunOneRequest();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(4u, prefs_delegate_ptr->write_count());
estimator.set_recent_effective_connection_type(EFFECTIVE_CONNECTION_TYPE_3G);
// Run a request so that effective connection type is recomputed, and
// observers are notified of change in the network quality..
estimator.RunOneRequest();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(5u, prefs_delegate_ptr->write_count());
// Prefs should not be read again.
EXPECT_EQ(1u, prefs_delegate_ptr->read_count());
EXPECT_EQ(2u, manager.ForceReadPrefsForTesting().size());
EXPECT_EQ(EFFECTIVE_CONNECTION_TYPE_3G,
manager.ForceReadPrefsForTesting()
.find(network_id)
->second.effective_connection_type());
estimator.set_recent_effective_connection_type(EFFECTIVE_CONNECTION_TYPE_4G);
estimator.RunOneRequest();
base::RunLoop().RunUntilIdle();
// Network Quality should be persisted to disk even if it matches the typical
// quality of the network. See crbug.com/890859.
EXPECT_EQ(2u, manager.ForceReadPrefsForTesting().size());
EXPECT_EQ(1u, manager.ForceReadPrefsForTesting().count(network_id));
EXPECT_EQ(6u, prefs_delegate_ptr->write_count());
manager.ShutdownOnPrefSequence();
}
TEST_F(NetworkQualitiesPrefManager, WriteAndReadWithMultipleNetworkIDs) {
static const size_t kMaxCacheSize = 20u;
// Force set the ECT to Slow 2G so that the ECT does not match the default
// ECT for the current connection type. This forces the prefs to be written
// for the current connection.
std::map<std::string, std::string> variation_params;
variation_params["force_effective_connection_type"] = "Slow-2G";
TestNetworkQualityEstimator estimator(variation_params);
std::unique_ptr<TestPrefDelegate> prefs_delegate(new TestPrefDelegate());
NetworkQualitiesPrefsManager manager(std::move(prefs_delegate));
manager.InitializeOnNetworkThread(&estimator);
base::RunLoop().RunUntilIdle();
estimator.SimulateNetworkChange(
NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test");
EXPECT_EQ(1u, manager.ForceReadPrefsForTesting().size());
estimator.set_recent_effective_connection_type(
EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
// Run a request so that effective connection type is recomputed, and
// observers are notified of change in the network quality.
estimator.RunOneRequest();
base::RunLoop().RunUntilIdle();
// Verify that the observer was notified, and the updated network quality was
// written to the prefs.
EXPECT_EQ(2u, manager.ForceReadPrefsForTesting().size());
// Change the network ID.
for (size_t i = 0; i < kMaxCacheSize; ++i) {
estimator.SimulateNetworkChange(
NetworkChangeNotifier::ConnectionType::CONNECTION_2G,
"test" + base::IntToString(i));
estimator.RunOneRequest();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(std::min(i + 3, kMaxCacheSize),
manager.ForceReadPrefsForTesting().size());
}
std::map<nqe::internal::NetworkID, nqe::internal::CachedNetworkQuality>
read_prefs = manager.ForceReadPrefsForTesting();
// Verify the contents of the prefs.
size_t count_2g_entries = 0;
for (std::map<nqe::internal::NetworkID,
nqe::internal::CachedNetworkQuality>::const_iterator it =
read_prefs.begin();
it != read_prefs.end(); ++it) {
if (it->first.type ==
NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN) {
continue;
}
EXPECT_EQ(0u, it->first.id.find("test", 0u));
EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_2G,
it->first.type);
EXPECT_EQ(EFFECTIVE_CONNECTION_TYPE_SLOW_2G,
it->second.effective_connection_type());
++count_2g_entries;
}
// At most one entry should be for the network with connection type
// NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN.
EXPECT_LE(kMaxCacheSize - 1, count_2g_entries);
base::HistogramTester histogram_tester;
estimator.OnPrefsRead(read_prefs);
histogram_tester.ExpectUniqueSample("NQE.Prefs.ReadSize", kMaxCacheSize, 1);
manager.ShutdownOnPrefSequence();
}
// Verifies that the prefs are cleared correctly.
TEST_F(NetworkQualitiesPrefManager, ClearPrefs) {
// Force set the ECT to Slow 2G so that the ECT does not match the default
// ECT for the current connection type. This forces the prefs to be written
// for the current connection.
std::map<std::string, std::string> variation_params;
variation_params["force_effective_connection_type"] = "Slow-2G";
TestNetworkQualityEstimator estimator(variation_params);
std::unique_ptr<TestPrefDelegate> prefs_delegate(new TestPrefDelegate());
NetworkQualitiesPrefsManager manager(std::move(prefs_delegate));
manager.InitializeOnNetworkThread(&estimator);
base::RunLoop().RunUntilIdle();
estimator.SimulateNetworkChange(
NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, "test");
EXPECT_EQ(1u, manager.ForceReadPrefsForTesting().size());
estimator.set_recent_effective_connection_type(
EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
// Run a request so that effective connection type is recomputed, and
// observers are notified of change in the network quality.
estimator.RunOneRequest();
base::RunLoop().RunUntilIdle();
// Verify that the observer was notified, and the updated network quality was
// written to the prefs.
EXPECT_EQ(2u, manager.ForceReadPrefsForTesting().size());
// Prefs must be completely cleared.
manager.ClearPrefs();
EXPECT_EQ(0u, manager.ForceReadPrefsForTesting().size());
estimator.set_recent_effective_connection_type(EFFECTIVE_CONNECTION_TYPE_2G);
// Run a request so that effective connection type is recomputed, and
// observers are notified of change in the network quality.
estimator.RunOneRequest();
base::RunLoop().RunUntilIdle();
// Verify that the observer was notified, and the updated network quality was
// written to the prefs.
EXPECT_EQ(1u, manager.ForceReadPrefsForTesting().size());
manager.ShutdownOnPrefSequence();
}
} // namespace
} // namespace net