blob: b55681ad73834c528db476f75d5464a251f6fb75 [file] [log] [blame]
// Copyright 2020 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 "net/dns/httpssvc_metrics.h"
#include <string>
#include <tuple>
#include "base/feature_list.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.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 {
// int: number of domains
// bool: extra leading comma
// bool: extra trailing comma
using DomainListQuirksTuple = std::tuple<int, bool, bool>;
// bool: DnsHttpssvc feature is enabled
// bool: DnsHttpssvcUseIntegrity feature param
// bool: DnsHttpssvcUseHttpssvc feature param
// bool: DnsHttpssvcControlDomainWildcard feature param
using HttpssvcFeatureTuple = std::tuple<bool, bool, bool, bool>;
// DomainListQuirksTuple: quirks for the experimental domain list.
// DomainListQuirksTuple: quirks for the control domain list.
// HttpssvcFeatureTuple: config for the whole DnsHttpssvc feature.
using ParsingTestParamTuple = std::
tuple<DomainListQuirksTuple, DomainListQuirksTuple, HttpssvcFeatureTuple>;
// bool: whether we are querying for an experimental domain or a control domain
// HttpssvcFeatureTuple: config for the whole DnsHttpssvc feature.
using MetricsTestParamTuple = std::tuple<bool, HttpssvcFeatureTuple>;
// Create a comma-separated list of |domains| with the given |quirks|.
std::string FlattenDomainList(const std::vector<std::string>& domains,
DomainListQuirksTuple quirks) {
int num_domains;
bool leading_comma, trailing_comma;
std::tie(num_domains, leading_comma, trailing_comma) = quirks;
CHECK_EQ(static_cast<size_t>(num_domains), domains.size());
std::string flattened = base::JoinString(domains, ",");
if (leading_comma)
flattened.insert(flattened.begin(), ',');
if (trailing_comma)
flattened.push_back(',');
return flattened;
}
// Intermediate representation constructed from test parameters.
struct HttpssvcFeatureConfig {
HttpssvcFeatureConfig() = default;
explicit HttpssvcFeatureConfig(const HttpssvcFeatureTuple& feature_tuple,
base::StringPiece experiment_domains,
base::StringPiece control_domains)
: experiment_domains(experiment_domains),
control_domains(control_domains) {
std::tie(enabled, use_integrity, use_httpssvc, control_domain_wildcard) =
feature_tuple;
}
void Apply(base::test::ScopedFeatureList* scoped_feature_list) const {
if (!enabled) {
scoped_feature_list->InitAndDisableFeature(features::kDnsHttpssvc);
return;
}
auto stringify = [](bool b) -> std::string { return b ? "true" : "false"; };
scoped_feature_list->InitAndEnableFeatureWithParameters(
features::kDnsHttpssvc,
{
{"DnsHttpssvcUseHttpssvc", stringify(use_httpssvc)},
{"DnsHttpssvcUseIntegrity", stringify(use_integrity)},
{"DnsHttpssvcEnableQueryOverInsecure", "false"},
{"DnsHttpssvcExperimentDomains", experiment_domains},
{"DnsHttpssvcControlDomains", control_domains},
{"DnsHttpssvcControlDomainWildcard",
stringify(control_domain_wildcard)},
});
}
bool enabled = false;
bool use_integrity = false;
bool use_httpssvc = false;
bool control_domain_wildcard = false;
std::string experiment_domains;
std::string control_domains;
};
std::vector<std::string> GenerateDomainList(base::StringPiece label, int n) {
std::vector<std::string> domains;
for (int i = 0; i < n; i++) {
domains.push_back(base::StrCat(
{"domain", base::NumberToString(i), ".", label, ".example"}));
}
return domains;
}
// Base for testing domain list parsing functions in
// net::features::dns_httpssvc_experiment.
class HttpssvcDomainParsingTest
: public ::testing::TestWithParam<ParsingTestParamTuple> {
public:
void SetUp() override {
DomainListQuirksTuple domain_quirks_experimental;
DomainListQuirksTuple domain_quirks_control;
HttpssvcFeatureTuple httpssvc_feature;
std::tie(domain_quirks_experimental, domain_quirks_control,
httpssvc_feature) = GetParam();
expected_experiment_domains_ = GenerateDomainList(
"experiment", std::get<0>(domain_quirks_experimental));
expected_control_domains_ =
GenerateDomainList("control", std::get<0>(domain_quirks_control));
config_ = HttpssvcFeatureConfig(
httpssvc_feature,
FlattenDomainList(expected_experiment_domains_,
domain_quirks_experimental),
FlattenDomainList(expected_control_domains_, domain_quirks_control));
config_.Apply(&scoped_feature_list_);
}
const HttpssvcFeatureConfig& config() { return config_; }
protected:
// The expected results of parsing the comma-separated domain lists in
// |experiment_domains| and |control_domains|, respectively.
std::vector<std::string> expected_experiment_domains_;
std::vector<std::string> expected_control_domains_;
private:
HttpssvcFeatureConfig config_;
base::test::ScopedFeatureList scoped_feature_list_;
};
// This instantiation tests the domain list parser against various quirks,
// e.g. leading comma.
INSTANTIATE_TEST_SUITE_P(
HttpssvcMetricsTestDomainParsing,
HttpssvcDomainParsingTest,
testing::Combine(
// DomainListQuirksTuple for experimental domains. To fight back
// combinatorial explosion of tests, this tuple is pared down more than
// the one for control domains. This should not significantly hurt test
// coverage because |IsExperimentDomain| and |IsControlDomain| rely on a
// shared helper function.
testing::Combine(testing::Values(0, 1),
testing::Values(false),
testing::Values(false)),
// DomainListQuirksTuple for control domains.
testing::Combine(testing::Range(0, 3),
testing::Bool(),
testing::Bool()),
// HttpssvcFeatureTuple
testing::Combine(
testing::Bool() /* DnsHttpssvc feature enabled? */,
testing::Bool() /* DnsHttpssvcUseIntegrity */,
testing::Bool() /* DnsHttpssvcUseHttpssvc */,
testing::Values(false) /* DnsHttpssvcControlDomainWildcard */)));
// Base for testing the metrics collection code in |HttpssvcMetrics|.
class HttpssvcMetricsTest : public ::testing::TestWithParam<bool> {
public:
void SetUp() override {
querying_experimental_ = GetParam();
config_ = HttpssvcFeatureConfig(
{true /* enabled */, true /* use_integrity */, true /* use_httpssvc */,
false /* control_domain_wildcard */},
"", "");
config_.Apply(&scoped_feature_list_);
}
std::string BuildMetricNamePrefix(base::StringPiece record_type_str,
base::StringPiece expect_str) const {
return base::StrCat({"Net.DNS.HTTPSSVC.", record_type_str, ".",
doh_provider_, ".", expect_str, "."});
}
template <typename T>
void ExpectSample(base::StringPiece name, absl::optional<T> sample) const {
if (sample)
histo().ExpectUniqueSample(name, *sample, 1);
else
histo().ExpectTotalCount(name, 0);
}
void ExpectSample(base::StringPiece name,
absl::optional<base::TimeDelta> sample) const {
absl::optional<int64_t> sample_ms;
if (sample)
sample_ms = {sample->InMilliseconds()};
ExpectSample<int64_t>(name, sample_ms);
}
void VerifyAddressResolveTimeMetric(
absl::optional<base::TimeDelta> expect_intact_time = absl::nullopt,
absl::optional<base::TimeDelta> expect_noerror_time = absl::nullopt) {
const std::string kExpectIntact =
base::StrCat({BuildMetricNamePrefix("RecordIntegrity", "ExpectIntact"),
"ResolveTimeNonIntegrityRecord"});
const std::string kExpectNoerror =
base::StrCat({BuildMetricNamePrefix("RecordIntegrity", "ExpectNoerror"),
"ResolveTimeNonIntegrityRecord"});
ExpectSample(kExpectIntact, expect_intact_time);
ExpectSample(kExpectNoerror, expect_noerror_time);
}
void VerifyIntegrityMetricsForExpectIntact(
absl::optional<HttpssvcDnsRcode> rcode,
absl::optional<bool> integrity,
absl::optional<bool> record_with_error,
absl::optional<base::TimeDelta> resolve_time_integrity,
absl::optional<int> resolve_time_ratio) const {
const std::string kPrefix =
BuildMetricNamePrefix("RecordIntegrity", "ExpectIntact");
const std::string kMetricDnsRcode = base::StrCat({kPrefix, "DnsRcode"});
const std::string kMetricIntegrity = base::StrCat({kPrefix, "Integrity"});
const std::string kMetricRecordWithError =
base::StrCat({kPrefix, "RecordWithError"});
const std::string kMetricResolveTimeIntegrity =
base::StrCat({kPrefix, "ResolveTimeIntegrityRecord"});
const std::string kMetricResolveTimeRatio =
base::StrCat({kPrefix, "ResolveTimeRatio"});
ExpectSample(kMetricDnsRcode, rcode);
ExpectSample(kMetricIntegrity, integrity);
ExpectSample(kMetricRecordWithError, record_with_error);
ExpectSample(kMetricResolveTimeIntegrity, resolve_time_integrity);
ExpectSample(kMetricResolveTimeRatio, resolve_time_ratio);
}
void VerifyHttpsMetricsForExpectIntact(
absl::optional<HttpssvcDnsRcode> rcode = absl::nullopt,
absl::optional<bool> parsable = absl::nullopt,
absl::optional<bool> record_with_error = absl::nullopt,
absl::optional<base::TimeDelta> resolve_time_https = absl::nullopt,
absl::optional<int> resolve_time_ratio = absl::nullopt) const {
const std::string kPrefix =
BuildMetricNamePrefix("RecordHttps", "ExpectIntact");
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 kMetricResolveTimeHttps =
base::StrCat({kPrefix, "ResolveTimeHttpsRecord"});
const std::string kMetricResolveTimeRatio =
base::StrCat({kPrefix, "ResolveTimeRatio"});
ExpectSample(kMetricDnsRcode, rcode);
ExpectSample(kMetricParsable, parsable);
ExpectSample(kMetricRecordWithError, record_with_error);
ExpectSample(kMetricResolveTimeHttps, resolve_time_https);
ExpectSample(kMetricResolveTimeRatio, resolve_time_ratio);
}
void VerifyIntegrityMetricsForExpectNoerror(
absl::optional<HttpssvcDnsRcode> rcode,
absl::optional<int> record_received,
absl::optional<base::TimeDelta> resolve_time_integrity,
absl::optional<int> resolve_time_ratio) const {
const std::string kPrefix =
BuildMetricNamePrefix("RecordIntegrity", "ExpectNoerror");
const std::string kMetricDnsRcode = base::StrCat({kPrefix, "DnsRcode"});
const std::string kMetricRecordReceived =
base::StrCat({kPrefix, "RecordReceived"});
const std::string kMetricResolveTimeIntegrity =
base::StrCat({kPrefix, "ResolveTimeIntegrityRecord"});
const std::string kMetricResolveTimeRatio =
base::StrCat({kPrefix, "ResolveTimeRatio"});
ExpectSample(kMetricDnsRcode, rcode);
ExpectSample(kMetricRecordReceived, record_received);
ExpectSample(kMetricResolveTimeIntegrity, resolve_time_integrity);
ExpectSample(kMetricResolveTimeRatio, resolve_time_ratio);
}
void VerifyHttpsMetricsForExpectNoerror(
absl::optional<HttpssvcDnsRcode> rcode = absl::nullopt,
absl::optional<bool> parsable = absl::nullopt,
absl::optional<bool> record_with_error = absl::nullopt,
absl::optional<base::TimeDelta> resolve_time_https = absl::nullopt,
absl::optional<int> resolve_time_ratio = absl::nullopt) const {
const std::string kPrefix =
BuildMetricNamePrefix("RecordHttps", "ExpectNoerror");
const std::string kMetricDnsRcode = base::StrCat({kPrefix, "DnsRcode"});
const std::string kMetricParsable =
"Net.DNS.HTTPSSVC.RecordHttps.AnyProvider.ExpectNoerror.Parsable";
const std::string kMetricRecordWithError =
"Net.DNS.HTTPSSVC.RecordHttps.AnyProvider.ExpectNoerror."
"RecordWithError";
const std::string kMetricResolveTimeHttps =
base::StrCat({kPrefix, "ResolveTimeHttpsRecord"});
const std::string kMetricResolveTimeRatio =
base::StrCat({kPrefix, "ResolveTimeRatio"});
ExpectSample(kMetricDnsRcode, rcode);
ExpectSample(kMetricParsable, parsable);
ExpectSample(kMetricRecordWithError, record_with_error);
ExpectSample(kMetricResolveTimeHttps, resolve_time_https);
ExpectSample(kMetricResolveTimeRatio, resolve_time_ratio);
}
void VerifyIntegrityMetricsForExpectIntact() {
VerifyIntegrityMetricsForExpectIntact(absl::nullopt, absl::nullopt,
absl::nullopt, absl::nullopt,
absl::nullopt);
}
void VerifyIntegrityMetricsForExpectNoerror() {
VerifyIntegrityMetricsForExpectNoerror(absl::nullopt, absl::nullopt,
absl::nullopt, absl::nullopt);
}
const base::HistogramTester& histo() const { return histogram_; }
const HttpssvcFeatureConfig& config() const { return config_; }
protected:
bool querying_experimental_;
private:
HttpssvcFeatureConfig config_;
base::test::ScopedFeatureList scoped_feature_list_;
base::HistogramTester histogram_;
std::string doh_provider_ = "Other";
};
// This instantiation focuses on whether the correct metrics are recorded. The
// domain list parser is already tested against encoding quirks in
// |HttpssvcMetricsTestDomainParsing|, so we fix the quirks at false.
INSTANTIATE_TEST_SUITE_P(HttpssvcMetricsTestSimple,
HttpssvcMetricsTest,
// Whether we are querying an experimental domain.
testing::Bool());
TEST_P(HttpssvcDomainParsingTest, ParseFeatureParamIntegrityDomains) {
HttpssvcExperimentDomainCache domain_cache;
const std::string kReservedDomain = "neither.example";
EXPECT_FALSE(domain_cache.IsExperimental(kReservedDomain));
EXPECT_EQ(domain_cache.IsControl(kReservedDomain),
config().enabled && config().control_domain_wildcard);
// If |config().use_integrity| is true, then we expect all domains in
// |expected_experiment_domains_| to be experimental (same goes for
// control domains). Otherwise, no domain should be considered experimental or
// control.
if (!config().enabled) {
// When the HTTPSSVC feature is disabled, no domain should be considered
// experimental or control.
for (const std::string& experiment_domain : expected_experiment_domains_) {
EXPECT_FALSE(domain_cache.IsExperimental(experiment_domain));
EXPECT_FALSE(domain_cache.IsControl(experiment_domain));
}
for (const std::string& control_domain : expected_control_domains_) {
EXPECT_FALSE(domain_cache.IsExperimental(control_domain));
EXPECT_FALSE(domain_cache.IsControl(control_domain));
}
} else if (config().use_integrity || config().use_httpssvc) {
for (const std::string& experiment_domain : expected_experiment_domains_) {
EXPECT_TRUE(domain_cache.IsExperimental(experiment_domain));
EXPECT_FALSE(domain_cache.IsControl(experiment_domain));
}
for (const std::string& control_domain : expected_control_domains_) {
EXPECT_FALSE(domain_cache.IsExperimental(control_domain));
EXPECT_TRUE(domain_cache.IsControl(control_domain));
}
return;
}
}
// Only record metrics for a non-integrity query.
TEST_P(HttpssvcMetricsTest, AddressAndExperimentalMissing) {
const base::TimeDelta kResolveTime = base::TimeDelta::FromMilliseconds(10);
absl::optional<HttpssvcMetrics> metrics(querying_experimental_);
metrics->SaveForAddressQuery(absl::nullopt, kResolveTime,
HttpssvcDnsRcode::kNoError);
metrics.reset(); // Record the metrics to UMA.
VerifyAddressResolveTimeMetric();
VerifyIntegrityMetricsForExpectIntact();
VerifyHttpsMetricsForExpectIntact();
VerifyIntegrityMetricsForExpectNoerror();
VerifyHttpsMetricsForExpectNoerror();
}
TEST_P(HttpssvcMetricsTest, AddressAndIntegrityIntact) {
const base::TimeDelta kResolveTime = base::TimeDelta::FromMilliseconds(10);
const base::TimeDelta kResolveTimeIntegrity =
base::TimeDelta::FromMilliseconds(15);
absl::optional<HttpssvcMetrics> metrics(querying_experimental_);
metrics->SaveForIntegrity(absl::nullopt, HttpssvcDnsRcode::kNoError, {true},
kResolveTimeIntegrity);
metrics->SaveForAddressQuery(absl::nullopt, kResolveTime,
HttpssvcDnsRcode::kNoError);
metrics.reset(); // Record the metrics to UMA.
VerifyHttpsMetricsForExpectIntact();
VerifyHttpsMetricsForExpectNoerror();
if (querying_experimental_) {
VerifyAddressResolveTimeMetric({kResolveTime} /* expect_intact_time */);
VerifyIntegrityMetricsForExpectIntact(
absl::nullopt /* rcode */, {true} /* integrity */,
absl::nullopt /* record_with_error */,
{kResolveTimeIntegrity} /* resolve_time_integrity */,
{15} /* resolve_time_ratio */);
VerifyIntegrityMetricsForExpectNoerror();
return;
}
VerifyIntegrityMetricsForExpectIntact();
VerifyAddressResolveTimeMetric(absl::nullopt /* expect_intact_time */,
{kResolveTime} /* expect_noerror_time */);
VerifyIntegrityMetricsForExpectNoerror(
{HttpssvcDnsRcode::kNoError} /* rcode */, {1} /* record_received */,
{kResolveTimeIntegrity} /* resolve_time_integrity */,
{15} /* resolve_time_ratio */);
}
TEST_P(HttpssvcMetricsTest, AddressAndHttpsParsable) {
const base::TimeDelta kResolveTime = base::TimeDelta::FromMilliseconds(10);
const base::TimeDelta kResolveTimeHttps =
base::TimeDelta::FromMilliseconds(15);
absl::optional<HttpssvcMetrics> metrics(querying_experimental_);
metrics->SaveForHttps(absl::nullopt, HttpssvcDnsRcode::kNoError, {true},
kResolveTimeHttps);
metrics->SaveForAddressQuery(absl::nullopt, kResolveTime,
HttpssvcDnsRcode::kNoError);
metrics.reset(); // Record the metrics to UMA.
VerifyIntegrityMetricsForExpectIntact();
VerifyIntegrityMetricsForExpectNoerror();
if (querying_experimental_) {
VerifyAddressResolveTimeMetric({kResolveTime} /* expect_intact_time */);
VerifyHttpsMetricsForExpectIntact(
absl::nullopt /* rcode */, {true} /* parsable */,
absl::nullopt /* record_with_error */,
{kResolveTimeHttps} /* resolve_time_https */,
{15} /* resolve_time_ratio */);
VerifyHttpsMetricsForExpectNoerror();
return;
}
VerifyHttpsMetricsForExpectIntact();
VerifyAddressResolveTimeMetric(absl::nullopt /* expect_intact_time */,
{kResolveTime} /* expect_noerror_time */);
VerifyHttpsMetricsForExpectNoerror(
{HttpssvcDnsRcode::kNoError} /* rcode */, {true} /* parsable */,
absl::nullopt /* record_with_error */,
{kResolveTimeHttps} /* resolve_time_https */,
{15} /* resolve_time_ratio */);
}
TEST_P(HttpssvcMetricsTest, AddressAndIntegrityIntactAndHttpsParsable) {
const base::TimeDelta kResolveTime = base::TimeDelta::FromMilliseconds(10);
const base::TimeDelta kResolveTimeIntegrity =
base::TimeDelta::FromMilliseconds(15);
const base::TimeDelta kResolveTimeHttps =
base::TimeDelta::FromMilliseconds(20);
absl::optional<HttpssvcMetrics> metrics(querying_experimental_);
metrics->SaveForIntegrity(absl::nullopt, HttpssvcDnsRcode::kNoError, {true},
kResolveTimeIntegrity);
metrics->SaveForHttps(absl::nullopt, HttpssvcDnsRcode::kNoError, {true},
kResolveTimeHttps);
metrics->SaveForAddressQuery(absl::nullopt, kResolveTime,
HttpssvcDnsRcode::kNoError);
metrics.reset(); // Record the metrics to UMA.
if (querying_experimental_) {
VerifyAddressResolveTimeMetric({kResolveTime} /* expect_intact_time */);
VerifyIntegrityMetricsForExpectIntact(
absl::nullopt /* rcode */, {true} /* integrity */,
absl::nullopt /* record_with_error */,
{kResolveTimeIntegrity} /* resolve_time_integrity */,
{15} /* resolve_time_ratio */);
VerifyHttpsMetricsForExpectIntact(
absl::nullopt /* rcode */, {true} /* parsable */,
absl::nullopt /* record_with_error */,
{kResolveTimeHttps} /* resolve_time_https */,
{20} /* resolve_time_ratio */);
VerifyIntegrityMetricsForExpectNoerror();
VerifyHttpsMetricsForExpectNoerror();
return;
}
VerifyIntegrityMetricsForExpectIntact();
VerifyHttpsMetricsForExpectIntact();
VerifyAddressResolveTimeMetric(absl::nullopt /* expect_intact_time */,
{kResolveTime} /* expect_noerror_time */);
VerifyIntegrityMetricsForExpectNoerror(
{HttpssvcDnsRcode::kNoError} /* rcode */, {1} /* record_received */,
{kResolveTimeIntegrity} /* resolve_time_integrity */,
{15} /* resolve_time_ratio */);
VerifyHttpsMetricsForExpectNoerror(
{HttpssvcDnsRcode::kNoError} /* rcode */, {true} /* parsable */,
absl::nullopt /* record_with_error */,
{kResolveTimeHttps} /* resolve_time_https */,
{20} /* resolve_time_ratio */);
}
// This test simulates an INTEGRITY response that includes no INTEGRITY records,
// but does have an error value for the RCODE.
TEST_P(HttpssvcMetricsTest, AddressAndIntegrityMissingWithRcode) {
const base::TimeDelta kResolveTime = base::TimeDelta::FromMilliseconds(10);
const base::TimeDelta kResolveTimeIntegrity =
base::TimeDelta::FromMilliseconds(15);
absl::optional<HttpssvcMetrics> metrics(querying_experimental_);
metrics->SaveForIntegrity(absl::nullopt, HttpssvcDnsRcode::kNxDomain, {},
kResolveTimeIntegrity);
metrics->SaveForAddressQuery(absl::nullopt, kResolveTime,
HttpssvcDnsRcode::kNoError);
metrics.reset(); // Record the metrics to UMA.
VerifyHttpsMetricsForExpectIntact();
VerifyHttpsMetricsForExpectNoerror();
if (querying_experimental_) {
VerifyAddressResolveTimeMetric({kResolveTime} /* expect_intact_time */);
VerifyIntegrityMetricsForExpectIntact(
{HttpssvcDnsRcode::kNxDomain} /* rcode */,
absl::nullopt /* integrity */, absl::nullopt /* record_with_error */,
{kResolveTimeIntegrity} /* resolve_time_integrity */,
{15} /* resolve_time_ratio */);
VerifyIntegrityMetricsForExpectNoerror();
return;
}
VerifyIntegrityMetricsForExpectIntact();
VerifyAddressResolveTimeMetric(absl::nullopt /* expect_intact_time */,
{kResolveTime} /* expect_noerror_time */);
VerifyIntegrityMetricsForExpectNoerror(
{HttpssvcDnsRcode::kNxDomain} /* rcode */,
absl::nullopt /* record_received */,
{kResolveTimeIntegrity} /* resolve_time_integrity */,
{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::TimeDelta::FromMilliseconds(10);
const base::TimeDelta kResolveTimeHttps =
base::TimeDelta::FromMilliseconds(15);
absl::optional<HttpssvcMetrics> metrics(querying_experimental_);
metrics->SaveForHttps(absl::nullopt, HttpssvcDnsRcode::kNxDomain, {},
kResolveTimeHttps);
metrics->SaveForAddressQuery(absl::nullopt, kResolveTime,
HttpssvcDnsRcode::kNoError);
metrics.reset(); // Record the metrics to UMA.
VerifyIntegrityMetricsForExpectIntact();
VerifyIntegrityMetricsForExpectNoerror();
if (querying_experimental_) {
VerifyAddressResolveTimeMetric({kResolveTime} /* expect_intact_time */);
VerifyHttpsMetricsForExpectIntact(
{HttpssvcDnsRcode::kNxDomain} /* rcode */, absl::nullopt /* parsable */,
absl::nullopt /* record_with_error */,
{kResolveTimeHttps} /* resolve_time_https */,
{15} /* resolve_time_ratio */);
VerifyHttpsMetricsForExpectNoerror();
return;
}
VerifyHttpsMetricsForExpectIntact();
VerifyAddressResolveTimeMetric(absl::nullopt /* expect_intact_time */,
{kResolveTime} /* expect_noerror_time */);
VerifyHttpsMetricsForExpectNoerror(
{HttpssvcDnsRcode::kNxDomain} /* rcode */, absl::nullopt /* parsable */,
absl::nullopt /* record_with_error */,
{kResolveTimeHttps} /* resolve_time_https */,
{15} /* resolve_time_ratio */);
}
// This test simulates an INTEGRITY response that includes an intact INTEGRITY
// record, but also has an error RCODE.
TEST_P(HttpssvcMetricsTest, AddressAndIntegrityIntactWithRcode) {
const base::TimeDelta kResolveTime = base::TimeDelta::FromMilliseconds(10);
const base::TimeDelta kResolveTimeIntegrity =
base::TimeDelta::FromMilliseconds(15);
absl::optional<HttpssvcMetrics> metrics(querying_experimental_);
metrics->SaveForIntegrity(absl::nullopt, HttpssvcDnsRcode::kNxDomain, {true},
kResolveTimeIntegrity);
metrics->SaveForAddressQuery(absl::nullopt, kResolveTime,
HttpssvcDnsRcode::kNoError);
metrics.reset(); // Record the metrics to UMA.
VerifyHttpsMetricsForExpectIntact();
VerifyHttpsMetricsForExpectNoerror();
if (querying_experimental_) {
VerifyAddressResolveTimeMetric({kResolveTime} /* expect_intact_time */);
VerifyIntegrityMetricsForExpectIntact(
// "DnsRcode" metric is omitted because we received an INTEGRITY record.
absl::nullopt /* rcode */,
// "Integrity" metric is omitted because the RCODE is not NOERROR.
absl::nullopt /* integrity */, {true} /* record_with_error */,
{kResolveTimeIntegrity} /* resolve_time_integrity */,
{15} /* resolve_time_ratio */);
VerifyIntegrityMetricsForExpectNoerror();
return;
}
VerifyIntegrityMetricsForExpectIntact();
VerifyAddressResolveTimeMetric(absl::nullopt /* expect_intact_time */,
{kResolveTime} /* expect_noerror_time */);
VerifyIntegrityMetricsForExpectNoerror(
{HttpssvcDnsRcode::kNxDomain} /* rcode */, {true} /* record_received */,
{kResolveTimeIntegrity} /* resolve_time_integrity */,
{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::TimeDelta::FromMilliseconds(10);
const base::TimeDelta kResolveTimeHttps =
base::TimeDelta::FromMilliseconds(15);
absl::optional<HttpssvcMetrics> metrics(querying_experimental_);
metrics->SaveForHttps(absl::nullopt, HttpssvcDnsRcode::kNxDomain, {true},
kResolveTimeHttps);
metrics->SaveForAddressQuery(absl::nullopt, kResolveTime,
HttpssvcDnsRcode::kNoError);
metrics.reset(); // Record the metrics to UMA.
VerifyIntegrityMetricsForExpectIntact();
VerifyIntegrityMetricsForExpectNoerror();
if (querying_experimental_) {
VerifyAddressResolveTimeMetric({kResolveTime} /* expect_intact_time */);
VerifyHttpsMetricsForExpectIntact(
// "DnsRcode" metric is omitted because we received an HTTPS record.
absl::nullopt /* rcode */,
// "parsable" metric is omitted because the RCODE is not NOERROR.
absl::nullopt /* parsable */, {true} /* record_with_error */,
{kResolveTimeHttps} /* resolve_time_https */,
{15} /* resolve_time_ratio */);
VerifyHttpsMetricsForExpectNoerror();
return;
}
VerifyHttpsMetricsForExpectIntact();
VerifyAddressResolveTimeMetric(absl::nullopt /* expect_intact_time */,
{kResolveTime} /* expect_noerror_time */);
VerifyHttpsMetricsForExpectNoerror(
{HttpssvcDnsRcode::kNxDomain} /* rcode */,
// "parsable" metric is omitted because the RCODE is not NOERROR.
absl::nullopt /* parsable */, {true} /* record_with_error */,
{kResolveTimeHttps} /* resolve_time_https */,
{15} /* resolve_time_ratio */);
}
// This test simulates an INTEGRITY response that includes a mangled INTEGRITY
// record *and* has an error RCODE.
TEST_P(HttpssvcMetricsTest, AddressAndIntegrityMangledWithRcode) {
const base::TimeDelta kResolveTime = base::TimeDelta::FromMilliseconds(10);
const base::TimeDelta kResolveTimeIntegrity =
base::TimeDelta::FromMilliseconds(15);
absl::optional<HttpssvcMetrics> metrics(querying_experimental_);
metrics->SaveForIntegrity(absl::nullopt, HttpssvcDnsRcode::kNxDomain, {false},
kResolveTimeIntegrity);
metrics->SaveForAddressQuery(absl::nullopt, kResolveTime,
HttpssvcDnsRcode::kNoError);
metrics.reset(); // Record the metrics to UMA.
VerifyHttpsMetricsForExpectIntact();
VerifyHttpsMetricsForExpectNoerror();
if (querying_experimental_) {
VerifyAddressResolveTimeMetric({kResolveTime} /* expect_intact_time */);
VerifyIntegrityMetricsForExpectIntact(
// "DnsRcode" metric is omitted because we received an INTEGRITY record.
absl::nullopt /* rcode */,
// "Integrity" metric is omitted because the RCODE is not NOERROR.
absl::nullopt /* integrity */, {true} /* record_with_error */,
{kResolveTimeIntegrity} /* resolve_time_integrity */,
{15} /* resolve_time_ratio */);
VerifyIntegrityMetricsForExpectNoerror();
return;
}
VerifyIntegrityMetricsForExpectIntact();
VerifyAddressResolveTimeMetric(absl::nullopt /* expect_intact_time */,
{kResolveTime} /* expect_noerror_time */);
VerifyIntegrityMetricsForExpectNoerror(
{HttpssvcDnsRcode::kNxDomain} /* rcode */, {true} /* record_received */,
{kResolveTimeIntegrity} /* resolve_time_integrity */,
{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::TimeDelta::FromMilliseconds(10);
const base::TimeDelta kResolveTimeHttps =
base::TimeDelta::FromMilliseconds(15);
absl::optional<HttpssvcMetrics> metrics(querying_experimental_);
metrics->SaveForHttps(absl::nullopt, HttpssvcDnsRcode::kNxDomain, {false},
kResolveTimeHttps);
metrics->SaveForAddressQuery(absl::nullopt, kResolveTime,
HttpssvcDnsRcode::kNoError);
metrics.reset(); // Record the metrics to UMA.
VerifyIntegrityMetricsForExpectIntact();
VerifyIntegrityMetricsForExpectNoerror();
if (querying_experimental_) {
VerifyAddressResolveTimeMetric({kResolveTime} /* expect_intact_time */);
VerifyHttpsMetricsForExpectIntact(
// "DnsRcode" metric is omitted because we received an HTTPS record.
absl::nullopt /* rcode */,
// "parsable" metric is omitted because the RCODE is not NOERROR.
absl::nullopt /* parsable */, {true} /* record_with_error */,
{kResolveTimeHttps} /* resolve_time_https */,
{15} /* resolve_time_ratio */);
VerifyHttpsMetricsForExpectNoerror();
return;
}
VerifyHttpsMetricsForExpectIntact();
VerifyAddressResolveTimeMetric(absl::nullopt /* expect_intact_time */,
{kResolveTime} /* expect_noerror_time */);
VerifyHttpsMetricsForExpectNoerror(
{HttpssvcDnsRcode::kNxDomain} /* rcode */,
// "parsable" metric is omitted because the RCODE is not NOERROR.
absl::nullopt /* parsable */, {true} /* record_with_error */,
{kResolveTimeHttps} /* resolve_time_https */,
{15} /* resolve_time_ratio */);
}
// This test simulates successful address queries and an INTEGRITY query that
// timed out.
TEST_P(HttpssvcMetricsTest, AddressAndIntegrityTimedOut) {
const base::TimeDelta kResolveTime = base::TimeDelta::FromMilliseconds(10);
const base::TimeDelta kResolveTimeIntegrity =
base::TimeDelta::FromMilliseconds(15);
absl::optional<HttpssvcMetrics> metrics(querying_experimental_);
metrics->SaveForIntegrity(absl::nullopt, HttpssvcDnsRcode::kTimedOut, {},
kResolveTimeIntegrity);
metrics->SaveForAddressQuery(absl::nullopt, kResolveTime,
HttpssvcDnsRcode::kNoError);
metrics.reset(); // Record the metrics to UMA.
VerifyHttpsMetricsForExpectIntact();
VerifyHttpsMetricsForExpectNoerror();
if (querying_experimental_) {
VerifyAddressResolveTimeMetric({kResolveTime} /* expect_intact_time */);
VerifyIntegrityMetricsForExpectIntact(
{HttpssvcDnsRcode::kTimedOut} /* rcode */,
// "Integrity" metric is omitted because the RCODE is not NOERROR.
absl::nullopt /* integrity */, absl::nullopt /* record_with_error */,
{kResolveTimeIntegrity} /* resolve_time_integrity */,
{15} /* resolve_time_ratio */);
VerifyIntegrityMetricsForExpectNoerror();
return;
}
VerifyIntegrityMetricsForExpectIntact();
VerifyAddressResolveTimeMetric(absl::nullopt /* expect_intact_time */,
{kResolveTime} /* expect_noerror_time */);
VerifyIntegrityMetricsForExpectNoerror(
{HttpssvcDnsRcode::kTimedOut} /* rcode */,
absl::nullopt /* record_received */,
{kResolveTimeIntegrity} /* resolve_time_integrity */,
{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::TimeDelta::FromMilliseconds(10);
const base::TimeDelta kResolveTimeHttps =
base::TimeDelta::FromMilliseconds(15);
absl::optional<HttpssvcMetrics> metrics(querying_experimental_);
metrics->SaveForHttps(absl::nullopt, HttpssvcDnsRcode::kTimedOut, {},
kResolveTimeHttps);
metrics->SaveForAddressQuery(absl::nullopt, kResolveTime,
HttpssvcDnsRcode::kNoError);
metrics.reset(); // Record the metrics to UMA.
VerifyIntegrityMetricsForExpectIntact();
VerifyIntegrityMetricsForExpectNoerror();
if (querying_experimental_) {
VerifyAddressResolveTimeMetric({kResolveTime} /* expect_intact_time */);
VerifyHttpsMetricsForExpectIntact(
{HttpssvcDnsRcode::kTimedOut} /* rcode */,
// "parsable" metric is omitted because the RCODE is not NOERROR.
absl::nullopt /* parsable */, absl::nullopt /* record_with_error */,
{kResolveTimeHttps} /* resolve_time_https */,
{15} /* resolve_time_ratio */);
VerifyIntegrityMetricsForExpectNoerror();
return;
}
VerifyHttpsMetricsForExpectIntact();
VerifyAddressResolveTimeMetric(absl::nullopt /* expect_intact_time */,
{kResolveTime} /* expect_noerror_time */);
VerifyHttpsMetricsForExpectNoerror(
{HttpssvcDnsRcode::kTimedOut} /* rcode */,
// "parsable" metric is omitted because the RCODE is not NOERROR.
absl::nullopt /* parsable */, absl::nullopt /* record_with_error */,
{kResolveTimeHttps} /* resolve_time_https */,
{15} /* resolve_time_ratio */);
}
} // namespace net