blob: 3194854c5d28ad69eba4487a15abb4624a18c215 [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 <string>
#include "base/command_line.h"
#include "base/deferred_sequenced_task_runner.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/path_service.h"
#include "base/process/memory.h"
#include "base/run_loop.h"
#include "base/task/post_task.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_impl.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/metrics/subprocess_metrics_provider.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/prefs/json_pref_store.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/network_service_util.h"
#include "content/public/common/service_manager_connection.h"
#include "content/public/common/service_names.mojom.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_base.h"
#include "content/public/test/browser_test_utils.h"
#include "net/base/filename_util.h"
#include "net/nqe/effective_connection_type.h"
#include "net/nqe/network_quality_estimator.h"
#include "net/nqe/network_quality_estimator_params.h"
#include "services/network/network_service.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/network_quality_tracker.h"
#include "services/network/public/mojom/network_service_test.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
void SimulateNetworkQualityChangeOnNetworkThread(
net::EffectiveConnectionType type) {
network::NetworkService::GetNetworkServiceForTesting()
->network_quality_estimator()
->SimulateNetworkQualityChangeForTesting(type);
}
// Retries fetching |histogram_name| until it contains at least |count| samples.
void RetryForHistogramUntilCountReached(base::HistogramTester* histogram_tester,
const std::string& histogram_name,
size_t count) {
while (true) {
const std::vector<base::Bucket> buckets =
histogram_tester->GetAllSamples(histogram_name);
size_t total_count = 0;
for (const auto& bucket : buckets)
total_count += bucket.count;
if (total_count >= count)
return;
content::FetchHistogramsFromChildProcesses();
SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
base::RunLoop().RunUntilIdle();
}
}
int GetHistogramSamplesSum(base::HistogramTester* histogram_tester,
const std::string& histogram_name) {
const std::vector<base::Bucket> buckets =
histogram_tester->GetAllSamples(histogram_name);
size_t sum = 0;
for (const auto& bucket : buckets)
sum += (bucket.count * bucket.min);
return sum;
}
class TestNetworkQualityObserver
: public network::NetworkQualityTracker::EffectiveConnectionTypeObserver {
public:
explicit TestNetworkQualityObserver(network::NetworkQualityTracker* tracker)
: run_loop_wait_effective_connection_type_(
net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
run_loop_(std::make_unique<base::RunLoop>()),
tracker_(tracker),
effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN) {
tracker_->AddEffectiveConnectionTypeObserver(this);
}
~TestNetworkQualityObserver() override {
tracker_->RemoveEffectiveConnectionTypeObserver(this);
}
// NetworkQualityTracker::EffectiveConnectionTypeObserver implementation:
void OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType type) override {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
net::EffectiveConnectionType queried_type =
tracker_->GetEffectiveConnectionType();
EXPECT_EQ(type, queried_type);
effective_connection_type_ = type;
if (effective_connection_type_ != run_loop_wait_effective_connection_type_)
return;
run_loop_->Quit();
}
void WaitForNotification(
net::EffectiveConnectionType run_loop_wait_effective_connection_type) {
if (effective_connection_type_ == run_loop_wait_effective_connection_type)
return;
ASSERT_NE(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
run_loop_wait_effective_connection_type);
run_loop_wait_effective_connection_type_ =
run_loop_wait_effective_connection_type;
run_loop_->Run();
run_loop_.reset(new base::RunLoop());
}
private:
net::EffectiveConnectionType run_loop_wait_effective_connection_type_;
std::unique_ptr<base::RunLoop> run_loop_;
network::NetworkQualityTracker* tracker_;
net::EffectiveConnectionType effective_connection_type_;
DISALLOW_COPY_AND_ASSIGN(TestNetworkQualityObserver);
};
} // namespace
class NetworkQualityEstimatorPrefsBrowserTest : public InProcessBrowserTest {
public:
NetworkQualityEstimatorPrefsBrowserTest()
: network_service_enabled_(
base::FeatureList::IsEnabled(network::features::kNetworkService)) {
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
}
// Simulates a network quality change.
void SimulateNetworkQualityChange(net::EffectiveConnectionType type) {
DCHECK(network_service_enabled_);
if (!content::IsOutOfProcessNetworkService()) {
content::GetNetworkTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(&SimulateNetworkQualityChangeOnNetworkThread, type));
return;
}
mojo::ScopedAllowSyncCallForTesting allow_sync_call;
content::StoragePartition* partition =
content::BrowserContext::GetDefaultStoragePartition(
browser()->profile());
DCHECK(partition->GetNetworkContext());
DCHECK(content::GetNetworkService());
network::mojom::NetworkServiceTestPtr network_service_test;
content::ServiceManagerConnection::GetForProcess()
->GetConnector()
->BindInterface(content::mojom::kNetworkServiceName,
&network_service_test);
base::RunLoop run_loop;
network_service_test->SimulateNetworkQualityChange(
type, base::BindOnce([](base::RunLoop* run_loop) { run_loop->Quit(); },
base::Unretained(&run_loop)));
run_loop.Run();
}
bool network_service_enabled() const { return network_service_enabled_; }
base::FilePath GetTempDirectory() { return temp_dir_.GetPath(); }
base::HistogramTester histogram_tester;
private:
const bool network_service_enabled_;
base::ScopedTempDir temp_dir_;
};
// Verify that prefs are read at startup, and the read prefs are notified to the
// network quality estimator.
IN_PROC_BROWSER_TEST_F(NetworkQualityEstimatorPrefsBrowserTest,
ReadPrefsAtStartup) {
// The check below ensures that "NQE.Prefs.ReadSize" contains at least one
// sample. This implies that NQE was notified of the read prefs.
RetryForHistogramUntilCountReached(&histogram_tester, "NQE.Prefs.ReadSize",
1);
}
// Verify that prefs are read at startup.
IN_PROC_BROWSER_TEST_F(NetworkQualityEstimatorPrefsBrowserTest,
ReadPrefsAtStartupCustomPrefFile) {
// The check below ensures that "NQE.Prefs.ReadSize" contains at least one
// sample. This implies that NQE was notified of the read prefs.
RetryForHistogramUntilCountReached(&histogram_tester, "NQE.Prefs.ReadSize",
1);
base::HistogramTester histogram_tester2;
// Create network context with JSON pref store pointing to the temp file.
network::mojom::NetworkContextPtr network_context;
network::mojom::NetworkContextParamsPtr context_params =
network::mojom::NetworkContextParams::New();
context_params->http_server_properties_path =
GetTempDirectory().Append(FILE_PATH_LITERAL("Network Persistent State"));
auto state = base::MakeRefCounted<JsonPrefStore>(
context_params->http_server_properties_path.value());
base::DictionaryValue pref_value;
base::Value value("2G");
pref_value.Set("network_id_foo",
base::Value::ToUniquePtrValue(value.Clone()));
state->SetValue("net.network_qualities",
base::Value::ToUniquePtrValue(pref_value.Clone()), 0);
// Wait for the pending commit to finish before creating the network context.
base::RunLoop loop;
state->CommitPendingWrite(
base::BindOnce([](base::RunLoop* loop) { loop->Quit(); }, &loop));
loop.Run();
content::GetNetworkService()->CreateNetworkContext(
mojo::MakeRequest(&network_context), std::move(context_params));
RetryForHistogramUntilCountReached(&histogram_tester2, "NQE.Prefs.ReadSize",
1);
// Pref value must be read from the temp file.
EXPECT_LE(1,
GetHistogramSamplesSum(&histogram_tester2, "NQE.Prefs.ReadSize"));
}
// Verify that prefs are read at startup, and written to later.
IN_PROC_BROWSER_TEST_F(NetworkQualityEstimatorPrefsBrowserTest, PrefsWritten) {
// The check below ensures that "NQE.Prefs.ReadSize" contains at least one
// sample. This implies that NQE was notified of the read prefs.
RetryForHistogramUntilCountReached(&histogram_tester, "NQE.Prefs.ReadSize",
1);
if (!network_service_enabled())
return;
// Change in network quality is guaranteed to trigger a pref write.
SimulateNetworkQualityChange(net::EFFECTIVE_CONNECTION_TYPE_2G);
TestNetworkQualityObserver network_quality_observer(
g_browser_process->network_quality_tracker());
network_quality_observer.WaitForNotification(
net::EFFECTIVE_CONNECTION_TYPE_2G);
RetryForHistogramUntilCountReached(&histogram_tester, "NQE.Prefs.WriteCount",
1);
}