Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(33)

Side by Side Diff: components/subresource_filter/core/browser/subresource_filter_features_unittest.cc

Issue 2838193002: Split the ScopedSubresourceFilterFeatureToggle into two helper classes. (Closed)
Patch Set: More polish. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/subresource_filter/core/browser/subresource_filter_features .h" 5 #include "components/subresource_filter/core/browser/subresource_filter_features .h"
6 6
7 #include <string> 7 #include <string>
8 8
9 #include "base/metrics/field_trial.h" 9 #include "base/metrics/field_trial.h"
10 #include "base/metrics/field_trial_params.h"
10 #include "components/subresource_filter/core/browser/subresource_filter_features _test_support.h" 11 #include "components/subresource_filter/core/browser/subresource_filter_features _test_support.h"
12 #include "components/variations/variations_associated_data.h"
11 #include "testing/gtest/include/gtest/gtest.h" 13 #include "testing/gtest/include/gtest/gtest.h"
12 14
13 namespace subresource_filter { 15 namespace subresource_filter {
16 namespace testing {
17
18 namespace {
19
20 constexpr const char kTestFieldTrialName[] = "FieldTrialNameShouldNotMatter";
21 constexpr const char kTestExperimentGroupName[] = "GroupNameShouldNotMatter";
22
23 class ScopedExperimentalStateToggle {
24 public:
25 ScopedExperimentalStateToggle(
26 base::FeatureList::OverrideState feature_state,
Charlie Harrison 2017/04/25 19:36:22 #include feature_list
engedy 2017/04/25 20:13:24 Done.
27 std::map<std::string, std::string> variation_params)
Charlie Harrison 2017/04/25 19:36:22 #include <map>
engedy 2017/04/25 20:13:24 Done.
28 : field_trial_list_(nullptr /* entropy_provider */),
29 scoped_configurator_(nullptr) {
30 EXPECT_TRUE(base::AssociateFieldTrialParams(
31 kTestFieldTrialName, kTestExperimentGroupName, variation_params));
32 base::FieldTrial* field_trial = base::FieldTrialList::CreateFieldTrial(
33 kTestFieldTrialName, kTestExperimentGroupName);
34
35 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
Charlie Harrison 2017/04/25 19:36:21 #include <memory>
Charlie Harrison 2017/04/25 19:36:21 can you use base::MakeUnique?
engedy 2017/04/25 20:13:25 Done.
engedy 2017/04/25 20:13:25 Done.
36 feature_list->RegisterFieldTrialOverride(
37 kSafeBrowsingSubresourceFilter.name, feature_state, field_trial);
38 scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
39 }
40
41 ~ScopedExperimentalStateToggle() {
42 variations::testing::ClearAllVariationParams();
43 }
44
45 private:
46 base::FieldTrialList field_trial_list_;
47
48 ScopedSubresourceFilterConfigurator scoped_configurator_;
49 base::test::ScopedFeatureList scoped_feature_list_;
50
51 DISALLOW_COPY_AND_ASSIGN(ScopedExperimentalStateToggle);
Charlie Harrison 2017/04/25 19:36:22 #include base/macros
engedy 2017/04/25 20:13:25 Done.
52 };
53
54 } // namespace
55 } // namespace testing
14 56
15 TEST(SubresourceFilterFeaturesTest, ActivationLevel) { 57 TEST(SubresourceFilterFeaturesTest, ActivationLevel) {
16 const struct { 58 const struct {
17 bool feature_enabled; 59 bool feature_enabled;
18 const char* activation_level_param; 60 const char* activation_level_param;
19 ActivationLevel expected_activation_level; 61 ActivationLevel expected_activation_level;
20 } kTestCases[] = {{false, "", ActivationLevel::DISABLED}, 62 } kTestCases[] = {{false, "", ActivationLevel::DISABLED},
21 {false, "disabled", ActivationLevel::DISABLED}, 63 {false, "disabled", ActivationLevel::DISABLED},
22 {false, "dryrun", ActivationLevel::DISABLED}, 64 {false, "dryrun", ActivationLevel::DISABLED},
23 {false, "enabled", ActivationLevel::DISABLED}, 65 {false, "enabled", ActivationLevel::DISABLED},
24 {false, "%$ garbage !%", ActivationLevel::DISABLED}, 66 {false, "%$ garbage !%", ActivationLevel::DISABLED},
25 {true, "", ActivationLevel::DISABLED}, 67 {true, "", ActivationLevel::DISABLED},
26 {true, "disable", ActivationLevel::DISABLED}, 68 {true, "disable", ActivationLevel::DISABLED},
27 {true, "Disable", ActivationLevel::DISABLED}, 69 {true, "Disable", ActivationLevel::DISABLED},
28 {true, "disabled", ActivationLevel::DISABLED}, 70 {true, "disabled", ActivationLevel::DISABLED},
29 {true, "%$ garbage !%", ActivationLevel::DISABLED}, 71 {true, "%$ garbage !%", ActivationLevel::DISABLED},
30 {true, kActivationLevelDryRun, ActivationLevel::DRYRUN}, 72 {true, kActivationLevelDryRun, ActivationLevel::DRYRUN},
31 {true, kActivationLevelEnabled, ActivationLevel::ENABLED}, 73 {true, kActivationLevelEnabled, ActivationLevel::ENABLED},
32 {true, "Enabled", ActivationLevel::ENABLED}}; 74 {true, "Enabled", ActivationLevel::ENABLED}};
33 75
34 for (const auto& test_case : kTestCases) { 76 for (const auto& test_case : kTestCases) {
35 SCOPED_TRACE(::testing::Message("Enabled = ") << test_case.feature_enabled); 77 SCOPED_TRACE(::testing::Message("Enabled = ") << test_case.feature_enabled);
36 SCOPED_TRACE(::testing::Message("ActivationLevelParam = \"") 78 SCOPED_TRACE(::testing::Message("ActivationLevelParam = \"")
37 << test_case.activation_level_param << "\""); 79 << test_case.activation_level_param << "\"");
38 80
39 base::FieldTrialList field_trial_list(nullptr /* entropy_provider */); 81 testing::ScopedExperimentalStateToggle scoped_experimental_state(
40 testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
41 test_case.feature_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE 82 test_case.feature_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE
42 : base::FeatureList::OVERRIDE_USE_DEFAULT, 83 : base::FeatureList::OVERRIDE_USE_DEFAULT,
43 test_case.activation_level_param, kActivationScopeNoSites); 84 {{kActivationLevelParameterName, test_case.activation_level_param},
85 {kActivationScopeParameterName, kActivationScopeNoSites}});
44 86
45 const auto active_configurations = GetActiveConfigurations(); 87 const auto active_configurations = GetActiveConfigurations();
46 const Configuration& actual_configuration = 88 const Configuration& actual_configuration =
47 active_configurations->the_one_and_only(); 89 active_configurations->the_one_and_only();
48 EXPECT_EQ(test_case.expected_activation_level, 90 EXPECT_EQ(test_case.expected_activation_level,
49 actual_configuration.activation_level); 91 actual_configuration.activation_level);
50 EXPECT_EQ(ActivationScope::NO_SITES, actual_configuration.activation_scope); 92 EXPECT_EQ(ActivationScope::NO_SITES, actual_configuration.activation_scope);
51 } 93 }
52 } 94 }
53 95
(...skipping 14 matching lines...) Expand all
68 {true, "no_sites", ActivationScope::NO_SITES}, 110 {true, "no_sites", ActivationScope::NO_SITES},
69 {true, "%$ garbage !%", ActivationScope::NO_SITES}, 111 {true, "%$ garbage !%", ActivationScope::NO_SITES},
70 {true, kActivationScopeAllSites, ActivationScope::ALL_SITES}, 112 {true, kActivationScopeAllSites, ActivationScope::ALL_SITES},
71 {true, kActivationScopeActivationList, ActivationScope::ACTIVATION_LIST}}; 113 {true, kActivationScopeActivationList, ActivationScope::ACTIVATION_LIST}};
72 114
73 for (const auto& test_case : kTestCases) { 115 for (const auto& test_case : kTestCases) {
74 SCOPED_TRACE(::testing::Message("Enabled = ") << test_case.feature_enabled); 116 SCOPED_TRACE(::testing::Message("Enabled = ") << test_case.feature_enabled);
75 SCOPED_TRACE(::testing::Message("ActivationScopeParam = \"") 117 SCOPED_TRACE(::testing::Message("ActivationScopeParam = \"")
76 << test_case.activation_scope_param << "\""); 118 << test_case.activation_scope_param << "\"");
77 119
78 base::FieldTrialList field_trial_list(nullptr /* entropy_provider */); 120 testing::ScopedExperimentalStateToggle scoped_experimental_state(
79 testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
80 test_case.feature_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE 121 test_case.feature_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE
81 : base::FeatureList::OVERRIDE_USE_DEFAULT, 122 : base::FeatureList::OVERRIDE_USE_DEFAULT,
82 kActivationLevelDisabled, test_case.activation_scope_param); 123 {{kActivationLevelParameterName, kActivationLevelDisabled},
124 {kActivationScopeParameterName, test_case.activation_scope_param}});
83 125
84 const auto active_configurations = GetActiveConfigurations(); 126 const auto active_configurations = GetActiveConfigurations();
85 const Configuration& actual_configuration = 127 const Configuration& actual_configuration =
86 active_configurations->the_one_and_only(); 128 active_configurations->the_one_and_only();
87 EXPECT_EQ(ActivationLevel::DISABLED, actual_configuration.activation_level); 129 EXPECT_EQ(ActivationLevel::DISABLED, actual_configuration.activation_level);
88 EXPECT_EQ(test_case.expected_activation_scope, 130 EXPECT_EQ(test_case.expected_activation_scope,
89 actual_configuration.activation_scope); 131 actual_configuration.activation_scope);
90 } 132 }
91 } 133 }
92 134
(...skipping 28 matching lines...) Expand all
121 {true, kActivationLevelEnabled, ActivationLevel::ENABLED, 163 {true, kActivationLevelEnabled, ActivationLevel::ENABLED,
122 kActivationScopeAllSites, ActivationScope::ALL_SITES}, 164 kActivationScopeAllSites, ActivationScope::ALL_SITES},
123 {true, kActivationLevelEnabled, ActivationLevel::ENABLED, 165 {true, kActivationLevelEnabled, ActivationLevel::ENABLED,
124 kActivationScopeActivationList, ActivationScope::ACTIVATION_LIST}, 166 kActivationScopeActivationList, ActivationScope::ACTIVATION_LIST},
125 {true, kActivationLevelEnabled, ActivationLevel::ENABLED, 167 {true, kActivationLevelEnabled, ActivationLevel::ENABLED,
126 kActivationScopeAllSites, ActivationScope::ALL_SITES}, 168 kActivationScopeAllSites, ActivationScope::ALL_SITES},
127 {false, kActivationLevelEnabled, ActivationLevel::DISABLED, 169 {false, kActivationLevelEnabled, ActivationLevel::DISABLED,
128 kActivationScopeAllSites, ActivationScope::NO_SITES}}; 170 kActivationScopeAllSites, ActivationScope::NO_SITES}};
129 171
130 for (const auto& test_case : kTestCases) { 172 for (const auto& test_case : kTestCases) {
131 base::FieldTrialList field_trial_list(nullptr /* entropy_provider */); 173 testing::ScopedExperimentalStateToggle scoped_experimental_state(
132 testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
133 test_case.feature_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE 174 test_case.feature_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE
134 : base::FeatureList::OVERRIDE_USE_DEFAULT, 175 : base::FeatureList::OVERRIDE_USE_DEFAULT,
135 test_case.activation_level_param, test_case.activation_scope_param); 176 {{kActivationLevelParameterName, test_case.activation_level_param},
177 {kActivationScopeParameterName, test_case.activation_scope_param}});
136 178
137 const auto active_configurations = GetActiveConfigurations(); 179 const auto active_configurations = GetActiveConfigurations();
138 const Configuration& actual_configuration = 180 const Configuration& actual_configuration =
139 active_configurations->the_one_and_only(); 181 active_configurations->the_one_and_only();
140 EXPECT_EQ(test_case.expected_activation_level, 182 EXPECT_EQ(test_case.expected_activation_level,
141 actual_configuration.activation_level); 183 actual_configuration.activation_level);
142 EXPECT_EQ(test_case.expected_activation_scope, 184 EXPECT_EQ(test_case.expected_activation_scope,
143 actual_configuration.activation_scope); 185 actual_configuration.activation_scope);
144 } 186 }
145 } 187 }
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
178 ActivationList::PHISHING_INTERSTITIAL}, 220 ActivationList::PHISHING_INTERSTITIAL},
179 {true, socEngCommaPhisingCommaGarbage.c_str(), 221 {true, socEngCommaPhisingCommaGarbage.c_str(),
180 ActivationList::PHISHING_INTERSTITIAL}, 222 ActivationList::PHISHING_INTERSTITIAL},
181 {true, "List1, List2", ActivationList::NONE}}; 223 {true, "List1, List2", ActivationList::NONE}};
182 224
183 for (const auto& test_case : kTestCases) { 225 for (const auto& test_case : kTestCases) {
184 SCOPED_TRACE(::testing::Message("Enabled = ") << test_case.feature_enabled); 226 SCOPED_TRACE(::testing::Message("Enabled = ") << test_case.feature_enabled);
185 SCOPED_TRACE(::testing::Message("ActivationListParam = \"") 227 SCOPED_TRACE(::testing::Message("ActivationListParam = \"")
186 << test_case.activation_list_param << "\""); 228 << test_case.activation_list_param << "\"");
187 229
188 base::FieldTrialList field_trial_list(nullptr /* entropy_provider */); 230 testing::ScopedExperimentalStateToggle scoped_experimental_state(
189 testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
190 test_case.feature_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE 231 test_case.feature_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE
191 : base::FeatureList::OVERRIDE_USE_DEFAULT, 232 : base::FeatureList::OVERRIDE_USE_DEFAULT,
192 kActivationLevelDisabled, kActivationScopeNoSites, 233 {{kActivationLevelParameterName, kActivationLevelDisabled},
193 test_case.activation_list_param); 234 {kActivationScopeParameterName, kActivationScopeNoSites},
235 {kActivationListsParameterName, test_case.activation_list_param}});
194 236
195 const auto active_configurations = GetActiveConfigurations(); 237 const auto active_configurations = GetActiveConfigurations();
196 const Configuration& actual_configuration = 238 const Configuration& actual_configuration =
197 active_configurations->the_one_and_only(); 239 active_configurations->the_one_and_only();
198 EXPECT_EQ(test_case.expected_activation_list, 240 EXPECT_EQ(test_case.expected_activation_list,
199 actual_configuration.activation_list); 241 actual_configuration.activation_list);
200 } 242 }
201 } 243 }
202 244
203 TEST(SubresourceFilterFeaturesTest, PerfMeasurementRate) { 245 TEST(SubresourceFilterFeaturesTest, PerfMeasurementRate) {
(...skipping 13 matching lines...) Expand all
217 {true, "1", 1}, 259 {true, "1", 1},
218 {true, "1.0", 1}, 260 {true, "1.0", 1},
219 {true, "0.333", 0.333}, 261 {true, "0.333", 0.333},
220 {true, "1e0", 1}}; 262 {true, "1e0", 1}};
221 263
222 for (const auto& test_case : kTestCases) { 264 for (const auto& test_case : kTestCases) {
223 SCOPED_TRACE(::testing::Message("Enabled = ") << test_case.feature_enabled); 265 SCOPED_TRACE(::testing::Message("Enabled = ") << test_case.feature_enabled);
224 SCOPED_TRACE(::testing::Message("PerfMeasurementParam = \"") 266 SCOPED_TRACE(::testing::Message("PerfMeasurementParam = \"")
225 << test_case.perf_measurement_param << "\""); 267 << test_case.perf_measurement_param << "\"");
226 268
227 base::FieldTrialList field_trial_list(nullptr /* entropy_provider */); 269 testing::ScopedExperimentalStateToggle scoped_experimental_state(
228 testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
229 test_case.feature_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE 270 test_case.feature_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE
230 : base::FeatureList::OVERRIDE_USE_DEFAULT, 271 : base::FeatureList::OVERRIDE_USE_DEFAULT,
231 {{kPerformanceMeasurementRateParameterName, 272 {{kPerformanceMeasurementRateParameterName,
232 test_case.perf_measurement_param}}); 273 test_case.perf_measurement_param}});
233 274
234 const auto active_configurations = GetActiveConfigurations(); 275 const auto active_configurations = GetActiveConfigurations();
235 const Configuration& actual_configuration = 276 const Configuration& actual_configuration =
236 active_configurations->the_one_and_only(); 277 active_configurations->the_one_and_only();
237 EXPECT_EQ(test_case.expected_perf_measurement_rate, 278 EXPECT_EQ(test_case.expected_perf_measurement_rate,
238 actual_configuration.performance_measurement_rate); 279 actual_configuration.performance_measurement_rate);
(...skipping 14 matching lines...) Expand all
253 {true, "invalid value", false}, 294 {true, "invalid value", false},
254 {true, "True", true}, 295 {true, "True", true},
255 {true, "TRUE", true}, 296 {true, "TRUE", true},
256 {true, "true", true}}; 297 {true, "true", true}};
257 298
258 for (const auto& test_case : kTestCases) { 299 for (const auto& test_case : kTestCases) {
259 SCOPED_TRACE(::testing::Message("Enabled = ") << test_case.feature_enabled); 300 SCOPED_TRACE(::testing::Message("Enabled = ") << test_case.feature_enabled);
260 SCOPED_TRACE(::testing::Message("SuppressNotificationsParam = \"") 301 SCOPED_TRACE(::testing::Message("SuppressNotificationsParam = \"")
261 << test_case.suppress_notifications_param << "\""); 302 << test_case.suppress_notifications_param << "\"");
262 303
263 base::FieldTrialList field_trial_list(nullptr /* entropy_provider */); 304 testing::ScopedExperimentalStateToggle scoped_experimental_state(
264 testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
265 test_case.feature_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE 305 test_case.feature_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE
266 : base::FeatureList::OVERRIDE_USE_DEFAULT, 306 : base::FeatureList::OVERRIDE_USE_DEFAULT,
267 {{kSuppressNotificationsParameterName, 307 {{kSuppressNotificationsParameterName,
268 test_case.suppress_notifications_param}}); 308 test_case.suppress_notifications_param}});
269 309
270 const auto active_configurations = GetActiveConfigurations(); 310 const auto active_configurations = GetActiveConfigurations();
271 const Configuration& actual_configuration = 311 const Configuration& actual_configuration =
272 active_configurations->the_one_and_only(); 312 active_configurations->the_one_and_only();
273 EXPECT_EQ(test_case.expected_suppress_notifications_value, 313 EXPECT_EQ(test_case.expected_suppress_notifications_value,
274 actual_configuration.should_suppress_notifications); 314 actual_configuration.should_suppress_notifications);
(...skipping 14 matching lines...) Expand all
289 {true, "invalid value", false}, 329 {true, "invalid value", false},
290 {true, "True", true}, 330 {true, "True", true},
291 {true, "TRUE", true}, 331 {true, "TRUE", true},
292 {true, "true", true}}; 332 {true, "true", true}};
293 333
294 for (const auto& test_case : kTestCases) { 334 for (const auto& test_case : kTestCases) {
295 SCOPED_TRACE(::testing::Message("Enabled = ") << test_case.feature_enabled); 335 SCOPED_TRACE(::testing::Message("Enabled = ") << test_case.feature_enabled);
296 SCOPED_TRACE(::testing::Message("WhitelistSiteOnReloadParam = \"") 336 SCOPED_TRACE(::testing::Message("WhitelistSiteOnReloadParam = \"")
297 << test_case.whitelist_site_on_reload_param << "\""); 337 << test_case.whitelist_site_on_reload_param << "\"");
298 338
299 base::FieldTrialList field_trial_list(nullptr /* entropy_provider */); 339 testing::ScopedExperimentalStateToggle scoped_experimental_state(
300 testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
301 test_case.feature_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE 340 test_case.feature_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE
302 : base::FeatureList::OVERRIDE_USE_DEFAULT, 341 : base::FeatureList::OVERRIDE_USE_DEFAULT,
303 {{kWhitelistSiteOnReloadParameterName, 342 {{kWhitelistSiteOnReloadParameterName,
304 test_case.whitelist_site_on_reload_param}}); 343 test_case.whitelist_site_on_reload_param}});
305 344
306 const auto active_configurations = GetActiveConfigurations(); 345 const auto active_configurations = GetActiveConfigurations();
307 const Configuration& actual_configuration = 346 const Configuration& actual_configuration =
308 active_configurations->the_one_and_only(); 347 active_configurations->the_one_and_only();
309 EXPECT_EQ(test_case.expected_whitelist_site_on_reload_value, 348 EXPECT_EQ(test_case.expected_whitelist_site_on_reload_value,
310 actual_configuration.should_whitelist_site_on_reload); 349 actual_configuration.should_whitelist_site_on_reload);
311 } 350 }
312 } 351 }
313 352
353 TEST(SubresourceFilterFeaturesTest, RulesetFlavor) {
354 const struct {
355 bool feature_enabled;
356 const char* ruleset_flavor_param;
357 const char* expected_ruleset_flavor_value;
358 } kTestCases[] = {
359 {false, "", ""}, {false, "a", ""}, {false, "test value", ""},
360 {true, "", ""}, {true, "a", "a"}, {true, "test value", "test value"}};
361
362 for (const auto& test_case : kTestCases) {
363 SCOPED_TRACE(::testing::Message("Enabled = ") << test_case.feature_enabled);
364 SCOPED_TRACE(::testing::Message("Flavor = \"")
365 << test_case.ruleset_flavor_param << "\"");
366
367 testing::ScopedExperimentalStateToggle scoped_experimental_state(
368 test_case.feature_enabled ? base::FeatureList::OVERRIDE_ENABLE_FEATURE
369 : base::FeatureList::OVERRIDE_USE_DEFAULT,
370 {{kRulesetFlavorParameterName, test_case.ruleset_flavor_param}});
371
372 const auto active_configurations = GetActiveConfigurations();
373 const Configuration& actual_configuration =
374 active_configurations->the_one_and_only();
375 EXPECT_EQ(std::string(test_case.expected_ruleset_flavor_value),
376 actual_configuration.ruleset_flavor);
377 }
378 }
314 } // namespace subresource_filter 379 } // namespace subresource_filter
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698