1*6777b538SAndroid Build Coastguard Worker // Copyright 2020 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #include "net/dns/httpssvc_metrics.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include <optional>
8*6777b538SAndroid Build Coastguard Worker #include <string>
9*6777b538SAndroid Build Coastguard Worker #include <string_view>
10*6777b538SAndroid Build Coastguard Worker #include <tuple>
11*6777b538SAndroid Build Coastguard Worker
12*6777b538SAndroid Build Coastguard Worker #include "base/feature_list.h"
13*6777b538SAndroid Build Coastguard Worker #include "base/strings/strcat.h"
14*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_number_conversions.h"
15*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_util.h"
16*6777b538SAndroid Build Coastguard Worker #include "base/test/metrics/histogram_tester.h"
17*6777b538SAndroid Build Coastguard Worker #include "base/test/scoped_feature_list.h"
18*6777b538SAndroid Build Coastguard Worker #include "net/base/features.h"
19*6777b538SAndroid Build Coastguard Worker #include "testing/gtest/include/gtest/gtest.h"
20*6777b538SAndroid Build Coastguard Worker
21*6777b538SAndroid Build Coastguard Worker namespace net {
22*6777b538SAndroid Build Coastguard Worker
23*6777b538SAndroid Build Coastguard Worker // Base for testing the metrics collection code in |HttpssvcMetrics|.
24*6777b538SAndroid Build Coastguard Worker class HttpssvcMetricsTest : public ::testing::TestWithParam<bool> {
25*6777b538SAndroid Build Coastguard Worker public:
SetUp()26*6777b538SAndroid Build Coastguard Worker void SetUp() override { secure_ = GetParam(); }
27*6777b538SAndroid Build Coastguard Worker
BuildMetricNamePrefix() const28*6777b538SAndroid Build Coastguard Worker std::string BuildMetricNamePrefix() const {
29*6777b538SAndroid Build Coastguard Worker return base::StrCat({"Net.DNS.HTTPSSVC.RecordHttps.",
30*6777b538SAndroid Build Coastguard Worker secure_ ? "Secure." : "Insecure.", "ExpectNoerror."});
31*6777b538SAndroid Build Coastguard Worker }
32*6777b538SAndroid Build Coastguard Worker
33*6777b538SAndroid Build Coastguard Worker template <typename T>
ExpectSample(std::string_view name,std::optional<T> sample) const34*6777b538SAndroid Build Coastguard Worker void ExpectSample(std::string_view name, std::optional<T> sample) const {
35*6777b538SAndroid Build Coastguard Worker if (sample)
36*6777b538SAndroid Build Coastguard Worker histo().ExpectUniqueSample(name, *sample, 1);
37*6777b538SAndroid Build Coastguard Worker else
38*6777b538SAndroid Build Coastguard Worker histo().ExpectTotalCount(name, 0);
39*6777b538SAndroid Build Coastguard Worker }
40*6777b538SAndroid Build Coastguard Worker
ExpectSample(std::string_view name,std::optional<base::TimeDelta> sample) const41*6777b538SAndroid Build Coastguard Worker void ExpectSample(std::string_view name,
42*6777b538SAndroid Build Coastguard Worker std::optional<base::TimeDelta> sample) const {
43*6777b538SAndroid Build Coastguard Worker std::optional<int64_t> sample_ms;
44*6777b538SAndroid Build Coastguard Worker if (sample)
45*6777b538SAndroid Build Coastguard Worker sample_ms = {sample->InMilliseconds()};
46*6777b538SAndroid Build Coastguard Worker ExpectSample<int64_t>(name, sample_ms);
47*6777b538SAndroid Build Coastguard Worker }
48*6777b538SAndroid Build Coastguard Worker
VerifyAddressResolveTimeMetric(std::optional<base::TimeDelta> expect_noerror_time=std::nullopt)49*6777b538SAndroid Build Coastguard Worker void VerifyAddressResolveTimeMetric(
50*6777b538SAndroid Build Coastguard Worker std::optional<base::TimeDelta> expect_noerror_time = std::nullopt) {
51*6777b538SAndroid Build Coastguard Worker const std::string kExpectNoerror =
52*6777b538SAndroid Build Coastguard Worker base::StrCat({BuildMetricNamePrefix(), "ResolveTimeAddress"});
53*6777b538SAndroid Build Coastguard Worker
54*6777b538SAndroid Build Coastguard Worker ExpectSample(kExpectNoerror, expect_noerror_time);
55*6777b538SAndroid Build Coastguard Worker }
56*6777b538SAndroid Build Coastguard Worker
VerifyHttpsMetricsForExpectNoerror(std::optional<HttpssvcDnsRcode> rcode=std::nullopt,std::optional<bool> parsable=std::nullopt,std::optional<bool> record_with_error=std::nullopt,std::optional<base::TimeDelta> resolve_time_https=std::nullopt,std::optional<int> resolve_time_ratio=std::nullopt) const57*6777b538SAndroid Build Coastguard Worker void VerifyHttpsMetricsForExpectNoerror(
58*6777b538SAndroid Build Coastguard Worker std::optional<HttpssvcDnsRcode> rcode = std::nullopt,
59*6777b538SAndroid Build Coastguard Worker std::optional<bool> parsable = std::nullopt,
60*6777b538SAndroid Build Coastguard Worker std::optional<bool> record_with_error = std::nullopt,
61*6777b538SAndroid Build Coastguard Worker std::optional<base::TimeDelta> resolve_time_https = std::nullopt,
62*6777b538SAndroid Build Coastguard Worker std::optional<int> resolve_time_ratio = std::nullopt) const {
63*6777b538SAndroid Build Coastguard Worker const std::string kPrefix = BuildMetricNamePrefix();
64*6777b538SAndroid Build Coastguard Worker const std::string kMetricDnsRcode = base::StrCat({kPrefix, "DnsRcode"});
65*6777b538SAndroid Build Coastguard Worker const std::string kMetricParsable = base::StrCat({kPrefix, "Parsable"});
66*6777b538SAndroid Build Coastguard Worker const std::string kMetricRecordWithError =
67*6777b538SAndroid Build Coastguard Worker base::StrCat({kPrefix, "RecordWithError"});
68*6777b538SAndroid Build Coastguard Worker const std::string kMetricResolveTimeExperimental =
69*6777b538SAndroid Build Coastguard Worker base::StrCat({kPrefix, "ResolveTimeExperimental"});
70*6777b538SAndroid Build Coastguard Worker const std::string kMetricResolveTimeRatio =
71*6777b538SAndroid Build Coastguard Worker base::StrCat({kPrefix, "ResolveTimeRatio"});
72*6777b538SAndroid Build Coastguard Worker
73*6777b538SAndroid Build Coastguard Worker ExpectSample(kMetricDnsRcode, rcode);
74*6777b538SAndroid Build Coastguard Worker ExpectSample(kMetricParsable, parsable);
75*6777b538SAndroid Build Coastguard Worker ExpectSample(kMetricRecordWithError, record_with_error);
76*6777b538SAndroid Build Coastguard Worker ExpectSample(kMetricResolveTimeExperimental, resolve_time_https);
77*6777b538SAndroid Build Coastguard Worker ExpectSample(kMetricResolveTimeRatio, resolve_time_ratio);
78*6777b538SAndroid Build Coastguard Worker }
79*6777b538SAndroid Build Coastguard Worker
histo() const80*6777b538SAndroid Build Coastguard Worker const base::HistogramTester& histo() const { return histogram_; }
81*6777b538SAndroid Build Coastguard Worker
82*6777b538SAndroid Build Coastguard Worker protected:
83*6777b538SAndroid Build Coastguard Worker bool secure_;
84*6777b538SAndroid Build Coastguard Worker
85*6777b538SAndroid Build Coastguard Worker private:
86*6777b538SAndroid Build Coastguard Worker base::HistogramTester histogram_;
87*6777b538SAndroid Build Coastguard Worker };
88*6777b538SAndroid Build Coastguard Worker
89*6777b538SAndroid Build Coastguard Worker INSTANTIATE_TEST_SUITE_P(HttpssvcMetricsTestSimple,
90*6777b538SAndroid Build Coastguard Worker HttpssvcMetricsTest,
91*6777b538SAndroid Build Coastguard Worker testing::Bool() // Querying over DoH or Do53.
92*6777b538SAndroid Build Coastguard Worker );
93*6777b538SAndroid Build Coastguard Worker
94*6777b538SAndroid Build Coastguard Worker // Only record metrics for a non-HTTPS query.
TEST_P(HttpssvcMetricsTest,AddressAndExperimentalMissing)95*6777b538SAndroid Build Coastguard Worker TEST_P(HttpssvcMetricsTest, AddressAndExperimentalMissing) {
96*6777b538SAndroid Build Coastguard Worker const base::TimeDelta kResolveTime = base::Milliseconds(10);
97*6777b538SAndroid Build Coastguard Worker auto metrics = std::make_optional<HttpssvcMetrics>(secure_);
98*6777b538SAndroid Build Coastguard Worker metrics->SaveForAddressQuery(kResolveTime, HttpssvcDnsRcode::kNoError);
99*6777b538SAndroid Build Coastguard Worker metrics.reset(); // Record the metrics to UMA.
100*6777b538SAndroid Build Coastguard Worker
101*6777b538SAndroid Build Coastguard Worker VerifyAddressResolveTimeMetric();
102*6777b538SAndroid Build Coastguard Worker VerifyHttpsMetricsForExpectNoerror();
103*6777b538SAndroid Build Coastguard Worker }
104*6777b538SAndroid Build Coastguard Worker
TEST_P(HttpssvcMetricsTest,AddressAndHttpsParsable)105*6777b538SAndroid Build Coastguard Worker TEST_P(HttpssvcMetricsTest, AddressAndHttpsParsable) {
106*6777b538SAndroid Build Coastguard Worker const base::TimeDelta kResolveTime = base::Milliseconds(10);
107*6777b538SAndroid Build Coastguard Worker const base::TimeDelta kResolveTimeHttps = base::Milliseconds(15);
108*6777b538SAndroid Build Coastguard Worker auto metrics = std::make_optional<HttpssvcMetrics>(secure_);
109*6777b538SAndroid Build Coastguard Worker metrics->SaveForHttps(HttpssvcDnsRcode::kNoError, {true}, kResolveTimeHttps);
110*6777b538SAndroid Build Coastguard Worker metrics->SaveForAddressQuery(kResolveTime, HttpssvcDnsRcode::kNoError);
111*6777b538SAndroid Build Coastguard Worker metrics.reset(); // Record the metrics to UMA.
112*6777b538SAndroid Build Coastguard Worker
113*6777b538SAndroid Build Coastguard Worker VerifyAddressResolveTimeMetric({kResolveTime} /* expect_noerror_time */);
114*6777b538SAndroid Build Coastguard Worker VerifyHttpsMetricsForExpectNoerror(
115*6777b538SAndroid Build Coastguard Worker {HttpssvcDnsRcode::kNoError} /* rcode */, {true} /* parsable */,
116*6777b538SAndroid Build Coastguard Worker std::nullopt /* record_with_error */,
117*6777b538SAndroid Build Coastguard Worker {kResolveTimeHttps} /* resolve_time_https */,
118*6777b538SAndroid Build Coastguard Worker {15} /* resolve_time_ratio */);
119*6777b538SAndroid Build Coastguard Worker }
120*6777b538SAndroid Build Coastguard Worker
121*6777b538SAndroid Build Coastguard Worker // This test simulates an HTTPS response that includes no HTTPS records,
122*6777b538SAndroid Build Coastguard Worker // but does have an error value for the RCODE.
TEST_P(HttpssvcMetricsTest,AddressAndHttpsMissingWithRcode)123*6777b538SAndroid Build Coastguard Worker TEST_P(HttpssvcMetricsTest, AddressAndHttpsMissingWithRcode) {
124*6777b538SAndroid Build Coastguard Worker const base::TimeDelta kResolveTime = base::Milliseconds(10);
125*6777b538SAndroid Build Coastguard Worker const base::TimeDelta kResolveTimeHttps = base::Milliseconds(15);
126*6777b538SAndroid Build Coastguard Worker
127*6777b538SAndroid Build Coastguard Worker auto metrics = std::make_optional<HttpssvcMetrics>(secure_);
128*6777b538SAndroid Build Coastguard Worker metrics->SaveForHttps(HttpssvcDnsRcode::kNxDomain, {}, kResolveTimeHttps);
129*6777b538SAndroid Build Coastguard Worker metrics->SaveForAddressQuery(kResolveTime, HttpssvcDnsRcode::kNoError);
130*6777b538SAndroid Build Coastguard Worker metrics.reset(); // Record the metrics to UMA.
131*6777b538SAndroid Build Coastguard Worker
132*6777b538SAndroid Build Coastguard Worker VerifyAddressResolveTimeMetric({kResolveTime} /* expect_noerror_time */);
133*6777b538SAndroid Build Coastguard Worker VerifyHttpsMetricsForExpectNoerror(
134*6777b538SAndroid Build Coastguard Worker {HttpssvcDnsRcode::kNxDomain} /* rcode */, std::nullopt /* parsable */,
135*6777b538SAndroid Build Coastguard Worker std::nullopt /* record_with_error */,
136*6777b538SAndroid Build Coastguard Worker {kResolveTimeHttps} /* resolve_time_https */,
137*6777b538SAndroid Build Coastguard Worker {15} /* resolve_time_ratio */);
138*6777b538SAndroid Build Coastguard Worker }
139*6777b538SAndroid Build Coastguard Worker
140*6777b538SAndroid Build Coastguard Worker // This test simulates an HTTPS response that includes a parsable HTTPS
141*6777b538SAndroid Build Coastguard Worker // record, but also has an error RCODE.
TEST_P(HttpssvcMetricsTest,AddressAndHttpsParsableWithRcode)142*6777b538SAndroid Build Coastguard Worker TEST_P(HttpssvcMetricsTest, AddressAndHttpsParsableWithRcode) {
143*6777b538SAndroid Build Coastguard Worker const base::TimeDelta kResolveTime = base::Milliseconds(10);
144*6777b538SAndroid Build Coastguard Worker const base::TimeDelta kResolveTimeHttps = base::Milliseconds(15);
145*6777b538SAndroid Build Coastguard Worker
146*6777b538SAndroid Build Coastguard Worker auto metrics = std::make_optional<HttpssvcMetrics>(secure_);
147*6777b538SAndroid Build Coastguard Worker metrics->SaveForHttps(HttpssvcDnsRcode::kNxDomain, {true}, kResolveTimeHttps);
148*6777b538SAndroid Build Coastguard Worker metrics->SaveForAddressQuery(kResolveTime, HttpssvcDnsRcode::kNoError);
149*6777b538SAndroid Build Coastguard Worker metrics.reset(); // Record the metrics to UMA.
150*6777b538SAndroid Build Coastguard Worker
151*6777b538SAndroid Build Coastguard Worker VerifyAddressResolveTimeMetric({kResolveTime} /* expect_noerror_time */);
152*6777b538SAndroid Build Coastguard Worker VerifyHttpsMetricsForExpectNoerror(
153*6777b538SAndroid Build Coastguard Worker {HttpssvcDnsRcode::kNxDomain} /* rcode */,
154*6777b538SAndroid Build Coastguard Worker // "parsable" metric is omitted because the RCODE is not NOERROR.
155*6777b538SAndroid Build Coastguard Worker std::nullopt /* parsable */, {true} /* record_with_error */,
156*6777b538SAndroid Build Coastguard Worker {kResolveTimeHttps} /* resolve_time_https */,
157*6777b538SAndroid Build Coastguard Worker {15} /* resolve_time_ratio */);
158*6777b538SAndroid Build Coastguard Worker }
159*6777b538SAndroid Build Coastguard Worker
160*6777b538SAndroid Build Coastguard Worker // This test simulates an HTTPS response that includes a mangled HTTPS
161*6777b538SAndroid Build Coastguard Worker // record *and* has an error RCODE.
TEST_P(HttpssvcMetricsTest,AddressAndHttpsMangledWithRcode)162*6777b538SAndroid Build Coastguard Worker TEST_P(HttpssvcMetricsTest, AddressAndHttpsMangledWithRcode) {
163*6777b538SAndroid Build Coastguard Worker const base::TimeDelta kResolveTime = base::Milliseconds(10);
164*6777b538SAndroid Build Coastguard Worker const base::TimeDelta kResolveTimeHttps = base::Milliseconds(15);
165*6777b538SAndroid Build Coastguard Worker auto metrics = std::make_optional<HttpssvcMetrics>(secure_);
166*6777b538SAndroid Build Coastguard Worker metrics->SaveForHttps(HttpssvcDnsRcode::kNxDomain, {false},
167*6777b538SAndroid Build Coastguard Worker kResolveTimeHttps);
168*6777b538SAndroid Build Coastguard Worker metrics->SaveForAddressQuery(kResolveTime, HttpssvcDnsRcode::kNoError);
169*6777b538SAndroid Build Coastguard Worker metrics.reset(); // Record the metrics to UMA.
170*6777b538SAndroid Build Coastguard Worker
171*6777b538SAndroid Build Coastguard Worker VerifyAddressResolveTimeMetric({kResolveTime} /* expect_noerror_time */);
172*6777b538SAndroid Build Coastguard Worker VerifyHttpsMetricsForExpectNoerror(
173*6777b538SAndroid Build Coastguard Worker {HttpssvcDnsRcode::kNxDomain} /* rcode */,
174*6777b538SAndroid Build Coastguard Worker // "parsable" metric is omitted because the RCODE is not NOERROR.
175*6777b538SAndroid Build Coastguard Worker std::nullopt /* parsable */, {true} /* record_with_error */,
176*6777b538SAndroid Build Coastguard Worker {kResolveTimeHttps} /* resolve_time_https */,
177*6777b538SAndroid Build Coastguard Worker {15} /* resolve_time_ratio */);
178*6777b538SAndroid Build Coastguard Worker }
179*6777b538SAndroid Build Coastguard Worker
180*6777b538SAndroid Build Coastguard Worker // This test simulates successful address queries and an HTTPS query that
181*6777b538SAndroid Build Coastguard Worker // timed out.
TEST_P(HttpssvcMetricsTest,AddressAndHttpsTimedOut)182*6777b538SAndroid Build Coastguard Worker TEST_P(HttpssvcMetricsTest, AddressAndHttpsTimedOut) {
183*6777b538SAndroid Build Coastguard Worker const base::TimeDelta kResolveTime = base::Milliseconds(10);
184*6777b538SAndroid Build Coastguard Worker const base::TimeDelta kResolveTimeHttps = base::Milliseconds(15);
185*6777b538SAndroid Build Coastguard Worker auto metrics = std::make_optional<HttpssvcMetrics>(secure_);
186*6777b538SAndroid Build Coastguard Worker metrics->SaveForHttps(HttpssvcDnsRcode::kTimedOut, {}, kResolveTimeHttps);
187*6777b538SAndroid Build Coastguard Worker metrics->SaveForAddressQuery(kResolveTime, HttpssvcDnsRcode::kNoError);
188*6777b538SAndroid Build Coastguard Worker metrics.reset(); // Record the metrics to UMA.
189*6777b538SAndroid Build Coastguard Worker
190*6777b538SAndroid Build Coastguard Worker VerifyAddressResolveTimeMetric({kResolveTime} /* expect_noerror_time */);
191*6777b538SAndroid Build Coastguard Worker VerifyHttpsMetricsForExpectNoerror(
192*6777b538SAndroid Build Coastguard Worker {HttpssvcDnsRcode::kTimedOut} /* rcode */,
193*6777b538SAndroid Build Coastguard Worker // "parsable" metric is omitted because the RCODE is not NOERROR.
194*6777b538SAndroid Build Coastguard Worker std::nullopt /* parsable */, std::nullopt /* record_with_error */,
195*6777b538SAndroid Build Coastguard Worker {kResolveTimeHttps} /* resolve_time_https */,
196*6777b538SAndroid Build Coastguard Worker {15} /* resolve_time_ratio */);
197*6777b538SAndroid Build Coastguard Worker }
198*6777b538SAndroid Build Coastguard Worker
199*6777b538SAndroid Build Coastguard Worker } // namespace net
200