summaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorsdefresne <sdefresne@chromium.org>2015-08-14 08:47:00 -0700
committerCommit bot <commit-bot@chromium.org>2015-08-14 15:47:37 +0000
commita59146f71f643c2cdd9465d6c1b11f052bd793e3 (patch)
tree71fbe8fa86fb2938d2468e41b3b1c92bdfcb5361 /components
parentd6b82982c568c32e254ccb43a7c4e30e9de7219d (diff)
downloadchromium_src-a59146f71f643c2cdd9465d6c1b11f052bd793e3.zip
chromium_src-a59146f71f643c2cdd9465d6c1b11f052bd793e3.tar.gz
chromium_src-a59146f71f643c2cdd9465d6c1b11f052bd793e3.tar.bz2
Componentize chrome/common/experiment_labels.{cc,h} and tests.
Move both chrome/common/experiment_labels.{cc,h} and the corresponding unit tests into components/variations as they only depends on //base and the component. BUG=520070 Review URL: https://codereview.chromium.org/1290173004 Cr-Commit-Position: refs/heads/master@{#343391}
Diffstat (limited to 'components')
-rw-r--r--components/components_tests.gyp1
-rw-r--r--components/variations.gypi2
-rw-r--r--components/variations/BUILD.gn3
-rw-r--r--components/variations/experiment_labels.cc116
-rw-r--r--components/variations/experiment_labels.h33
-rw-r--r--components/variations/experiment_labels_unittest.cc194
6 files changed, 349 insertions, 0 deletions
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index 53d86ab..27b4299 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -655,6 +655,7 @@
'variations/active_field_trials_unittest.cc',
'variations/caching_permuted_entropy_provider_unittest.cc',
'variations/entropy_provider_unittest.cc',
+ 'variations/experiment_labels_unittest.cc',
'variations/metrics_util_unittest.cc',
'variations/net/variations_http_header_provider_unittest.cc',
'variations/study_filtering_unittest.cc',
diff --git a/components/variations.gypi b/components/variations.gypi
index 73a5e88..6fd429c 100644
--- a/components/variations.gypi
+++ b/components/variations.gypi
@@ -30,6 +30,8 @@
'variations/caching_permuted_entropy_provider.h',
'variations/entropy_provider.cc',
'variations/entropy_provider.h',
+ 'variations/experiment_labels.cc',
+ 'variations/experiment_labels.h',
'variations/metrics_util.cc',
'variations/metrics_util.h',
'variations/pref_names.cc',
diff --git a/components/variations/BUILD.gn b/components/variations/BUILD.gn
index 5ceed31..e471d95 100644
--- a/components/variations/BUILD.gn
+++ b/components/variations/BUILD.gn
@@ -18,6 +18,8 @@ source_set("variations") {
"caching_permuted_entropy_provider.h",
"entropy_provider.cc",
"entropy_provider.h",
+ "experiment_labels.cc",
+ "experiment_labels.h",
"metrics_util.cc",
"metrics_util.h",
"pref_names.cc",
@@ -70,6 +72,7 @@ source_set("unit_tests") {
"active_field_trials_unittest.cc",
"caching_permuted_entropy_provider_unittest.cc",
"entropy_provider_unittest.cc",
+ "experiment_labels_unittest.cc",
"metrics_util_unittest.cc",
"net/variations_http_header_provider_unittest.cc",
"study_filtering_unittest.cc",
diff --git a/components/variations/experiment_labels.cc b/components/variations/experiment_labels.cc
new file mode 100644
index 0000000..3f12cd8
--- /dev/null
+++ b/components/variations/experiment_labels.cc
@@ -0,0 +1,116 @@
+// Copyright 2014 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/experiment_labels.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/variations/variations_associated_data.h"
+#include "components/variations/variations_experiment_util.h"
+
+namespace chrome_variations {
+
+namespace {
+
+const char kVariationPrefix[] = "CrVar";
+
+// This method builds a single experiment label for a Chrome Variation,
+// including a timestamp that is a year in the future from |current_time|. Since
+// multiple headers can be transmitted, |count| is a number that is appended
+// after the label key to differentiate the labels.
+base::string16 CreateSingleExperimentLabel(int count,
+ variations::VariationID id,
+ const base::Time& current_time) {
+ // Build the parts separately so they can be validated.
+ const base::string16 key =
+ base::ASCIIToUTF16(kVariationPrefix) + base::IntToString16(count);
+ DCHECK_LE(key.size(), 8U);
+ const base::string16 value = base::IntToString16(id);
+ DCHECK_LE(value.size(), 8U);
+ base::string16 label(key);
+ label += base::ASCIIToUTF16("=");
+ label += value;
+ label += base::ASCIIToUTF16("|");
+ label += variations::BuildExperimentDateString(current_time);
+ return label;
+}
+
+} // namespace
+
+base::string16 BuildGoogleUpdateExperimentLabel(
+ const base::FieldTrial::ActiveGroups& active_groups) {
+ base::string16 experiment_labels;
+ int counter = 0;
+
+ const base::Time current_time(base::Time::Now());
+
+ // Find all currently active VariationIDs associated with Google Update.
+ for (base::FieldTrial::ActiveGroups::const_iterator it =
+ active_groups.begin(); it != active_groups.end(); ++it) {
+ const variations::VariationID id =
+ variations::GetGoogleVariationID(variations::GOOGLE_UPDATE_SERVICE,
+ it->trial_name, it->group_name);
+
+ if (id == variations::EMPTY_ID)
+ continue;
+
+ if (!experiment_labels.empty())
+ experiment_labels += variations::kExperimentLabelSeparator;
+ experiment_labels += CreateSingleExperimentLabel(++counter, id,
+ current_time);
+ }
+
+ return experiment_labels;
+}
+
+base::string16 ExtractNonVariationLabels(const base::string16& labels) {
+ // First, split everything by the label separator.
+ std::vector<base::StringPiece16> entries = base::SplitStringPiece(
+ labels, base::StringPiece16(&variations::kExperimentLabelSeparator, 1),
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+ // For each label, keep the ones that do not look like a Variations label.
+ base::string16 non_variation_labels;
+ for (const base::StringPiece16& entry : entries) {
+ if (entry.empty() ||
+ base::StartsWith(entry,
+ base::ASCIIToUTF16(kVariationPrefix),
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ continue;
+ }
+
+ // Dump the whole thing, including the timestamp.
+ if (!non_variation_labels.empty())
+ non_variation_labels += variations::kExperimentLabelSeparator;
+ entry.AppendToString(&non_variation_labels);
+ }
+
+ return non_variation_labels;
+}
+
+base::string16 CombineExperimentLabels(const base::string16& variation_labels,
+ const base::string16& other_labels) {
+ base::StringPiece16 separator(&variations::kExperimentLabelSeparator, 1);
+ DCHECK(!base::StartsWith(variation_labels, separator,
+ base::CompareCase::SENSITIVE));
+ DCHECK(!base::EndsWith(variation_labels, separator,
+ base::CompareCase::SENSITIVE));
+ DCHECK(!base::StartsWith(other_labels, separator,
+ base::CompareCase::SENSITIVE));
+ DCHECK(!base::EndsWith(other_labels, separator,
+ base::CompareCase::SENSITIVE));
+ // Note that if either label is empty, a separator is not necessary.
+ base::string16 combined_labels = other_labels;
+ if (!other_labels.empty() && !variation_labels.empty())
+ combined_labels += variations::kExperimentLabelSeparator;
+ combined_labels += variation_labels;
+ return combined_labels;
+}
+
+} // namespace chrome_variations
diff --git a/components/variations/experiment_labels.h b/components/variations/experiment_labels.h
new file mode 100644
index 0000000..58bf7a6
--- /dev/null
+++ b/components/variations/experiment_labels.h
@@ -0,0 +1,33 @@
+// Copyright 2014 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.
+
+#ifndef COMPONENTS_VARIATIONS_EXPERIMENT_LABELS_H_
+#define COMPONENTS_VARIATIONS_EXPERIMENT_LABELS_H_
+
+#include "base/metrics/field_trial.h"
+#include "base/strings/string16.h"
+
+namespace chrome_variations {
+
+// Takes the list of active groups and builds the label for the ones that have
+// Google Update VariationID associated with them. This will return an empty
+// string if there are no such groups.
+base::string16 BuildGoogleUpdateExperimentLabel(
+ const base::FieldTrial::ActiveGroups& active_groups);
+
+// Creates a final combined experiment labels string with |variation_labels|
+// and |other_labels|, appropriately appending a separator based on their
+// contents. It is assumed that |variation_labels| and |other_labels| do not
+// have leading or trailing separators.
+base::string16 CombineExperimentLabels(const base::string16& variation_labels,
+ const base::string16& other_labels);
+
+// Takes the value of experiment_labels from the registry and returns a valid
+// experiment_labels string value containing only the labels that are not
+// associated with Chrome Variations.
+base::string16 ExtractNonVariationLabels(const base::string16& labels);
+
+} // namespace chrome_variations
+
+#endif // COMPONENTS_VARIATIONS_EXPERIMENT_LABELS_H_
diff --git a/components/variations/experiment_labels_unittest.cc b/components/variations/experiment_labels_unittest.cc
new file mode 100644
index 0000000..1f3379d
--- /dev/null
+++ b/components/variations/experiment_labels_unittest.cc
@@ -0,0 +1,194 @@
+// Copyright 2014 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/experiment_labels.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/metrics/field_trial.h"
+#include "base/strings/string_split.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/variations/variations_associated_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chrome_variations {
+
+TEST(ExperimentLabelsTest, BuildGoogleUpdateExperimentLabel) {
+ const variations::VariationID TEST_VALUE_A = 3300200;
+ const variations::VariationID TEST_VALUE_B = 3300201;
+ const variations::VariationID TEST_VALUE_C = 3300202;
+ const variations::VariationID TEST_VALUE_D = 3300203;
+
+ struct {
+ const char* active_group_pairs;
+ const char* expected_ids;
+ } test_cases[] = {
+ // Empty group.
+ {"", ""},
+ // Group of 1.
+ {"FieldTrialA#Default", "3300200"},
+ // Group of 1, doesn't have an associated ID.
+ {"FieldTrialA#DoesNotExist", ""},
+ // Group of 3.
+ {"FieldTrialA#Default#FieldTrialB#Group1#FieldTrialC#Default",
+ "3300200#3300201#3300202"},
+ // Group of 3, one doesn't have an associated ID.
+ {"FieldTrialA#Default#FieldTrialB#DoesNotExist#FieldTrialC#Default",
+ "3300200#3300202"},
+ // Group of 3, all three don't have an associated ID.
+ {"FieldTrialX#Default#FieldTrialB#DoesNotExist#FieldTrialC#Default",
+ "3300202"},
+ };
+
+ // Register a few VariationIDs.
+ AssociateGoogleVariationID(variations::GOOGLE_UPDATE_SERVICE, "FieldTrialA",
+ "Default", TEST_VALUE_A);
+ AssociateGoogleVariationID(variations::GOOGLE_UPDATE_SERVICE, "FieldTrialB",
+ "Group1", TEST_VALUE_B);
+ AssociateGoogleVariationID(variations::GOOGLE_UPDATE_SERVICE, "FieldTrialC",
+ "Default", TEST_VALUE_C);
+ AssociateGoogleVariationID(variations::GOOGLE_UPDATE_SERVICE, "FieldTrialD",
+ "Default", TEST_VALUE_D); // Not actually used.
+
+ for (size_t i = 0; i < arraysize(test_cases); ++i) {
+ // Parse the input groups.
+ base::FieldTrial::ActiveGroups groups;
+ std::vector<std::string> group_data = base::SplitString(
+ test_cases[i].active_group_pairs, "#",
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+ ASSERT_EQ(0U, group_data.size() % 2);
+ for (size_t j = 0; j < group_data.size(); j += 2) {
+ base::FieldTrial::ActiveGroup group;
+ group.trial_name = group_data[j];
+ group.group_name = group_data[j + 1];
+ groups.push_back(group);
+ }
+
+ // Parse the expected output.
+ std::vector<std::string> expected_ids_list = base::SplitString(
+ test_cases[i].expected_ids, "#",
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+ std::string experiment_labels_string = base::UTF16ToUTF8(
+ BuildGoogleUpdateExperimentLabel(groups));
+
+ // Split the VariationIDs from the labels for verification below.
+ std::set<std::string> parsed_ids;
+ for (const std::string& label : base::SplitString(
+ experiment_labels_string, ";",
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
+ // The ID is precisely between the '=' and '|' characters in each label.
+ size_t index_of_equals = label.find('=');
+ size_t index_of_pipe = label.find('|');
+ ASSERT_NE(std::string::npos, index_of_equals);
+ ASSERT_NE(std::string::npos, index_of_pipe);
+ ASSERT_GT(index_of_pipe, index_of_equals);
+ parsed_ids.insert(label.substr(index_of_equals + 1,
+ index_of_pipe - index_of_equals - 1));
+ }
+
+ // Verify that the resulting string contains each of the expected labels,
+ // and nothing more. Note that the date is stripped out and ignored.
+ for (std::vector<std::string>::const_iterator it =
+ expected_ids_list.begin(); it != expected_ids_list.end(); ++it) {
+ std::set<std::string>::iterator it2 = parsed_ids.find(*it);
+ EXPECT_TRUE(parsed_ids.end() != it2);
+ parsed_ids.erase(it2);
+ }
+ EXPECT_TRUE(parsed_ids.empty());
+ } // for
+}
+
+TEST(ExperimentLabelsTest, CombineExperimentLabels) {
+ struct {
+ const char* variations_labels;
+ const char* other_labels;
+ const char* expected_label;
+ } test_cases[] = {
+ {"A=B|Tue, 21 Jan 2014 15:30:21 GMT",
+ "C=D|Tue, 21 Jan 2014 15:30:21 GMT",
+ "C=D|Tue, 21 Jan 2014 15:30:21 GMT;A=B|Tue, 21 Jan 2014 15:30:21 GMT"},
+ {"A=B|Tue, 21 Jan 2014 15:30:21 GMT",
+ "",
+ "A=B|Tue, 21 Jan 2014 15:30:21 GMT"},
+ {"",
+ "A=B|Tue, 21 Jan 2014 15:30:21 GMT",
+ "A=B|Tue, 21 Jan 2014 15:30:21 GMT"},
+ {"A=B|Tue, 21 Jan 2014 15:30:21 GMT;C=D|Tue, 21 Jan 2014 15:30:21 GMT",
+ "P=Q|Tue, 21 Jan 2014 15:30:21 GMT;X=Y|Tue, 21 Jan 2014 15:30:21 GMT",
+ "P=Q|Tue, 21 Jan 2014 15:30:21 GMT;X=Y|Tue, 21 Jan 2014 15:30:21 GMT;"
+ "A=B|Tue, 21 Jan 2014 15:30:21 GMT;C=D|Tue, 21 Jan 2014 15:30:21 GMT"},
+ {"",
+ "",
+ ""},
+ };
+
+ for (size_t i = 0; i < arraysize(test_cases); ++i) {
+ std::string result = base::UTF16ToUTF8(CombineExperimentLabels(
+ base::ASCIIToUTF16(test_cases[i].variations_labels),
+ base::ASCIIToUTF16(test_cases[i].other_labels)));
+ EXPECT_EQ(test_cases[i].expected_label, result);
+ }
+}
+
+TEST(ExperimentLabelsTest, ExtractNonVariationLabels) {
+ struct {
+ const char* input_label;
+ const char* expected_output;
+ } test_cases[] = {
+ // Empty
+ {"", ""},
+ // One
+ {"gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT",
+ "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
+ // Three
+ {"CrVar1=123|Tue, 21 Jan 2014 15:30:21 GMT;"
+ "experiment1=456|Tue, 21 Jan 2014 15:30:21 GMT;"
+ "experiment2=789|Tue, 21 Jan 2014 15:30:21 GMT;"
+ "CrVar1=123|Tue, 21 Jan 2014 15:30:21 GMT",
+ "experiment1=456|Tue, 21 Jan 2014 15:30:21 GMT;"
+ "experiment2=789|Tue, 21 Jan 2014 15:30:21 GMT"},
+ // One and one Variation
+ {"gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT;"
+ "CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT",
+ "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
+ // One and one Variation, flipped
+ {"CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT;"
+ "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT",
+ "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
+ // Sandwiched
+ {"CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT;"
+ "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT;"
+ "CrVar2=3310003|Tue, 21 Jan 2014 15:30:21 GMT;"
+ "CrVar3=3310004|Tue, 21 Jan 2014 15:30:21 GMT",
+ "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
+ // Only Variations
+ {"CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT;"
+ "CrVar2=3310003|Tue, 21 Jan 2014 15:30:21 GMT;"
+ "CrVar3=3310004|Tue, 21 Jan 2014 15:30:21 GMT",
+ ""},
+ // Empty values
+ {"gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT;"
+ "CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT",
+ "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
+ // Trailing semicolon
+ {"gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT;"
+ "CrVar1=3310002|Tue, 21 Jan 2014 15:30:21 GMT;", // Note the semi here.
+ "gcapi_brand=123|Tue, 21 Jan 2014 15:30:21 GMT"},
+ // Semis
+ {";;;;", ""},
+ };
+
+ for (size_t i = 0; i < arraysize(test_cases); ++i) {
+ std::string non_variation_labels = base::UTF16ToUTF8(
+ ExtractNonVariationLabels(
+ base::ASCIIToUTF16(test_cases[i].input_label)));
+ EXPECT_EQ(test_cases[i].expected_output, non_variation_labels);
+ }
+}
+
+} // namespace chrome_variations