blob: f7743f8add34c5e5ee6ea73d432faba447a0077d [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: jmarantz@google.com (Joshua Marantz)
#include "net/instaweb/rewriter/public/url_partnership.h"
#include "net/instaweb/rewriter/public/domain_lawyer.h"
#include "net/instaweb/rewriter/public/rewrite_options.h"
#include "net/instaweb/rewriter/public/rewrite_test_base.h"
#include "pagespeed/kernel/base/google_message_handler.h"
#include "pagespeed/kernel/base/gtest.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/http/google_url.h"
namespace {
const char kOriginalRequest[] = "http://www.nytimes.com/index.html";
const char kResourceUrl1[] = "r/styles/style.css?appearance=reader/writer?";
const char kResourceUrl2[] = "r/styles/style2.css?appearance=reader";
const char kResourceUrl3[] = "r/main.css";
const char kCdnResourceUrl[] = "http://graphics8.nytimes.com/styles/style.css";
// Resources 1-3 but specified absolutely
const char kAbsoluteResourceUrl1[] =
"http://www.nytimes.com/r/styles/style.css?appearance=reader/writer?";
const char kAbsoluteResourceUrl2[] =
"http://www.nytimes.com/r/styles/style2.css?appearance=reader";
const char kAbsoluteResourceUrl3[] = "http://www.nytimes.com/r/main.css";
} // namespace
namespace net_instaweb {
class UrlPartnershipTest : public RewriteTestBase {
protected:
UrlPartnershipTest()
: styles_path_("http://www.nytimes.com/r/styles/"),
r_path_("http://www.nytimes.com/r/"),
style_url_("style.css?appearance=reader/writer?"),
style2_url_("style2.css?appearance=reader") {
}
virtual void SetUp() {
RewriteTestBase::SetUp();
GoogleUrl original_gurl(kOriginalRequest);
partnership_.reset(new UrlPartnership(rewrite_driver()));
partnership_->Reset(original_gurl);
}
// Add up to 3 URLs -- url2 and url3 are ignored if null.
bool AddUrls(const char* url1, const char* url2, const char* url3) {
bool ret = partnership_->AddUrl(url1, &message_handler_);
if (url2 != NULL) {
ret &= partnership_->AddUrl(url2, &message_handler_);
}
if (url3 != NULL) {
ret &= partnership_->AddUrl(url3, &message_handler_);
}
return ret;
}
// Gets the full path of an index as a GoogleString.
GoogleString FullPath(int index) {
const GoogleUrl* gurl = partnership_->FullPath(index);
StringPiece spec = gurl->Spec();
return GoogleString(spec.data(), spec.size());
}
DomainLawyer* domain_lawyer() { return options()->WriteableDomainLawyer(); }
scoped_ptr<UrlPartnership> partnership_;
GoogleString styles_path_;
GoogleString r_path_;
GoogleString style_url_;
GoogleString style2_url_;
GoogleMessageHandler message_handler_;
};
TEST_F(UrlPartnershipTest, OneUrlFlow) {
ASSERT_TRUE(AddUrls(kResourceUrl1, NULL, NULL));
ASSERT_EQ(1, partnership_->num_urls());
EXPECT_EQ(styles_path_, partnership_->ResolvedBase());
EXPECT_EQ(style_url_, partnership_->RelativePath(0));
EXPECT_EQ(styles_path_ + style_url_, FullPath(0));
}
TEST_F(UrlPartnershipTest, OneUrlFlowAbsolute) {
ASSERT_TRUE(AddUrls(kAbsoluteResourceUrl1, NULL, NULL));
ASSERT_EQ(1, partnership_->num_urls());
EXPECT_EQ(styles_path_, partnership_->ResolvedBase());
EXPECT_EQ(style_url_, partnership_->RelativePath(0));
EXPECT_EQ(styles_path_ + style_url_, FullPath(0));
}
TEST_F(UrlPartnershipTest, TwoUrlFlowSamePath) {
AddUrls(kResourceUrl1, kResourceUrl2, NULL);
ASSERT_EQ(2, partnership_->num_urls());
EXPECT_EQ(styles_path_, partnership_->ResolvedBase());
EXPECT_EQ(style_url_, partnership_->RelativePath(0));
EXPECT_EQ(style2_url_, partnership_->RelativePath(1));
}
TEST_F(UrlPartnershipTest, TwoUrlFlowSamePathMixed) {
AddUrls(kAbsoluteResourceUrl1, kResourceUrl2, NULL);
ASSERT_EQ(2, partnership_->num_urls());
EXPECT_EQ(styles_path_, partnership_->ResolvedBase());
EXPECT_EQ(style_url_, partnership_->RelativePath(0));
EXPECT_EQ(style2_url_, partnership_->RelativePath(1));
}
TEST_F(UrlPartnershipTest, ThreeUrlFlowDifferentPaths) {
AddUrls(kResourceUrl1, kResourceUrl2, kResourceUrl3);
ASSERT_EQ(3, partnership_->num_urls());
EXPECT_EQ(r_path_, partnership_->ResolvedBase());
// We add 2 to the expected values of the 3 kResourceUrl* below to
// skip over the "r/".
EXPECT_EQ(GoogleString(kResourceUrl1 + 2), partnership_->RelativePath(0));
EXPECT_EQ(GoogleString(kResourceUrl2 + 2), partnership_->RelativePath(1));
EXPECT_EQ(GoogleString(kResourceUrl3 + 2), partnership_->RelativePath(2));
}
TEST_F(UrlPartnershipTest, ThreeUrlFlowDifferentPathsAbsolute) {
AddUrls(kAbsoluteResourceUrl1, kAbsoluteResourceUrl2, kAbsoluteResourceUrl3);
ASSERT_EQ(3, partnership_->num_urls());
EXPECT_EQ(r_path_, partnership_->ResolvedBase());
// We add 2 to the expected values of the 3 kResourceUrl* below to
// skip over the "r/".
EXPECT_EQ(GoogleString(kResourceUrl1 + 2), partnership_->RelativePath(0));
EXPECT_EQ(GoogleString(kResourceUrl2 + 2), partnership_->RelativePath(1));
EXPECT_EQ(GoogleString(kResourceUrl3 + 2), partnership_->RelativePath(2));
}
TEST_F(UrlPartnershipTest, ThreeUrlFlowDifferentPathsMixed) {
AddUrls(kAbsoluteResourceUrl1, kResourceUrl2, kAbsoluteResourceUrl3);
ASSERT_EQ(3, partnership_->num_urls());
EXPECT_EQ(r_path_, partnership_->ResolvedBase());
// We add 2 to the expected values of the 3 kResourceUrl* below to
// skip over the "r/".
EXPECT_EQ(GoogleString(kResourceUrl1 + 2), partnership_->RelativePath(0));
EXPECT_EQ(GoogleString(kResourceUrl2 + 2), partnership_->RelativePath(1));
EXPECT_EQ(GoogleString(kResourceUrl3 + 2), partnership_->RelativePath(2));
}
TEST_F(UrlPartnershipTest, ExternalDomainNotDeclared) {
EXPECT_FALSE(AddUrls(kCdnResourceUrl, NULL, NULL));
}
TEST_F(UrlPartnershipTest, ExternalDomainDeclared) {
domain_lawyer()->AddDomain("http://graphics8.nytimes.com", &message_handler_);
EXPECT_TRUE(partnership_->AddUrl(kCdnResourceUrl, &message_handler_));
}
TEST_F(UrlPartnershipTest, ExternalDomainDeclaredButNotMapped) {
// This test shows that while we can start partnerships from nytimes.com
// or graphics8.nytimes.com, we cannot combine those without a mapping.
domain_lawyer()->AddDomain("http://graphics8.nytimes.com", &message_handler_);
EXPECT_TRUE(partnership_->AddUrl(kCdnResourceUrl, &message_handler_));
EXPECT_FALSE(partnership_->AddUrl(kResourceUrl1, &message_handler_));
}
TEST_F(UrlPartnershipTest, AbsExternalDomainDeclaredButNotMapped) {
// This test shows that while we can start partnerships from nytimes.com
// or graphics8.nytimes.com, we cannot combine those without a mapping.
domain_lawyer()->AddDomain("http://graphics8.nytimes.com", &message_handler_);
EXPECT_TRUE(partnership_->AddUrl(kCdnResourceUrl, &message_handler_));
EXPECT_FALSE(partnership_->AddUrl(kAbsoluteResourceUrl1, &message_handler_));
}
TEST_F(UrlPartnershipTest, EmptyTail) {
EXPECT_FALSE(partnership_->AddUrl("", &message_handler_));
EXPECT_TRUE(partnership_->AddUrl("http://www.nytimes.com",
&message_handler_));
EXPECT_TRUE(partnership_->AddUrl("http://www.nytimes.com/",
&message_handler_));
}
TEST_F(UrlPartnershipTest, EmptyWithPartner) {
GoogleUrl base_gurl("http://www.google.com/styles/x.html");
UrlPartnership p(rewrite_driver());
p.Reset(base_gurl);
EXPECT_TRUE(p.AddUrl("/styles", &message_handler_));
EXPECT_FALSE(p.AddUrl("", &message_handler_));
EXPECT_TRUE(p.AddUrl("/", &message_handler_));
EXPECT_TRUE(p.AddUrl("..", &message_handler_));
}
TEST_F(UrlPartnershipTest, NeedsATrim) {
AddUrls(" http://www.nytimes.com/needs_a_trim.jpg ", NULL, NULL);
EXPECT_EQ(GoogleString("needs_a_trim.jpg"), partnership_->RelativePath(0));
}
TEST_F(UrlPartnershipTest, RemoveLast) {
AddUrls(kAbsoluteResourceUrl1, kAbsoluteResourceUrl2, kAbsoluteResourceUrl3);
EXPECT_EQ(r_path_, partnership_->ResolvedBase());
partnership_->RemoveLast();
EXPECT_EQ(styles_path_, partnership_->ResolvedBase());
}
TEST_F(UrlPartnershipTest, ResourcesFromMappedDomains) {
domain_lawyer()->AddRewriteDomainMapping(
"http://graphics8.nytimes.com", "http://www.nytimes.com",
&message_handler_);
domain_lawyer()->AddRewriteDomainMapping(
"http://graphics8.nytimes.com", "http://styles.com", &message_handler_);
// We can legally combine resources across multiple domains if they are
// all mapped together.
ASSERT_TRUE(AddUrls(kCdnResourceUrl, kResourceUrl1,
"http://styles.com/external.css"));
EXPECT_EQ("http://graphics8.nytimes.com/", partnership_->ResolvedBase());
}
TEST_F(UrlPartnershipTest, ResourcesFromMappedSameDomainsDifferentPaths) {
domain_lawyer()->AddRewriteDomainMapping(
"http://cdn.com/nytimes", "http://www.nytimes.com", &message_handler_);
domain_lawyer()->AddRewriteDomainMapping(
"http://cdn.com/nytimes", "http://styles.com", &message_handler_);
domain_lawyer()->AddDomain("http://cdn.com/notw", &message_handler_);
// We can combine these because they're mapped to the same domain but
// different paths.
EXPECT_TRUE(AddUrls("http://cdn.com/notw/style.css",
"r/styles/style.css?appearance=reader/writer?",
"http://styles.com/external.css"));
EXPECT_EQ("http://cdn.com/", partnership_->ResolvedBase());
}
TEST_F(UrlPartnershipTest, ResourcesFromMappedSameDomainsSamePaths) {
domain_lawyer()->AddRewriteDomainMapping(
"http://cdn.com/nytimes", "http://www.nytimes.com", &message_handler_);
domain_lawyer()->AddRewriteDomainMapping(
"http://cdn.com/nytimes", "http://styles.com", &message_handler_);
// We can legally combine resources across multiple domains if they all
// map to the same domain+path.
ASSERT_TRUE(AddUrls("http://cdn.com/nytimes/style.css",
"r/styles/style.css?appearance=reader/writer?",
"http://styles.com/external.css"));
EXPECT_EQ("http://cdn.com/nytimes/", partnership_->ResolvedBase());
}
TEST_F(UrlPartnershipTest, ResourcesFromMappedDifferentDomainsSamePaths) {
domain_lawyer()->AddRewriteDomainMapping(
"http://cdn.com/nytimes", "http://www.nytimes.com", &message_handler_);
domain_lawyer()->AddRewriteDomainMapping(
"http://cdn.com/nytimes", "http://styles.com", &message_handler_);
domain_lawyer()->AddRewriteDomainMapping(
"http://cdn.com/nypost", "http://www.nypost.com", &message_handler_);
domain_lawyer()->AddRewriteDomainMapping(
"http://cdn.com/nypost", "http://money.com", &message_handler_);
// We can combine these because they all map to cdn.com.
ASSERT_TRUE(AddUrls("http://cdn.com/nypost/style.css",
"r/styles/style.css?appearance=reader/writer?",
"http://money.com/external.css"));
EXPECT_EQ("http://cdn.com/", partnership_->ResolvedBase());
}
TEST_F(UrlPartnershipTest, AllowDisallow) {
options()->Disallow("*/*.css");
options()->Allow("*/a*.css");
EXPECT_FALSE(partnership_->AddUrl("foo.css", &message_handler_));
EXPECT_TRUE(partnership_->AddUrl("afoo.css", &message_handler_));
EXPECT_TRUE(partnership_->AddUrl("foo.jpg", &message_handler_));
}
TEST_F(UrlPartnershipTest, CombineAcrossPaths) {
options()->set_combine_across_paths(true);
EXPECT_TRUE(partnership_->AddUrl("a/foo.css", &message_handler_));
EXPECT_TRUE(partnership_->AddUrl("b/bar.css", &message_handler_));
EXPECT_TRUE(partnership_->AddUrl("a/baz.css", &message_handler_));
}
TEST_F(UrlPartnershipTest, NoCombineAcrossPaths) {
options()->set_combine_across_paths(false);
EXPECT_TRUE(partnership_->AddUrl("a/foo.css", &message_handler_));
EXPECT_FALSE(partnership_->AddUrl("b/bar.css", &message_handler_));
EXPECT_TRUE(partnership_->AddUrl("a/baz.css", &message_handler_));
}
} // namespace net_instaweb