| /* |
| * Copyright 2012 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // Author: nforman@google.com (Naomi Forman) |
| |
| // Unit-test the experiment utilities. |
| |
| #include "net/instaweb/rewriter/public/experiment_util.h" |
| |
| #include "net/instaweb/rewriter/public/rewrite_options.h" |
| #include "net/instaweb/rewriter/public/rewrite_options_test_base.h" |
| #include "pagespeed/kernel/base/gtest.h" |
| #include "pagespeed/kernel/base/null_message_handler.h" |
| #include "pagespeed/kernel/base/scoped_ptr.h" |
| #include "pagespeed/kernel/base/time_util.h" |
| #include "pagespeed/kernel/base/timer.h" |
| #include "pagespeed/kernel/http/http_names.h" |
| #include "pagespeed/kernel/http/request_headers.h" |
| #include "pagespeed/kernel/http/response_headers.h" |
| #include "pagespeed/kernel/http/user_agent_matcher.h" |
| |
| namespace net_instaweb { |
| |
| namespace experiment { |
| |
| class ExperimentUtilTest : public RewriteOptionsTestBase<RewriteOptions> { |
| }; |
| |
| TEST_F(ExperimentUtilTest, GetCookieState) { |
| RequestHeaders req_headers; |
| int state; |
| // Empty headers, cookie not set. |
| EXPECT_FALSE(GetExperimentCookieState(req_headers, &state)); |
| EXPECT_EQ(kExperimentNotSet, state); |
| |
| // Headers with malformed experiment cookie, cookie not set. |
| req_headers.Add(HttpAttributes::kCookie, "PageSpeedExperiment=absdfkjs"); |
| EXPECT_FALSE(GetExperimentCookieState(req_headers, &state)); |
| EXPECT_EQ(kExperimentNotSet, state); |
| |
| // Headers with valid experiment cookie in None (i.e. not in experiment) |
| // state set. |
| req_headers.Clear(); |
| req_headers.Add(HttpAttributes::kCookie, "PageSpeedExperiment=0"); |
| EXPECT_TRUE(GetExperimentCookieState(req_headers, &state)); |
| EXPECT_EQ(0, state); |
| |
| // Headers with valid experiment cookie in experiment 1. |
| req_headers.Clear(); |
| req_headers.Add(HttpAttributes::kCookie, "PageSpeedExperiment=1"); |
| EXPECT_TRUE(GetExperimentCookieState(req_headers, &state)); |
| EXPECT_EQ(1, state); |
| |
| // Headers with valid experiment cookie in experiment 2. |
| req_headers.Clear(); |
| req_headers.Add(HttpAttributes::kCookie, "PageSpeedExperiment=2"); |
| EXPECT_TRUE(GetExperimentCookieState(req_headers, &state)); |
| EXPECT_EQ(2, state); |
| |
| // Headers with valid experiment cookie in experiment 2. |
| req_headers.Clear(); |
| req_headers.Add(HttpAttributes::kCookie, |
| "cookie=a;PageSpeedExperiment=2;something=foo"); |
| EXPECT_TRUE(GetExperimentCookieState(req_headers, &state)); |
| EXPECT_EQ(2, state); |
| } |
| |
| // Test that we remove the PageSpeedExperiment cookie when it's there, and leave |
| // the remainder of the cookies in tact. |
| TEST_F(ExperimentUtilTest, RemoveExperimentCookie) { |
| RequestHeaders req_headers; |
| req_headers.Add(HttpAttributes::kAcceptEncoding, "gzip"); |
| req_headers.Add(HttpAttributes::kCookie, |
| "something=random;PageSpeedExperiment=18:2;another=cookie"); |
| RemoveExperimentCookie(&req_headers); |
| |
| GoogleString expected = "something=random;another=cookie"; |
| EXPECT_EQ(expected, req_headers.Lookup1(HttpAttributes::kCookie)); |
| |
| req_headers.Clear(); |
| req_headers.Add(HttpAttributes::kCookie, "abd=123;jsjsj=4444"); |
| RemoveExperimentCookie(&req_headers); |
| |
| expected = "abd=123;jsjsj=4444"; |
| EXPECT_EQ(expected, req_headers.Lookup1(HttpAttributes::kCookie)); |
| } |
| |
| // Check that DetermineExperimentState behaves vaguely as expected. |
| TEST_F(ExperimentUtilTest, DetermineExperimentState) { |
| RewriteOptions options(thread_system_.get()); |
| options.set_running_experiment(true); |
| NullMessageHandler handler; |
| RequestHeaders headers; |
| UserAgentMatcher ua_matcher; |
| ASSERT_TRUE(options.AddExperimentSpec("id=1;percent=35", &handler)); |
| ASSERT_TRUE(options.AddExperimentSpec("id=2;percent=35", &handler)); |
| ASSERT_EQ(2, options.num_experiments()); |
| int none = 0; |
| int in_a = 0; |
| int in_b = 0; |
| int runs = 1000000; |
| // In 100000000 runs, with 70% of the traffic in an experiment, we should |
| // get some of each. |
| for (int i = 0; i < runs; ++i) { |
| int state = DetermineExperimentState(&options, headers, ua_matcher); |
| switch (state) { |
| case kNoExperiment: // explicitly not in experiment |
| ++none; |
| break; |
| case 1: // in id=1 |
| ++in_a; |
| break; |
| case 2: // in id=2 |
| ++in_b; |
| break; |
| default: |
| ASSERT_TRUE(0) << "Got unknown state"; |
| } |
| } |
| // Make sure they're all in a reasonable range. Since we do this |
| // randomly, they'll probably never be exactly 35/35/30. |
| // This gives us a 10% buffer in each direction for each bucket. |
| EXPECT_TRUE(none < .4 * runs); |
| EXPECT_TRUE(none > .2 * runs); |
| EXPECT_TRUE(in_a < .45 * runs); |
| EXPECT_TRUE(in_a > .25 * runs); |
| EXPECT_TRUE(in_b < .45 * runs); |
| EXPECT_TRUE(in_b > .25 * runs); |
| } |
| |
| TEST_F(ExperimentUtilTest, AnyActiveExperiments) { |
| RewriteOptions options(thread_system_.get()); |
| options.set_running_experiment(true); |
| NullMessageHandler handler; |
| ASSERT_TRUE(options.AddExperimentSpec("id=2;percent=0", &handler)); |
| ASSERT_TRUE(options.AddExperimentSpec("id=8;percent=0", &handler)); |
| EXPECT_FALSE(AnyActiveExperiments(&options)); |
| ASSERT_TRUE(options.AddExperimentSpec("id=1;percent=1", &handler)); |
| EXPECT_TRUE(AnyActiveExperiments(&options)); |
| } |
| |
| // Check that SetExperimentCookie sets the cookie on the appropriate |
| // domain, and with the correct expiration. |
| TEST_F(ExperimentUtilTest, SetExperimentCookie) { |
| ResponseHeaders resp_headers; |
| GoogleString url = "http://www.test.com/stuff/some_page.html"; |
| SetExperimentCookie(&resp_headers, 1, url, Timer::kWeekMs); |
| EXPECT_TRUE(resp_headers.Has(HttpAttributes::kSetCookie)); |
| ConstStringStarVector v; |
| EXPECT_TRUE(resp_headers.Lookup(HttpAttributes::kSetCookie, &v)); |
| ASSERT_EQ(1, v.size()); |
| GoogleString expires; |
| ConvertTimeToString(Timer::kWeekMs, &expires); |
| GoogleString expected = StringPrintf( |
| "PageSpeedExperiment=1; Expires=%s; Domain=.www.test.com; Path=/", |
| expires.c_str()); |
| EXPECT_EQ(expected, *v[0]); |
| } |
| |
| } // namespace experiment |
| |
| } // namespace net_instaweb |