• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 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 "components/cronet/android/cronet_base_feature.h"
6 #include "base/feature_list.h"
7 #include "base/metrics/field_trial_params.h"
8 #include "base/test/scoped_feature_list.h"
9 #include "testing/gmock/include/gmock/gmock.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 
12 namespace cronet {
13 namespace {
14 
15 using ::org::chromium::net::httpflags::BaseFeatureOverrides;
16 using ::testing::IsEmpty;
17 using ::testing::Pair;
18 using ::testing::UnorderedElementsAre;
19 
20 // base::Feature uses a caching mechanism where repeated
21 // `base::FeatureList::IsEnabled()` calls do not actually check the overrides
22 // list and instead return the previously returned value according to the cache
23 // state in the base::Feature itself. See `base::Feature::cached_value` and
24 // `base::FeatureList::caching_context_`.
25 //
26 // This caching mechanism breaks isolation between our unit tests, so we need
27 // to flush the cache between tests. We can do that by making sure that the
28 // feature list caching context (basically, a cache generation counter) is set
29 // to a value that is different from the one it had in the last
30 // base::FeatureList::IsEnabled() call. This will cause a cache miss on the next
31 // base::FeatureList::IsEnabled() call. If we don't do anything this won't be
32 // the case, as the code under test always installs a base::FeatureList with a
33 // caching context value of 1 (which is the default).
34 //
35 // base::test::ScopedFeatureList uses the exact same approach internally, but we
36 // can't piggy-back on that because we need to change the caching context of the
37 // base::FeatureList that is instantiated by the code under test, not the one
38 // that base::test::ScopedFeatureList installs.
FlushBaseFeatureCache()39 void FlushBaseFeatureCache() {
40   auto* const feature_list = base::FeatureList::GetInstance();
41   if (feature_list == nullptr) {
42     return;
43   }
44   // Start at 32768 to avoid collisions with ScopedFeatureList's own
45   // `g_current_caching_context` (which starts at 1).
46   static uint16_t g_feature_list_caching_context = 32768;
47   feature_list->SetCachingContextForTesting(g_feature_list_caching_context++);
48 }
49 
50 constexpr char kTestFeatureDisabledByDefaultName[] =
51     "CronetBaseFeatureTestFeatureDisabledByDefault";
52 BASE_FEATURE(kTestFeatureDisabledByDefault,
53              kTestFeatureDisabledByDefaultName,
54              base::FEATURE_DISABLED_BY_DEFAULT);
55 
56 constexpr char kTestFeatureEnabledByDefaultName[] =
57     "CronetBaseFeatureTestFeatureEnabledByDefault";
58 BASE_FEATURE(kTestFeatureEnabledByDefault,
59              kTestFeatureEnabledByDefaultName,
60              base::FEATURE_ENABLED_BY_DEFAULT);
61 
62 constexpr char kTestFeatureWithParamsName[] =
63     "CronetBaseFeatureTestFeatureWithParams";
64 BASE_FEATURE(kTestFeatureWithParams,
65              kTestFeatureWithParamsName,
66              base::FEATURE_DISABLED_BY_DEFAULT);
67 constexpr char kTestParamName[] = "test_param";
68 constexpr char kTestParamDefaultValue[] = "test_param_default_value";
69 constexpr base::FeatureParam<std::string> kTestParam{
70     &kTestFeatureWithParams, kTestParamName, kTestParamDefaultValue};
71 
TEST(ApplyBaseFeatureOverrides,NoOpOnEmptyOverrides)72 TEST(ApplyBaseFeatureOverrides, NoOpOnEmptyOverrides) {
73   base::test::ScopedFeatureList scoped_feature_list;
74   scoped_feature_list.InitWithNullFeatureAndFieldTrialLists();
75   ApplyBaseFeatureOverrides(BaseFeatureOverrides());
76   FlushBaseFeatureCache();
77   EXPECT_FALSE(base::FeatureList::IsEnabled(kTestFeatureDisabledByDefault));
78   EXPECT_TRUE(base::FeatureList::IsEnabled(kTestFeatureEnabledByDefault));
79 }
80 
TEST(ApplyBaseFeatureOverrides,OverridesFeatureToEnabled)81 TEST(ApplyBaseFeatureOverrides, OverridesFeatureToEnabled) {
82   base::test::ScopedFeatureList scoped_feature_list;
83   scoped_feature_list.InitWithNullFeatureAndFieldTrialLists();
84   BaseFeatureOverrides overrides;
85   (*overrides.mutable_feature_states())[kTestFeatureDisabledByDefaultName]
86       .set_enabled(true);
87   ApplyBaseFeatureOverrides(overrides);
88   FlushBaseFeatureCache();
89   EXPECT_TRUE(base::FeatureList::IsEnabled(kTestFeatureDisabledByDefault));
90 }
91 
TEST(ApplyBaseFeatureOverrides,OverridesFeatureToDisabled)92 TEST(ApplyBaseFeatureOverrides, OverridesFeatureToDisabled) {
93   base::test::ScopedFeatureList scoped_feature_list;
94   scoped_feature_list.InitWithNullFeatureAndFieldTrialLists();
95   BaseFeatureOverrides overrides;
96   (*overrides.mutable_feature_states())[kTestFeatureEnabledByDefaultName]
97       .set_enabled(false);
98   ApplyBaseFeatureOverrides(overrides);
99   FlushBaseFeatureCache();
100   EXPECT_FALSE(base::FeatureList::IsEnabled(kTestFeatureEnabledByDefault));
101 }
102 
TEST(ApplyBaseFeatureOverrides,DoesNotOverrideFeature)103 TEST(ApplyBaseFeatureOverrides, DoesNotOverrideFeature) {
104   base::test::ScopedFeatureList scoped_feature_list;
105   scoped_feature_list.InitWithNullFeatureAndFieldTrialLists();
106   BaseFeatureOverrides overrides;
107   (*overrides.mutable_feature_states())[kTestFeatureEnabledByDefaultName];
108   ApplyBaseFeatureOverrides(overrides);
109   FlushBaseFeatureCache();
110   EXPECT_TRUE(base::FeatureList::IsEnabled(kTestFeatureEnabledByDefault));
111 }
112 
TEST(ApplyBaseFeatureOverrides,NoOpIfBaseFeatureAlreadyInitialized)113 TEST(ApplyBaseFeatureOverrides, NoOpIfBaseFeatureAlreadyInitialized) {
114   base::test::ScopedFeatureList scoped_feature_list;
115   scoped_feature_list.InitWithEmptyFeatureAndFieldTrialLists();
116   BaseFeatureOverrides overrides;
117   (*overrides.mutable_feature_states())[kTestFeatureDisabledByDefaultName]
118       .set_enabled(true);
119   ApplyBaseFeatureOverrides(overrides);
120   FlushBaseFeatureCache();
121   EXPECT_FALSE(base::FeatureList::IsEnabled(kTestFeatureDisabledByDefault));
122 }
123 
TEST(ApplyBaseFeatureOverrides,DoesNotAssociateFeatureParamsIfNoParamsAreProvided)124 TEST(ApplyBaseFeatureOverrides,
125      DoesNotAssociateFeatureParamsIfNoParamsAreProvided) {
126   base::test::ScopedFeatureList scoped_feature_list;
127   scoped_feature_list.InitWithNullFeatureAndFieldTrialLists();
128   BaseFeatureOverrides overrides;
129   (*overrides.mutable_feature_states())[kTestFeatureWithParamsName].set_enabled(
130       true);
131   ApplyBaseFeatureOverrides(overrides);
132   FlushBaseFeatureCache();
133   base::FieldTrialParams params;
134   EXPECT_FALSE(
135       base::GetFieldTrialParamsByFeature(kTestFeatureWithParams, &params));
136   EXPECT_THAT(params, IsEmpty());
137   EXPECT_EQ(kTestParam.Get(), kTestParamDefaultValue);
138 }
139 
TEST(ApplyBaseFeatureOverrides,AssociatesFeatureParams)140 TEST(ApplyBaseFeatureOverrides, AssociatesFeatureParams) {
141   base::test::ScopedFeatureList scoped_feature_list;
142   scoped_feature_list.InitWithNullFeatureAndFieldTrialLists();
143   BaseFeatureOverrides overrides;
144   constexpr char kParamValue[] = "test_param_value";
145   auto& feature_state =
146       (*overrides.mutable_feature_states())[kTestFeatureWithParamsName];
147   feature_state.set_enabled(true);
148   (*feature_state.mutable_params())[kTestParamName] = kParamValue;
149   ApplyBaseFeatureOverrides(overrides);
150   FlushBaseFeatureCache();
151   base::FieldTrialParams params;
152   EXPECT_TRUE(
153       base::GetFieldTrialParamsByFeature(kTestFeatureWithParams, &params));
154   EXPECT_THAT(params, UnorderedElementsAre(Pair(kTestParamName, kParamValue)));
155   EXPECT_EQ(kTestParam.Get(), kParamValue);
156 }
157 
158 }  // namespace
159 }  // namespace cronet
160