123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376 |
- // Copyright 2022 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 "components/variations/processed_study.h"
- #include <cstdint>
- #include "base/test/metrics/histogram_tester.h"
- #include "components/variations/proto/study.pb.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace variations {
- namespace {
- const char kInvalidStudyReasonHistogram[] = "Variations.InvalidStudyReason";
- const uint32_t kMaxProbabilityValue =
- std::numeric_limits<base::FieldTrial::Probability>::max();
- // Adds an experiment with the given name and probability to a study.
- Study::Experiment* AddExperiment(const std::string& name,
- uint32_t probability,
- Study* study) {
- Study::Experiment* experiment = study->add_experiment();
- experiment->set_name(name);
- experiment->set_probability_weight(probability);
- return experiment;
- }
- // Creates a study with the given name.
- Study CreateStudy(const std::string& name) {
- Study study;
- study.set_name(name);
- return study;
- }
- // Creates a valid study named "Study". This study has min and max version
- // filters, min and max OS version filters, and three groups: Default, Enabled,
- // and Disabled. The Enabled and Disabled groups have GWS IDs. The study's
- // default experiment is the Default group.
- Study CreateValidStudy() {
- Study study = CreateStudy("Study");
- Study::Filter* filter = study.mutable_filter();
- filter->set_min_version("1.1.*");
- filter->set_max_version("2.2.2.2");
- filter->set_min_os_version("1");
- filter->set_max_os_version("2.*");
- Study::Experiment* default_experiment = AddExperiment("Default", 0, &study);
- Study::Experiment* enabled_experiment = AddExperiment("Enabled", 50, &study);
- enabled_experiment->set_google_web_experiment_id(1);
- Study::Experiment* disabled_experiment =
- AddExperiment("Disabled", 50, &study);
- disabled_experiment->set_google_web_experiment_id(2);
- study.set_default_experiment_name(default_experiment->name());
- return study;
- }
- } // namespace
- TEST(ProcessedStudyTest, InitValidStudy) {
- base::HistogramTester histogram_tester;
- Study study = CreateValidStudy();
- ProcessedStudy processed_study;
- EXPECT_TRUE(processed_study.Init(&study, false));
- histogram_tester.ExpectTotalCount(kInvalidStudyReasonHistogram, 0);
- }
- // Verifies that a study with an invalid min version filter is invalid.
- TEST(ProcessedStudyTest, InitInvalidMinVersion) {
- base::HistogramTester histogram_tester;
- Study study = CreateValidStudy();
- study.mutable_filter()->set_min_version("invalid");
- ProcessedStudy processed_study;
- EXPECT_FALSE(processed_study.Init(&study, false));
- histogram_tester.ExpectUniqueSample(
- kInvalidStudyReasonHistogram, InvalidStudyReason::kInvalidMinVersion, 1);
- }
- // Verifies that a study with an invalid max version filter is invalid.
- TEST(ProcessedStudyTest, InitInvalidMaxVersion) {
- base::HistogramTester histogram_tester;
- Study study = CreateValidStudy();
- study.mutable_filter()->set_max_version("1.invalid.1");
- ProcessedStudy processed_study;
- EXPECT_FALSE(processed_study.Init(&study, false));
- histogram_tester.ExpectUniqueSample(
- kInvalidStudyReasonHistogram, InvalidStudyReason::kInvalidMaxVersion, 1);
- }
- // Verifies that a study with an invalid min OS version filter is invalid.
- TEST(ProcessedStudyTest, InitInvalidMinOsVersion) {
- base::HistogramTester histogram_tester;
- Study study = CreateValidStudy();
- study.mutable_filter()->set_min_os_version("0.*.0");
- ProcessedStudy processed_study;
- EXPECT_FALSE(processed_study.Init(&study, false));
- histogram_tester.ExpectUniqueSample(kInvalidStudyReasonHistogram,
- InvalidStudyReason::kInvalidMinOsVersion,
- 1);
- }
- // Verifies that a study with an invalid max OS version filter is invalid.
- TEST(ProcessedStudyTest, InitInvalidMaxOsVersion) {
- base::HistogramTester histogram_tester;
- Study study = CreateValidStudy();
- study.mutable_filter()->set_max_os_version("\001\000\000\003");
- ProcessedStudy processed_study;
- EXPECT_FALSE(processed_study.Init(&study, false));
- histogram_tester.ExpectUniqueSample(kInvalidStudyReasonHistogram,
- InvalidStudyReason::kInvalidMaxOsVersion,
- 1);
- }
- // Verifies that a study with a blank study name is invalid.
- TEST(ProcessedStudyTest, InitBlankStudyName) {
- base::HistogramTester histogram_tester;
- Study study = CreateValidStudy();
- study.set_name("");
- ProcessedStudy processed_study;
- EXPECT_FALSE(processed_study.Init(&study, false));
- histogram_tester.ExpectUniqueSample(kInvalidStudyReasonHistogram,
- InvalidStudyReason::kBlankStudyName, 1);
- }
- // Verifies that a study with an experiment that has no name is invalid.
- TEST(ProcessedStudyTest, InitMissingExperimentName) {
- base::HistogramTester histogram_tester;
- Study study = CreateValidStudy();
- AddExperiment("", 0, &study);
- ProcessedStudy processed_study;
- EXPECT_FALSE(processed_study.Init(&study, false));
- histogram_tester.ExpectUniqueSample(
- kInvalidStudyReasonHistogram, InvalidStudyReason::kMissingExperimentName,
- 1);
- }
- // Verifies that a study with multiple experiments that are named the same is
- // invalid.
- TEST(ProcessedStudyTest, InitRepeatedExperimentName) {
- base::HistogramTester histogram_tester;
- Study study = CreateValidStudy();
- AddExperiment("Group", 0, &study);
- AddExperiment("Group", 0, &study);
- ProcessedStudy processed_study;
- EXPECT_FALSE(processed_study.Init(&study, false));
- histogram_tester.ExpectUniqueSample(
- kInvalidStudyReasonHistogram, InvalidStudyReason::kRepeatedExperimentName,
- 1);
- }
- // Verifies that a study with an experiment that specified both a trigger and
- // non-trigger GWS id is invalid.
- TEST(ProcessedStudyTest, InitTriggerAndNonTriggerExperimentId) {
- base::HistogramTester histogram_tester;
- Study study = CreateValidStudy();
- Study::Experiment* experiment = AddExperiment("Group", 0, &study);
- experiment->set_google_web_experiment_id(123);
- experiment->set_google_web_trigger_experiment_id(123);
- ProcessedStudy processed_study;
- EXPECT_FALSE(processed_study.Init(&study, false));
- histogram_tester.ExpectUniqueSample(
- kInvalidStudyReasonHistogram,
- InvalidStudyReason::kTriggerAndNonTriggerExperimentId, 1);
- }
- // Verifies that a study with an experiment that has a probability over the
- // maximum is invalid.
- TEST(ProcessedStudyTest, InitExperimentProbabilityOverflow) {
- base::HistogramTester histogram_tester;
- Study study = CreateStudy("Study");
- AddExperiment("Group", kMaxProbabilityValue + 1, &study);
- ProcessedStudy processed_study;
- EXPECT_FALSE(processed_study.Init(&study, false));
- histogram_tester.ExpectUniqueSample(
- kInvalidStudyReasonHistogram,
- InvalidStudyReason::kExperimentProbabilityOverflow, 1);
- }
- // Verifies that a study with groups whose total probability is over the maximum
- // is invalid.
- TEST(ProcessedStudyTest, InitTotalProbabilityOverflow) {
- base::HistogramTester histogram_tester;
- Study study = CreateStudy("Study");
- AddExperiment("Group1", kMaxProbabilityValue, &study);
- AddExperiment("Group2", 1, &study);
- ProcessedStudy processed_study;
- EXPECT_FALSE(processed_study.Init(&study, false));
- histogram_tester.ExpectUniqueSample(
- kInvalidStudyReasonHistogram,
- InvalidStudyReason::kTotalProbabilityOverflow, 1);
- }
- // Verifies that a study that specifies a default experiment name but does not
- // contain an experiment with that name is invalid.
- TEST(ProcessedStudyTest, InitMissingDefaultExperimentInList) {
- base::HistogramTester histogram_tester;
- Study study = CreateValidStudy();
- study.set_default_experiment_name("NonExistentGroup");
- ProcessedStudy processed_study;
- EXPECT_FALSE(processed_study.Init(&study, false));
- histogram_tester.ExpectUniqueSample(
- kInvalidStudyReasonHistogram,
- InvalidStudyReason::kMissingDefaultExperimentInList, 1);
- }
- TEST(ProcessedStudyTest, ValidateStudy) {
- Study study;
- study.set_name("study");
- study.set_default_experiment_name("def");
- AddExperiment("abc", 100, &study);
- Study::Experiment* default_group = AddExperiment("def", 200, &study);
- ProcessedStudy processed_study;
- EXPECT_TRUE(processed_study.Init(&study, false));
- EXPECT_EQ(300, processed_study.total_probability());
- EXPECT_FALSE(processed_study.all_assignments_to_one_group());
- // Min version checks.
- study.mutable_filter()->set_min_version("1.2.3.*");
- EXPECT_TRUE(processed_study.Init(&study, false));
- study.mutable_filter()->set_min_version("1.*.3");
- EXPECT_FALSE(processed_study.Init(&study, false));
- study.mutable_filter()->set_min_version("1.2.3");
- EXPECT_TRUE(processed_study.Init(&study, false));
- // Max version checks.
- study.mutable_filter()->set_max_version("2.3.4.*");
- EXPECT_TRUE(processed_study.Init(&study, false));
- study.mutable_filter()->set_max_version("*.3");
- EXPECT_FALSE(processed_study.Init(&study, false));
- study.mutable_filter()->set_max_version("2.3.4");
- EXPECT_TRUE(processed_study.Init(&study, false));
- // A blank default study is allowed.
- study.clear_default_experiment_name();
- EXPECT_TRUE(processed_study.Init(&study, false));
- study.set_default_experiment_name("xyz");
- EXPECT_FALSE(processed_study.Init(&study, false));
- study.set_default_experiment_name("def");
- default_group->clear_name();
- EXPECT_FALSE(processed_study.Init(&study, false));
- default_group->set_name("def");
- EXPECT_TRUE(processed_study.Init(&study, false));
- Study::Experiment* repeated_group = study.add_experiment();
- repeated_group->set_name("abc");
- repeated_group->set_probability_weight(1);
- EXPECT_FALSE(processed_study.Init(&study, false));
- }
- TEST(ProcessedStudyTest, ValidateStudyWithAssociatedFeatures) {
- Study study;
- study.set_name("study");
- study.set_default_experiment_name("def");
- Study::Experiment* exp1 = AddExperiment("exp1", 100, &study);
- Study::Experiment* exp2 = AddExperiment("exp2", 100, &study);
- Study::Experiment* exp3 = AddExperiment("exp3", 100, &study);
- AddExperiment("def", 100, &study);
- ProcessedStudy processed_study;
- EXPECT_TRUE(processed_study.Init(&study, false));
- EXPECT_EQ(400, processed_study.total_probability());
- EXPECT_THAT(processed_study.associated_features(), ::testing::IsEmpty());
- const char kFeature1Name[] = "Feature1";
- const char kFeature2Name[] = "Feature2";
- exp1->mutable_feature_association()->add_enable_feature(kFeature1Name);
- EXPECT_TRUE(processed_study.Init(&study, false));
- EXPECT_THAT(processed_study.associated_features(),
- ::testing::ElementsAre(kFeature1Name));
- exp1->clear_feature_association();
- exp1->mutable_feature_association()->add_enable_feature(kFeature1Name);
- exp1->mutable_feature_association()->add_enable_feature(kFeature2Name);
- EXPECT_TRUE(processed_study.Init(&study, false));
- // Since there's multiple different features, |associated_features| should now
- // contain them all.
- EXPECT_THAT(processed_study.associated_features(),
- ::testing::ElementsAre(kFeature1Name, kFeature2Name));
- exp1->clear_feature_association();
- exp1->mutable_feature_association()->add_enable_feature(kFeature1Name);
- exp2->mutable_feature_association()->add_enable_feature(kFeature1Name);
- exp3->mutable_feature_association()->add_disable_feature(kFeature1Name);
- EXPECT_TRUE(processed_study.Init(&study, false));
- EXPECT_THAT(processed_study.associated_features(),
- ::testing::ElementsAre(kFeature1Name));
- // Setting a different feature name on exp2 should cause |associated_features|
- // to contain both feature names.
- exp2->mutable_feature_association()->set_enable_feature(0, kFeature2Name);
- EXPECT_TRUE(processed_study.Init(&study, false));
- EXPECT_THAT(processed_study.associated_features(),
- ::testing::ElementsAre(kFeature1Name, kFeature2Name));
- // Setting a different activation type should result in empty
- // |associated_features|.
- study.set_activation_type(Study::ACTIVATE_ON_STARTUP);
- EXPECT_TRUE(processed_study.Init(&study, false));
- EXPECT_THAT(processed_study.associated_features(), ::testing::IsEmpty());
- }
- TEST(ProcessedStudyTest, ProcessedStudyAllAssignmentsToOneGroup) {
- Study study;
- study.set_name("study1");
- study.set_default_experiment_name("def");
- AddExperiment("def", 100, &study);
- ProcessedStudy processed_study;
- EXPECT_TRUE(processed_study.Init(&study, false));
- EXPECT_TRUE(processed_study.all_assignments_to_one_group());
- AddExperiment("abc", 0, &study);
- AddExperiment("flag", 0, &study)->set_forcing_flag("flag_test1");
- EXPECT_TRUE(processed_study.Init(&study, false));
- EXPECT_TRUE(processed_study.all_assignments_to_one_group());
- AddExperiment("xyz", 1, &study);
- EXPECT_TRUE(processed_study.Init(&study, false));
- EXPECT_FALSE(processed_study.all_assignments_to_one_group());
- // Try with default group and first group being at 0.
- Study study2;
- study2.set_name("study2");
- study2.set_default_experiment_name("def");
- AddExperiment("def", 0, &study2);
- AddExperiment("xyz", 34, &study2);
- EXPECT_TRUE(processed_study.Init(&study2, false));
- EXPECT_TRUE(processed_study.all_assignments_to_one_group());
- AddExperiment("abc", 12, &study2);
- EXPECT_TRUE(processed_study.Init(&study2, false));
- EXPECT_FALSE(processed_study.all_assignments_to_one_group());
- }
- } // namespace variations
|