| /* |
| * Copyright 2011 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) |
| |
| // Unit-test the resource slot comparator. |
| |
| #include "net/instaweb/rewriter/public/resource_slot.h" |
| |
| #include <set> |
| #include <utility> // for std::pair |
| |
| #include "net/instaweb/rewriter/public/data_url_input_resource.h" |
| #include "net/instaweb/rewriter/public/resource.h" |
| #include "net/instaweb/rewriter/public/rewrite_driver.h" |
| #include "net/instaweb/rewriter/public/rewrite_test_base.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" // for StrCat |
| #include "pagespeed/kernel/html/html_element.h" |
| #include "pagespeed/kernel/html/html_name.h" |
| #include "pagespeed/kernel/html/html_writer_filter.h" |
| #include "pagespeed/kernel/http/content_type.h" |
| |
| namespace { |
| |
| static const char kHtmlUrl[] = "http://html.parse.test/event_list_test.html"; |
| |
| } // namespace |
| |
| namespace net_instaweb { |
| |
| class ResourceSlotTest : public RewriteTestBase { |
| protected: |
| virtual bool AddBody() const { return false; } |
| |
| virtual void SetUp() { |
| RewriteTestBase::SetUp(); |
| |
| // Set up 4 slots for testing. |
| RewriteDriver* driver = rewrite_driver(); |
| ASSERT_TRUE(driver->StartParseId(kHtmlUrl, "resource_slot_test", |
| kContentTypeHtml)); |
| elements_[0] = driver->NewElement(NULL, HtmlName::kLink); |
| driver->AddAttribute(elements_[0], HtmlName::kHref, "v1"); |
| driver->AddAttribute(elements_[0], HtmlName::kSrc, "v2"); |
| elements_[1] = driver->NewElement(NULL, HtmlName::kLink); |
| driver->AddAttribute(element(1), HtmlName::kHref, "v3"); |
| driver->AddAttribute(element(1), HtmlName::kSrc, "v4"); |
| |
| driver->AddElement(element(0), 1); |
| driver->CloseElement(element(0), HtmlElement::BRIEF_CLOSE, 1); |
| driver->AddElement(element(1), 2); |
| driver->CloseElement(element(1), HtmlElement::BRIEF_CLOSE, 3); |
| |
| slots_[0] = MakeSlot(0, 0); |
| slots_[1] = MakeSlot(0, 1); |
| slots_[2] = MakeSlot(1, 0); |
| slots_[3] = MakeSlot(1, 1); |
| } |
| |
| virtual void TearDown() { |
| rewrite_driver()->FinishParse(); |
| RewriteTestBase::TearDown(); |
| } |
| |
| HtmlResourceSlotPtr MakeSlot(int element_index, int attribute_index) { |
| ResourcePtr empty; |
| HtmlResourceSlot* slot = new HtmlResourceSlot( |
| empty, element(element_index), |
| attribute(element_index, attribute_index), |
| html_parse()); |
| return HtmlResourceSlotPtr(slot); |
| } |
| |
| bool InsertAndReturnTrueIfAdded(const HtmlResourceSlotPtr& slot) { |
| std::pair<HtmlResourceSlotSet::iterator, bool> p = slot_set_.insert(slot); |
| return p.second; |
| } |
| |
| int num_slots() const { return slot_set_.size(); } |
| const HtmlResourceSlotPtr slot(int index) const { return slots_[index]; } |
| HtmlElement* element(int index) { return elements_[index]; } |
| HtmlElement::Attribute* attribute(int element_index, int attribute_index) { |
| HtmlElement* el = element(element_index); |
| HtmlElement::AttributeList* attrs = el->mutable_attributes(); |
| int pos = 0; |
| for (net_instaweb::HtmlElement::AttributeIterator i(attrs->begin()); |
| i != attrs->end(); ++i, ++pos) { |
| if (pos == attribute_index) { |
| return i.Get(); |
| } |
| } |
| return NULL; |
| } |
| |
| GoogleString GetHtmlDomAsString() { |
| output_buffer_.clear(); |
| html_parse()->ApplyFilter(html_writer_filter_.get()); |
| return output_buffer_; |
| } |
| |
| private: |
| HtmlResourceSlotSet slot_set_; |
| HtmlResourceSlotPtr slots_[4]; |
| HtmlElement* elements_[2]; |
| }; |
| |
| TEST_F(ResourceSlotTest, Accessors) { |
| EXPECT_EQ(element(0), slot(0)->element()); |
| EXPECT_EQ(attribute(0, 0), slot(0)->attribute()); |
| EXPECT_EQ(element(0), slot(1)->element()); |
| EXPECT_EQ(attribute(0, 1), slot(1)->attribute()); |
| EXPECT_EQ(element(1), slot(2)->element()); |
| EXPECT_EQ(attribute(1, 0), slot(2)->attribute()); |
| EXPECT_EQ(element(1), slot(3)->element()); |
| EXPECT_EQ(attribute(1, 1), slot(3)->attribute()); |
| EXPECT_FALSE(slot(0)->was_optimized()); |
| slot(0)->set_was_optimized(true); |
| EXPECT_TRUE(slot(0)->was_optimized()); |
| |
| EXPECT_EQ("resource_slot_test:1", slot(0)->LocationString()); |
| EXPECT_EQ("resource_slot_test:2-3", slot(2)->LocationString()); |
| |
| const char kDataUrl[] = "data:text/plain,Huh"; |
| ResourcePtr resource = |
| DataUrlInputResource::Make(kDataUrl, rewrite_driver()); |
| ResourceSlotPtr fetch_slot(new FetchResourceSlot(resource)); |
| EXPECT_EQ(StrCat("Fetch of ", kDataUrl), fetch_slot->LocationString()); |
| } |
| |
| TEST_F(ResourceSlotTest, Comparator) { |
| for (int i = 0; i < 4; ++i) { |
| EXPECT_TRUE(InsertAndReturnTrueIfAdded(slot(i))); |
| } |
| EXPECT_EQ(4, num_slots()); |
| |
| // Adding an equivalent slot should fail and leave the number of remembered |
| // slots unchanged. |
| ResourcePtr empty; |
| HtmlResourceSlotPtr s4_dup(MakeSlot(1, 1)); |
| EXPECT_FALSE(InsertAndReturnTrueIfAdded(s4_dup)) |
| << "s4_dup is equivalent to slots_[3] so it should not add to the set"; |
| EXPECT_EQ(4, num_slots()); |
| } |
| |
| // Tests that a slot resource-update has the desired effect on the DOM. |
| TEST_F(ResourceSlotTest, RenderUpdate) { |
| SetupWriter(); |
| |
| // Before update: first href=v1. |
| EXPECT_EQ("<link href=\"v1\" src=\"v2\"/><link href=\"v3\" src=\"v4\"/>", |
| GetHtmlDomAsString()); |
| |
| GoogleUrl gurl("http://html.parse.test/new_css.css"); |
| bool unused; |
| ResourcePtr updated(rewrite_driver()->CreateInputResource(gurl, &unused)); |
| slot(0)->SetResource(updated); |
| slot(0)->Render(); |
| |
| // After update: first href=new_css.css. Note: that we relativize the URL. |
| EXPECT_EQ( |
| "<link href=\"new_css.css\" src=\"v2\"/><link href=\"v3\" src=\"v4\"/>", |
| GetHtmlDomAsString()); |
| } |
| |
| // Tests that a slot deletion takes effect as expected. |
| TEST_F(ResourceSlotTest, RenderDelete) { |
| SetupWriter(); |
| |
| // Before update: first link is present. |
| EXPECT_EQ("<link href=\"v1\" src=\"v2\"/><link href=\"v3\" src=\"v4\"/>", |
| GetHtmlDomAsString()); |
| |
| EXPECT_FALSE(slot(0)->should_delete_element()); |
| EXPECT_FALSE(slot(0)->disable_further_processing()); |
| slot(0)->RequestDeleteElement(); |
| EXPECT_TRUE(slot(0)->should_delete_element()); |
| EXPECT_TRUE(slot(0)->disable_further_processing()); |
| slot(0)->Render(); |
| |
| // After update, first link is gone. |
| EXPECT_EQ("<link href=\"v3\" src=\"v4\"/>", GetHtmlDomAsString()); |
| } |
| |
| } // namespace net_instaweb |