// Copyright 2020 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/dns/httpssvc_metrics.h" #include #include #include #include #include "base/feature_list.h" #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "net/base/features.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { // Base for testing the metrics collection code in |HttpssvcMetrics|. class HttpssvcMetricsTest : public ::testing::TestWithParam { public: void SetUp() override { secure_ = GetParam(); } std::string BuildMetricNamePrefix() const { return base::StrCat({"Net.DNS.HTTPSSVC.RecordHttps.", secure_ ? "Secure." : "Insecure.", "ExpectNoerror."}); } template void ExpectSample(std::string_view name, std::optional sample) const { if (sample) histo().ExpectUniqueSample(name, *sample, 1); else histo().ExpectTotalCount(name, 0); } void ExpectSample(std::string_view name, std::optional sample) const { std::optional sample_ms; if (sample) sample_ms = {sample->InMilliseconds()}; ExpectSample(name, sample_ms); } void VerifyAddressResolveTimeMetric( std::optional expect_noerror_time = std::nullopt) { const std::string kExpectNoerror = base::StrCat({BuildMetricNamePrefix(), "ResolveTimeAddress"}); ExpectSample(kExpectNoerror, expect_noerror_time); } void VerifyHttpsMetricsForExpectNoerror( std::optional rcode = std::nullopt, std::optional parsable = std::nullopt, std::optional record_with_error = std::nullopt, std::optional resolve_time_https = std::nullopt, std::optional resolve_time_ratio = std::nullopt) const { const std::string kPrefix = BuildMetricNamePrefix(); const std::string kMetricDnsRcode = base::StrCat({kPrefix, "DnsRcode"}); const std::string kMetricParsable = base::StrCat({kPrefix, "Parsable"}); const std::string kMetricRecordWithError = base::StrCat({kPrefix, "RecordWithError"}); const std::string kMetricResolveTimeExperimental = base::StrCat({kPrefix, "ResolveTimeExperimental"}); const std::string kMetricResolveTimeRatio = base::StrCat({kPrefix, "ResolveTimeRatio"}); ExpectSample(kMetricDnsRcode, rcode); ExpectSample(kMetricParsable, parsable); ExpectSample(kMetricRecordWithError, record_with_error); ExpectSample(kMetricResolveTimeExperimental, resolve_time_https); ExpectSample(kMetricResolveTimeRatio, resolve_time_ratio); } const base::HistogramTester& histo() const { return histogram_; } protected: bool secure_; private: base::HistogramTester histogram_; }; INSTANTIATE_TEST_SUITE_P(HttpssvcMetricsTestSimple, HttpssvcMetricsTest, testing::Bool() // Querying over DoH or Do53. ); // Only record metrics for a non-HTTPS query. TEST_P(HttpssvcMetricsTest, AddressAndExperimentalMissing) { const base::TimeDelta kResolveTime = base::Milliseconds(10); auto metrics = std::make_optional(secure_); metrics->SaveForAddressQuery(kResolveTime, HttpssvcDnsRcode::kNoError); metrics.reset(); // Record the metrics to UMA. VerifyAddressResolveTimeMetric(); VerifyHttpsMetricsForExpectNoerror(); } TEST_P(HttpssvcMetricsTest, AddressAndHttpsParsable) { const base::TimeDelta kResolveTime = base::Milliseconds(10); const base::TimeDelta kResolveTimeHttps = base::Milliseconds(15); auto metrics = std::make_optional(secure_); metrics->SaveForHttps(HttpssvcDnsRcode::kNoError, {true}, kResolveTimeHttps); metrics->SaveForAddressQuery(kResolveTime, HttpssvcDnsRcode::kNoError); metrics.reset(); // Record the metrics to UMA. VerifyAddressResolveTimeMetric({kResolveTime} /* expect_noerror_time */); VerifyHttpsMetricsForExpectNoerror( {HttpssvcDnsRcode::kNoError} /* rcode */, {true} /* parsable */, std::nullopt /* record_with_error */, {kResolveTimeHttps} /* resolve_time_https */, {15} /* resolve_time_ratio */); } // This test simulates an HTTPS response that includes no HTTPS records, // but does have an error value for the RCODE. TEST_P(HttpssvcMetricsTest, AddressAndHttpsMissingWithRcode) { const base::TimeDelta kResolveTime = base::Milliseconds(10); const base::TimeDelta kResolveTimeHttps = base::Milliseconds(15); auto metrics = std::make_optional(secure_); metrics->SaveForHttps(HttpssvcDnsRcode::kNxDomain, {}, kResolveTimeHttps); metrics->SaveForAddressQuery(kResolveTime, HttpssvcDnsRcode::kNoError); metrics.reset(); // Record the metrics to UMA. VerifyAddressResolveTimeMetric({kResolveTime} /* expect_noerror_time */); VerifyHttpsMetricsForExpectNoerror( {HttpssvcDnsRcode::kNxDomain} /* rcode */, std::nullopt /* parsable */, std::nullopt /* record_with_error */, {kResolveTimeHttps} /* resolve_time_https */, {15} /* resolve_time_ratio */); } // This test simulates an HTTPS response that includes a parsable HTTPS // record, but also has an error RCODE. TEST_P(HttpssvcMetricsTest, AddressAndHttpsParsableWithRcode) { const base::TimeDelta kResolveTime = base::Milliseconds(10); const base::TimeDelta kResolveTimeHttps = base::Milliseconds(15); auto metrics = std::make_optional(secure_); metrics->SaveForHttps(HttpssvcDnsRcode::kNxDomain, {true}, kResolveTimeHttps); metrics->SaveForAddressQuery(kResolveTime, HttpssvcDnsRcode::kNoError); metrics.reset(); // Record the metrics to UMA. VerifyAddressResolveTimeMetric({kResolveTime} /* expect_noerror_time */); VerifyHttpsMetricsForExpectNoerror( {HttpssvcDnsRcode::kNxDomain} /* rcode */, // "parsable" metric is omitted because the RCODE is not NOERROR. std::nullopt /* parsable */, {true} /* record_with_error */, {kResolveTimeHttps} /* resolve_time_https */, {15} /* resolve_time_ratio */); } // This test simulates an HTTPS response that includes a mangled HTTPS // record *and* has an error RCODE. TEST_P(HttpssvcMetricsTest, AddressAndHttpsMangledWithRcode) { const base::TimeDelta kResolveTime = base::Milliseconds(10); const base::TimeDelta kResolveTimeHttps = base::Milliseconds(15); auto metrics = std::make_optional(secure_); metrics->SaveForHttps(HttpssvcDnsRcode::kNxDomain, {false}, kResolveTimeHttps); metrics->SaveForAddressQuery(kResolveTime, HttpssvcDnsRcode::kNoError); metrics.reset(); // Record the metrics to UMA. VerifyAddressResolveTimeMetric({kResolveTime} /* expect_noerror_time */); VerifyHttpsMetricsForExpectNoerror( {HttpssvcDnsRcode::kNxDomain} /* rcode */, // "parsable" metric is omitted because the RCODE is not NOERROR. std::nullopt /* parsable */, {true} /* record_with_error */, {kResolveTimeHttps} /* resolve_time_https */, {15} /* resolve_time_ratio */); } // This test simulates successful address queries and an HTTPS query that // timed out. TEST_P(HttpssvcMetricsTest, AddressAndHttpsTimedOut) { const base::TimeDelta kResolveTime = base::Milliseconds(10); const base::TimeDelta kResolveTimeHttps = base::Milliseconds(15); auto metrics = std::make_optional(secure_); metrics->SaveForHttps(HttpssvcDnsRcode::kTimedOut, {}, kResolveTimeHttps); metrics->SaveForAddressQuery(kResolveTime, HttpssvcDnsRcode::kNoError); metrics.reset(); // Record the metrics to UMA. VerifyAddressResolveTimeMetric({kResolveTime} /* expect_noerror_time */); VerifyHttpsMetricsForExpectNoerror( {HttpssvcDnsRcode::kTimedOut} /* rcode */, // "parsable" metric is omitted because the RCODE is not NOERROR. std::nullopt /* parsable */, std::nullopt /* record_with_error */, {kResolveTimeHttps} /* resolve_time_https */, {15} /* resolve_time_ratio */); } } // namespace net