#include <cmath>
#include <map>
#include <string>
#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "build/build_config.h"
#include "content/browser/network/network_quality_observer_impl.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "net/base/network_change_notifier.h"
#include "net/base/network_change_notifier_factory.h"
#include "net/dns/mock_host_resolver.h"
#include "net/log/test_net_log.h"
#include "net/nqe/effective_connection_type.h"
#include "services/network/test/test_network_quality_tracker.h"
namespace {
int GetTotalSampleCount(base::HistogramTester* tester,
const std::string& histogram) {
int count = 0;
std::vector<base::Bucket> buckets = tester->GetAllSamples(histogram);
for (const auto& bucket : buckets)
count += bucket.count;
return count;
}
void VerifyRtt(base::TimeDelta expected_rtt, int32_t got_rtt_milliseconds) {
EXPECT_EQ(0, got_rtt_milliseconds % 50)
<< " got_rtt_milliseconds=" << got_rtt_milliseconds;
if (expected_rtt > base::Milliseconds(3000))
expected_rtt = base::Milliseconds(3000);
EXPECT_GE((expected_rtt.InMilliseconds() * 0.1) + 50,
std::abs(expected_rtt.InMilliseconds() - got_rtt_milliseconds))
<< " expected_rtt=" << expected_rtt
<< " got_rtt_milliseconds=" << got_rtt_milliseconds;
}
void VerifyDownlinkKbps(double expected_kbps, double got_kbps) {
int quotient = static_cast<int>(got_kbps / 50);
double mod = got_kbps - 50 * quotient;
EXPECT_LE(0.0, mod);
EXPECT_GT(50.0, mod);
EXPECT_TRUE(mod < (1e-5) || (50 - mod) < 1e-5) << " got_kbps=" << got_kbps;
if (expected_kbps > 10000)
expected_kbps = 10000;
EXPECT_GE((expected_kbps * 0.1) + 50, std::abs(expected_kbps - got_kbps))
<< " expected_kbps=" << expected_kbps << " got_kbps=" << got_kbps;
}
class MockNetworkChangeNotifierWifi : public net::NetworkChangeNotifier {
public:
void GetCurrentMaxBandwidthAndConnectionType(
double* max_bandwidth_mbps,
ConnectionType* connection_type) const override {
*connection_type = NetworkChangeNotifier::CONNECTION_WIFI;
*max_bandwidth_mbps =
net::NetworkChangeNotifier::GetMaxBandwidthMbpsForConnectionSubtype(
net::NetworkChangeNotifier::SUBTYPE_WIFI_N);
}
ConnectionType GetCurrentConnectionType() const override {
return NetworkChangeNotifier::CONNECTION_WIFI;
}
};
}
namespace content {
class NetInfoBrowserTest : public content::ContentBrowserTest {
public:
NetInfoBrowserTest()
: test_network_quality_tracker_(
std::make_unique<network::TestNetworkQualityTracker>()) {}
network::NetworkQualityTracker* GetNetworkQualityTracker() const {
return test_network_quality_tracker_.get();
}
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(switches::kEnableNetworkInformationDownlinkMax);
command_line->AppendSwitch(
switches::kEnableExperimentalWebPlatformFeatures);
}
void SetUp() override {
net::NetworkChangeNotifier::SetTestNotificationsOnly(true);
content::ContentBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
base::RunLoop().RunUntilIdle();
}
static void SetConnectionType(
net::NetworkChangeNotifier::ConnectionType type,
net::NetworkChangeNotifier::ConnectionSubtype subtype) {
net::NetworkChangeNotifier::NotifyObserversOfMaxBandwidthChangeForTests(
net::NetworkChangeNotifier::GetMaxBandwidthMbpsForConnectionSubtype(
subtype),
type);
base::RunLoop().RunUntilIdle();
}
std::string RunScriptExtractString(const std::string& script) {
return EvalJs(shell(), script).ExtractString();
}
bool RunScriptExtractBool(const std::string& script) {
return EvalJs(shell(), script).ExtractBool();
}
double RunScriptExtractDouble(const std::string& script) {
return EvalJs(shell(), script).ExtractDouble();
}
int RunScriptExtractInt(const std::string& script) {
return EvalJs(shell(), script).ExtractInt();
}
private:
std::unique_ptr<network::TestNetworkQualityTracker>
test_network_quality_tracker_;
};
IN_PROC_BROWSER_TEST_F(NetInfoBrowserTest, VerifyNetworkStateInitialized) {
net::NetworkChangeNotifier::DisableForTest disable_for_test;
MockNetworkChangeNotifierWifi mock_notifier;
EXPECT_TRUE(NavigateToURL(shell(), content::GetTestUrl("", "net_info.html")));
EXPECT_TRUE(RunScriptExtractBool("getOnLine()"));
EXPECT_EQ("wifi", RunScriptExtractString("getType()"));
EXPECT_EQ(net::NetworkChangeNotifier::GetMaxBandwidthMbpsForConnectionSubtype(
net::NetworkChangeNotifier::SUBTYPE_WIFI_N),
RunScriptExtractDouble("getDownlinkMax()"));
}
IN_PROC_BROWSER_TEST_F(NetInfoBrowserTest, NetworkChangePlumbsToNavigator) {
EXPECT_TRUE(NavigateToURL(shell(), content::GetTestUrl("", "net_info.html")));
SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI,
net::NetworkChangeNotifier::SUBTYPE_WIFI_N);
EXPECT_EQ("wifi", RunScriptExtractString("getType()"));
EXPECT_EQ(net::NetworkChangeNotifier::GetMaxBandwidthMbpsForConnectionSubtype(
net::NetworkChangeNotifier::SUBTYPE_WIFI_N),
RunScriptExtractDouble("getDownlinkMax()"));
SetConnectionType(net::NetworkChangeNotifier::CONNECTION_ETHERNET,
net::NetworkChangeNotifier::SUBTYPE_GIGABIT_ETHERNET);
EXPECT_EQ("ethernet", RunScriptExtractString("getType()"));
EXPECT_EQ(net::NetworkChangeNotifier::GetMaxBandwidthMbpsForConnectionSubtype(
net::NetworkChangeNotifier::SUBTYPE_GIGABIT_ETHERNET),
RunScriptExtractDouble("getDownlinkMax()"));
}
IN_PROC_BROWSER_TEST_F(NetInfoBrowserTest, IsOnline) {
EXPECT_TRUE(NavigateToURL(shell(), content::GetTestUrl("", "net_info.html")));
SetConnectionType(net::NetworkChangeNotifier::CONNECTION_ETHERNET,
net::NetworkChangeNotifier::SUBTYPE_GIGABIT_ETHERNET);
EXPECT_TRUE(RunScriptExtractBool("getOnLine()"));
SetConnectionType(net::NetworkChangeNotifier::CONNECTION_NONE,
net::NetworkChangeNotifier::SUBTYPE_NONE);
EXPECT_FALSE(RunScriptExtractBool("getOnLine()"));
SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI,
net::NetworkChangeNotifier::SUBTYPE_WIFI_N);
EXPECT_TRUE(RunScriptExtractBool("getOnLine()"));
}
IN_PROC_BROWSER_TEST_F(NetInfoBrowserTest, TwoRenderViewsInOneProcess) {
SetConnectionType(net::NetworkChangeNotifier::CONNECTION_ETHERNET,
net::NetworkChangeNotifier::SUBTYPE_GIGABIT_ETHERNET);
EXPECT_TRUE(NavigateToURL(shell(), content::GetTestUrl("", "net_info.html")));
EXPECT_TRUE(RunScriptExtractBool("getOnLine()"));
SetConnectionType(net::NetworkChangeNotifier::CONNECTION_NONE,
net::NetworkChangeNotifier::SUBTYPE_NONE);
EXPECT_FALSE(RunScriptExtractBool("getOnLine()"));
EXPECT_TRUE(ExecJs(shell(), "window.open(\"net_info.html\")"));
EXPECT_FALSE(RunScriptExtractBool("getOnLine()"));
}
IN_PROC_BROWSER_TEST_F(NetInfoBrowserTest,
NetworkQualityEstimatorNotInitialized) {
base::HistogramTester histogram_tester;
NetworkQualityObserverImpl impl(GetNetworkQualityTracker());
EXPECT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/net_info.html")));
EXPECT_EQ(0, RunScriptExtractInt("getRtt()"));
VerifyDownlinkKbps(10000, RunScriptExtractDouble("getDownlink()") * 1000);
}
IN_PROC_BROWSER_TEST_F(NetInfoBrowserTest,
EffectiveConnectionTypeChangeNotified) {
base::HistogramTester histogram_tester;
NetworkQualityObserverImpl impl(GetNetworkQualityTracker());
base::TimeDelta http_rtt(base::Milliseconds(1000));
int32_t downstream_throughput_kbps = 300;
GetNetworkQualityTracker()->ReportRTTsAndThroughputForTesting(
http_rtt, downstream_throughput_kbps);
EXPECT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/net_info.html")));
FetchHistogramsFromChildProcesses();
int samples =
GetTotalSampleCount(&histogram_tester, "NQE.RenderThreadNotified");
EXPECT_LT(0, samples);
GetNetworkQualityTracker()->ReportEffectiveConnectionTypeForTesting(
net::EFFECTIVE_CONNECTION_TYPE_2G);
EXPECT_EQ("2g", RunScriptExtractString("getEffectiveType()"));
GetNetworkQualityTracker()->ReportEffectiveConnectionTypeForTesting(
net::EFFECTIVE_CONNECTION_TYPE_3G);
EXPECT_EQ("3g", RunScriptExtractString("getEffectiveType()"));
FetchHistogramsFromChildProcesses();
EXPECT_GT(GetTotalSampleCount(&histogram_tester, "NQE.RenderThreadNotified"),
samples);
}
IN_PROC_BROWSER_TEST_F(NetInfoBrowserTest, NetworkQualityChangeNotified) {
base::HistogramTester histogram_tester;
NetworkQualityObserverImpl impl(GetNetworkQualityTracker());
base::TimeDelta http_rtt(base::Milliseconds(1000));
int32_t downstream_throughput_kbps = 300;
GetNetworkQualityTracker()->ReportRTTsAndThroughputForTesting(
http_rtt, downstream_throughput_kbps);
EXPECT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/net_info.html")));
FetchHistogramsFromChildProcesses();
EXPECT_FALSE(
histogram_tester.GetAllSamples("NQE.RenderThreadNotified").empty());
VerifyRtt(http_rtt, RunScriptExtractInt("getRtt()"));
VerifyDownlinkKbps(downstream_throughput_kbps,
RunScriptExtractDouble("getDownlink()") * 1000);
http_rtt = base::Seconds(10);
downstream_throughput_kbps = 3000;
GetNetworkQualityTracker()->ReportRTTsAndThroughputForTesting(
http_rtt, downstream_throughput_kbps);
base::RunLoop().RunUntilIdle();
VerifyRtt(http_rtt, RunScriptExtractInt("getRtt()"));
VerifyDownlinkKbps(downstream_throughput_kbps,
RunScriptExtractDouble("getDownlink()") * 1000);
}
IN_PROC_BROWSER_TEST_F(NetInfoBrowserTest, NetworkQualityChangeRounded) {
base::HistogramTester histogram_tester;
NetworkQualityObserverImpl impl(GetNetworkQualityTracker());
base::TimeDelta http_rtt(base::Milliseconds(103));
int32_t downstream_throughput_kbps = 8303;
GetNetworkQualityTracker()->ReportRTTsAndThroughputForTesting(
http_rtt, downstream_throughput_kbps);
EXPECT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/net_info.html")));
VerifyRtt(http_rtt, RunScriptExtractInt("getRtt()"));
VerifyDownlinkKbps(downstream_throughput_kbps,
RunScriptExtractDouble("getDownlink()") * 1000);
http_rtt = base::Milliseconds(1103);
downstream_throughput_kbps = 1307;
GetNetworkQualityTracker()->ReportRTTsAndThroughputForTesting(
http_rtt, downstream_throughput_kbps);
base::RunLoop().RunUntilIdle();
VerifyRtt(http_rtt, RunScriptExtractInt("getRtt()"));
VerifyDownlinkKbps(downstream_throughput_kbps,
RunScriptExtractDouble("getDownlink()") * 1000);
http_rtt = base::Milliseconds(2112);
downstream_throughput_kbps = 2112;
GetNetworkQualityTracker()->ReportRTTsAndThroughputForTesting(
http_rtt, downstream_throughput_kbps);
base::RunLoop().RunUntilIdle();
VerifyRtt(http_rtt, RunScriptExtractInt("getRtt()"));
VerifyDownlinkKbps(downstream_throughput_kbps,
RunScriptExtractDouble("getDownlink()") * 1000);
}
IN_PROC_BROWSER_TEST_F(NetInfoBrowserTest, NetworkQualityChangeUpperLimit) {
base::HistogramTester histogram_tester;
NetworkQualityObserverImpl impl(GetNetworkQualityTracker());
base::TimeDelta http_rtt(base::Milliseconds(12003));
int32_t downstream_throughput_kbps = 30300;
GetNetworkQualityTracker()->ReportRTTsAndThroughputForTesting(
http_rtt, downstream_throughput_kbps);
EXPECT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/net_info.html")));
VerifyRtt(http_rtt, RunScriptExtractInt("getRtt()"));
VerifyDownlinkKbps(downstream_throughput_kbps,
RunScriptExtractDouble("getDownlink()") * 1000);
}
IN_PROC_BROWSER_TEST_F(NetInfoBrowserTest, NetworkQualityRandomized) {
base::HistogramTester histogram_tester;
NetworkQualityObserverImpl impl(GetNetworkQualityTracker());
base::TimeDelta http_rtt(base::Milliseconds(2000));
int32_t downstream_throughput_kbps = 3000;
GetNetworkQualityTracker()->ReportRTTsAndThroughputForTesting(
http_rtt, downstream_throughput_kbps);
EXPECT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/net_info.html")));
VerifyRtt(http_rtt, RunScriptExtractInt("getRtt()"));
VerifyDownlinkKbps(downstream_throughput_kbps,
RunScriptExtractDouble("getDownlink()") * 1000);
const int32_t rtt_noise_milliseconds = RunScriptExtractInt("getRtt()") - 2000;
const int32_t downlink_noise_kbps =
RunScriptExtractDouble("getDownlink()") * 1000 - 3000;
EXPECT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/net_info.html")));
VerifyRtt(http_rtt, RunScriptExtractInt("getRtt()"));
VerifyDownlinkKbps(downstream_throughput_kbps,
RunScriptExtractDouble("getDownlink()") * 1000);
EXPECT_EQ(rtt_noise_milliseconds, RunScriptExtractInt("getRtt()") - 2000);
EXPECT_EQ(downlink_noise_kbps,
RunScriptExtractDouble("getDownlink()") * 1000 - 3000);
for (size_t i = 0; i < 10; ++i) {
std::string fake_hostname = "example" + base::NumberToString(i) + ".com";
EXPECT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
fake_hostname, "/net_info.html")));
VerifyRtt(http_rtt, RunScriptExtractInt("getRtt()"));
VerifyDownlinkKbps(downstream_throughput_kbps,
RunScriptExtractDouble("getDownlink()") * 1000);
int32_t new_rtt_noise_milliseconds = RunScriptExtractInt("getRtt()") - 2000;
int32_t new_downlink_noise_kbps =
RunScriptExtractDouble("getDownlink()") * 1000 - 3000;
if (rtt_noise_milliseconds != new_rtt_noise_milliseconds &&
downlink_noise_kbps != new_downlink_noise_kbps) {
return;
}
}
NOTREACHED() << "Noise not added to the network quality estimates";
}
IN_PROC_BROWSER_TEST_F(NetInfoBrowserTest, NetworkQualityChangeNotNotified) {
base::HistogramTester histogram_tester;
NetworkQualityObserverImpl impl(GetNetworkQualityTracker());
base::TimeDelta http_rtt(base::Milliseconds(1123));
int32_t downstream_throughput_kbps = 1303;
GetNetworkQualityTracker()->ReportRTTsAndThroughputForTesting(
http_rtt, downstream_throughput_kbps);
EXPECT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/net_info.html")));
VerifyRtt(http_rtt, RunScriptExtractInt("getRtt()"));
VerifyDownlinkKbps(downstream_throughput_kbps,
RunScriptExtractDouble("getDownlink()") * 1000);
http_rtt = base::Milliseconds(1223);
downstream_throughput_kbps = 1403;
GetNetworkQualityTracker()->ReportRTTsAndThroughputForTesting(
http_rtt, downstream_throughput_kbps);
base::RunLoop().RunUntilIdle();
VerifyRtt(base::Milliseconds(1100), RunScriptExtractInt("getRtt()"));
VerifyDownlinkKbps(1300, RunScriptExtractDouble("getDownlink()") * 1000);
http_rtt = base::Milliseconds(2223);
downstream_throughput_kbps = 1403;
GetNetworkQualityTracker()->ReportRTTsAndThroughputForTesting(
http_rtt, downstream_throughput_kbps);
base::RunLoop().RunUntilIdle();
VerifyRtt(base::Milliseconds(2200), RunScriptExtractInt("getRtt()"));
VerifyDownlinkKbps(1400, RunScriptExtractDouble("getDownlink()") * 1000);
}
}