blob: 41afcbe5f38d8ea570902e990acbf872e5624b68 [file] [log] [blame]
/*
* Copyright 2010 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: bmcquade@google.com (Bryan McQuade)
#include "net/instaweb/rewriter/public/rewrite_options.h"
#include "net/instaweb/rewriter/public/domain_lawyer.h"
#include "net/instaweb/rewriter/public/experiment_util.h"
#include "net/instaweb/rewriter/public/rewrite_options_test_base.h"
#include "pagespeed/kernel/base/gtest.h"
#include "pagespeed/kernel/base/google_message_handler.h"
#include "pagespeed/kernel/base/message_handler.h"
#include "pagespeed/kernel/base/message_handler_test_base.h"
#include "pagespeed/kernel/base/mock_hasher.h"
#include "pagespeed/kernel/base/mock_timer.h"
#include "pagespeed/kernel/base/null_message_handler.h"
#include "pagespeed/kernel/base/null_thread_system.h"
#include "pagespeed/kernel/http/google_url.h"
#include "pagespeed/kernel/http/request_headers.h"
namespace net_instaweb {
class RewriteOptionsTest : public RewriteOptionsTestBase<RewriteOptions> {
protected:
typedef RewriteOptions::FilterSet FilterSet;
RewriteOptionsTest() : options_(&thread_system_) {
}
bool NoneEnabled() {
FilterSet s;
return OnlyEnabled(s);
}
bool OnlyEnabled(const FilterSet& filters) {
bool ret = true;
for (RewriteOptions::Filter f = RewriteOptions::kFirstFilter;
ret && (f < RewriteOptions::kEndOfFilters);
f = static_cast<RewriteOptions::Filter>(f + 1)) {
if (filters.IsSet(f)) {
if (!options_.Enabled(f)) {
ret = false;
}
} else {
if (options_.Enabled(f)) {
ret = false;
}
}
}
return ret;
}
bool OnlyEnabled(RewriteOptions::Filter filter) {
FilterSet s;
s.Insert(filter);
return OnlyEnabled(s);
}
void MergeOptions(const RewriteOptions& one, const RewriteOptions& two) {
options_.Merge(one);
options_.Merge(two);
}
// Tests either SetOptionFromName or SetOptionFromNameAndLog depending
// on 'test_log_variant'
void TestNameSet(RewriteOptions::OptionSettingResult expected_result,
bool test_log_variant,
const StringPiece& name,
const StringPiece& value,
MessageHandler* handler) {
if (test_log_variant) {
bool expected = (expected_result == RewriteOptions::kOptionOk);
EXPECT_EQ(
expected,
options_.SetOptionFromNameAndLog(name, value, handler));
} else {
GoogleString msg;
EXPECT_EQ(expected_result,
options_.SetOptionFromName(name, value, &msg));
// Should produce a message exactly when not OK.
EXPECT_EQ(expected_result != RewriteOptions::kOptionOk, !msg.empty())
<< msg;
}
}
// Helper method that is used to verify different kinds of merges between
// InlineResourcesWithoutExplicitAuthorization values for global and local
// options.
void VerifyInlineUnauthorizedResourceTypeMerges(
StringPiece global_option_val,
StringPiece local_option_val,
bool expect_script,
bool expect_stylesheet) {
scoped_ptr<RewriteOptions> new_options(new RewriteOptions(&thread_system_));
// Initialize global options.
scoped_ptr<RewriteOptions> global_options(
new RewriteOptions(&thread_system_));
if (!global_option_val.empty()) {
RewriteOptions::ResourceCategorySet x;
ASSERT_TRUE(RewriteOptions::ParseInlineUnauthorizedResourceType(
global_option_val, &x));
global_options->set_inline_unauthorized_resource_types(x);
}
// Initialize local options.
RewriteOptions local_options(&thread_system_);
if (!local_option_val.empty()) {
RewriteOptions::ResourceCategorySet x;
ASSERT_TRUE(RewriteOptions::ParseInlineUnauthorizedResourceType(
local_option_val, &x));
local_options.set_inline_unauthorized_resource_types(x);
}
// Merge the options.
new_options->Merge(*global_options);
new_options->Merge(local_options);
// Check what resource types have been authorized.
EXPECT_EQ(
expect_script,
new_options->HasInlineUnauthorizedResourceType(semantic_type::kScript))
<< "Global: " << global_option_val << ", local: " << local_option_val;
EXPECT_EQ(
expect_stylesheet,
new_options->HasInlineUnauthorizedResourceType(
semantic_type::kStylesheet))
<< "Global: " << global_option_val << ", local: " << local_option_val;
}
// Adds an experiment spec to the options. We take the spec as a
// const char* and make a scoped GoogleString specifically to reproduce
// a bug with lifetime of the experiment option names.
bool AddExperimentSpec(const char* spec) {
NullMessageHandler handler;
GoogleString spec_string(spec);
return options_.AddExperimentSpec(spec_string, &handler);
}
void SetupTestExperimentSpecs() {
options_.SetRewriteLevel(RewriteOptions::kCoreFilters);
options_.set_running_experiment(true);
EXPECT_TRUE(AddExperimentSpec("id=1;percent=15;enable=defer_javascript;"
"options=CssInlineMaxBytes=1024"));
EXPECT_TRUE(AddExperimentSpec(
"id=2;percent=15;enable=resize_images;options=BogusOption=35"));
EXPECT_TRUE(AddExperimentSpec("id=3;percent=15;enable=defer_javascript"));
EXPECT_TRUE(AddExperimentSpec("id=4;percent=15;enable=defer_javascript;"
"options=CssInlineMaxBytes=Cabbage"));
EXPECT_TRUE(AddExperimentSpec(
"id=5;percent=15;enable=defer_javascript;"
"options=Potato=Carrot,5=10,6==9,CssInlineMaxBytes=1024"));
EXPECT_TRUE(AddExperimentSpec(
"id=6;percent=15;enable=defer_javascript;"
"options=JsOutlineMinBytes=4096,JpegRecompresssionQuality=50,"
"CssInlineMaxBytes=100,JsInlineMaxBytes=123"));
}
void VerifyMapOrigin(const DomainLawyer& lawyer,
const GoogleString& serving_url,
const GoogleString& expected_origin_domain,
const GoogleString& expected_host_header,
bool expected_is_proxy) {
GoogleString actual_origin_domain;
GoogleString actual_host_header;
bool actual_is_proxy;
EXPECT_TRUE(lawyer.MapOrigin(serving_url, &actual_origin_domain,
&actual_host_header, &actual_is_proxy));
EXPECT_EQ(expected_origin_domain, actual_origin_domain);
EXPECT_EQ(expected_host_header, actual_host_header);
EXPECT_EQ(expected_is_proxy, actual_is_proxy);
}
void VerifyNoMapOrigin(const DomainLawyer& lawyer,
const GoogleString& serving_domain) {
GoogleUrl url(serving_domain);
ASSERT_TRUE(url.IsWebValid());
EXPECT_FALSE(lawyer.IsOriginKnown(url));
}
void VerifyAllowVaryOn(const GoogleString& input_str,
bool expected_valid,
bool expected_allow_auto,
bool expected_allow_save_data,
bool expected_allow_user_agent,
bool expected_allow_accept,
const GoogleString& expected_str) {
RewriteOptions::OptionSettingResult is_valid =
options_.SetOptionFromName(RewriteOptions::kAllowVaryOn, input_str);
if (expected_valid) {
EXPECT_EQ(RewriteOptions::kOptionOk, is_valid);
} else {
EXPECT_EQ(RewriteOptions::kOptionValueInvalid, is_valid);
return; // No more checking
}
EXPECT_EQ(expected_allow_auto, options_.AllowVaryOnAuto());
EXPECT_EQ(expected_allow_save_data, options_.AllowVaryOnSaveData());
EXPECT_EQ(expected_allow_user_agent,
options_.AllowVaryOnUserAgent());
EXPECT_EQ(expected_allow_accept, options_.AllowVaryOnAccept());
EXPECT_STREQ(expected_str, options_.AllowVaryOnToString());
}
void VerifyMergingAllowVaryOn(const GoogleString& old_option_str,
const GoogleString& new_option_str,
const GoogleString& expected_option_str) {
RewriteOptions merged_options(&thread_system_);
RewriteOptions new_options(&thread_system_);
if (!old_option_str.empty()) {
EXPECT_EQ(RewriteOptions::kOptionOk,
merged_options.SetOptionFromName(RewriteOptions::kAllowVaryOn,
old_option_str));
}
if (!new_option_str.empty()) {
EXPECT_EQ(RewriteOptions::kOptionOk,
new_options.SetOptionFromName(RewriteOptions::kAllowVaryOn,
new_option_str));
}
merged_options.Merge(new_options);
EXPECT_STREQ(expected_option_str, merged_options.AllowVaryOnToString());
}
void TestSetOptionFromName(bool test_log_variant);
NullThreadSystem thread_system_;
RewriteOptions options_;
MockHasher hasher_;
};
TEST_F(RewriteOptionsTest, EnabledStates) {
options_.set_enabled(RewriteOptions::kEnabledUnplugged);
ASSERT_FALSE(options_.enabled());
ASSERT_TRUE(options_.unplugged());
options_.set_enabled(RewriteOptions::kEnabledOff);
ASSERT_FALSE(options_.enabled());
ASSERT_FALSE(options_.unplugged());
options_.set_enabled(RewriteOptions::kEnabledOn);
ASSERT_TRUE(options_.enabled());
ASSERT_FALSE(options_.unplugged());
}
TEST_F(RewriteOptionsTest, DefaultEnabledFilters) {
ASSERT_TRUE(OnlyEnabled(RewriteOptions::kHtmlWriterFilter));
}
TEST_F(RewriteOptionsTest, InstrumentationDisabled) {
// Make sure the kCoreFilters enables some filters.
options_.SetRewriteLevel(RewriteOptions::kCoreFilters);
ASSERT_TRUE(options_.Enabled(RewriteOptions::kExtendCacheCss));
ASSERT_TRUE(options_.Enabled(RewriteOptions::kExtendCacheImages));
// Now disable all filters and make sure none are enabled.
for (RewriteOptions::Filter f = RewriteOptions::kFirstFilter;
f < RewriteOptions::kEndOfFilters;
f = static_cast<RewriteOptions::Filter>(f + 1)) {
options_.DisableFilter(f);
}
ASSERT_TRUE(NoneEnabled());
}
TEST_F(RewriteOptionsTest, DisableTrumpsEnable) {
// Disable the default filter.
options_.DisableFilter(RewriteOptions::kHtmlWriterFilter);
for (RewriteOptions::Filter f = RewriteOptions::kFirstFilter;
f < RewriteOptions::kEndOfFilters;
f = static_cast<RewriteOptions::Filter>(f + 1)) {
options_.DisableFilter(f);
options_.EnableFilter(f);
}
}
TEST_F(RewriteOptionsTest, ForceEnableFilter) {
options_.DisableFilter(RewriteOptions::kHtmlWriterFilter);
options_.EnableFilter(RewriteOptions::kHtmlWriterFilter);
EXPECT_FALSE(options_.Enabled(RewriteOptions::kHtmlWriterFilter));
options_.ForceEnableFilter(RewriteOptions::kHtmlWriterFilter);
EXPECT_TRUE(options_.Enabled(RewriteOptions::kHtmlWriterFilter));
}
TEST_F(RewriteOptionsTest, NumFilterInLevels) {
const RewriteOptions::RewriteLevel levels[] = {
RewriteOptions::kOptimizeForBandwidth,
RewriteOptions::kCoreFilters,
RewriteOptions::kMobilizeFilters,
RewriteOptions::kTestingCoreFilters,
RewriteOptions::kAllFilters
};
for (int i = 0; i < arraysize(levels); ++i) {
options_.SetRewriteLevel(levels[i]);
FilterSet s;
for (RewriteOptions::Filter f = RewriteOptions::kFirstFilter;
f < RewriteOptions::kEndOfFilters;
f = static_cast<RewriteOptions::Filter>(f + 1)) {
if (options_.Enabled(f)) {
s.Insert(f);
}
}
// Make sure that more than one filter is enabled in the filter set.
ASSERT_GT(s.size(), 1);
}
}
TEST_F(RewriteOptionsTest, Enable) {
FilterSet s;
for (RewriteOptions::Filter f = RewriteOptions::kFirstFilter;
f < RewriteOptions::kEndOfFilters;
f = static_cast<RewriteOptions::Filter>(f + 1)) {
s.Insert(f);
s.Insert(RewriteOptions::kHtmlWriterFilter); // enabled by default
options_.EnableFilter(f);
ASSERT_TRUE(OnlyEnabled(s));
}
}
TEST_F(RewriteOptionsTest, CommaSeparatedList) {
FilterSet s;
s.Insert(RewriteOptions::kAddInstrumentation);
s.Insert(RewriteOptions::kLeftTrimUrls);
s.Insert(RewriteOptions::kHtmlWriterFilter); // enabled by default
static const char kList[] = "add_instrumentation,trim_urls";
NullMessageHandler handler;
ASSERT_TRUE(
options_.EnableFiltersByCommaSeparatedList(kList, &handler));
ASSERT_TRUE(OnlyEnabled(s));
ASSERT_TRUE(
options_.DisableFiltersByCommaSeparatedList(kList, &handler));
ASSERT_TRUE(OnlyEnabled(RewriteOptions::kHtmlWriterFilter)); // default
}
TEST_F(RewriteOptionsTest, CompoundFlag) {
FilterSet s;
s.Insert(RewriteOptions::kConvertGifToPng);
s.Insert(RewriteOptions::kConvertJpegToProgressive);
s.Insert(RewriteOptions::kConvertJpegToWebp);
s.Insert(RewriteOptions::kConvertPngToJpeg);
s.Insert(RewriteOptions::kConvertToWebpLossless);
s.Insert(RewriteOptions::kInlineImages);
s.Insert(RewriteOptions::kJpegSubsampling);
s.Insert(RewriteOptions::kRecompressJpeg);
s.Insert(RewriteOptions::kRecompressPng);
s.Insert(RewriteOptions::kRecompressWebp);
s.Insert(RewriteOptions::kResizeImages);
s.Insert(RewriteOptions::kStripImageMetaData);
s.Insert(RewriteOptions::kStripImageColorProfile);
s.Insert(RewriteOptions::kHtmlWriterFilter); // enabled by default
static const char kList[] = "rewrite_images";
NullMessageHandler handler;
ASSERT_TRUE(
options_.EnableFiltersByCommaSeparatedList(kList, &handler));
ASSERT_TRUE(OnlyEnabled(s));
ASSERT_TRUE(
options_.DisableFiltersByCommaSeparatedList(kList, &handler));
ASSERT_TRUE(OnlyEnabled(RewriteOptions::kHtmlWriterFilter)); // default
}
TEST_F(RewriteOptionsTest, CompoundFlagRecompressImages) {
FilterSet s;
s.Insert(RewriteOptions::kConvertGifToPng);
s.Insert(RewriteOptions::kConvertJpegToProgressive);
s.Insert(RewriteOptions::kConvertJpegToWebp);
s.Insert(RewriteOptions::kJpegSubsampling);
s.Insert(RewriteOptions::kRecompressJpeg);
s.Insert(RewriteOptions::kRecompressPng);
s.Insert(RewriteOptions::kRecompressWebp);
s.Insert(RewriteOptions::kStripImageMetaData);
s.Insert(RewriteOptions::kStripImageColorProfile);
s.Insert(RewriteOptions::kHtmlWriterFilter); // enabled by default
static const char kList[] = "recompress_images";
NullMessageHandler handler;
ASSERT_TRUE(
options_.EnableFiltersByCommaSeparatedList(kList, &handler));
ASSERT_TRUE(OnlyEnabled(s));
ASSERT_TRUE(
options_.DisableFiltersByCommaSeparatedList(kList, &handler));
ASSERT_TRUE(OnlyEnabled(RewriteOptions::kHtmlWriterFilter)); // default
}
TEST_F(RewriteOptionsTest, ParseRewriteLevel) {
RewriteOptions::RewriteLevel level;
EXPECT_TRUE(RewriteOptions::ParseRewriteLevel("PassThrough", &level));
EXPECT_EQ(RewriteOptions::kPassThrough, level);
EXPECT_TRUE(RewriteOptions::ParseRewriteLevel("CoreFilters", &level));
EXPECT_EQ(RewriteOptions::kCoreFilters, level);
EXPECT_TRUE(RewriteOptions::ParseRewriteLevel("MobilizeFilters", &level));
EXPECT_EQ(RewriteOptions::kMobilizeFilters, level);
EXPECT_FALSE(RewriteOptions::ParseRewriteLevel(NULL, &level));
EXPECT_FALSE(RewriteOptions::ParseRewriteLevel("", &level));
EXPECT_FALSE(RewriteOptions::ParseRewriteLevel("Garbage", &level));
}
TEST_F(RewriteOptionsTest, IsRequestDeclined) {
RewriteOptions one(&thread_system_);
one.AddRejectedUrlWildcard("*blocked*");
one.AddRejectedHeaderWildcard(HttpAttributes::kUserAgent,
"*blocked UA*");
one.AddRejectedHeaderWildcard(HttpAttributes::kXForwardedFor,
"12.34.13.*");
RequestHeaders headers;
headers.Add(HttpAttributes::kUserAgent, "Chrome");
ASSERT_FALSE(one.IsRequestDeclined("www.test.com/a", &headers));
ASSERT_TRUE(one.IsRequestDeclined("www.test.com/blocked", &headers));
headers.Add(HttpAttributes::kUserAgent, "this is blocked UA agent");
ASSERT_TRUE(one.IsRequestDeclined("www.test.com/a", &headers));
headers.Add(HttpAttributes::kUserAgent, "Chrome");
headers.Add(HttpAttributes::kXForwardedFor, "12.34.13.1");
ASSERT_TRUE(one.IsRequestDeclined("www.test.com/a", &headers));
headers.Clear();
ASSERT_FALSE(one.IsRequestDeclined("www.test.com/a", &headers));
}
TEST_F(RewriteOptionsTest, IsRequestDeclinedMerge) {
RewriteOptions one(&thread_system_), two(&thread_system_);
RequestHeaders headers;
one.AddRejectedUrlWildcard("http://www.a.com/b/*");
EXPECT_TRUE(one.IsRequestDeclined("http://www.a.com/b/sdsd123", &headers));
EXPECT_FALSE(one.IsRequestDeclined("http://www.a.com/", &headers));
EXPECT_FALSE(one.IsRequestDeclined("http://www.b.com/b/", &headers));
two.AddRejectedHeaderWildcard(HttpAttributes::kUserAgent, "*Chrome*");
two.AddRejectedUrlWildcard("http://www.b.com/b/*");
MergeOptions(one, two);
EXPECT_TRUE(options_.IsRequestDeclined("http://www.a.com/b/sds13", &headers));
EXPECT_FALSE(options_.IsRequestDeclined("http://www.a.com/", &headers));
EXPECT_TRUE(options_.IsRequestDeclined("http://www.b.com/b/", &headers));
headers.Add(HttpAttributes::kUserAgent, "firefox");
EXPECT_FALSE(options_.IsRequestDeclined("http://www.a.com/", &headers));
headers.Add(HttpAttributes::kUserAgent, "abc Chrome 456");
EXPECT_TRUE(options_.IsRequestDeclined("http://www.a.com/", &headers));
}
TEST_F(RewriteOptionsTest, MergeLevelsDefault) {
RewriteOptions one(&thread_system_), two(&thread_system_);
MergeOptions(one, two);
EXPECT_EQ(RewriteOptions::kPassThrough, options_.level());
}
TEST_F(RewriteOptionsTest, MergeLevelsOneCore) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.SetRewriteLevel(RewriteOptions::kCoreFilters);
MergeOptions(one, two);
EXPECT_EQ(RewriteOptions::kCoreFilters, options_.level());
}
TEST_F(RewriteOptionsTest, MergeLevelsOneCoreTwoPass) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.SetRewriteLevel(RewriteOptions::kCoreFilters);
two.SetRewriteLevel(RewriteOptions::kPassThrough); // overrides default
MergeOptions(one, two);
EXPECT_EQ(RewriteOptions::kPassThrough, options_.level());
}
TEST_F(RewriteOptionsTest, MergeLevelsOnePassTwoCore) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.SetRewriteLevel(RewriteOptions::kPassThrough); // overrides default
two.SetRewriteLevel(RewriteOptions::kCoreFilters); // overrides one
MergeOptions(one, two);
EXPECT_EQ(RewriteOptions::kCoreFilters, options_.level());
}
TEST_F(RewriteOptionsTest, MergeLevelsBothCore) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.SetRewriteLevel(RewriteOptions::kCoreFilters);
two.SetRewriteLevel(RewriteOptions::kCoreFilters);
MergeOptions(one, two);
EXPECT_EQ(RewriteOptions::kCoreFilters, options_.level());
}
TEST_F(RewriteOptionsTest, MergeFilterPassThrough) {
RewriteOptions one(&thread_system_), two(&thread_system_);
MergeOptions(one, two);
EXPECT_FALSE(options_.Enabled(RewriteOptions::kAddHead));
}
TEST_F(RewriteOptionsTest, MergeFilterEnaOne) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.EnableFilter(RewriteOptions::kAddHead);
MergeOptions(one, two);
EXPECT_TRUE(options_.Enabled(RewriteOptions::kAddHead));
}
TEST_F(RewriteOptionsTest, MergeFilterEnaTwo) {
RewriteOptions one(&thread_system_), two(&thread_system_);
two.EnableFilter(RewriteOptions::kAddHead);
MergeOptions(one, two);
EXPECT_TRUE(options_.Enabled(RewriteOptions::kAddHead));
}
TEST_F(RewriteOptionsTest, MergeFilterEnaOneDisTwo) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.EnableFilter(RewriteOptions::kAddHead);
two.DisableFilter(RewriteOptions::kAddHead);
MergeOptions(one, two);
EXPECT_FALSE(options_.Enabled(RewriteOptions::kAddHead));
}
TEST_F(RewriteOptionsTest, MergeFilterDisOneEnaTwo) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.DisableFilter(RewriteOptions::kAddHead);
two.EnableFilter(RewriteOptions::kAddHead);
MergeOptions(one, two);
EXPECT_TRUE(options_.Enabled(RewriteOptions::kAddHead));
}
TEST_F(RewriteOptionsTest, MergeCoreFilter) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.SetRewriteLevel(RewriteOptions::kCoreFilters);
MergeOptions(one, two);
EXPECT_TRUE(options_.Enabled(RewriteOptions::kExtendCacheCss));
}
TEST_F(RewriteOptionsTest, MergeCoreFilterEnaOne) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.SetRewriteLevel(RewriteOptions::kCoreFilters);
one.EnableFilter(RewriteOptions::kExtendCacheCss);
MergeOptions(one, two);
EXPECT_TRUE(options_.Enabled(RewriteOptions::kExtendCacheCss));
}
TEST_F(RewriteOptionsTest, MergeCoreFilterEnaTwo) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.SetRewriteLevel(RewriteOptions::kCoreFilters);
two.EnableFilter(RewriteOptions::kExtendCacheCss);
MergeOptions(one, two);
EXPECT_TRUE(options_.Enabled(RewriteOptions::kExtendCacheCss));
}
TEST_F(RewriteOptionsTest, MergeCoreFilterEnaOneDisTwo) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.SetRewriteLevel(RewriteOptions::kCoreFilters);
one.EnableFilter(RewriteOptions::kExtendCacheImages);
two.DisableFilter(RewriteOptions::kExtendCacheImages);
MergeOptions(one, two);
EXPECT_FALSE(options_.Enabled(RewriteOptions::kExtendCacheImages));
}
TEST_F(RewriteOptionsTest, MergeCoreFilterDisOne) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.SetRewriteLevel(RewriteOptions::kCoreFilters);
one.DisableFilter(RewriteOptions::kExtendCacheCss);
MergeOptions(one, two);
EXPECT_FALSE(options_.Enabled(RewriteOptions::kExtendCacheCss));
}
TEST_F(RewriteOptionsTest, MergeCoreFilterDisOneEnaTwo) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.SetRewriteLevel(RewriteOptions::kCoreFilters);
one.DisableFilter(RewriteOptions::kExtendCacheScripts);
two.EnableFilter(RewriteOptions::kExtendCacheScripts);
MergeOptions(one, two);
EXPECT_TRUE(options_.Enabled(RewriteOptions::kExtendCacheScripts));
}
TEST_F(RewriteOptionsTest, MergeThresholdDefault) {
RewriteOptions one(&thread_system_), two(&thread_system_);
MergeOptions(one, two);
EXPECT_EQ(RewriteOptions::kDefaultCssInlineMaxBytes,
options_.css_inline_max_bytes());
}
TEST_F(RewriteOptionsTest, MergeThresholdOne) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.set_css_inline_max_bytes(5);
MergeOptions(one, two);
EXPECT_EQ(5, options_.css_inline_max_bytes());
}
TEST_F(RewriteOptionsTest, MergeThresholdTwo) {
RewriteOptions one(&thread_system_), two(&thread_system_);
two.set_css_inline_max_bytes(6);
MergeOptions(one, two);
EXPECT_EQ(6, options_.css_inline_max_bytes());
}
TEST_F(RewriteOptionsTest, MergeThresholdOverride) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.set_css_inline_max_bytes(5);
two.set_css_inline_max_bytes(6);
MergeOptions(one, two);
EXPECT_EQ(6, options_.css_inline_max_bytes());
}
TEST_F(RewriteOptionsTest, MergeCacheInvalidationTimeStampDefault) {
RewriteOptions one(&thread_system_), two(&thread_system_);
MergeOptions(one, two);
EXPECT_FALSE(options_.has_cache_invalidation_timestamp_ms());
}
TEST_F(RewriteOptionsTest, MergeCacheInvalidationTimeStampOne) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.UpdateCacheInvalidationTimestampMs(11111111);
MergeOptions(one, two);
EXPECT_EQ(11111111, options_.cache_invalidation_timestamp());
}
TEST_F(RewriteOptionsTest, MergeCacheInvalidationTimeStampTwo) {
RewriteOptions one(&thread_system_), two(&thread_system_);
two.UpdateCacheInvalidationTimestampMs(22222222);
MergeOptions(one, two);
EXPECT_EQ(22222222, options_.cache_invalidation_timestamp());
}
TEST_F(RewriteOptionsTest, MergeCacheInvalidationTimeStampOneLarger) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.UpdateCacheInvalidationTimestampMs(33333333);
two.UpdateCacheInvalidationTimestampMs(22222222);
MergeOptions(one, two);
EXPECT_EQ(33333333, options_.cache_invalidation_timestamp());
}
TEST_F(RewriteOptionsTest, MergeCacheInvalidationTimeStampTwoLarger) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.UpdateCacheInvalidationTimestampMs(11111111);
two.UpdateCacheInvalidationTimestampMs(22222222);
MergeOptions(one, two);
EXPECT_EQ(22222222, options_.cache_invalidation_timestamp());
}
TEST_F(RewriteOptionsTest, MergeDistributed) {
RewriteOptions one(&thread_system_), two(&thread_system_);
EXPECT_FALSE(options_.Distributable(RewriteOptions::kCacheExtenderId));
EXPECT_FALSE(options_.Distributable(RewriteOptions::kImageCompressionId));
EXPECT_FALSE(options_.Distributable(RewriteOptions::kCssFilterId));
one.DistributeFilter(RewriteOptions::kCacheExtenderId);
two.DistributeFilter(RewriteOptions::kImageCompressionId);
MergeOptions(one, two);
EXPECT_TRUE(options_.Distributable(RewriteOptions::kCacheExtenderId));
EXPECT_TRUE(options_.Distributable(RewriteOptions::kImageCompressionId));
EXPECT_FALSE(options_.Distributable(RewriteOptions::kCssFilterId));
}
TEST_F(RewriteOptionsTest, MergeOnlyProcessScopeOptions) {
RewriteOptions dest(&thread_system_), src(&thread_system_);
dest.set_image_max_rewrites_at_once(2);
dest.set_max_url_segment_size(1);
src.set_image_max_rewrites_at_once(5);
src.set_max_url_segment_size(4);
dest.MergeOnlyProcessScopeOptions(src);
// Pulled in set_image_max_rewrites_at_once, which is process scope,
// but not the other option.
EXPECT_EQ(5, dest.image_max_rewrites_at_once());
EXPECT_EQ(1, dest.max_url_segment_size());
}
TEST_F(RewriteOptionsTest, Allow) {
options_.Allow("*.css");
EXPECT_TRUE(options_.IsAllowed("abcd.css"));
options_.Disallow("a*.css");
EXPECT_FALSE(options_.IsAllowed("abcd.css"));
options_.Allow("ab*.css");
EXPECT_TRUE(options_.IsAllowed("abcd.css"));
options_.Disallow("abc*.css");
EXPECT_FALSE(options_.IsAllowed("abcd.css"));
}
TEST_F(RewriteOptionsTest, MergeAllow) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.Allow("*.css");
EXPECT_TRUE(one.IsAllowed("abcd.css"));
one.Disallow("a*.css");
EXPECT_FALSE(one.IsAllowed("abcd.css"));
two.Allow("ab*.css");
EXPECT_TRUE(two.IsAllowed("abcd.css"));
two.Disallow("abc*.css");
EXPECT_FALSE(two.IsAllowed("abcd.css"));
MergeOptions(one, two);
EXPECT_FALSE(options_.IsAllowed("abcd.css"));
EXPECT_FALSE(options_.IsAllowed("abc.css"));
EXPECT_TRUE(options_.IsAllowed("ab.css"));
EXPECT_FALSE(options_.IsAllowed("a.css"));
}
TEST_F(RewriteOptionsTest, DisableAllFilters) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.EnableFilter(RewriteOptions::kAddHead);
two.EnableFilter(RewriteOptions::kExtendCacheCss);
two.DisableAllFilters(); // Should disable both.
EXPECT_FALSE(options_.Enabled(RewriteOptions::kExtendCacheCss));
MergeOptions(one, two);
EXPECT_FALSE(options_.Enabled(RewriteOptions::kAddHead));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kExtendCacheCss));
}
TEST_F(RewriteOptionsTest, DisableAllFiltersNotExplicitlyEnabled) {
RewriteOptions one(&thread_system_), two(&thread_system_);
one.EnableFilter(RewriteOptions::kAddHead);
two.EnableFilter(RewriteOptions::kExtendCacheCss);
two.DisableAllFiltersNotExplicitlyEnabled(); // Should disable AddHead.
MergeOptions(one, two);
// Make sure AddHead enabling didn't leak through.
EXPECT_FALSE(options_.Enabled(RewriteOptions::kAddHead));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kExtendCacheCss));
}
TEST_F(RewriteOptionsTest, DisableAllFiltersOverrideFilterLevel) {
// Disable the default enabled filter.
options_.DisableFilter(RewriteOptions::kHtmlWriterFilter);
options_.SetRewriteLevel(RewriteOptions::kCoreFilters);
options_.EnableFilter(RewriteOptions::kAddHead);
options_.DisableAllFiltersNotExplicitlyEnabled();
// Check that *only* AddHead is enabled, even though we have CoreFilters
// level set.
EXPECT_TRUE(OnlyEnabled(RewriteOptions::kAddHead));
}
TEST_F(RewriteOptionsTest, ForbidFilter) {
// Forbid a core filter: this will disable it.
options_.SetRewriteLevel(RewriteOptions::kCoreFilters);
options_.ForbidFilter(RewriteOptions::kExtendCacheCss);
EXPECT_FALSE(options_.Enabled(RewriteOptions::kExtendCacheCss));
EXPECT_TRUE(options_.Forbidden(
RewriteOptions::FilterId(RewriteOptions::kExtendCacheCss)));
// Forbid a filter, then try to merge in an enablement: it won't take.
// At the same time, merge in a new "forbiddenment": it will take.
RewriteOptions one(&thread_system_), two(&thread_system_);
one.SetRewriteLevel(RewriteOptions::kCoreFilters);
one.ForbidFilter(RewriteOptions::kExtendCacheCss);
two.SetRewriteLevel(RewriteOptions::kCoreFilters);
two.ForbidFilter(RewriteOptions::kFlattenCssImports);
one.Merge(two);
EXPECT_FALSE(one.Enabled(RewriteOptions::kExtendCacheCss));
EXPECT_FALSE(one.Enabled(RewriteOptions::kFlattenCssImports));
EXPECT_TRUE(one.Forbidden(
RewriteOptions::FilterId(RewriteOptions::kExtendCacheCss)));
EXPECT_TRUE(one.Forbidden(
RewriteOptions::FilterId(RewriteOptions::kFlattenCssImports)));
}
TEST_F(RewriteOptionsTest, AllDoesNotImplyStripScrips) {
options_.SetRewriteLevel(RewriteOptions::kAllFilters);
EXPECT_TRUE(options_.Enabled(RewriteOptions::kCombineCss));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kStripScripts));
}
TEST_F(RewriteOptionsTest, ExplicitlyEnabledDangerousFilters) {
options_.SetRewriteLevel(RewriteOptions::kAllFilters);
options_.EnableFilter(RewriteOptions::kStripScripts);
EXPECT_FALSE(options_.Enabled(RewriteOptions::kDivStructure));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kStripScripts));
options_.EnableFilter(RewriteOptions::kDivStructure);
EXPECT_TRUE(options_.Enabled(RewriteOptions::kDivStructure));
}
TEST_F(RewriteOptionsTest, CoreAndNotDangerous) {
options_.SetRewriteLevel(RewriteOptions::kCoreFilters);
EXPECT_FALSE(options_.Enabled(RewriteOptions::kAddInstrumentation));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kCombineCss));
}
TEST_F(RewriteOptionsTest, CoreByNameNotLevel) {
NullMessageHandler handler;
options_.SetRewriteLevel(RewriteOptions::kPassThrough);
ASSERT_TRUE(options_.EnableFiltersByCommaSeparatedList("core", &handler));
// Test the same ones as tested in InstrumentationDisabled.
ASSERT_TRUE(options_.Enabled(RewriteOptions::kExtendCacheCss));
ASSERT_TRUE(options_.Enabled(RewriteOptions::kExtendCacheImages));
// Test these for PlusAndMinus validation.
EXPECT_FALSE(options_.Enabled(RewriteOptions::kDivStructure));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kInlineCss));
}
TEST_F(RewriteOptionsTest, PlusAndMinus) {
static const char kList[] =
"core,+div_structure, -inline_css,+extend_cache_css";
NullMessageHandler handler;
options_.SetRewriteLevel(RewriteOptions::kPassThrough);
ASSERT_TRUE(options_.AdjustFiltersByCommaSeparatedList(kList, &handler));
// Test the same ones as tested in InstrumentationDisabled.
ASSERT_TRUE(options_.Enabled(RewriteOptions::kExtendCacheCss));
ASSERT_TRUE(options_.Enabled(RewriteOptions::kExtendCacheImages));
// These should be opposite from normal.
EXPECT_TRUE(options_.Enabled(RewriteOptions::kDivStructure));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kInlineCss));
}
TEST_F(RewriteOptionsTest, SetDefaultRewriteLevel) {
NullMessageHandler handler;
RewriteOptions new_options(&thread_system_);
new_options.SetDefaultRewriteLevel(RewriteOptions::kCoreFilters);
EXPECT_FALSE(options_.Enabled(RewriteOptions::kExtendCacheCss));
options_.Merge(new_options);
EXPECT_TRUE(options_.Enabled(RewriteOptions::kExtendCacheCss));
}
void RewriteOptionsTest::TestSetOptionFromName(bool test_log_variant) {
NullMessageHandler handler;
// TODO(sriharis): Add tests for all Options here per LookupOptionByNameTest.
TestNameSet(RewriteOptions::kOptionOk,
test_log_variant,
"FetcherTimeOutMs",
"1024",
&handler);
// Default for this is 5 * Timer::kSecondMs.
EXPECT_EQ(1024, options_.blocking_fetch_timeout_ms());
TestNameSet(RewriteOptions::kOptionOk,
test_log_variant,
"CssInlineMaxBytes",
"1024",
&handler);
// Default for this is 2048.
EXPECT_EQ(1024L, options_.css_inline_max_bytes());
TestNameSet(RewriteOptions::kOptionOk,
test_log_variant,
"JpegRecompressionQuality",
"1",
&handler);
// Default is -1.
EXPECT_EQ(1, options_.ImageJpegQuality());
TestNameSet(RewriteOptions::kOptionOk,
test_log_variant,
"CombineAcrossPaths",
"false",
&handler);
// Default is true
EXPECT_FALSE(options_.combine_across_paths());
TestNameSet(RewriteOptions::kOptionOk,
test_log_variant,
"BeaconUrl",
"http://www.example.com/beacon",
&handler);
EXPECT_EQ("http://www.example.com/beacon", options_.beacon_url().http);
EXPECT_EQ("https://www.example.com/beacon", options_.beacon_url().https);
TestNameSet(RewriteOptions::kOptionOk,
test_log_variant,
"BeaconUrl",
"http://www.example.com/beacon2 https://www.example.com/beacon3",
&handler);
EXPECT_EQ("http://www.example.com/beacon2", options_.beacon_url().http);
EXPECT_EQ("https://www.example.com/beacon3", options_.beacon_url().https);
TestNameSet(RewriteOptions::kOptionOk,
test_log_variant,
"BeaconUrl",
"/pagespeed_beacon?",
&handler);
EXPECT_EQ("/pagespeed_beacon?", options_.beacon_url().http);
EXPECT_EQ("/pagespeed_beacon?", options_.beacon_url().https);
RewriteOptions::RewriteLevel old_level = options_.level();
TestNameSet(RewriteOptions::kOptionValueInvalid,
test_log_variant,
"RewriteLevel",
"does_not_work",
&handler);
EXPECT_EQ(old_level, options_.level());
TestNameSet(RewriteOptions::kOptionNameUnknown,
test_log_variant,
"InvalidName",
"example",
&handler);
TestNameSet(RewriteOptions::kOptionValueInvalid,
test_log_variant,
"JsInlineMaxBytes",
"NOT_INT",
&handler);
EXPECT_EQ(RewriteOptions::kDefaultJsInlineMaxBytes,
options_.js_inline_max_bytes()); // unchanged from default.
}
TEST_F(RewriteOptionsTest, SetOptionFromName) {
TestSetOptionFromName(false);
}
TEST_F(RewriteOptionsTest, SetOptionFromNameAndLog) {
TestSetOptionFromName(true);
}
// All the base option names are explicitly enumerated here. Modifications are
// handled by the explicit tests. Additions/deletions are handled by checking
// the count explicitly (and assuming we add/delete an option value when we
// add/delete an option name).
TEST_F(RewriteOptionsTest, LookupOptionByNameTest) {
const char* const option_names[] = {
RewriteOptions::kAcceptInvalidSignatures,
RewriteOptions::kAccessControlAllowOrigins,
RewriteOptions::kAddOptionsToUrls,
RewriteOptions::kAllowLoggingUrlsInLogRecord,
RewriteOptions::kAllowOptionsToBeSetByCookies,
RewriteOptions::kAllowVaryOn,
RewriteOptions::kAlwaysMobilize,
RewriteOptions::kAlwaysRewriteCss,
RewriteOptions::kAnalyticsID,
RewriteOptions::kAvoidRenamingIntrospectiveJavascript,
RewriteOptions::kAwaitPcacheLookup,
RewriteOptions::kBeaconReinstrumentTimeSec,
RewriteOptions::kBeaconUrl,
RewriteOptions::kBlinkMaxHtmlSizeRewritable,
RewriteOptions::kCacheFragment,
RewriteOptions::kCacheSmallImagesUnrewritten,
RewriteOptions::kClientDomainRewrite,
RewriteOptions::kCombineAcrossPaths,
RewriteOptions::kContentExperimentID,
RewriteOptions::kContentExperimentVariantID,
RewriteOptions::kCriticalImagesBeaconEnabled,
RewriteOptions::kCriticalLineConfig,
RewriteOptions::kCssFlattenMaxBytes,
RewriteOptions::kCssImageInlineMaxBytes,
RewriteOptions::kCssInlineMaxBytes,
RewriteOptions::kCssOutlineMinBytes,
RewriteOptions::kCssPreserveURLs,
RewriteOptions::kDefaultCacheHtml,
RewriteOptions::kDisableBackgroundFetchesForBots,
RewriteOptions::kDisableRewriteOnNoTransform,
RewriteOptions::kDistributeFetches,
RewriteOptions::kDistributedRewriteKey,
RewriteOptions::kDistributedRewriteServers,
RewriteOptions::kDistributedRewriteTimeoutMs,
RewriteOptions::kDomainRewriteCookies,
RewriteOptions::kDomainRewriteHyperlinks,
RewriteOptions::kDomainShardCount,
RewriteOptions::kDownstreamCachePurgeMethod,
RewriteOptions::kDownstreamCacheRebeaconingKey,
RewriteOptions::kDownstreamCacheRewrittenPercentageThreshold,
RewriteOptions::kEnableAggressiveRewritersForMobile,
RewriteOptions::kEnableBlinkHtmlChangeDetection,
RewriteOptions::kEnableBlinkHtmlChangeDetectionLogging,
RewriteOptions::kEnableCachePurge,
RewriteOptions::kEnableDeferJsExperimental,
RewriteOptions::kEnableExtendedInstrumentation,
RewriteOptions::kEnableFlushEarlyCriticalCss,
RewriteOptions::kEnableLazyLoadHighResImages,
RewriteOptions::kEnablePrioritizingScripts,
RewriteOptions::kEnabled,
RewriteOptions::kEnrollExperiment,
RewriteOptions::kExperimentCookieDurationMs,
RewriteOptions::kExperimentSlot,
RewriteOptions::kFetcherTimeOutMs,
RewriteOptions::kFinderPropertiesCacheExpirationTimeMs,
RewriteOptions::kFinderPropertiesCacheRefreshTimeMs,
RewriteOptions::kFlushBufferLimitBytes,
RewriteOptions::kFlushHtml,
RewriteOptions::kFlushMoreResourcesEarlyIfTimePermits,
RewriteOptions::kForbidAllDisabledFilters,
RewriteOptions::kGoogleFontCssInlineMaxBytes,
RewriteOptions::kHideRefererUsingMeta,
RewriteOptions::kHttpCacheCompressionLevel,
RewriteOptions::kIdleFlushTimeMs,
RewriteOptions::kImageInlineMaxBytes,
RewriteOptions::kImageJpegNumProgressiveScans,
RewriteOptions::kImageJpegNumProgressiveScansForSmallScreens,
RewriteOptions::kImageJpegQualityForSaveData,
RewriteOptions::kImageJpegRecompressionQuality,
RewriteOptions::kImageJpegRecompressionQualityForSmallScreens,
RewriteOptions::kImageLimitOptimizedPercent,
RewriteOptions::kImageLimitRenderedAreaPercent,
RewriteOptions::kImageLimitResizeAreaPercent,
RewriteOptions::kImageMaxRewritesAtOnce,
RewriteOptions::kImagePreserveURLs,
RewriteOptions::kImageRecompressionQuality,
RewriteOptions::kImageResolutionLimitBytes,
RewriteOptions::kImageWebpQualityForSaveData,
RewriteOptions::kImageWebpRecompressionQuality,
RewriteOptions::kImageWebpRecompressionQualityForSmallScreens,
RewriteOptions::kImageWebpAnimatedRecompressionQuality,
RewriteOptions::kImageWebpTimeoutMs,
RewriteOptions::kImplicitCacheTtlMs,
RewriteOptions::kIncreaseSpeedTracking,
RewriteOptions::kInlineOnlyCriticalImages,
RewriteOptions::kInlineResourcesWithoutExplicitAuthorization,
RewriteOptions::kInPlacePreemptiveRewriteCss,
RewriteOptions::kInPlacePreemptiveRewriteCssImages,
RewriteOptions::kInPlacePreemptiveRewriteImages,
RewriteOptions::kInPlacePreemptiveRewriteJavascript,
RewriteOptions::kInPlaceResourceOptimization,
RewriteOptions::kInPlaceRewriteDeadlineMs,
RewriteOptions::kInPlaceWaitForOptimized,
RewriteOptions::kJsInlineMaxBytes,
RewriteOptions::kJsOutlineMinBytes,
RewriteOptions::kJsPreserveURLs,
RewriteOptions::kLazyloadImagesAfterOnload,
RewriteOptions::kLazyloadImagesBlankUrl,
RewriteOptions::kLoadFromFileCacheTtlMs,
RewriteOptions::kLogBackgroundRewrite,
RewriteOptions::kLogMobilizationSamples,
RewriteOptions::kLogRewriteTiming,
RewriteOptions::kLogUrlIndices,
RewriteOptions::kLowercaseHtmlNames,
RewriteOptions::kMaxCacheableResponseContentLength,
RewriteOptions::kMaxCombinedCssBytes,
RewriteOptions::kMaxCombinedJsBytes,
RewriteOptions::kMaxHtmlCacheTimeMs,
RewriteOptions::kMaxHtmlParseBytes,
RewriteOptions::kMaxImageBytesForWebpInCss,
RewriteOptions::kMaxImageSizeLowResolutionBytes,
RewriteOptions::kMaxInlinedPreviewImagesIndex,
RewriteOptions::kMaxLowResImageSizeBytes,
RewriteOptions::kMaxLowResToHighResImageSizePercentage,
RewriteOptions::kMaxPrefetchJsElements,
RewriteOptions::kMaxRewriteInfoLogSize,
RewriteOptions::kMaxUrlSegmentSize,
RewriteOptions::kMaxUrlSize,
RewriteOptions::kMetadataCacheStalenessThresholdMs,
RewriteOptions::kMinCacheTtlMs,
RewriteOptions::kMinImageSizeLowResolutionBytes,
RewriteOptions::kMinResourceCacheTimeToRewriteMs,
RewriteOptions::kMobBeaconCategory,
RewriteOptions::kMobBeaconUrl,
RewriteOptions::kMobConfig,
RewriteOptions::kMobConversionId,
RewriteOptions::kMobIframe,
RewriteOptions::kMobIframeDisable,
RewriteOptions::kMobIframeViewport,
RewriteOptions::kMobLayout,
RewriteOptions::kMobMapConversionLabel,
RewriteOptions::kMobMapLocation,
RewriteOptions::kMobNav,
RewriteOptions::kMobLabeledMode,
RewriteOptions::kMobNavClasses,
RewriteOptions::kMobPhoneConversionLabel,
RewriteOptions::kMobPhoneNumber,
RewriteOptions::kMobStatic,
RewriteOptions::kMobTheme,
RewriteOptions::kModifyCachingHeaders,
RewriteOptions::kNoop,
RewriteOptions::kNoTransformOptimizedImages,
RewriteOptions::kNonCacheablesForCachePartialHtml,
RewriteOptions::kObliviousPagespeedUrls,
RewriteOptions::kOptionCookiesDurationMs,
RewriteOptions::kOverrideCachingTtlMs,
RewriteOptions::kPreserveSubresourceHints,
RewriteOptions::kPreserveUrlRelativity,
RewriteOptions::kPrivateNotVaryForIE,
RewriteOptions::kProactiveResourceFreshening,
RewriteOptions::kProactivelyFreshenUserFacingRequest,
RewriteOptions::kProgressiveJpegMinBytes,
RewriteOptions::kPubliclyCacheMismatchedHashesExperimental,
RewriteOptions::kRejectBlacklisted,
RewriteOptions::kRejectBlacklistedStatusCode,
RewriteOptions::kRemoteConfigurationTimeoutMs,
RewriteOptions::kRemoteConfigurationUrl,
RewriteOptions::kReportUnloadTime,
RewriteOptions::kRequestOptionOverride,
RewriteOptions::kRespectVary,
RewriteOptions::kRespectXForwardedProto,
RewriteOptions::kResponsiveImageDensities,
RewriteOptions::kRewriteDeadlineMs,
RewriteOptions::kRewriteLevel,
RewriteOptions::kRewriteRandomDropPercentage,
RewriteOptions::kRewriteUncacheableResources,
RewriteOptions::kRunningExperiment,
RewriteOptions::kServeGhostClickBusterWithSplitHtml,
RewriteOptions::kServeSplitHtmlInTwoChunks,
RewriteOptions::kServeStaleIfFetchError,
RewriteOptions::kServeStaleWhileRevalidateThresholdSec,
RewriteOptions::kServeWebpToAnyAgent,
RewriteOptions::kServeXhrAccessControlHeaders,
RewriteOptions::kStickyQueryParameters,
RewriteOptions::kSupportNoScriptEnabled,
RewriteOptions::kTestOnlyPrioritizeCriticalCssDontApplyOriginalCss,
RewriteOptions::kUrlSigningKey,
RewriteOptions::kUseAnalyticsJs,
RewriteOptions::kUseBlankImageForInlinePreview,
RewriteOptions::kUseExperimentalJsMinifier,
RewriteOptions::kUseFallbackPropertyCacheValues,
RewriteOptions::kUseSelectorsForCriticalCss,
RewriteOptions::kUseSmartDiffInBlink,
RewriteOptions::kXModPagespeedHeaderValue,
RewriteOptions::kXPsaBlockingRewrite,
};
// Check that every option can be looked up by name.
std::set<StringPiece> tested_names;
for (int i = 0; i < arraysize(option_names); ++i) {
EXPECT_TRUE(NULL != RewriteOptions::LookupOptionByName(option_names[i]))
<< option_names[i] << " cannot be looked up by name!";
tested_names.insert(option_names[i]);
}
// Now go through the named options in all_properties_ and check that each
// one has been tested.
int named_properties = 0;
for (int i = 0, n = RewriteOptions::all_properties_->size(); i < n; ++i) {
StringPiece name =
RewriteOptions::all_properties_->property(i)->option_name();
if (!name.empty()) {
++named_properties;
EXPECT_NE(tested_names.end(), tested_names.find(name))
<< name << " has not been tested!";
}
}
EXPECT_EQ(named_properties, tested_names.size());
// Check that case doesn't matter when looking up directives.
EXPECT_TRUE(NULL != RewriteOptions::LookupOptionByName("EnableRewriting"));
EXPECT_TRUE(NULL != RewriteOptions::LookupOptionByName("eNaBlErEWrItIng"));
}
// All the non-base option names are explicitly enumerated here. Modifications
// are handled by the explicit tests. Additions/deletions are NOT handled.
TEST_F(RewriteOptionsTest, LookupNonBaseOptionByNameTest) {
// Use macro so that the failure message tells us the name of the option
// failing the test; using a function would obscure that.
#define FailLookupOptionByName(name) \
EXPECT_TRUE(NULL == RewriteOptions::LookupOptionByName(name))
// The following are not accessible by name, they are handled explicitly
// by name comparison. We could/should test them all using their setters,
// though -some- of them are (cf. ParseAndSetOptionFromName1/2/3 following).
// Non-scalar options
FailLookupOptionByName(RewriteOptions::kAllow);
FailLookupOptionByName(RewriteOptions::kBlockingRewriteRefererUrls);
FailLookupOptionByName(RewriteOptions::kDisableFilters);
FailLookupOptionByName(RewriteOptions::kDisallow);
FailLookupOptionByName(RewriteOptions::kDistributableFilters);
FailLookupOptionByName(RewriteOptions::kDomain);
FailLookupOptionByName(RewriteOptions::kDownstreamCachePurgeLocationPrefix);
FailLookupOptionByName(RewriteOptions::kEnableFilters);
FailLookupOptionByName(RewriteOptions::kExperimentVariable);
FailLookupOptionByName(RewriteOptions::kExperimentSpec);
FailLookupOptionByName(RewriteOptions::kForbidFilters);
FailLookupOptionByName(RewriteOptions::kRetainComment);
// 2-arg options
FailLookupOptionByName(RewriteOptions::kCustomFetchHeader);
FailLookupOptionByName(RewriteOptions::kLoadFromFile);
FailLookupOptionByName(RewriteOptions::kLoadFromFileMatch);
FailLookupOptionByName(RewriteOptions::kLoadFromFileRule);
FailLookupOptionByName(RewriteOptions::kLoadFromFileRuleMatch);
FailLookupOptionByName(RewriteOptions::kMapOriginDomain);
FailLookupOptionByName(RewriteOptions::kMapProxyDomain);
FailLookupOptionByName(RewriteOptions::kMapRewriteDomain);
FailLookupOptionByName(RewriteOptions::kShardDomain);
// 3-arg options
FailLookupOptionByName(RewriteOptions::kUrlValuedAttribute);
FailLookupOptionByName(RewriteOptions::kLibrary);
// system/ and apache/ options.
FailLookupOptionByName(RewriteOptions::kCacheFlushFilename);
FailLookupOptionByName(RewriteOptions::kCacheFlushPollIntervalSec);
FailLookupOptionByName(RewriteOptions::kCompressMetadataCache);
FailLookupOptionByName(RewriteOptions::kFetchFromModSpdy);
FailLookupOptionByName(RewriteOptions::kFetchHttps);
FailLookupOptionByName(RewriteOptions::kFetcherProxy);
FailLookupOptionByName(RewriteOptions::kFileCacheCleanIntervalMs);
FailLookupOptionByName(RewriteOptions::kFileCachePath);
FailLookupOptionByName(RewriteOptions::kFileCacheCleanSizeKb);
FailLookupOptionByName(RewriteOptions::kFileCacheCleanInodeLimit);
FailLookupOptionByName(RewriteOptions::kLogDir);
FailLookupOptionByName(RewriteOptions::kLruCacheByteLimit);
FailLookupOptionByName(RewriteOptions::kLruCacheKbPerProcess);
FailLookupOptionByName(RewriteOptions::kMemcachedServers);
FailLookupOptionByName(RewriteOptions::kMemcachedThreads);
FailLookupOptionByName(RewriteOptions::kMemcachedTimeoutUs);
FailLookupOptionByName(RewriteOptions::kRateLimitBackgroundFetches);
FailLookupOptionByName(RewriteOptions::kUseSharedMemLocking);
FailLookupOptionByName(RewriteOptions::kSlurpDirectory);
FailLookupOptionByName(RewriteOptions::kSlurpFlushLimit);
FailLookupOptionByName(RewriteOptions::kSlurpReadOnly);
FailLookupOptionByName(RewriteOptions::kStatisticsEnabled);
FailLookupOptionByName(RewriteOptions::kStatisticsLoggingEnabled);
FailLookupOptionByName(RewriteOptions::kStatisticsLoggingChartsCSS);
FailLookupOptionByName(RewriteOptions::kStatisticsLoggingChartsJS);
FailLookupOptionByName(RewriteOptions::kStatisticsLoggingIntervalMs);
FailLookupOptionByName(RewriteOptions::kStatisticsLoggingMaxFileSizeKb);
FailLookupOptionByName(RewriteOptions::kTestProxy);
FailLookupOptionByName(RewriteOptions::kTestProxySlurp);
}
TEST_F(RewriteOptionsTest, ParseAndSetOptionFromName1) {
GoogleString msg;
NullMessageHandler handler;
// Unknown option.
EXPECT_EQ(RewriteOptions::kOptionNameUnknown,
options_.ParseAndSetOptionFromName1("arghh", "", &msg, &handler));
// Simple scalar option.
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName1("JsInlineMaxBytes", "42",
&msg, &handler));
EXPECT_EQ(42, options_.js_inline_max_bytes());
// Scalar with invalid value.
EXPECT_EQ(RewriteOptions::kOptionValueInvalid,
options_.ParseAndSetOptionFromName1("JsInlineMaxBytes", "one",
&msg, &handler));
EXPECT_EQ("Cannot set option JsInlineMaxBytes to one. ", msg);
// Complex, valid value.
EXPECT_FALSE(options_.Enabled(RewriteOptions::kDebug));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kOutlineCss));
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName1(
"EnableFilters", "debug,outline_css", &msg, &handler));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kDebug));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kOutlineCss));
// Complex, invalid value.
EXPECT_EQ(RewriteOptions::kOptionValueInvalid,
options_.ParseAndSetOptionFromName1(
"EnableFilters", "no_such_filter", &msg, &handler));
EXPECT_EQ("Failed to enable some filters.", msg);
// Disallow/Allow.
options_.Disallow("*");
EXPECT_FALSE(options_.IsAllowed("example.com"));
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName1(
RewriteOptions::kAllow, "*.com", &msg, &handler));
EXPECT_TRUE(options_.IsAllowed("example.com"));
EXPECT_TRUE(options_.IsAllowed("evil.com"));
EXPECT_FALSE(options_.IsAllowed("example.org"));
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName1(
RewriteOptions::kDisallow, "*evil*", &msg, &handler));
EXPECT_TRUE(options_.IsAllowed("example.com"));
EXPECT_FALSE(options_.IsAllowed("evil.com"));
// Disable/forbid filters (enable covered above).
options_.EnableFilter(RewriteOptions::kDebug);
options_.EnableFilter(RewriteOptions::kOutlineCss);
EXPECT_TRUE(options_.Enabled(RewriteOptions::kDebug));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kOutlineCss));
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName1(
RewriteOptions::kDisableFilters, "debug,outline_css",
&msg, &handler));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kDebug));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kOutlineCss));
EXPECT_EQ(RewriteOptions::kOptionValueInvalid,
options_.ParseAndSetOptionFromName1(
RewriteOptions::kDisableFilters, "nosuch",
&msg, &handler));
EXPECT_EQ("Failed to disable some filters.", msg);
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName1(
RewriteOptions::kForbidFilters, "debug",
&msg, &handler));
EXPECT_FALSE(
options_.Forbidden(options_.FilterId(RewriteOptions::kOutlineCss)));
EXPECT_TRUE(
options_.Forbidden(options_.FilterId(RewriteOptions::kDebug)));
EXPECT_EQ(RewriteOptions::kOptionValueInvalid,
options_.ParseAndSetOptionFromName1(
RewriteOptions::kForbidFilters, "nosuch",
&msg, &handler));
EXPECT_EQ("Failed to forbid some filters.", msg);
// Domain.
GoogleUrl main("http://example.com");
GoogleUrl content("http://static.example.com");
EXPECT_FALSE(options_.domain_lawyer()->IsDomainAuthorized(main, content));
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName1(
RewriteOptions::kDomain, "static.example.com",
&msg, &handler));
EXPECT_TRUE(options_.domain_lawyer()->IsDomainAuthorized(main, content)) <<
options_.domain_lawyer()->ToString();
// Downstream cache purge location prefix.
// 1) Valid location.
GoogleUrl valid_downstream_cache("http://caching-layer.example.com:8118");
EXPECT_FALSE(options_.domain_lawyer()->IsOriginKnown(valid_downstream_cache));
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName1(
RewriteOptions::kDownstreamCachePurgeLocationPrefix,
"http://caching-layer.example.com:8118/mypurgepath",
&msg, &handler));
EXPECT_TRUE(options_.domain_lawyer()->IsOriginKnown(valid_downstream_cache));
EXPECT_EQ("http://caching-layer.example.com:8118/mypurgepath",
options_.downstream_cache_purge_location_prefix());
// 2) Invalid location.
EXPECT_EQ(RewriteOptions::kOptionValueInvalid,
options_.ParseAndSetOptionFromName1(
RewriteOptions::kDownstreamCachePurgeLocationPrefix,
"",
&msg, &handler));
EXPECT_EQ("Downstream cache purge location prefix is invalid.", msg);
// Experiments.
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName1(
RewriteOptions::kExperimentSpec,
"id=2;enable=recompress_png;percent=50",
&msg, &handler));
RewriteOptions::ExperimentSpec* spec = options_.GetExperimentSpec(2);
ASSERT_TRUE(spec != NULL);
EXPECT_EQ(2, spec->id());
EXPECT_EQ(50, spec->percent());
EXPECT_EQ(1, spec->enabled_filters().size());
EXPECT_TRUE(
spec->enabled_filters().IsSet(RewriteOptions::kRecompressPng));
EXPECT_EQ(RewriteOptions::kOptionValueInvalid,
options_.ParseAndSetOptionFromName1(
RewriteOptions::kExperimentSpec, "@)#@(#@(#@)((#)@",
&msg, &handler));
EXPECT_EQ("not a valid experiment spec", msg);
EXPECT_NE(4, options_.experiment_ga_slot());
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName1(
RewriteOptions::kExperimentVariable, "4", &msg, &handler));
EXPECT_EQ(4, options_.experiment_ga_slot());
EXPECT_EQ(RewriteOptions::kOptionValueInvalid,
options_.ParseAndSetOptionFromName1(
RewriteOptions::kExperimentVariable, "10", &msg, &handler));
EXPECT_EQ("must be an integer between 1 and 5", msg);
// Retain comment.
EXPECT_FALSE(options_.IsRetainedComment("important"));
EXPECT_FALSE(options_.IsRetainedComment("silly"));
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName1(
RewriteOptions::kRetainComment, "*port*", &msg, &handler));
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName1(
RewriteOptions::kBlockingRewriteRefererUrls,
"http://www.test.com/*", &msg, &handler));
EXPECT_TRUE(options_.IsBlockingRewriteRefererUrlPatternPresent());
EXPECT_TRUE(options_.IsBlockingRewriteEnabledForReferer(
"http://www.test.com/"));
EXPECT_FALSE(options_.IsBlockingRewriteEnabledForReferer(
"http://www.testa.com/"));
EXPECT_TRUE(options_.IsRetainedComment("important"));
EXPECT_FALSE(options_.IsRetainedComment("silly"));
}
TEST_F(RewriteOptionsTest, ParseAndSetOptionFromName2) {
GoogleString msg;
NullMessageHandler handler;
// Unknown option.
EXPECT_EQ(RewriteOptions::kOptionNameUnknown,
options_.ParseAndSetOptionFromName2("arghh", "", "",
&msg, &handler));
// Option mapped, but not a 2-argument.
EXPECT_EQ(RewriteOptions::kOptionNameUnknown,
options_.ParseAndSetOptionFromName2("JsInlineMaxBytes", "", "",
&msg, &handler));
// Valid value.
EXPECT_EQ(0, options_.num_custom_fetch_headers());
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName2(
"CustomFetchHeader", "header", "value", &msg, &handler));
ASSERT_EQ(1, options_.num_custom_fetch_headers());
EXPECT_EQ("header", options_.custom_fetch_header(0)->name);
EXPECT_EQ("value", options_.custom_fetch_header(0)->value);
// Invalid value.
EXPECT_EQ(RewriteOptions::kOptionValueInvalid,
options_.ParseAndSetOptionFromName2(
"LoadFromFileRule", "weird", "42", &msg, &handler));
EXPECT_EQ("Argument 1 must be either 'Allow' or 'Disallow'", msg);
// Various LoadFromFile options.
GoogleString file_out;
GoogleUrl url1("http://www.example.com/a.css");
EXPECT_FALSE(
options_.file_load_policy()->ShouldLoadFromFile(url1, &file_out));
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName2(
RewriteOptions::kLoadFromFile, "http://www.example.com",
"/example/", &msg, &handler));
EXPECT_TRUE(
options_.file_load_policy()->ShouldLoadFromFile(url1, &file_out));
EXPECT_EQ("/example/a.css", file_out);
GoogleUrl url2("http://www.example.com/styles/b.css");
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName2(
RewriteOptions::kLoadFromFileMatch,
"^http://www.example.com/styles/([^/]*)", "/style/\\1",
&msg, &handler));
EXPECT_TRUE(
options_.file_load_policy()->ShouldLoadFromFile(url2, &file_out));
EXPECT_EQ("/style/b.css", file_out);
EXPECT_EQ(RewriteOptions::kOptionValueInvalid,
options_.ParseAndSetOptionFromName2(
RewriteOptions::kLoadFromFileMatch,
"[a-", "/style/\\1",
&msg, &handler));
EXPECT_EQ("File mapping regular expression must match beginning of string. "
"(Must start with '^'.)", msg);
EXPECT_EQ(RewriteOptions::kOptionValueInvalid,
options_.ParseAndSetOptionFromName2(
RewriteOptions::kLoadFromFileRuleMatch,
"Allow", "[a-",
&msg, &handler));
// Not testing the message since it's RE2-originated.
GoogleUrl url3("http://www.example.com/images/a.png");
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName2(
RewriteOptions::kLoadFromFileRule,
"Disallow", "/example/images/",
&msg, &handler));
EXPECT_FALSE(
options_.file_load_policy()->ShouldLoadFromFile(url3, &file_out));
GoogleUrl url4("http://www.example.com/images/a.jpeg");
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName2(
RewriteOptions::kLoadFromFileRuleMatch,
"Allow", "\\.jpeg", &msg, &handler));
EXPECT_FALSE(
options_.file_load_policy()->ShouldLoadFromFile(url3, &file_out));
EXPECT_TRUE(
options_.file_load_policy()->ShouldLoadFromFile(url4, &file_out));
EXPECT_EQ("/example/images/a.jpeg", file_out);
// Domain lawyer options.
scoped_ptr<RewriteOptions> options2(new RewriteOptions(&thread_system_));
EXPECT_EQ(RewriteOptions::kOptionOk,
options2->ParseAndSetOptionFromName2(
RewriteOptions::kMapOriginDomain,
"localhost/example", "www.example.com",
&msg, &handler));
EXPECT_EQ("http://localhost/example/\n"
"http://www.example.com/ Auth "
"OriginDomain:http://localhost/example/\n",
options2->domain_lawyer()->ToString());
scoped_ptr<RewriteOptions> options3(new RewriteOptions(&thread_system_));
// This is an option 2 or 3, so test 2 here and 3 below.
EXPECT_EQ(RewriteOptions::kOptionOk,
options3->ParseAndSetOptionFromName3(
RewriteOptions::kMapProxyDomain,
"mainsite.com/static", "static.mainsite.com", "",
&msg, &handler));
EXPECT_EQ("http://mainsite.com/static/ Auth "
"ProxyOriginDomain:http://static.mainsite.com/\n"
"http://static.mainsite.com/ Auth "
"ProxyDomain:http://mainsite.com/static/\n",
options3->domain_lawyer()->ToString());
scoped_ptr<RewriteOptions> options4(new RewriteOptions(&thread_system_));
EXPECT_EQ(RewriteOptions::kOptionOk,
options4->ParseAndSetOptionFromName2(
RewriteOptions::kMapRewriteDomain,
"cdn.example.com", "*example.com",
&msg, &handler));
EXPECT_EQ("http://*example.com/ Auth RewriteDomain:http://cdn.example.com/\n"
"http://cdn.example.com/ Auth\n",
options4->domain_lawyer()->ToString());
scoped_ptr<RewriteOptions> options5(new RewriteOptions(&thread_system_));
EXPECT_EQ(RewriteOptions::kOptionOk,
options5->ParseAndSetOptionFromName2(
RewriteOptions::kShardDomain,
"https://www.example.com",
"https://example1.cdn.com,https://example2.cdn.com",
&msg, &handler));
EXPECT_EQ("https://example1.cdn.com/ Auth "
"RewriteDomain:https://www.example.com/\n"
"https://example2.cdn.com/ Auth "
"RewriteDomain:https://www.example.com/\n"
"https://www.example.com/ Auth Shards:"
"{https://example1.cdn.com/, "
"https://example2.cdn.com/}\n",
options5->domain_lawyer()->ToString());
}
TEST_F(RewriteOptionsTest, ParseAndSetOptionFromName3) {
GoogleString msg;
NullMessageHandler handler;
// Unknown option.
EXPECT_EQ(RewriteOptions::kOptionNameUnknown,
options_.ParseAndSetOptionFromName3("arghh", "", "", "",
&msg, &handler));
// Option mapped, but not a 2-argument.
EXPECT_EQ(RewriteOptions::kOptionNameUnknown,
options_.ParseAndSetOptionFromName3("JsInlineMaxBytes", "", "", "",
&msg, &handler));
// Valid value.
EXPECT_EQ(0, options_.num_url_valued_attributes());
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName3(
"UrlValuedAttribute", "span", "src", "Hyperlink",
&msg, &handler));
ASSERT_EQ(1, options_.num_url_valued_attributes());
StringPiece element, attribute;
semantic_type::Category category;
options_.UrlValuedAttribute(0, &element, &attribute, &category);
EXPECT_EQ("span", element);
EXPECT_EQ("src", attribute);
EXPECT_EQ(semantic_type::kHyperlink, category);
// Invalid value.
EXPECT_EQ(RewriteOptions::kOptionValueInvalid,
options_.ParseAndSetOptionFromName3(
"UrlValuedAttribute", "span", "src", "nonsense",
&msg, &handler));
EXPECT_EQ("Invalid resource category: nonsense", msg);
// Domain lawyer.
scoped_ptr<RewriteOptions> options(new RewriteOptions(&thread_system_));
EXPECT_EQ(RewriteOptions::kOptionOk,
options->ParseAndSetOptionFromName3(
RewriteOptions::kMapProxyDomain,
"myproxy.com/static",
"static.origin.com",
"myproxy.cdn.com",
&msg, &handler));
EXPECT_EQ("http://myproxy.cdn.com/ Auth "
"ProxyOriginDomain:http://static.origin.com/\n"
"http://myproxy.com/static/ Auth "
"RewriteDomain:http://myproxy.cdn.com/ "
"ProxyOriginDomain:http://static.origin.com/\n"
"http://static.origin.com/ Auth "
"ProxyDomain:http://myproxy.cdn.com/\n",
options->domain_lawyer()->ToString());
options_.EnableFilter(RewriteOptions::kCanonicalizeJavascriptLibraries);
GoogleString sig;
options_.javascript_library_identification()->AppendSignature(&sig);
EXPECT_EQ("", sig);
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName3(
RewriteOptions::kLibrary, "43567", "5giEj_jl-Ag5G8",
"http://www.example.com/url.js",
&msg, &handler));
sig.clear();
options_.javascript_library_identification()->AppendSignature(&sig);
EXPECT_EQ("S:43567_H:5giEj_jl-Ag5G8_J:http://www.example.com/url.js", sig);
EXPECT_EQ(RewriteOptions::kOptionValueInvalid,
options_.ParseAndSetOptionFromName3(
RewriteOptions::kLibrary, "43567", "#@#)@(#@)",
"http://www.example.com/url.js",
&msg, &handler));
EXPECT_EQ("Format is size md5 url; bad md5 #@#)@(#@) or "
"URL http://www.example.com/url.js", msg);
}
TEST_F(RewriteOptionsTest, SetOptionFromQuery) {
// Unknown option.
EXPECT_EQ(RewriteOptions::kOptionNameUnknown,
options_.SetOptionFromQuery("arghh", ""));
// Known option with a bad value.
EXPECT_EQ(RewriteOptions::kOptionValueInvalid,
options_.SetOptionFromQuery(RewriteOptions::kCssFlattenMaxBytes,
"nuh-uh"));
// Known option with a good value.
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.SetOptionFromQuery(RewriteOptions::kCssFlattenMaxBytes,
"123"));
}
TEST_F(RewriteOptionsTest, ExperimentSpecTest) {
// Test that we handle experiment specs properly, and that when we set the
// options to one experiment or another, it works.
NullMessageHandler handler;
options_.SetRewriteLevel(RewriteOptions::kCoreFilters);
options_.set_ga_id("UA-111111-1");
// Set the default slot to 4.
options_.set_experiment_ga_slot(4);
EXPECT_FALSE(options_.AddExperimentSpec("id=0", &handler));
EXPECT_TRUE(options_.AddExperimentSpec(
"id=7;percent=10;level=CoreFilters;enabled=sprite_images;"
"disabled=inline_css;options=InlineJavascriptMaxBytes=600000", &handler));
// Extra spaces to test whitespace handling.
EXPECT_TRUE(options_.AddExperimentSpec("id=2; percent=15;ga=UA-2222-1;"
"disabled=insert_ga ;slot=3;",
&handler));
// Invalid slot - make sure the spec still gets added, and the slot defaults
// to the global slot (4).
EXPECT_TRUE(options_.AddExperimentSpec("id=17;percent=3;slot=8", &handler));
options_.SetExperimentState(7);
EXPECT_EQ(RewriteOptions::kCoreFilters, options_.level());
EXPECT_TRUE(options_.Enabled(RewriteOptions::kSpriteImages));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kInlineCss));
// This experiment didn't have a ga_id, so make sure we still have the
// global ga_id.
EXPECT_EQ("UA-111111-1", options_.ga_id());
EXPECT_EQ(4, options_.experiment_ga_slot());
// insert_ga can not be disabled in any experiment because that filter injects
// the instrumentation we use to collect the data.
options_.SetExperimentState(2);
EXPECT_FALSE(options_.Enabled(RewriteOptions::kInlineCss));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kSpriteImages));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kLeftTrimUrls));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kInsertGA));
EXPECT_EQ(3, options_.experiment_ga_slot());
// This experiment specified a ga_id, so make sure that we set it.
EXPECT_EQ("UA-2222-1", options_.ga_id());
options_.SetExperimentState(17);
EXPECT_EQ(4, options_.experiment_ga_slot());
options_.SetExperimentState(7);
EXPECT_EQ("a", options_.GetExperimentStateStr());
options_.SetExperimentState(2);
EXPECT_EQ("b", options_.GetExperimentStateStr());
options_.SetExperimentState(17);
EXPECT_EQ("c", options_.GetExperimentStateStr());
options_.SetExperimentState(experiment::kExperimentNotSet);
EXPECT_EQ("", options_.GetExperimentStateStr());
options_.SetExperimentState(experiment::kNoExperiment);
EXPECT_EQ("", options_.GetExperimentStateStr());
options_.SetExperimentStateStr("a");
EXPECT_EQ("a", options_.GetExperimentStateStr());
options_.SetExperimentStateStr("b");
EXPECT_EQ("b", options_.GetExperimentStateStr());
options_.SetExperimentStateStr("c");
EXPECT_EQ("c", options_.GetExperimentStateStr());
// Invalid state index 'd'; we only added three specs above.
options_.SetExperimentStateStr("d");
// No effect on the experiment state; stay with 'c' from before.
EXPECT_EQ("c", options_.GetExperimentStateStr());
// Check a state index that will be out of bounds in the other direction.
options_.SetExperimentStateStr("`");
// Still no effect on the experiment state.
EXPECT_EQ("c", options_.GetExperimentStateStr());
// Check that we have a maximum size of 26 concurrent experiment specs.
// Get us up to 26.
for (int i = options_.num_experiments(); i < 26 ; ++i) {
int tmp_id = i+100; // Don't want conflict with experiments added above.
EXPECT_TRUE(options_.AddExperimentSpec(
StrCat("id=", IntegerToString(tmp_id),
";percent=1;default"), &handler));
}
EXPECT_EQ(26, options_.num_experiments());
// Object to adding a 27th.
EXPECT_FALSE(options_.AddExperimentSpec("id=200;percent=1;default",
&handler));
}
TEST_F(RewriteOptionsTest, DefaultExperimentSpecTest) {
NullMessageHandler handler;
options_.SetRewriteLevel(RewriteOptions::kCoreFilters);
options_.EnableFilter(RewriteOptions::kStripScripts);
options_.EnableFilter(RewriteOptions::kSpriteImages);
options_.set_ga_id("UA-111111-1");
// Check that we can combine 'default', 'enable' & 'disable', and 'options'.
// strip_scripts was expressly enabled in addition to core and should stay on.
// extend_cache_css is on because it's a core filter and should stay on.
// defer_javascript is off by default but turned on by our spec.
// local_storage_cache is off by default but turned on by our spec.
// inline_css is on by default but turned off by our spec.
// CssInlineMaxBytes is 1024 by default but set to 66 by our spec.
options_.SetExperimentState(experiment::kNoExperiment);
EXPECT_TRUE(options_.Enabled(RewriteOptions::kExtendCacheCss));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kStripScripts));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kSpriteImages));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kDeferJavascript));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kLocalStorageCache));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kInlineCss));
EXPECT_TRUE(options_.AddExperimentSpec(
"id=18;percent=0;default"
";enable=defer_javascript,local_storage_cache"
";disable=inline_css,sprite_images"
";options=CssInlineMaxBytes=66", &handler));
options_.SetExperimentState(18);
EXPECT_TRUE(options_.Enabled(RewriteOptions::kExtendCacheCss));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kStripScripts));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kSpriteImages));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kDeferJavascript));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kLocalStorageCache));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kInlineCss));
}
TEST_F(RewriteOptionsTest, PreserveURLDefaults) {
// This test serves as a warning. If you enable preserve URLs by default then
// many unit tests will fail due to filters being omitted from the HTML path.
// Further, preserve_urls is not explicitly tested for the 'false' case, it is
// assumed to be tested by the normal unit tests since the default value is
// false.
EXPECT_FALSE(options_.image_preserve_urls());
EXPECT_FALSE(options_.css_preserve_urls());
EXPECT_FALSE(options_.js_preserve_urls());
}
TEST_F(RewriteOptionsTest, RewriteDeadlineTest) {
EXPECT_EQ(RewriteOptions::kDefaultRewriteDeadlineMs,
options_.rewrite_deadline_ms());
options_.set_rewrite_deadline_ms(40);
EXPECT_EQ(40, options_.rewrite_deadline_ms());
}
TEST_F(RewriteOptionsTest, ExperimentPrintTest) {
NullMessageHandler handler;
options_.SetRewriteLevel(RewriteOptions::kCoreFilters);
options_.set_ga_id("UA-111111-1");
options_.set_running_experiment(true);
EXPECT_FALSE(options_.AddExperimentSpec("id=2;enabled=rewrite_css;",
&handler));
EXPECT_TRUE(options_.AddExperimentSpec("id=1;percent=15;default", &handler));
EXPECT_TRUE(options_.AddExperimentSpec("id=7;percent=15;level=AllFilters;",
&handler));
EXPECT_TRUE(options_.AddExperimentSpec("id=2;percent=15;enabled=rewrite_css;"
"options=InlineCssMaxBytes=4096,"
"InlineJsMaxBytes=4;"
"ga_id=122333-4", &handler));
options_.SetExperimentState(-7);
// No experiment changes.
EXPECT_EQ("", options_.ToExperimentDebugString());
EXPECT_EQ("", options_.ToExperimentString());
options_.SetExperimentState(1);
EXPECT_EQ("Experiment: 1; id=1;ga=UA-111111-1;percent=15;default",
options_.ToExperimentDebugString());
EXPECT_EQ("Experiment: 1", options_.ToExperimentString());
options_.SetExperimentState(7);
EXPECT_EQ("Experiment: 7", options_.ToExperimentString());
options_.SetExperimentState(2);
// Note the options= section.
EXPECT_EQ("Experiment: 2; id=2;ga=122333-4;percent=15;enabled=cf;"
"options=InlineCssMaxBytes=4096,InlineJsMaxBytes=4",
options_.ToExperimentDebugString());
EXPECT_EQ("Experiment: 2", options_.ToExperimentString());
// Make sure we set the ga_id to the one specified by spec 2.
EXPECT_EQ("122333-4", options_.ga_id());
}
TEST_F(RewriteOptionsTest, ExperimentOptionsTestDefaultUnchanged) {
SetupTestExperimentSpecs();
// Default for this is 2048.
EXPECT_EQ(2048L, options_.css_inline_max_bytes());
}
TEST_F(RewriteOptionsTest, ExperimentOptionsTestCssInlineChange) {
SetupTestExperimentSpecs();
options_.SetExperimentState(1);
EXPECT_EQ(1024L, options_.css_inline_max_bytes());
}
TEST_F(RewriteOptionsTest, ExperimentOptionsTestCssInlineChangeToDefault) {
SetupTestExperimentSpecs();
options_.SetExperimentState(3);
EXPECT_EQ(2048L, options_.css_inline_max_bytes());
}
TEST_F(RewriteOptionsTest, ExperimentOptionsTestCssInlineChangeToInvalid) {
SetupTestExperimentSpecs();
options_.SetExperimentState(4);
EXPECT_EQ(2048L, options_.css_inline_max_bytes());
}
TEST_F(RewriteOptionsTest, ExperimentOptionsTestCssInlineWithIllegalOptions) {
SetupTestExperimentSpecs();
options_.SetExperimentState(5);
EXPECT_EQ(1024L, options_.css_inline_max_bytes());
}
TEST_F(RewriteOptionsTest, ExperimentOptionsTestMultipleOptions) {
SetupTestExperimentSpecs();
options_.SetExperimentState(6);
EXPECT_EQ(100L, options_.css_inline_max_bytes());
EXPECT_EQ(123L, options_.js_inline_max_bytes());
}
TEST_F(RewriteOptionsTest, ExperimentOptionsTestToString) {
SetupTestExperimentSpecs();
// Just compare the experiments, not the rest of the OptionsToString output.
GoogleString options_string = options_.OptionsToString();
StringPieceVector lines;
StringPieceVector experiments;;
SplitStringPieceToVector(options_string, "\n", &lines, true);
for (int i = 0, n = lines.size(); i < n; ++i) {
if (lines[i].starts_with("Experiment ")) {
experiments.push_back(lines[i]);
}
}
EXPECT_STREQ("Experiment id=1;percent=15;enabled=dj;"
"options=CssInlineMaxBytes=1024",
experiments[0]);
EXPECT_STREQ("Experiment id=2;percent=15;enabled=ri;"
"options=BogusOption=35",
experiments[1]);
EXPECT_STREQ("Experiment id=3;percent=15;enabled=dj",
experiments[2]);
EXPECT_STREQ("Experiment id=4;percent=15;enabled=dj;"
"options=CssInlineMaxBytes=Cabbage",
experiments[3]);
EXPECT_STREQ("Experiment id=5;percent=15;enabled=dj;"
"options=5=10,"
"6=9,"
"CssInlineMaxBytes=1024,"
"Potato=Carrot",
experiments[4]);
EXPECT_STREQ("Experiment id=6;percent=15;enabled=dj;"
"options=CssInlineMaxBytes=100,"
"JpegRecompresssionQuality=50,"
"JsInlineMaxBytes=123,"
"JsOutlineMinBytes=4096",
experiments[5]);
}
TEST_F(RewriteOptionsTest, ExperimentMergeTest) {
NullMessageHandler handler;
RewriteOptions::ExperimentSpec *spec = new
RewriteOptions::ExperimentSpec("id=1;percentage=15;"
"enable=defer_javascript;"
"options=CssInlineMaxBytes=100",
&options_, &handler);
RewriteOptions::ExperimentSpec *spec2 = new
RewriteOptions::ExperimentSpec("id=2;percentage=25;enable=resize_images;"
"options=CssInlineMaxBytes=125", &options_,
&handler);
options_.InsertExperimentSpecInVector(spec);
options_.InsertExperimentSpecInVector(spec2);
options_.SetExperimentState(1);
EXPECT_EQ(15, spec->percent());
EXPECT_EQ(1, spec->id());
EXPECT_TRUE(options_.Enabled(RewriteOptions::kDeferJavascript));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kResizeImages));
EXPECT_EQ(100L, options_.css_inline_max_bytes());
spec->Merge(*spec2);
options_.SetExperimentState(1);
EXPECT_EQ(25, spec->percent());
EXPECT_EQ(1, spec->id());
EXPECT_TRUE(options_.Enabled(RewriteOptions::kDeferJavascript));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kResizeImages));
EXPECT_EQ(125L, options_.css_inline_max_bytes());
}
TEST_F(RewriteOptionsTest, ExperimentOptionLifetimeTest) {
NullMessageHandler handler;
// This allocates a character array on the stack and initializes it with the
// specified string including a null terminator. The size of the array is
// taken from the length of the string. The array is ours to modify.
char str_spec[] = ("id=1;percentage=15;"
"enable=defer_javascript;"
"options=CssInlineMaxBytes=100");
EXPECT_TRUE(options_.AddExperimentSpec(str_spec, &handler));
// ExperimentSpec must not keep any references into str_spec because it's
// not guaranteed to stick around or stay constant. We modify str_spec to
// make sure ExperimentSpec hasn't kept a reference.
str_spec[sizeof(str_spec) - 2] = '9';
options_.SetExperimentState(1);
// If ExperimentSpec just kept pointers into str_spec then we'll get 109 here.
EXPECT_EQ(100L, options_.css_inline_max_bytes());
}
TEST_F(RewriteOptionsTest, ExperimentDeviceTypeParseTest) {
NullMessageHandler handler;
{
GoogleString spec_str("id=1;percent=15;"
"matches_device_type=desktop");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ(spec_str, spec.ToString());
EXPECT_TRUE(spec.matches_device_type(UserAgentMatcher::kDesktop));
EXPECT_FALSE(spec.matches_device_type(UserAgentMatcher::kTablet));
EXPECT_FALSE(spec.matches_device_type(UserAgentMatcher::kMobile));
}
{
GoogleString spec_str("id=1;percent=15;"
"matches_device_type=tablet,mobile");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ(spec_str, spec.ToString());
EXPECT_FALSE(spec.matches_device_type(UserAgentMatcher::kDesktop));
EXPECT_TRUE(spec.matches_device_type(UserAgentMatcher::kTablet));
EXPECT_TRUE(spec.matches_device_type(UserAgentMatcher::kMobile));
}
{
GoogleString spec_str("id=1;percent=15;"
"matches_device_type=desktop,tablet,mobile");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ(spec_str, spec.ToString());
EXPECT_TRUE(spec.matches_device_type(UserAgentMatcher::kDesktop));
EXPECT_TRUE(spec.matches_device_type(UserAgentMatcher::kTablet));
EXPECT_TRUE(spec.matches_device_type(UserAgentMatcher::kMobile));
}
{
GoogleString spec_str("id=1;percent=15");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ(spec_str, spec.ToString());
EXPECT_TRUE(spec.matches_device_type(UserAgentMatcher::kDesktop));
EXPECT_TRUE(spec.matches_device_type(UserAgentMatcher::kTablet));
EXPECT_TRUE(spec.matches_device_type(UserAgentMatcher::kMobile));
}
}
TEST_F(RewriteOptionsTest, ExperimentDeviceTypeRangeUnderflowDeathTest) {
RewriteOptions::ExperimentSpec spec(1);
UserAgentMatcher::DeviceType device_type(
static_cast<UserAgentMatcher::DeviceType>(-1));
#ifdef NDEBUG
EXPECT_FALSE(spec.matches_device_type(device_type));
#else
EXPECT_DEATH(spec.matches_device_type(device_type),
"DeviceType out of range:");
#endif
}
TEST_F(RewriteOptionsTest, ExperimentDeviceTypeRangeOverflowDeathTest) {
RewriteOptions::ExperimentSpec spec(1);
UserAgentMatcher::DeviceType device_type(UserAgentMatcher::kEndOfDeviceType);
#ifdef NDEBUG
EXPECT_FALSE(spec.matches_device_type(device_type));
#else
EXPECT_DEATH(spec.matches_device_type(device_type),
"DeviceType out of range:");
#endif
}
TEST_F(RewriteOptionsTest, DeviceTypeMergeTest) {
NullMessageHandler handler;
{
// From a spec with a device_type to one without.
RewriteOptions::ExperimentSpec spec1(
"id=1;percent=15;matches_device_type=mobile",
&options_, &handler);
RewriteOptions::ExperimentSpec spec2(
"id=2;percent=30",
&options_, &handler);
spec2.Merge(spec1);
EXPECT_EQ("id=2;percent=15;matches_device_type=mobile",
spec2.ToString());
}
{
// From a spec without a device_type to one with.
RewriteOptions::ExperimentSpec spec1(
"id=1;percent=15",
&options_, &handler);
RewriteOptions::ExperimentSpec spec2(
"id=2;percent=30;matches_device_type=mobile",
&options_, &handler);
spec2.Merge(spec1);
EXPECT_EQ("id=2;percent=15;matches_device_type=mobile",
spec2.ToString());
}
{
// Two specs, both with a device_type.
RewriteOptions::ExperimentSpec spec1(
"id=1;percent=15;matches_device_type=tablet",
&options_, &handler);
RewriteOptions::ExperimentSpec spec2(
"id=2;percent=30;matches_device_type=desktop",
&options_, &handler);
spec2.Merge(spec1);
EXPECT_EQ("id=2;percent=15;matches_device_type=tablet",
spec2.ToString());
}
{
// Neither spec has a device type.
RewriteOptions::ExperimentSpec spec1(
"id=1;percent=15",
&options_, &handler);
RewriteOptions::ExperimentSpec spec2(
"id=2;percent=30",
&options_, &handler);
spec2.Merge(spec1);
EXPECT_EQ("id=2;percent=15", spec2.ToString());
}
}
TEST_F(RewriteOptionsTest, AlternateOriginDomainMergeTest) {
GoogleMessageHandler handler;
{
// From a spec with an alternate_origin_domain to one without.
RewriteOptions::ExperimentSpec spec1(
"id=1;percent=15;alternate_origin_domain=foo.com:bar.com", &options_,
&handler);
RewriteOptions::ExperimentSpec spec2(
"id=2;percent=30",
&options_, &handler);
spec2.Merge(spec1);
EXPECT_EQ("id=2;percent=15;alternate_origin_domain=foo.com:bar.com",
spec2.ToString());
}
{
// From a spec without an alternate_origin_domain to one with.
RewriteOptions::ExperimentSpec spec1(
"id=1;percent=15",
&options_, &handler);
RewriteOptions::ExperimentSpec spec2(
"id=2;percent=30;alternate_origin_domain=foo.com:bar.com", &options_,
&handler);
spec2.Merge(spec1);
EXPECT_EQ("id=2;percent=15;alternate_origin_domain=foo.com:bar.com",
spec2.ToString());
}
{
// Two specs, both with alternate_origin_domains
RewriteOptions::ExperimentSpec spec1(
"id=1;percent=15;alternate_origin_domain=foo.com:bar.com", &options_,
&handler);
RewriteOptions::ExperimentSpec spec2(
"id=2;percent=30;alternate_origin_domain=baz.com:qux.com", &options_,
&handler);
spec2.Merge(spec1);
EXPECT_EQ("id=2;percent=15;alternate_origin_domain=foo.com:bar.com",
spec2.ToString());
}
}
TEST_F(RewriteOptionsTest, AlternateOriginDomainParseTest) {
GoogleMessageHandler handler;
{
// Single domain, no host header.
GoogleString spec_str(
"id=1;percent=15;"
"alternate_origin_domain=example.com:ref.example.com");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ(spec_str, spec.ToString());
DomainLawyer lawyer;
spec.ApplyAlternateOriginsToDomainLawyer(&lawyer, &handler);
VerifyMapOrigin(lawyer, "http://example.com", "http://ref.example.com/",
"example.com", false);
VerifyMapOrigin(lawyer, "https://example.com", "https://ref.example.com/",
"example.com", false);
}
{
// Single domain, port, no host header.
GoogleString spec_str(
"id=1;percent=15;"
"alternate_origin_domain=example.com:\"ref.example.com:99\"");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ(spec_str, spec.ToString());
DomainLawyer lawyer;
spec.ApplyAlternateOriginsToDomainLawyer(&lawyer, &handler);
VerifyMapOrigin(lawyer, "http://example.com",
"http://ref.example.com:99/", "example.com", false);
VerifyMapOrigin(lawyer, "https://example.com",
"https://ref.example.com:99/", "example.com", false);
}
{
// Single domain with host header.
GoogleString spec_str(
"id=1;percent=15;"
"alternate_origin_domain=example.com:ref.example.com:exh.com");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ(spec_str, spec.ToString());
DomainLawyer lawyer;
spec.ApplyAlternateOriginsToDomainLawyer(&lawyer, &handler);
VerifyMapOrigin(lawyer, "http://example.com", "http://ref.example.com/",
"exh.com", false);
VerifyMapOrigin(lawyer, "https://example.com", "https://ref.example.com/",
"exh.com", false);
}
{
// Single domain with host header and port on both.
GoogleString spec_str(
"id=1;percent=15;"
"alternate_origin_domain=ex.com:\"ref.ex.com:88\":\"exh.com:42\"");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ(spec_str, spec.ToString());
DomainLawyer lawyer;
spec.ApplyAlternateOriginsToDomainLawyer(&lawyer, &handler);
VerifyMapOrigin(lawyer, "http://ex.com", "http://ref.ex.com:88/",
"exh.com:42", false);
VerifyMapOrigin(lawyer, "https://ex.com", "https://ref.ex.com:88/",
"exh.com:42", false);
}
{
// Single domain with port and host header and port on both.
GoogleString spec_str(
"id=1;percent=15;"
"alternate_origin_domain="
"\"ex.com:63\":\"ref.ex.com:88\":\"exh.com:42\"");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ(spec_str, spec.ToString());
DomainLawyer lawyer;
spec.ApplyAlternateOriginsToDomainLawyer(&lawyer, &handler);
VerifyMapOrigin(lawyer, "http://ex.com", "http://ex.com/",
"ex.com", false);
VerifyMapOrigin(lawyer, "http://ex.com:63", "http://ref.ex.com:88/",
"exh.com:42", false);
VerifyMapOrigin(lawyer, "https://ex.com:63", "https://ref.ex.com:88/",
"exh.com:42", false);
}
{
// Multiple domains with a host header
GoogleString spec_str(
"id=1;percent=15;"
"alternate_origin_domain=foo.com,bar.com:ref.com:host.com");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ(spec_str, spec.ToString());
DomainLawyer lawyer;
spec.ApplyAlternateOriginsToDomainLawyer(&lawyer, &handler);
VerifyMapOrigin(lawyer, "http://foo.com", "http://ref.com/", "host.com",
false);
VerifyMapOrigin(lawyer, "https://foo.com", "https://ref.com/", "host.com",
false);
VerifyMapOrigin(lawyer, "http://bar.com", "http://ref.com/", "host.com",
false);
VerifyMapOrigin(lawyer, "https://bar.com", "https://ref.com/", "host.com",
false);
}
}
TEST_F(RewriteOptionsTest, AlternateOriginDomainNegativeParseTest) {
GoogleMessageHandler handler;
{
// Empty alternate_origin_domain spec.
GoogleString spec_str(
"id=1;percent=15;"
"alternate_origin_domain=");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ("id=1;percent=15", spec.ToString());
}
{
// Missing origin domain.
GoogleString spec_str(
"id=1;percent=15;"
"alternate_origin_domain=bad.com");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ("id=1;percent=15", spec.ToString());
DomainLawyer lawyer;
spec.ApplyAlternateOriginsToDomainLawyer(&lawyer, &handler);
VerifyNoMapOrigin(lawyer, "http://bad.com");
VerifyNoMapOrigin(lawyer, "https://bad.com");
}
{
// Trailing colon with missing origin domain.
GoogleString spec_str(
"id=1;percent=15;"
"alternate_origin_domain=baz.com:");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ("id=1;percent=15", spec.ToString());
DomainLawyer lawyer;
spec.ApplyAlternateOriginsToDomainLawyer(&lawyer, &handler);
VerifyNoMapOrigin(lawyer, "http://baz.com");
VerifyNoMapOrigin(lawyer, "https://baz.com");
}
{
// Unqoted port
GoogleString spec_str(
"id=1;percent=15;"
"alternate_origin_domain=baz.com:456");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ("id=1;percent=15", spec.ToString());
DomainLawyer lawyer;
spec.ApplyAlternateOriginsToDomainLawyer(&lawyer, &handler);
VerifyNoMapOrigin(lawyer, "http://baz.com");
VerifyNoMapOrigin(lawyer, "https://baz.com");
}
{
// Trailing comma in serving domain.
GoogleString spec_str(
"id=1;percent=15;"
"alternate_origin_domain=joe.com,:ref.com");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ("id=1;percent=15;alternate_origin_domain=joe.com:ref.com",
spec.ToString());
DomainLawyer lawyer;
spec.ApplyAlternateOriginsToDomainLawyer(&lawyer, &handler);
VerifyMapOrigin(lawyer, "http://joe.com", "http://ref.com/", "joe.com",
false);
VerifyMapOrigin(lawyer, "https://joe.com", "https://ref.com/", "joe.com",
false);
}
{
// Trailing colon for empty host header.
GoogleString spec_str(
"id=1;percent=15;"
"alternate_origin_domain=jim.com:ref.com");
GoogleString spec_str_plus_colon = spec_str + ":";
RewriteOptions::ExperimentSpec spec(spec_str_plus_colon, &options_,
&handler);
EXPECT_EQ(spec_str, spec.ToString());
DomainLawyer lawyer;
spec.ApplyAlternateOriginsToDomainLawyer(&lawyer, &handler);
VerifyMapOrigin(lawyer, "http://jim.com", "http://ref.com/", "jim.com",
false);
VerifyMapOrigin(lawyer, "https://jim.com", "https://ref.com/", "jim.com",
false);
}
{
// Non numeric serving domain port.
GoogleString spec_str(
"id=1;percent=15;"
"alternate_origin_domain=\"jim.com:a\"");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ("id=1;percent=15", spec.ToString());
DomainLawyer lawyer;
spec.ApplyAlternateOriginsToDomainLawyer(&lawyer, &handler);
VerifyNoMapOrigin(lawyer, "http://jim.com");
VerifyNoMapOrigin(lawyer, "https://jim.com");
}
{
// Non numeric reference domain port.
GoogleString spec_str(
"id=1;percent=15;"
"alternate_origin_domain=jim.com:\"jam.com:a\"");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ("id=1;percent=15", spec.ToString());
DomainLawyer lawyer;
spec.ApplyAlternateOriginsToDomainLawyer(&lawyer, &handler);
VerifyNoMapOrigin(lawyer, "http://jim.com");
VerifyNoMapOrigin(lawyer, "https://jim.com");
}
{
// Non numeric host header port.
GoogleString spec_str(
"id=1;percent=15;"
"alternate_origin_domain=jim.com:jam.com:\"jom.com:g\"");
RewriteOptions::ExperimentSpec spec(spec_str, &options_, &handler);
EXPECT_EQ("id=1;percent=15", spec.ToString());
DomainLawyer lawyer;
spec.ApplyAlternateOriginsToDomainLawyer(&lawyer, &handler);
VerifyNoMapOrigin(lawyer, "http://jim.com");
VerifyNoMapOrigin(lawyer, "https://jim.com");
}
}
TEST_F(RewriteOptionsTest, SetOptionsFromName) {
TestMessageHandler handler;
RewriteOptions::OptionSet option_set;
option_set.insert(RewriteOptions::OptionStringPair(
"CssInlineMaxBytes", "1024"));
EXPECT_TRUE(options_.SetOptionsFromName(option_set, &handler));
EXPECT_TRUE(handler.messages().empty());
option_set.insert(RewriteOptions::OptionStringPair(
"Not an Option", "nothing"));
EXPECT_FALSE(options_.SetOptionsFromName(option_set, &handler));
EXPECT_FALSE(handler.messages().empty());
}
// TODO(sriharis): Add thorough ComputeSignature tests
TEST_F(RewriteOptionsTest, ComputeSignatureWildcardGroup) {
options_.ComputeSignature();
GoogleString signature1 = options_.signature();
// Tweak allow_resources_ and check that signature changes.
options_.ClearSignatureForTesting();
options_.Disallow("http://www.example.com/*");
options_.ComputeSignature();
GoogleString signature2 = options_.signature();
EXPECT_NE(signature1, signature2);
// Tweak retain_comments and check that signature changes.
options_.ClearSignatureForTesting();
options_.RetainComment("TEST");
options_.ComputeSignature();
GoogleString signature3 = options_.signature();
EXPECT_NE(signature1, signature3);
EXPECT_NE(signature2, signature3);
}
TEST_F(RewriteOptionsTest, ComputeSignatureOptionEffect) {
options_.ClearSignatureForTesting();
options_.set_css_image_inline_max_bytes(2048);
options_.set_in_place_rewriting_enabled(false);
options_.ComputeSignature();
GoogleString signature1 = options_.signature();
// Changing an Option used in signature computation will change the signature.
options_.ClearSignatureForTesting();
options_.set_css_image_inline_max_bytes(1024);
options_.ComputeSignature();
GoogleString signature2 = options_.signature();
EXPECT_NE(signature1, signature2);
// Changing an Option not used in signature computation will not change the
// signature.
options_.ClearSignatureForTesting();
options_.set_in_place_rewriting_enabled(true);
options_.ComputeSignature();
GoogleString signature3 = options_.signature();
// See the comment in RewriteOptions::RewriteOptions -- we need to leave
// signatures sensitive to ajax_rewriting.
EXPECT_NE(signature2, signature3);
}
TEST_F(RewriteOptionsTest, SignatureIgnoresDebug) {
options_.ClearSignatureForTesting();
options_.EnableFilter(RewriteOptions::kCombineCss);
options_.ComputeSignature();
scoped_ptr<RewriteOptions> options2(options_.Clone());
options2->ClearSignatureForTesting();
options2->EnableFilter(RewriteOptions::kDebug);
options2->ComputeSignature();
EXPECT_STREQ(options_.signature(), options2->signature());
EXPECT_FALSE(options_.IsEqual(*options2));
}
TEST_F(RewriteOptionsTest, IsEqual) {
RewriteOptions a(&thread_system_), b(&thread_system_);
a.ComputeSignature();
b.ComputeSignature();
EXPECT_TRUE(a.IsEqual(b));
a.ClearSignatureForTesting();
a.EnableFilter(RewriteOptions::kSpriteImages);
a.ComputeSignature();
EXPECT_FALSE(a.IsEqual(b));
b.ClearSignatureForTesting();
b.EnableFilter(RewriteOptions::kSpriteImages);
b.ComputeSignature();
EXPECT_TRUE(a.IsEqual(b));
}
TEST_F(RewriteOptionsTest, ComputeSignatureEmptyIdempotent) {
options_.ClearSignatureForTesting();
options_.DisallowTroublesomeResources();
options_.ComputeSignature();
GoogleString signature1 = options_.signature();
options_.ClearSignatureForTesting();
// Merging in empty RewriteOptions should not change the signature.
RewriteOptions options2(&thread_system_);
options_.Merge(options2);
options_.ComputeSignature();
EXPECT_EQ(signature1, options_.signature());
}
TEST_F(RewriteOptionsTest, ImageOptimizableCheck) {
options_.ClearFilters();
options_.EnableFilter(RewriteOptions::kRecompressJpeg);
EXPECT_TRUE(options_.ImageOptimizationEnabled());
options_.DisableFilter(RewriteOptions::kRecompressJpeg);
EXPECT_FALSE(options_.ImageOptimizationEnabled());
options_.EnableFilter(RewriteOptions::kRecompressPng);
EXPECT_TRUE(options_.ImageOptimizationEnabled());
options_.DisableFilter(RewriteOptions::kRecompressPng);
EXPECT_FALSE(options_.ImageOptimizationEnabled());
options_.EnableFilter(RewriteOptions::kRecompressWebp);
EXPECT_TRUE(options_.ImageOptimizationEnabled());
options_.DisableFilter(RewriteOptions::kRecompressWebp);
EXPECT_FALSE(options_.ImageOptimizationEnabled());
options_.EnableFilter(RewriteOptions::kConvertGifToPng);
EXPECT_TRUE(options_.ImageOptimizationEnabled());
options_.DisableFilter(RewriteOptions::kConvertGifToPng);
EXPECT_FALSE(options_.ImageOptimizationEnabled());
options_.EnableFilter(RewriteOptions::kConvertJpegToWebp);
EXPECT_TRUE(options_.ImageOptimizationEnabled());
options_.DisableFilter(RewriteOptions::kConvertJpegToWebp);
EXPECT_FALSE(options_.ImageOptimizationEnabled());
options_.EnableFilter(RewriteOptions::kConvertPngToJpeg);
EXPECT_TRUE(options_.ImageOptimizationEnabled());
options_.DisableFilter(RewriteOptions::kConvertPngToJpeg);
EXPECT_FALSE(options_.ImageOptimizationEnabled());
options_.EnableFilter(RewriteOptions::kConvertToWebpLossless);
EXPECT_TRUE(options_.ImageOptimizationEnabled());
options_.DisableFilter(RewriteOptions::kConvertToWebpLossless);
EXPECT_FALSE(options_.ImageOptimizationEnabled());
options_.EnableFilter(RewriteOptions::kConvertToWebpAnimated);
EXPECT_TRUE(options_.ImageOptimizationEnabled());
options_.DisableFilter(RewriteOptions::kConvertToWebpAnimated);
EXPECT_FALSE(options_.ImageOptimizationEnabled());
}
TEST_F(RewriteOptionsTest, UrlCacheInvalidationTest) {
options_.AddUrlCacheInvalidationEntry("one*", 10L, true);
options_.AddUrlCacheInvalidationEntry("two*", 25L, false);
options_.AddUrlCacheInvalidationEntry("four", 40L, false);
options_.AddUrlCacheInvalidationEntry("five", 50L, false);
options_.AddUrlCacheInvalidationEntry("six", 60L, false);
RewriteOptions options1(&thread_system_);
options1.AddUrlCacheInvalidationEntry("one*", 20L, true);
options1.AddUrlCacheInvalidationEntry("three*", 23L, false);
options1.AddUrlCacheInvalidationEntry("three*", 30L, true);
options1.AddUrlCacheInvalidationEntry("four", 39L, false);
options1.AddUrlCacheInvalidationEntry("five", 51L, false);
options1.AddUrlCacheInvalidationEntry("seven", 70L, false);
options_.Merge(options1);
EXPECT_TRUE(options_.IsUrlCacheInvalidationEntriesSorted());
EXPECT_FALSE(options_.IsUrlCacheValid("one1", 9L, true));
EXPECT_FALSE(options_.IsUrlCacheValid("one1", 19L, true));
EXPECT_TRUE(options_.IsUrlCacheValid("one1", 21L, true));
EXPECT_FALSE(options_.IsUrlCacheValid("two2", 21L, true));
EXPECT_TRUE(options_.IsUrlCacheValid("two2", 26L, true));
EXPECT_TRUE(options_.IsUrlCacheValid("three3", 31L, true));
EXPECT_FALSE(options_.IsUrlCacheValid("four", 40L, true));
EXPECT_TRUE(options_.IsUrlCacheValid("four", 41L, true));
EXPECT_FALSE(options_.IsUrlCacheValid("five", 51L, true));
EXPECT_TRUE(options_.IsUrlCacheValid("five", 52L, true));
EXPECT_FALSE(options_.IsUrlCacheValid("six", 60L, true));
EXPECT_TRUE(options_.IsUrlCacheValid("six", 61L, true));
EXPECT_FALSE(options_.IsUrlCacheValid("seven", 70L, true));
EXPECT_TRUE(options_.IsUrlCacheValid("seven", 71L, true));
}
TEST_F(RewriteOptionsTest, UrlCacheInvalidationSignatureTest) {
options_.ComputeSignature();
GoogleString signature1 = options_.signature();
options_.ClearSignatureForTesting();
options_.AddUrlCacheInvalidationEntry("one*", 10L, true);
options_.ComputeSignature();
GoogleString signature2 = options_.signature();
EXPECT_EQ(signature1, signature2);
options_.ClearSignatureForTesting();
options_.AddUrlCacheInvalidationEntry("two*", 10L, false);
options_.ComputeSignature();
GoogleString signature3 = options_.signature();
EXPECT_NE(signature2, signature3);
}
TEST_F(RewriteOptionsTest, EnabledFiltersRequiringJavaScriptTest) {
RewriteOptions foo(&thread_system_);
foo.ClearFilters();
foo.EnableFilter(RewriteOptions::kDeferJavascript);
foo.EnableFilter(RewriteOptions::kResizeImages);
RewriteOptions::FilterVector foo_fs;
foo.GetEnabledFiltersRequiringScriptExecution(&foo_fs);
EXPECT_FALSE(foo_fs.empty());
EXPECT_EQ(1, foo_fs.size());
RewriteOptions bar(&thread_system_);
bar.ClearFilters();
bar.EnableFilter(RewriteOptions::kResizeImages);
bar.EnableFilter(RewriteOptions::kConvertPngToJpeg);
RewriteOptions::FilterVector bar_fs;
bar.GetEnabledFiltersRequiringScriptExecution(&bar_fs);
EXPECT_TRUE(bar_fs.empty());
}
TEST_F(RewriteOptionsTest, FilterLookupMethods) {
EXPECT_STREQ("Add Head",
RewriteOptions::FilterName(RewriteOptions::kAddHead));
EXPECT_STREQ("Remove Comments",
RewriteOptions::FilterName(RewriteOptions::kRemoveComments));
// Can't do these unless we remove the LOG(DFATAL) from FilterName().
// EXPECT_STREQ("End of Filters",
// RewriteOptions::FilterName(RewriteOptions::kEndOfFilters));
// EXPECT_STREQ("Unknown Filter",
// RewriteOptions::FilterName(
// static_cast<RewriteOptions::Filter>(-1)));
EXPECT_STREQ("ah",
RewriteOptions::FilterId(RewriteOptions::kAddHead));
EXPECT_STREQ("rc",
RewriteOptions::FilterId(RewriteOptions::kRemoveComments));
// Can't do these unless we remove the LOG(DFATAL) from FilterName().
// EXPECT_STREQ("UF",
// RewriteOptions::FilterId(RewriteOptions::kEndOfFilters));
// EXPECT_STREQ("UF",
// RewriteOptions::FilterId(
// static_cast<RewriteOptions::Filter>(-1)));
EXPECT_EQ(RewriteOptions::kEndOfFilters,
RewriteOptions::LookupFilterById(" "));
EXPECT_EQ(RewriteOptions::kAddHead,
RewriteOptions::LookupFilterById("ah"));
EXPECT_EQ(RewriteOptions::kRemoveComments,
RewriteOptions::LookupFilterById("rc"));
EXPECT_EQ(RewriteOptions::kEndOfFilters,
RewriteOptions::LookupFilterById("zz"));
EXPECT_EQ(RewriteOptions::kEndOfFilters,
RewriteOptions::LookupFilterById("UF"));
EXPECT_EQ(RewriteOptions::kEndOfFilters,
RewriteOptions::LookupFilterById("junk"));
EXPECT_EQ(RewriteOptions::kEndOfFilters,
RewriteOptions::LookupFilterById(""));
EXPECT_EQ(RewriteOptions::kEndOfFilters,
RewriteOptions::LookupFilterById(NULL));
EXPECT_EQ(RewriteOptions::kAnalyticsID,
RewriteOptions::LookupOptionNameById("ig"));
EXPECT_EQ(RewriteOptions::kImageJpegRecompressionQuality,
RewriteOptions::LookupOptionNameById("iq"));
EXPECT_TRUE(RewriteOptions::LookupOptionNameById(" ").empty());
EXPECT_TRUE(RewriteOptions::LookupOptionNameById("junk").empty());
EXPECT_TRUE(RewriteOptions::LookupOptionNameById("").empty());
EXPECT_TRUE(RewriteOptions::LookupOptionNameById(NULL).empty());
}
TEST_F(RewriteOptionsTest, ParseBeaconUrl) {
RewriteOptions::BeaconUrl beacon_url;
GoogleString url = "www.example.com";
GoogleString url2 = "www.example.net";
EXPECT_FALSE(RewriteOptions::ParseBeaconUrl("", &beacon_url));
EXPECT_FALSE(RewriteOptions::ParseBeaconUrl("a b c", &beacon_url));
EXPECT_TRUE(RewriteOptions::ParseBeaconUrl("http://" + url, &beacon_url));
EXPECT_STREQ("http://" + url, beacon_url.http);
EXPECT_STREQ("https://" + url, beacon_url.https);
EXPECT_TRUE(RewriteOptions::ParseBeaconUrl("https://" + url, &beacon_url));
EXPECT_STREQ("https://" + url, beacon_url.http);
EXPECT_STREQ("https://" + url, beacon_url.https);
EXPECT_TRUE(RewriteOptions::ParseBeaconUrl(
"http://" + url + " " + "https://" + url2, &beacon_url));
EXPECT_STREQ("http://" + url, beacon_url.http);
EXPECT_STREQ("https://" + url2, beacon_url.https);
// Verify that ets parameters get stripped from the beacon_url
EXPECT_TRUE(RewriteOptions::ParseBeaconUrl("http://" + url + "?ets=" + " " +
"https://"+ url2 + "?foo=bar&ets=",
&beacon_url));
EXPECT_STREQ("http://" + url, beacon_url.http);
EXPECT_STREQ("https://" + url2 + "?foo=bar", beacon_url.https);
EXPECT_STREQ("http://" + url, beacon_url.http_in);
EXPECT_STREQ("https://" + url2, beacon_url.https_in);
EXPECT_TRUE(RewriteOptions::ParseBeaconUrl("/mod_pagespeed_beacon?a=b",
&beacon_url));
EXPECT_STREQ("/mod_pagespeed_beacon?a=b", beacon_url.http);
EXPECT_STREQ("/mod_pagespeed_beacon?a=b", beacon_url.https);
EXPECT_STREQ("/mod_pagespeed_beacon", beacon_url.http_in);
EXPECT_STREQ("/mod_pagespeed_beacon", beacon_url.https_in);
}
TEST_F(RewriteOptionsTest, AccessOptionByIdAndName) {
const char* id = NULL;
GoogleString value;
bool was_set = false;
EXPECT_TRUE(options_.OptionValue(
RewriteOptions::kImageJpegRecompressionQuality, &id, &was_set, &value));
EXPECT_FALSE(was_set);
EXPECT_STREQ("iq", id);
const StringPiece kBogusOptionName("bogosity!");
EXPECT_EQ(RewriteOptions::kOptionNameUnknown,
options_.SetOptionFromName(kBogusOptionName, ""));
EXPECT_EQ(RewriteOptions::kOptionValueInvalid,
options_.SetOptionFromName(
RewriteOptions::kImageJpegRecompressionQuality, "garbage"));
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.SetOptionFromName(
RewriteOptions::kImageJpegRecompressionQuality, "63"));
id = NULL;
EXPECT_TRUE(options_.OptionValue(
RewriteOptions::kImageJpegRecompressionQuality, &id, &was_set, &value));
EXPECT_TRUE(was_set);
EXPECT_STREQ("iq", id);
EXPECT_STREQ("63", value);
EXPECT_FALSE(options_.OptionValue(kBogusOptionName, &id, &was_set, &value));
}
TEST_F(RewriteOptionsTest, AccessAcrossThreads) {
#ifndef NDEBUG // Depends on bits set in rewrite_options.cc under debug
NullThreadSystem null_thread_system;
null_thread_system.set_current_thread(5);
RewriteOptions options(&null_thread_system);
// We can continue to modify in the same thread.
EXPECT_TRUE(options.ModificationOK());
// Unmodified, we could switch to a different thread.
null_thread_system.set_current_thread(6);
EXPECT_TRUE(options.ModificationOK());
null_thread_system.set_current_thread(5);
// Now make a modification. We can continue to modify in the same thread.
options.set_enabled(RewriteOptions::kEnabledOff);
EXPECT_TRUE(options.ModificationOK());
// But from a different thread we must not modify.
null_thread_system.set_current_thread(4);
EXPECT_FALSE(options.ModificationOK());
// Back in thread 5 we can modify.
null_thread_system.set_current_thread(5);
EXPECT_TRUE(options.ModificationOK());
// We can merge from the same thread, but not from a different one.
EXPECT_TRUE(options.MergeOK());
null_thread_system.set_current_thread(4);
EXPECT_FALSE(options.MergeOK());
// Clearing the signature gets us on a clean slate and we can take over
// from thread 4.
options.ClearSignatureWithCaution();
EXPECT_TRUE(options.MergeOK());
// Once we freeze it we can merge from it.
options.Freeze();
EXPECT_TRUE(options.MergeOK());
null_thread_system.set_current_thread(5);
EXPECT_TRUE(options.MergeOK());
#endif
}
TEST_F(RewriteOptionsTest, ParseAndSetDeprecatedOptionFromName1) {
GoogleString msg;
NullMessageHandler handler;
// 'ImageWebpRecompressionQuality' is replaced by 'WebpRecompressionQuality'.
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName1("ImageWebpRecompressionQuality",
"12", &msg, &handler));
EXPECT_EQ(12, options_.ImageWebpQuality());
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName1("WebpRecompressionQuality",
"23", &msg, &handler));
EXPECT_EQ(23, options_.ImageWebpQuality());
// 'ImageWebpRecompressionQualityForSmallScreens' is replaced by
// 'WebpRecompressionQualityForSmallScreens'.
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName1(
"ImageWebpRecompressionQualityForSmallScreens",
"34", &msg, &handler));
EXPECT_EQ(34, options_.ImageWebpQualityForSmallScreen());
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.ParseAndSetOptionFromName1(
"WebpRecompressionQualityForSmallScreens",
"45", &msg, &handler));
EXPECT_EQ(45, options_.ImageWebpQualityForSmallScreen());
}
TEST_F(RewriteOptionsTest, BandwidthMode) {
scoped_ptr<RewriteOptions> vhost_options(new RewriteOptions(&thread_system_));
vhost_options->SetRewriteLevel(RewriteOptions::kOptimizeForBandwidth);
EXPECT_FALSE(vhost_options->Enabled(RewriteOptions::kCombineCss));
EXPECT_TRUE(vhost_options->Enabled(RewriteOptions::kConvertGifToPng));
EXPECT_TRUE(vhost_options->Enabled(
RewriteOptions::kConvertJpegToProgressive));
EXPECT_TRUE(vhost_options->Enabled(RewriteOptions::kConvertJpegToWebp));
EXPECT_TRUE(vhost_options->Enabled(RewriteOptions::kConvertPngToJpeg));
EXPECT_TRUE(vhost_options->Enabled(
RewriteOptions::kInPlaceOptimizeForBrowser));
EXPECT_TRUE(vhost_options->Enabled(RewriteOptions::kJpegSubsampling));
EXPECT_TRUE(vhost_options->Enabled(RewriteOptions::kRecompressJpeg));
EXPECT_TRUE(vhost_options->Enabled(RewriteOptions::kRecompressPng));
EXPECT_TRUE(vhost_options->Enabled(RewriteOptions::kRecompressWebp));
EXPECT_TRUE(vhost_options->Enabled(RewriteOptions::kRewriteCss));
EXPECT_TRUE(vhost_options->Enabled(
RewriteOptions::kRewriteJavascriptExternal));
EXPECT_TRUE(vhost_options->Enabled(RewriteOptions::kRewriteJavascriptInline));
EXPECT_TRUE(vhost_options->Enabled(RewriteOptions::kStripImageColorProfile));
EXPECT_TRUE(vhost_options->Enabled(RewriteOptions::kStripImageMetaData));
EXPECT_TRUE(vhost_options->Enabled(
RewriteOptions::kInPlaceOptimizeForBrowser));
EXPECT_TRUE(vhost_options->in_place_rewriting_enabled());
EXPECT_TRUE(vhost_options->css_preserve_urls());
EXPECT_TRUE(vhost_options->image_preserve_urls());
EXPECT_TRUE(vhost_options->js_preserve_urls());
// We use preemptive rewrites so that there's a chance that a first or
// second view will yield optimized resources.
EXPECT_TRUE(vhost_options->in_place_preemptive_rewrite_css());
EXPECT_TRUE(vhost_options->in_place_preemptive_rewrite_css_images());
EXPECT_TRUE(vhost_options->in_place_preemptive_rewrite_images());
EXPECT_TRUE(vhost_options->in_place_preemptive_rewrite_javascript());
// Now override a bandwidth-option. Let's say it's OK to mutate
// CSS urls.
vhost_options->set_css_preserve_urls(false);
EXPECT_FALSE(vhost_options->css_preserve_urls());
// JS and Image URLs must still be preserved.
EXPECT_TRUE(vhost_options->image_preserve_urls());
EXPECT_TRUE(vhost_options->js_preserve_urls());
// Now merge with an options-set with Core enabled many of these answers
// change.
scoped_ptr<RewriteOptions> core(new RewriteOptions(&thread_system_));
scoped_ptr<RewriteOptions> vhost_core(new RewriteOptions(&thread_system_));
core->SetRewriteLevel(RewriteOptions::kCoreFilters);
vhost_core->Merge(*vhost_options);
vhost_core->Merge(*core);
EXPECT_TRUE(vhost_core->Enabled(RewriteOptions::kCombineCss));
EXPECT_TRUE(vhost_core->Enabled(RewriteOptions::kRecompressJpeg));
EXPECT_TRUE(vhost_core->Enabled(RewriteOptions::kRewriteCss));
EXPECT_TRUE(vhost_core->Enabled(RewriteOptions::kRewriteJavascriptExternal));
EXPECT_TRUE(vhost_core->Enabled(RewriteOptions::kRewriteJavascriptInline));
EXPECT_FALSE(vhost_core->Enabled(RewriteOptions::kInPlaceOptimizeForBrowser));
EXPECT_TRUE(vhost_core->in_place_rewriting_enabled());
EXPECT_FALSE(vhost_core->css_preserve_urls());
EXPECT_FALSE(vhost_core->image_preserve_urls());
EXPECT_FALSE(vhost_core->js_preserve_urls());
// Finally, merge in another option-set that is bandwidth-only. We'll
// revert back to the bandwidth-behavior, but we will inherit the override
// for CSS preservation we made.
scoped_ptr<RewriteOptions> bandwidth(new RewriteOptions(&thread_system_));
bandwidth->SetRewriteLevel(RewriteOptions::kOptimizeForBandwidth);
MergeOptions(*vhost_core, *bandwidth);
EXPECT_FALSE(options_.Enabled(RewriteOptions::kCombineCss));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kRecompressJpeg));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kRewriteCss));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kRewriteJavascriptExternal));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kRewriteJavascriptInline));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kInPlaceOptimizeForBrowser));
EXPECT_TRUE(options_.in_place_rewriting_enabled());
EXPECT_FALSE(options_.css_preserve_urls());
EXPECT_TRUE(options_.image_preserve_urls());
EXPECT_TRUE(options_.js_preserve_urls());
}
TEST_F(RewriteOptionsTest, BandwidthOverride) {
options_.SetRewriteLevel(RewriteOptions::kOptimizeForBandwidth);
EXPECT_FALSE(options_.Enabled(RewriteOptions::kCombineCss));
options_.EnableFilter(RewriteOptions::kCombineCss);
EXPECT_TRUE(options_.Enabled(RewriteOptions::kCombineCss));
// Now test it the other way around.
RewriteOptions other_way(&thread_system_);
other_way.SetRewriteLevel(RewriteOptions::kOptimizeForBandwidth);
other_way.ComputeSignature();
EXPECT_FALSE(other_way.Enabled(RewriteOptions::kCombineCss));
other_way.ClearSignatureForTesting();
other_way.EnableFilter(RewriteOptions::kCombineCss);
other_way.ComputeSignature();
EXPECT_TRUE(other_way.Enabled(RewriteOptions::kCombineCss));
}
TEST_F(RewriteOptionsTest, PreserveOverridesCoreCss) {
options_.SetRewriteLevel(RewriteOptions::kCoreFilters);
options_.set_css_preserve_urls(true);
options_.ComputeSignature();
EXPECT_FALSE(options_.Enabled(RewriteOptions::kCombineCss));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kExtendCacheCss));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kInlineCss));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kInlineGoogleFontCss));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kInlineImportToLink));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kLeftTrimUrls));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kOutlineCss));
}
TEST_F(RewriteOptionsTest, ExplicitCssFiltersOverridePreserve) {
options_.set_css_preserve_urls(true);
options_.ClearSignatureForTesting();
options_.EnableFilter(RewriteOptions::kCombineCss);
options_.EnableFilter(RewriteOptions::kExtendCacheCss);
options_.EnableFilter(RewriteOptions::kInlineCss);
options_.EnableFilter(RewriteOptions::kInlineGoogleFontCss);
options_.EnableFilter(RewriteOptions::kInlineImportToLink);
options_.EnableFilter(RewriteOptions::kLeftTrimUrls);
options_.EnableFilter(RewriteOptions::kOutlineCss);
options_.ComputeSignature();
EXPECT_TRUE(options_.Enabled(RewriteOptions::kCombineCss));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kExtendCacheCss));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kInlineCss));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kInlineGoogleFontCss));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kInlineImportToLink));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kLeftTrimUrls));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kOutlineCss));
}
TEST_F(RewriteOptionsTest, PreserveOverridesCoreImages) {
options_.SetRewriteLevel(RewriteOptions::kCoreFilters);
options_.set_image_preserve_urls(true);
options_.ComputeSignature();
EXPECT_FALSE(options_.Enabled(RewriteOptions::kDelayImages));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kExtendCacheImages));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kInlineImages));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kLazyloadImages));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kResizeImages));
EXPECT_FALSE(options_.Enabled(
RewriteOptions::kResizeToRenderedImageDimensions));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kSpriteImages));
}
TEST_F(RewriteOptionsTest, ExplicitImageFiltersOverridePreserve) {
options_.set_image_preserve_urls(true);
options_.EnableFilter(RewriteOptions::kDelayImages);
options_.EnableFilter(RewriteOptions::kExtendCacheImages);
options_.EnableFilter(RewriteOptions::kInlineImages);
options_.EnableFilter(RewriteOptions::kLazyloadImages);
options_.EnableFilter(RewriteOptions::kResizeImages);
options_.EnableFilter(RewriteOptions::kResizeToRenderedImageDimensions);
options_.EnableFilter(RewriteOptions::kSpriteImages);
options_.ComputeSignature();
EXPECT_TRUE(options_.Enabled(RewriteOptions::kDelayImages));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kExtendCacheImages));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kInlineImages));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kLazyloadImages));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kResizeImages));
EXPECT_TRUE(options_.Enabled(
RewriteOptions::kResizeToRenderedImageDimensions));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kSpriteImages));
}
TEST_F(RewriteOptionsTest, PreserveOverridesCoreJavaScript) {
options_.SetRewriteLevel(RewriteOptions::kCoreFilters);
options_.set_js_preserve_urls(true);
options_.ComputeSignature();
EXPECT_FALSE(options_.Enabled(
RewriteOptions::kCanonicalizeJavascriptLibraries));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kCombineJavascript));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kDeferJavascript));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kExtendCacheScripts));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kInlineJavascript));
EXPECT_FALSE(options_.Enabled(RewriteOptions::kOutlineJavascript));
}
TEST_F(RewriteOptionsTest, ExplicitJavaScriptFiltersOverridesPreserve) {
options_.EnableFilter(RewriteOptions::kCanonicalizeJavascriptLibraries);
options_.EnableFilter(RewriteOptions::kCombineJavascript);
options_.EnableFilter(RewriteOptions::kDeferJavascript);
options_.EnableFilter(RewriteOptions::kExtendCacheScripts);
options_.EnableFilter(RewriteOptions::kInlineJavascript);
options_.EnableFilter(RewriteOptions::kOutlineJavascript);
options_.set_js_preserve_urls(true);
options_.ComputeSignature();
EXPECT_TRUE(options_.Enabled(
RewriteOptions::kCanonicalizeJavascriptLibraries));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kCombineJavascript));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kDeferJavascript));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kExtendCacheScripts));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kInlineJavascript));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kOutlineJavascript));
}
TEST_F(RewriteOptionsTest, ExtendCacheScriptsOverridesPreserve) {
RewriteOptions global_options(&thread_system_);
global_options.set_js_preserve_urls(true);
global_options.SetRewriteLevel(RewriteOptions::kCoreFilters);
global_options.ComputeSignature();
EXPECT_FALSE(global_options.Enabled(RewriteOptions::kInlineJavascript));
RewriteOptions vhost_options(&thread_system_);
vhost_options.EnableFilter(RewriteOptions::kExtendCacheScripts);
MergeOptions(global_options, vhost_options);
options_.ComputeSignature();
EXPECT_TRUE(options_.Enabled(RewriteOptions::kInlineJavascript));
EXPECT_FALSE(options_.js_preserve_urls());
}
TEST_F(RewriteOptionsTest, ExtendCacheImagesOverridesPreserve) {
RewriteOptions global_options(&thread_system_);
global_options.set_image_preserve_urls(true);
global_options.SetRewriteLevel(RewriteOptions::kCoreFilters);
global_options.ComputeSignature();
EXPECT_FALSE(global_options.Enabled(RewriteOptions::kInlineImages));
RewriteOptions vhost_options(&thread_system_);
vhost_options.EnableFilter(RewriteOptions::kExtendCacheImages);
MergeOptions(global_options, vhost_options);
options_.ComputeSignature();
EXPECT_TRUE(options_.Enabled(RewriteOptions::kInlineImages));
EXPECT_FALSE(options_.image_preserve_urls());
}
TEST_F(RewriteOptionsTest, ExtendCacheStylesOverridesPreserve) {
RewriteOptions global_options(&thread_system_);
global_options.set_css_preserve_urls(true);
global_options.SetRewriteLevel(RewriteOptions::kCoreFilters);
global_options.ComputeSignature();
EXPECT_FALSE(global_options.Enabled(RewriteOptions::kInlineCss));
RewriteOptions vhost_options(&thread_system_);
vhost_options.EnableFilter(RewriteOptions::kExtendCacheCss);
MergeOptions(global_options, vhost_options);
options_.ComputeSignature();
EXPECT_TRUE(options_.Enabled(RewriteOptions::kInlineCss));
EXPECT_FALSE(options_.css_preserve_urls());
}
TEST_F(RewriteOptionsTest, PreserveOverridesExplicitFiltersScripts) {
RewriteOptions global_options(&thread_system_);
global_options.EnableFilter(RewriteOptions::kExtendCacheScripts);
global_options.ComputeSignature();
RewriteOptions vhost_options(&thread_system_);
vhost_options.set_js_preserve_urls(true);
MergeOptions(global_options, vhost_options);
options_.ComputeSignature();
EXPECT_FALSE(options_.Enabled(RewriteOptions::kExtendCacheScripts));
EXPECT_TRUE(options_.js_preserve_urls());
}
TEST_F(RewriteOptionsTest, PreserveOverridesExplicitFiltersImages) {
RewriteOptions global_options(&thread_system_);
global_options.EnableFilter(RewriteOptions::kExtendCacheImages);
global_options.ComputeSignature();
RewriteOptions vhost_options(&thread_system_);
vhost_options.set_image_preserve_urls(true);
MergeOptions(global_options, vhost_options);
options_.ComputeSignature();
EXPECT_FALSE(options_.Enabled(RewriteOptions::kExtendCacheImages));
EXPECT_TRUE(options_.image_preserve_urls());
}
TEST_F(RewriteOptionsTest, PreserveOverridesExplicitFiltersStyles) {
RewriteOptions global_options(&thread_system_);
global_options.EnableFilter(RewriteOptions::kExtendCacheCss);
global_options.ComputeSignature();
RewriteOptions vhost_options(&thread_system_);
vhost_options.set_css_preserve_urls(true);
MergeOptions(global_options, vhost_options);
options_.ComputeSignature();
EXPECT_FALSE(options_.Enabled(RewriteOptions::kExtendCacheCss));
EXPECT_TRUE(options_.css_preserve_urls());
}
TEST_F(RewriteOptionsTest, MergeInlineResourcesWithoutExplicitAuthorization) {
// Different variations of "off" and no-value in global and local options.
VerifyInlineUnauthorizedResourceTypeMerges("off", "", false, false);
VerifyInlineUnauthorizedResourceTypeMerges("off", "off", false, false);
VerifyInlineUnauthorizedResourceTypeMerges("", "off", false, false);
VerifyInlineUnauthorizedResourceTypeMerges("", "", false, false);
// Local has "script", and global has effective "off".
VerifyInlineUnauthorizedResourceTypeMerges("off", "script", true, false);
VerifyInlineUnauthorizedResourceTypeMerges("", "script", true, false);
// Local has no-value and global has "script".
VerifyInlineUnauthorizedResourceTypeMerges("script", "", true, false);
// Local has "off" and global has "script".
VerifyInlineUnauthorizedResourceTypeMerges("script", "off", false, false);
// Merging of script, stylesheet.
VerifyInlineUnauthorizedResourceTypeMerges(
"script", "stylesheet", false, true);
VerifyInlineUnauthorizedResourceTypeMerges(
"script", "script,stylesheet", true, true);
VerifyInlineUnauthorizedResourceTypeMerges(
"script,stylesheet", "stylesheet", false, true);
VerifyInlineUnauthorizedResourceTypeMerges(
"script,stylesheet", "", true, true);
}
TEST_F(RewriteOptionsTest, OptionsToString) {
options_.SetRewriteLevel(RewriteOptions::kPassThrough);
options_.UpdateCacheInvalidationTimestampMs(MockTimer::kApr_5_2010_ms);
options_.EnableFilter(RewriteOptions::kSpriteImages);
options_.set_inline_only_critical_images(true);
RewriteOptions::ResourceCategorySet resources;
resources.insert(semantic_type::kImage);
resources.insert(semantic_type::kScript);
options_.set_inline_unauthorized_resource_types(resources);
options_.set_lazyload_images_blank_url("1.gif");
NullMessageHandler handler;
options_.WriteableDomainLawyer()->AddOriginDomainMapping(
"origin.com", "from.com", "host.com", &handler);
// These two options must be set to override settings established in
// RewriteOptions' constructor when running on valgrind, otherwise
// we'll see different results from OptionsForString.
options_.set_rewrite_deadline_ms(100);
options_.set_in_place_rewrite_deadline_ms(200);
EXPECT_STREQ(StrCat(
"Version: ", IntegerToString(RewriteOptions::kOptionsVersion), ": on\n"
"\n"
"Filters\n"
"hw\tFlushes html\n" // TODO(jmarantz): remove from base config?
"is\tSprite Images\n"
"\n"
"Options\n"
" InlineOnlyCriticalImages (ioci) True\n"
" InlineResourcesWithoutExplicitAuthorization (irwea) Image,Script\n"
" InPlaceRewriteDeadlineMs (iprdm) 200\n"
" LazyloadImagesBlankUrl (llbu) 1.gif\n"
" RewriteDeadlinePerFlushMs (rdm) 100\n"
" RewriteLevel (l) Pass Through\n"
"\n"
"Domain Lawyer\n"
" http://from.com/ Auth OriginDomain:http://origin.com/\n"
" http://origin.com/ HostHeader:host.com\n"
"\n"
"Invalidation Timestamp: Mon, 05 Apr 2010 18:51:26 GMT "
"(1270493486000)\n"),
options_.OptionsToString());
}
TEST_F(RewriteOptionsTest, ColorUtilTest) {
RewriteOptions::Color out;
EXPECT_FALSE(RewriteOptions::ParseFromString("", &out));
EXPECT_FALSE(RewriteOptions::ParseFromString("!123456", &out));
EXPECT_FALSE(RewriteOptions::ParseFromString("#12345", &out));
EXPECT_TRUE(RewriteOptions::ParseFromString("#123456", &out));
EXPECT_EQ(0x12u, out.r);
EXPECT_EQ(0x34u, out.g);
EXPECT_EQ(0x56u, out.b);
EXPECT_TRUE(RewriteOptions::ParseFromString("#ABCDEF", &out));
EXPECT_EQ(0xabu, out.r);
EXPECT_EQ(0xcdu, out.g);
EXPECT_EQ(0xefu, out.b);
EXPECT_EQ("#abcdef", RewriteOptions::ToString(out));
}
TEST_F(RewriteOptionsTest, OptionsScopeApplications) {
NullMessageHandler handler;
GoogleString msg;
scoped_ptr<RewriteOptions> new_options(new RewriteOptions(&thread_system_));
// MaxHtmlParseBytes has RewriteOptions::kProcessScope.
// Setting this value should work.
RewriteOptions::OptionSettingResult result =
new_options->ParseAndSetOptionFromNameWithScope(
RewriteOptions::kMaxHtmlParseBytes, "44",
RewriteOptions::kProcessScope, &msg, &handler);
EXPECT_EQ("", msg);
EXPECT_EQ(result, RewriteOptions::kOptionOk);
// Setting the value with a max_scope of RewriteOptions::kQueryScope should
// not work.
result = new_options->ParseAndSetOptionFromNameWithScope(
RewriteOptions::kMaxHtmlParseBytes, "44", RewriteOptions::kQueryScope,
&msg, &handler);
EXPECT_EQ("", msg);
EXPECT_EQ(result, RewriteOptions::kOptionNameUnknown);
}
TEST_F(RewriteOptionsTest, ParseFloats) {
RewriteOptions::ResponsiveDensities densities, expected_densities;
expected_densities.push_back(2);
expected_densities.push_back(2.8);
expected_densities.push_back(3.1);
EXPECT_TRUE(RewriteOptions::ParseFromString("2, 2.8, 3.1", &densities));
EXPECT_EQ(expected_densities, densities);
EXPECT_STREQ("2,2.8,3.1", RewriteOptions::ToString(densities));
EXPECT_TRUE(RewriteOptions::ParseFromString("2.8, 2, 3.1", &densities));
EXPECT_EQ(expected_densities, densities);
EXPECT_STREQ("2,2.8,3.1", RewriteOptions::ToString(densities));
EXPECT_TRUE(RewriteOptions::ParseFromString("3.1, 2.8, 2", &densities));
EXPECT_EQ(expected_densities, densities);
EXPECT_STREQ("2,2.8,3.1", RewriteOptions::ToString(densities));
EXPECT_TRUE(RewriteOptions::ParseFromString("13", &densities));
ASSERT_EQ(1, densities.size());
EXPECT_EQ(13, densities[0]);
EXPECT_STREQ("13", RewriteOptions::ToString(densities));
EXPECT_FALSE(RewriteOptions::ParseFromString("", &densities));
EXPECT_FALSE(RewriteOptions::ParseFromString("Hello", &densities));
EXPECT_FALSE(RewriteOptions::ParseFromString("1, 2; 3", &densities));
EXPECT_FALSE(RewriteOptions::ParseFromString("1, 2, 3f", &densities));
EXPECT_FALSE(RewriteOptions::ParseFromString("1, 2, -5", &densities));
EXPECT_FALSE(RewriteOptions::ParseFromString("1.2.3", &densities));
EXPECT_FALSE(RewriteOptions::ParseFromString("1 2 3", &densities));
}
TEST_F(RewriteOptionsTest, MobilizeFiltersTest) {
options_.SetRewriteLevel(RewriteOptions::kMobilizeFilters);
EXPECT_TRUE(options_.Enabled(RewriteOptions::kMobilize));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kRewriteCss));
EXPECT_TRUE(options_.Enabled(RewriteOptions::kRewriteDomains));
EXPECT_TRUE(options_.css_preserve_urls());
EXPECT_TRUE(options_.domain_rewrite_hyperlinks());
EXPECT_TRUE(options_.mob_nav());
EXPECT_FALSE(options_.mob_always());
EXPECT_FALSE(options_.mob_layout());
}
TEST_F(RewriteOptionsTest, ParseAllowVaryOn) {
// Explicitly listed headers should be supported, independently of "Via"
// header.
VerifyAllowVaryOn("User-Agent",
true /* expected_valid */,
false /* expected_allow_auto */,
false /* expected_allow_save_data */,
true /* expected_allow_user_agent */,
false /* expected_allow_accept */,
"User-Agent");
VerifyAllowVaryOn("Save-Data",
true /* expected_valid */,
false /* expected_allow_auto */,
true /* expected_allow_save_data */,
false /* expected_allow_user_agent */,
false /* expected_allow_accept */,
"Save-Data");
VerifyAllowVaryOn("Accept",
true /* expected_valid */,
false /* expected_allow_auto */,
false /* expected_allow_save_data */,
false /* expected_allow_user_agent */,
true /* expected_allow_accept */,
"Accept");
VerifyAllowVaryOn("Save-Data,Accept,User-Agent",
true /* expected_valid */,
false /* expected_allow_auto */,
true /* expected_allow_save_data */,
true /* expected_allow_user_agent */,
true /* expected_allow_accept */,
"Accept,Save-Data,User-Agent");
VerifyAllowVaryOn("Save-Data,Accept,User-Agent",
true /* expected_valid */,
false /* expected_allow_auto */,
true /* expected_allow_save_data */,
true /* expected_allow_user_agent */,
true /* expected_allow_accept */,
"Accept,Save-Data,User-Agent");
// Case and empty space don't matter.
VerifyAllowVaryOn(" accept,SAVE-DATA, uSER-aGENT ",
true /* expected_valid */,
false /* expected_allow_auto */,
true /* expected_allow_save_data */,
true /* expected_allow_user_agent */,
true /* expected_allow_accept */,
"Accept,Save-Data,User-Agent");
// "None" disables all headers.
VerifyAllowVaryOn("None",
true /* expected_valid */,
false /* expected_allow_auto */,
false /* expected_allow_save_data */,
false /* expected_allow_user_agent */,
false /* expected_allow_accept */,
"None");
VerifyAllowVaryOn("nONE ",
true /* expected_valid */,
false /* expected_allow_auto */,
false /* expected_allow_save_data */,
false /* expected_allow_user_agent */,
false /* expected_allow_accept */,
"None");
// In "Auto" mode, the "Auto" bit is set and the "Save-Data" header is
// enabled. Caller can decide which other headers to allow.
VerifyAllowVaryOn("AUTO",
true /* expected_valid */,
true /* expected_allow_auto */,
true /* expected_allow_save_data */,
false /* expected_allow_user_agent */,
false /* expected_allow_accept */,
"Auto");
VerifyAllowVaryOn(" auto ",
true /* expected_valid */,
true /* expected_allow_auto */,
true /* expected_allow_save_data */,
false /* expected_allow_user_agent */,
false /* expected_allow_accept */,
"Auto");
const bool not_used = false;
// Unsupported or invalid headers will not be accepted.
VerifyAllowVaryOn("Content-Length,User-Agent",
false /* expected_valid */,
not_used, not_used, not_used, not_used, "not-used");
VerifyAllowVaryOn(", ,User-Agent,Invalid",
false /* expected_valid */,
not_used, not_used, not_used, not_used, "not-used");
VerifyAllowVaryOn("Content-Length,Invalid",
false /* expected_valid */,
not_used, not_used, not_used, not_used, "not-used");
// Mixing "Auto" with "None", or mixing either of them with other headers
// is not allowed.
VerifyAllowVaryOn("Auto,None",
false /* expected_valid */,
not_used, not_used, not_used, not_used, "not-used");
VerifyAllowVaryOn("Auto,Accept",
false /* expected_valid */,
not_used, not_used, not_used, not_used, "not-used");
VerifyAllowVaryOn("Content-Length,None",
false /* expected_valid */,
not_used, not_used, not_used, not_used, "not-used");
// Empty string and extra comma are disallowed.
VerifyAllowVaryOn("",
false /* expected_valid */,
not_used, not_used, not_used, not_used, "not-used");
VerifyAllowVaryOn(" ",
false /* expected_valid */,
not_used, not_used, not_used, not_used, "not-used");
VerifyAllowVaryOn(",",
false /* expected_valid */,
not_used, not_used, not_used, not_used, "not-used");
VerifyAllowVaryOn(", ,, ",
false /* expected_valid */,
not_used, not_used, not_used, not_used, "not-used");
VerifyAllowVaryOn("accept,",
false /* expected_valid */,
not_used, not_used, not_used, not_used, "not-used");
}
TEST_F(RewriteOptionsTest, MergeAllowVaryOnOptions) {
// New option, if specified, will always overwrite the old one.
VerifyMergingAllowVaryOn("Accept,User-Agent", "Save-Data", "Save-Data");
VerifyMergingAllowVaryOn("Accept", "Save-Data", "Save-Data");
VerifyMergingAllowVaryOn("Accept", "None", "None");
VerifyMergingAllowVaryOn("", "Save-Data", "Save-Data");
VerifyMergingAllowVaryOn("", "None", "None");
VerifyMergingAllowVaryOn("", "Auto", "Auto");
// New option, is un-specified, will be ignored.
VerifyMergingAllowVaryOn("Accept,User-Agent", "", "Accept,User-Agent");
VerifyMergingAllowVaryOn("None", "", "None");
VerifyMergingAllowVaryOn("Auto", "", "Auto");
// If neither option has been specified, the default will be used.
VerifyMergingAllowVaryOn("", "", "Auto");
}
TEST_F(RewriteOptionsTest, ImageQualitiesOverride) {
options_.set_image_recompress_quality(1);
options_.set_image_webp_recompress_quality(20);
options_.set_image_webp_recompress_quality_for_small_screens(30);
options_.set_image_webp_quality_for_save_data(40);
options_.set_image_webp_animated_recompress_quality(50);
options_.set_image_jpeg_recompress_quality(21);
options_.set_image_jpeg_recompress_quality_for_small_screens(31);
options_.set_image_jpeg_quality_for_save_data(41);
options_.set_image_jpeg_num_progressive_scans(5);
options_.set_image_jpeg_num_progressive_scans_for_small_screens(3);
CHECK_EQ(20, options_.ImageWebpQuality());
CHECK_EQ(30, options_.ImageWebpQualityForSmallScreen());
CHECK_EQ(40, options_.ImageWebpQualityForSaveData());
CHECK_EQ(50, options_.ImageWebpAnimatedQuality());
CHECK_EQ(21, options_.ImageJpegQuality());
CHECK_EQ(31, options_.ImageJpegQualityForSmallScreen());
CHECK_EQ(41, options_.ImageJpegQualityForSaveData());
CHECK_EQ(5, options_.image_jpeg_num_progressive_scans());
CHECK_EQ(3, options_.ImageJpegNumProgressiveScansForSmallScreen());
EXPECT_TRUE(options_.HasValidSmallScreenQualities());
EXPECT_TRUE(options_.HasValidSaveDataQualities());
}
TEST_F(RewriteOptionsTest, ImageQualitiesSubEqualToBase) {
options_.set_image_recompress_quality(1);
options_.set_image_webp_recompress_quality(20);
options_.set_image_webp_recompress_quality_for_small_screens(20);
options_.set_image_webp_quality_for_save_data(20);
options_.set_image_webp_animated_recompress_quality(20);
options_.set_image_jpeg_recompress_quality(21);
options_.set_image_jpeg_recompress_quality_for_small_screens(21);
options_.set_image_jpeg_quality_for_save_data(21);
options_.set_image_jpeg_num_progressive_scans(5);
options_.set_image_jpeg_num_progressive_scans_for_small_screens(5);
CHECK_EQ(20, options_.ImageWebpQuality());
CHECK_EQ(20, options_.ImageWebpQualityForSmallScreen());
CHECK_EQ(20, options_.ImageWebpQualityForSaveData());
CHECK_EQ(20, options_.ImageWebpAnimatedQuality());
CHECK_EQ(21, options_.ImageJpegQuality());
CHECK_EQ(21, options_.ImageJpegQualityForSmallScreen());
CHECK_EQ(21, options_.ImageJpegQualityForSaveData());
CHECK_EQ(5, options_.image_jpeg_num_progressive_scans());
CHECK_EQ(5, options_.ImageJpegNumProgressiveScansForSmallScreen());
EXPECT_FALSE(options_.HasValidSmallScreenQualities());
EXPECT_FALSE(options_.HasValidSaveDataQualities());
}
TEST_F(RewriteOptionsTest, ImageQualitiesSubInheritFromBase) {
options_.set_image_recompress_quality(1);
options_.set_image_webp_recompress_quality(-1);
options_.set_image_webp_recompress_quality_for_small_screens(-1);
options_.set_image_webp_quality_for_save_data(-1);
options_.set_image_webp_animated_recompress_quality(-1);
options_.set_image_jpeg_recompress_quality(-1);
options_.set_image_jpeg_recompress_quality_for_small_screens(-1);
options_.set_image_jpeg_quality_for_save_data(-1);
options_.set_image_jpeg_num_progressive_scans(5);
options_.set_image_jpeg_num_progressive_scans_for_small_screens(-1);
CHECK_EQ(1, options_.ImageWebpQuality());
CHECK_EQ(1, options_.ImageWebpQualityForSmallScreen());
CHECK_EQ(1, options_.ImageWebpQualityForSaveData());
CHECK_EQ(1, options_.ImageWebpAnimatedQuality());
CHECK_EQ(1, options_.ImageJpegQuality());
CHECK_EQ(1, options_.ImageJpegQualityForSmallScreen());
CHECK_EQ(1, options_.ImageJpegQualityForSaveData());
CHECK_EQ(5, options_.image_jpeg_num_progressive_scans());
CHECK_EQ(5, options_.ImageJpegNumProgressiveScansForSmallScreen());
EXPECT_FALSE(options_.HasValidSmallScreenQualities());
EXPECT_FALSE(options_.HasValidSaveDataQualities());
}
TEST_F(RewriteOptionsTest, ImageQualitiesAllDisabled) {
options_.set_image_recompress_quality(-1);
options_.set_image_webp_recompress_quality(-1);
options_.set_image_webp_recompress_quality_for_small_screens(-1);
options_.set_image_webp_quality_for_save_data(-1);
options_.set_image_webp_animated_recompress_quality(-1);
options_.set_image_jpeg_recompress_quality(-1);
options_.set_image_jpeg_recompress_quality_for_small_screens(-1);
options_.set_image_jpeg_quality_for_save_data(-1);
CHECK_EQ(-1, options_.ImageWebpQuality());
CHECK_EQ(-1, options_.ImageWebpQualityForSmallScreen());
CHECK_EQ(-1, options_.ImageWebpQualityForSaveData());
CHECK_EQ(-1, options_.ImageWebpAnimatedQuality());
CHECK_EQ(-1, options_.ImageJpegQuality());
CHECK_EQ(-1, options_.ImageJpegQualityForSmallScreen());
CHECK_EQ(-1, options_.ImageJpegQualityForSaveData());
EXPECT_FALSE(options_.HasValidSmallScreenQualities());
EXPECT_FALSE(options_.HasValidSaveDataQualities());
}
TEST_F(RewriteOptionsTest, SupportSaveData) {
// By default, AllowVaryOn is set to "Auto" which implies "Save-Data".
options_.set_image_jpeg_quality_for_save_data(-1);
options_.set_image_webp_quality_for_save_data(-1);
EXPECT_FALSE(options_.HasValidSaveDataQualities());
EXPECT_TRUE(options_.AllowVaryOnSaveData());
EXPECT_FALSE(options_.SupportSaveData());
options_.set_image_jpeg_quality_for_save_data(20);
options_.set_image_webp_quality_for_save_data(30);
EXPECT_TRUE(options_.HasValidSaveDataQualities());
EXPECT_TRUE(options_.AllowVaryOnSaveData());
EXPECT_TRUE(options_.SupportSaveData());
// Disallow vary on "Save-Data".
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.SetOptionFromName(RewriteOptions::kAllowVaryOn,
"None"));
options_.set_image_jpeg_quality_for_save_data(-1);
options_.set_image_webp_quality_for_save_data(-1);
EXPECT_FALSE(options_.HasValidSaveDataQualities());
EXPECT_FALSE(options_.AllowVaryOnSaveData());
EXPECT_FALSE(options_.SupportSaveData());
options_.set_image_jpeg_quality_for_save_data(20);
options_.set_image_webp_quality_for_save_data(30);
EXPECT_TRUE(options_.HasValidSaveDataQualities());
EXPECT_FALSE(options_.AllowVaryOnSaveData());
EXPECT_FALSE(options_.SupportSaveData());
// Explicitly allow vary on "Save-Data".
EXPECT_EQ(RewriteOptions::kOptionOk,
options_.SetOptionFromName(RewriteOptions::kAllowVaryOn,
"Save-Data"));
EXPECT_TRUE(options_.HasValidSaveDataQualities());
EXPECT_TRUE(options_.AllowVaryOnSaveData());
EXPECT_TRUE(options_.SupportSaveData());
options_.set_image_jpeg_quality_for_save_data(-1);
options_.set_image_webp_quality_for_save_data(-1);
EXPECT_FALSE(options_.HasValidSaveDataQualities());
EXPECT_TRUE(options_.AllowVaryOnSaveData());
EXPECT_FALSE(options_.SupportSaveData());
}
} // namespace net_instaweb