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