blob: 9360de8b8d969b34c93cea0a706b8f5971f620bc [file] [log] [blame]
/*
* Copyright 2013 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: bharathbhushan@google.com (Bharath Bhushan Kowshik Raghupathi)
//
// Test code for SplitHtmlHelper filter.
#include "net/instaweb/rewriter/public/split_html_helper_filter.h"
#include "net/instaweb/http/public/log_record.h"
#include "net/instaweb/http/public/logging_proto_impl.h"
#include "net/instaweb/http/public/request_context.h"
#include "net/instaweb/rewriter/public/critical_images_finder.h"
#include "net/instaweb/rewriter/public/critical_images_finder_test_base.h"
#include "net/instaweb/rewriter/public/delay_images_filter.h"
#include "net/instaweb/rewriter/public/lazyload_images_filter.h"
#include "net/instaweb/rewriter/public/rewrite_driver.h"
#include "net/instaweb/rewriter/public/rewrite_options.h"
#include "net/instaweb/rewriter/public/rewrite_test_base.h"
#include "net/instaweb/rewriter/public/server_context.h"
#include "pagespeed/kernel/base/gtest.h"
#include "pagespeed/kernel/base/ref_counted_ptr.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/base/wildcard.h" // for Wildcard
#include "pagespeed/kernel/http/content_type.h"
#include "pagespeed/kernel/http/http_names.h"
#include "pagespeed/kernel/http/request_headers.h"
#include "pagespeed/kernel/http/user_agent_matcher_test_base.h"
#include "pagespeed/opt/logging/enums.pb.h"
namespace net_instaweb {
namespace {
const char kSampleJpegData[] = "data:image/jpeg;base64*";
} // namespace
class SplitHtmlHelperFilterTest : public RewriteTestBase {
public:
SplitHtmlHelperFilterTest() {}
protected:
virtual void SetUp() {
RewriteTestBase::SetUp();
SetCurrentUserAgent(
UserAgentMatcherTestBase::kChrome18UserAgent);
SetHtmlMimetype(); // Prevent insertion of CDATA tags to static JS.
}
void Init() {
options()->EnableFilter(RewriteOptions::kSplitHtmlHelper);
options()->set_critical_line_config("div[@id=\"b\"],"
"div[@id=\"c\"]");
rewrite_driver()->AddFilters();
rewrite_driver()->set_critical_images_info(new CriticalImagesInfo);
}
void CheckNumCriticalImages(int expected) {
CriticalImagesInfo* critical_images_info =
rewrite_driver()->critical_images_info();
EXPECT_EQ(expected, critical_images_info->html_critical_images.size());
}
void CheckCriticalImage(StringPiece url) {
CriticalImagesFinder* finder =
rewrite_driver()->server_context()->critical_images_finder();
EXPECT_TRUE(finder->IsHtmlCriticalImage(url, rewrite_driver()));
}
void CheckLoggingStatus(RewriterHtmlApplication::Status status) {
rewrite_driver()->log_record()->WriteLog();
for (int i = 0; i < logging_info()->rewriter_stats_size(); i++) {
if (logging_info()->rewriter_stats(i).id() == "se" &&
logging_info()->rewriter_stats(i).has_html_status()) {
EXPECT_EQ(status, logging_info()->rewriter_stats(i).html_status());
return;
}
}
FAIL();
}
void SetBtfRequest() {
rewrite_driver()->request_context()->set_split_request_type(
RequestContext::SPLIT_BELOW_THE_FOLD);
}
void SetAtfRequest() {
rewrite_driver()->request_context()->set_split_request_type(
RequestContext::SPLIT_ABOVE_THE_FOLD);
}
GoogleString GetLazyloadImageTag(const GoogleString& url, bool no_transform) {
return StrCat("<img data-pagespeed-lazy-src='", url, "'",
no_transform ? " data-pagespeed-no-transform" : "",
" src=\"/psajs/1.0.gif\" "
"onload=\"", LazyloadImagesFilter::kImageOnloadCode,
"\" onerror=\"this.onerror=null;",
LazyloadImagesFilter::kImageOnloadCode, "\">");
}
GoogleString GetInlinePreviewImageTag(const GoogleString& url,
const GoogleString& low_res_src) {
return StrCat(
"<img data-pagespeed-high-res-src=\"", url, "\" src=\"", low_res_src,
"\" onload=\"", DelayImagesFilter::kImageOnloadCode,
"\" onerror=\"this.onerror=null;", DelayImagesFilter::kImageOnloadCode,
"\">");
}
GoogleString GetImageOnloadScriptBlock() const {
return StrCat("<script data-pagespeed-no-defer type=\"text/javascript\">",
DelayImagesFilter::kImageOnloadJsSnippet,
"</script>");
}
RequestHeaders request_headers_;
};
TEST_F(SplitHtmlHelperFilterTest, BasicTest) {
Init();
EXPECT_TRUE(options()->Enabled(RewriteOptions::kSplitHtmlHelper));
ValidateNoChanges("split_helper_basic_test", "");
CheckLoggingStatus(RewriterHtmlApplication::ACTIVE);
}
TEST_F(SplitHtmlHelperFilterTest, DisabledTest1) {
Init();
EXPECT_TRUE(options()->Enabled(RewriteOptions::kSplitHtmlHelper));
options()->ClearSignatureForTesting();
options()->set_critical_line_config("");
ValidateNoChanges("split_helper_disabled1", "");
CheckLoggingStatus(RewriterHtmlApplication::DISABLED);
}
TEST_F(SplitHtmlHelperFilterTest, DisabledTest2) {
Init();
EXPECT_TRUE(options()->Enabled(RewriteOptions::kSplitHtmlHelper));
SetCurrentUserAgent("does_not_support_split");
ValidateNoChanges("split_helper_disabled2", "");
CheckLoggingStatus(RewriterHtmlApplication::USER_AGENT_NOT_SUPPORTED);
}
TEST_F(SplitHtmlHelperFilterTest, AtfRequest) {
Init();
ValidateExpected(
"split_helper_atf",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg'></div>"
"<div id='c'><img src='3.jpeg'></div>"
"</body>",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg' data-pagespeed-no-transform></div>"
"<div id='c'><img src='3.jpeg' data-pagespeed-no-transform></div>"
"</body>");
CheckNumCriticalImages(1);
CheckCriticalImage("http://test.com/1.jpeg");
CheckLoggingStatus(RewriterHtmlApplication::ACTIVE);
}
TEST_F(SplitHtmlHelperFilterTest, ExplicitAtfRequest) {
Init();
SetAtfRequest();
ValidateExpected(
"split_helper_explicit_atf",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg'></div>"
"<div id='c'><img src='3.jpeg'></div>"
"</body>",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg' data-pagespeed-no-transform></div>"
"<div id='c'><img src='3.jpeg' data-pagespeed-no-transform></div>"
"</body>");
CheckNumCriticalImages(1);
CheckCriticalImage("http://test.com/1.jpeg");
CheckLoggingStatus(RewriterHtmlApplication::ACTIVE);
}
TEST_F(SplitHtmlHelperFilterTest, AtfRequestWithCriticalImages) {
Init();
CriticalImagesInfo* critical_images_info = new CriticalImagesInfo;
critical_images_info->html_critical_images.insert("http://test.com/4.jpeg");
critical_images_info->html_critical_images.insert("http://test.com/5.jpeg");
rewrite_driver()->set_critical_images_info(critical_images_info);
CheckNumCriticalImages(2);
ValidateExpected(
"split_helper_atf",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg'></div>"
"<div id='c'><img src='3.jpeg'></div>"
"</body>",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg' data-pagespeed-no-transform></div>"
"<div id='c'><img src='3.jpeg' data-pagespeed-no-transform></div>"
"</body>");
CheckNumCriticalImages(1);
CheckCriticalImage("http://test.com/1.jpeg");
CheckLoggingStatus(RewriterHtmlApplication::ACTIVE);
}
TEST_F(SplitHtmlHelperFilterTest, AtfRequestWithNullCriticalImages) {
Init();
rewrite_driver()->set_critical_images_info(NULL);
ValidateExpected(
"split_helper_atf_with_null_critical_images",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg'></div>"
"<div id='c'><img src='3.jpeg'></div>"
"</body>",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg' data-pagespeed-no-transform></div>"
"<div id='c'><img src='3.jpeg' data-pagespeed-no-transform></div>"
"</body>");
CheckNumCriticalImages(1);
CheckCriticalImage("http://test.com/1.jpeg");
CheckLoggingStatus(RewriterHtmlApplication::ACTIVE);
}
TEST_F(SplitHtmlHelperFilterTest, BtfRequest) {
Init();
SetBtfRequest();
CriticalImagesInfo* critical_images_info = new CriticalImagesInfo;
critical_images_info->html_critical_images.insert("http://test.com/1.jpeg");
rewrite_driver()->set_critical_images_info(critical_images_info);
CheckNumCriticalImages(1);
ValidateNoChanges(
"split_helper_btf",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg'></div>"
"<div id='c'><img src='3.jpeg'></div>"
"</body>");
CheckNumCriticalImages(0);
CheckLoggingStatus(RewriterHtmlApplication::ACTIVE);
}
TEST_F(SplitHtmlHelperFilterTest, AtfRequestTwoXpaths) {
Init();
options()->ClearSignatureForTesting();
options()->set_critical_line_config("div[@id=\"b\"]:div[@id=\"d\"]");
ValidateExpected(
"split_helper_atf_2xpaths",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg'>"
"<div id='c'><img src='3.jpeg'></div>"
"</div>"
"<div id='d'><img src='4.jpeg'></div>"
"</body>",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg' data-pagespeed-no-transform>"
"<div id='c'><img src='3.jpeg' data-pagespeed-no-transform></div>"
"</div>"
"<div id='d'><img src='4.jpeg'></div>"
"</body>");
CheckNumCriticalImages(2);
CheckCriticalImage("http://test.com/1.jpeg");
CheckCriticalImage("http://test.com/4.jpeg");
CheckLoggingStatus(RewriterHtmlApplication::ACTIVE);
}
TEST_F(SplitHtmlHelperFilterTest, AtfRequestXPathWithChildCount) {
Init();
options()->ClearSignatureForTesting();
options()->set_critical_line_config("div[2]:div[4]");
ValidateExpected(
"split_helper_atf_xpath_with_child_count",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg'></div>"
"<div id='c'><img src='3.jpeg'></div>"
"<div id='d'><img src='4.jpeg'></div>"
"</body>",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg' data-pagespeed-no-transform></div>"
"<div id='c'><img src='3.jpeg' data-pagespeed-no-transform></div>"
"<div id='d'><img src='4.jpeg'></div>"
"</body>");
CheckNumCriticalImages(2);
CheckCriticalImage("http://test.com/1.jpeg");
CheckCriticalImage("http://test.com/4.jpeg");
CheckLoggingStatus(RewriterHtmlApplication::ACTIVE);
}
TEST_F(SplitHtmlHelperFilterTest, AtfRequestNoDeferCases) {
Init();
options()->ClearSignatureForTesting();
options()->set_critical_line_config("div[2]:div[4]");
ValidateExpected(
"split_helper_atf_nodefer_cases",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<script data-pagespeed-no-defer></script>"
"<div id='b'><img src='2.jpeg'>"
"<script data-pagespeed-no-defer></script>"
"<div id='c' data-pagespeed-no-defer></div>"
"</div>"
"<div id='d'><img src='3.jpeg'></div>"
"<div id='e'><img src='4.jpeg'></div>"
"</body>",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<script data-pagespeed-no-defer></script>"
"<div id='b'><img src='2.jpeg' data-pagespeed-no-transform>"
"<script data-pagespeed-no-defer></script>"
"<div id='c' data-pagespeed-no-defer></div>"
"</div>"
"<div id='d'><img src='3.jpeg' data-pagespeed-no-transform></div>"
"<div id='e'><img src='4.jpeg'></div>"
"</body>");
CheckNumCriticalImages(2);
CheckCriticalImage("http://test.com/1.jpeg");
CheckCriticalImage("http://test.com/4.jpeg");
CheckLoggingStatus(RewriterHtmlApplication::ACTIVE);
}
TEST_F(SplitHtmlHelperFilterTest, AtfRequestNonCountedChildren) {
Init();
options()->ClearSignatureForTesting();
options()->set_critical_line_config("div[2]");
ValidateExpected(
"split_helper_atf_nodefer_cases",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<link>"
"<script></script>"
"<noscript></noscript>"
"<style></style>"
"<div id='b'><img src='2.jpeg'></div>"
"<div id='c'><img src='3.jpeg'></div>"
"</body>",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<link>"
"<script></script>"
"<noscript></noscript>"
"<style></style>"
"<div id='b'><img src='2.jpeg' data-pagespeed-no-transform></div>"
"<div id='c'><img src='3.jpeg' data-pagespeed-no-transform></div>"
"</body>");
CheckNumCriticalImages(1);
CheckCriticalImage("http://test.com/1.jpeg");
CheckLoggingStatus(RewriterHtmlApplication::ACTIVE);
}
TEST_F(SplitHtmlHelperFilterTest, BtfRequestConfigInHeader) {
Init();
SetBtfRequest();
options()->ClearSignatureForTesting();
options()->set_critical_line_config("");
request_headers_.Add(HttpAttributes::kXPsaSplitConfig, "div[2]");
rewrite_driver()->SetRequestHeaders(request_headers_);
ValidateNoChanges(
"split_helper_btf_request_config_in_header",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg'></div>"
"<div id='c'><img src='3.jpeg'></div>"
"</body>");
CheckNumCriticalImages(0);
CheckLoggingStatus(RewriterHtmlApplication::ACTIVE);
}
TEST_F(SplitHtmlHelperFilterTest, AtfRequestWithLazyload) {
TestCriticalImagesFinder* finder =
new TestCriticalImagesFinder(NULL, statistics());
server_context()->set_critical_images_finder(finder);
options()->EnableFilter(RewriteOptions::kLazyloadImages);
options()->set_support_noscript_enabled(false);
Init();
ValidateExpected(
"split_helper_atf_with_lazyload",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg'></div>"
"<div id='c'><img src='3.jpeg'></div>"
"</body>",
StrCat(
GetLazyloadScriptHtml(),
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'>",
GetLazyloadImageTag("2.jpeg", true),
"</div>",
"<div id='c'>", GetLazyloadImageTag("3.jpeg", true), "</div>",
GetLazyloadPostscriptHtml(),
"</body>"));
CheckNumCriticalImages(1);
CheckCriticalImage("http://test.com/1.jpeg");
CheckLoggingStatus(RewriterHtmlApplication::ACTIVE);
}
TEST_F(SplitHtmlHelperFilterTest, BtfRequestWithLazyload) {
TestCriticalImagesFinder* finder =
new TestCriticalImagesFinder(NULL, statistics());
server_context()->set_critical_images_finder(finder);
options()->EnableFilter(RewriteOptions::kLazyloadImages);
options()->set_support_noscript_enabled(false);
Init();
SetBtfRequest();
GoogleString expected_output_html = StrCat(
GetLazyloadScriptHtml(),
"<body>"
"<div id='a'>",
GetLazyloadImageTag("1.jpeg", false),
"</div>"
"<div id='b'>", GetLazyloadImageTag("2.jpeg", false), "</div>"
"<div id='c'>", GetLazyloadImageTag("3.jpeg", false), "</div>",
GetLazyloadPostscriptHtml(),
"</body>");
ValidateExpected(
"split_helper_btf_with_lazyload",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg'></div>"
"<div id='c'><img src='3.jpeg'></div>"
"</body>",
expected_output_html);
CheckNumCriticalImages(0);
CheckLoggingStatus(RewriterHtmlApplication::ACTIVE);
}
TEST_F(SplitHtmlHelperFilterTest, AtfRequestWithInlinePreview) {
TestCriticalImagesFinder* finder =
new TestCriticalImagesFinder(NULL, statistics());
server_context()->set_critical_images_finder(finder);
options()->EnableFilter(RewriteOptions::kDelayImages);
options()->set_support_noscript_enabled(false);
Init();
AddFileToMockFetcher("http://test.com/1.jpeg", "Sample.jpg",
kContentTypeJpeg, 100);
GoogleString input_html = "<body>"
"<div id='a'><img src=\"1.jpeg\"></div>"
"<div id='b'><img src=\"2.jpeg\"></div>"
"<div id='c'><img src=\"3.jpeg\"></div>"
"</body>";
GoogleString output_html = StrCat(
"<html>\n<body>"
"<div id='a'>",
GetImageOnloadScriptBlock(),
GetInlinePreviewImageTag("1.jpeg", kSampleJpegData),
"</div>"
"<div id='b'><img src=\"2.jpeg\"></div>"
"<div id='c'><img src=\"3.jpeg\"></div>"
"</body></html>");
Parse("split_helper_atf_with_inline_preview", input_html);
EXPECT_TRUE(Wildcard(output_html).Match(output_buffer_))
<< "Expected:\n" << output_html << "\n\nGot:\n" << output_buffer_;
CheckNumCriticalImages(1);
CheckCriticalImage("http://test.com/1.jpeg");
CheckLoggingStatus(RewriterHtmlApplication::ACTIVE);
}
TEST_F(SplitHtmlHelperFilterTest, BtfRequestWithInlinePreview) {
TestCriticalImagesFinder* finder =
new TestCriticalImagesFinder(NULL, statistics());
server_context()->set_critical_images_finder(finder);
options()->EnableFilter(RewriteOptions::kDelayImages);
options()->set_support_noscript_enabled(false);
Init();
SetBtfRequest();
AddFileToMockFetcher("http://test.com/1.jpeg", "Sample.jpg",
kContentTypeJpeg, 100);
AddFileToMockFetcher("http://test.com/2.jpeg", "Sample.jpg",
kContentTypeJpeg, 100);
AddFileToMockFetcher("http://test.com/3.jpeg", "Sample.jpg",
kContentTypeJpeg, 100);
ValidateExpected(
"split_helper_atf",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg'></div>"
"<div id='c'><img src='3.jpeg'></div>"
"</body>",
"<body>"
"<div id='a'><img src='1.jpeg'></div>"
"<div id='b'><img src='2.jpeg'></div>"
"<div id='c'><img src='3.jpeg'></div>"
"</body>");
CheckNumCriticalImages(0);
CheckLoggingStatus(RewriterHtmlApplication::ACTIVE);
}
TEST_F(SplitHtmlHelperFilterTest, AtfNestedPanelsRequestWithInlinePreview) {
TestCriticalImagesFinder* finder =
new TestCriticalImagesFinder(NULL, statistics());
server_context()->set_critical_images_finder(finder);
options()->EnableFilter(RewriteOptions::kDelayImages);
options()->set_support_noscript_enabled(false);
Init();
AddFileToMockFetcher("http://test.com/1.jpeg", "Sample.jpg",
kContentTypeJpeg, 100);
GoogleString input_html = "<body>"
"<div id='a'><img src=\"1.jpeg\"></div>"
"<div id='b'>"
"<div id='c'></div>"
"<img src=\"2.jpeg\"></div>"
"<div id='d'><img src=\"3.jpeg\"></div>"
"</body>";
GoogleString output_html = StrCat(
"<html>\n<body>"
"<div id='a'>",
GetImageOnloadScriptBlock(),
GetInlinePreviewImageTag("1.jpeg", kSampleJpegData),
"</div>"
"<div id='b'>"
"<div id='c'></div>"
"<img src=\"2.jpeg\"></div>"
"<div id='d'><img src=\"3.jpeg\"></div>"
"</body></html>");
Parse("split_helper_atf_with_inline_preview", input_html);
EXPECT_TRUE(Wildcard(output_html).Match(output_buffer_))
<< "Expected:\n" << output_html << "\n\nGot:\n" << output_buffer_;
CheckNumCriticalImages(1);
CheckCriticalImage("http://test.com/1.jpeg");
CheckLoggingStatus(RewriterHtmlApplication::ACTIVE);
}
} // namespace net_instaweb