text: /*
   * 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)
  
  // Unit-test some small filters.
  
  #include "net/instaweb/rewriter/public/rewrite_options.h"
  #include "net/instaweb/rewriter/public/rewrite_test_base.h"
  #include "pagespeed/kernel/base/gtest.h"
  #include "pagespeed/kernel/base/hasher.h"
  #include "pagespeed/kernel/base/mock_message_handler.h"
  #include "pagespeed/kernel/base/string.h"
  #include "pagespeed/kernel/base/string_util.h"
  #include "pagespeed/kernel/cache/lru_cache.h"
  #include "pagespeed/kernel/html/html_parse_test_base.h"
  #include "pagespeed/kernel/http/content_type.h"
  
  namespace net_instaweb {
  
  namespace {
  
  class RewriterTest : public RewriteTestBase {
    // We want to be able to edit the exact HTML (not have tags added).
    virtual bool AddHtmlTags() const { return false; }
  };
  
  TEST_F(RewriterTest, AddHead) {
    AddFilter(RewriteOptions::kAddHead);
    // Note: Head is added before <body>, but inside <html>.
    ValidateExpected("add_head",
        "<html><body><p>text</p></body></html>",
        "<html><head/><body><p>text</p></body></html>");
  }
  
  TEST_F(RewriterTest, AddHeadNoBody) {
    AddFilter(RewriteOptions::kAddHead);
    // Note: Still adds a <head/> element even though there's no body.
    ValidateExpected("add_head_no_body", "<p>text</p>", "<head/><p>text</p>");
    // We don't report errors (regression test for Issue 134)
    EXPECT_EQ(0, message_handler_.SeriousMessages());
  }
  
  TEST_F(RewriterTest, AddHeadEmpty) {
    AddFilter(RewriteOptions::kAddHead);
    // Add head even if there are no elements.
    ValidateExpected("add_head_empty", "", "<head/>");
  }
  
  TEST_F(RewriterTest, DontAddExtraHead) {
    AddFilter(RewriteOptions::kAddHead);
    // Add head even if there are no elements.
    ValidateNoChanges("dont_add_extra_head", "<head/>");
  }
  
  TEST_F(RewriterTest, AddDuplicateHead) {
    AddFilter(RewriteOptions::kAddHead);
    // Add head before first non-head non-html element. We do not look ahead to
    // see if there's a head later (combine_heads can fix that up).
    ValidateExpected("add_duplicate_head",
                     "<p>text</p><head/>", "<head/><p>text</p><head/>");
  }
  
  TEST_F(RewriterTest, MergeHead) {
    AddFilter(RewriteOptions::kCombineHeads);
    ValidateExpected("merge_2_heads",
        "<head a><p>1</p></head>4<head b>2<link x>3</head><link y>end",
        "<head a><p>1</p>2<link x>3</head>4<link y>end");
    ValidateExpected("merge_3_heads",
        "<head a><p>1</p></head>4<head b>2<link x>3</head><link y>"
        "<body>b<head><link z></head>ye</body>",
        "<head a><p>1</p>2<link x>3<link z></head>4<link y>"
        "<body>bye</body>");
  }
  
  TEST_F(RewriterTest, HandlingOfInvalidUrls) {
    UseMd5Hasher();
    AddFilter(RewriteOptions::kRewriteCss);
  
    const char kCssData[] = "a { color: red }";
    const char kMinimizedCssData[] = "a{color:red}";
    SetResponseWithDefaultHeaders("a.css", kContentTypeCss, kCssData, 100);
  
    // Fetching the real rewritten resource name should work.
    // TODO(sligocki): This will need to be regolded if naming format changes.
    GoogleString hash = hasher()->Hash(kMinimizedCssData);
    GoogleString good_url =
        Encode(kTestDomain, RewriteOptions::kCssFilterId, hash, "a.css", "css");
    EXPECT_TRUE(TryFetchResource(good_url));
  
    // Querying with an appended query should work fine, too, and cause a cache
    // hit from the above, not recomputation
    int inserts_before = lru_cache()->num_inserts();
    int hits_before = lru_cache()->num_hits();
    EXPECT_TRUE(TryFetchResource(StrCat(good_url, "?foo")));
    int inserts_after = lru_cache()->num_inserts();
    EXPECT_EQ(0, lru_cache()->num_identical_reinserts());
    EXPECT_EQ(inserts_before, inserts_after);
    EXPECT_EQ(hits_before + 1, lru_cache()->num_hits());
  
    // Fetching variants should not cause system problems.
    // Changing hash still works.
    // Note: If any of these switch from true to false, that's probably fine.
    // We'd just like to keep track of what causes errors and what doesn't.
    EXPECT_TRUE(TryFetchResource(Encode(kTestDomain, RewriteOptions::kCssFilterId,
                                        "foobar", "a.css", "css")));
  
    // ... we even accept fetches with invalid extensions.
    EXPECT_TRUE(
        TryFetchResource(Encode(kTestDomain, RewriteOptions::kCssFilterId, hash,
                                "a.css", "ext")));
  
    // Changing other fields can lead to error.
    GoogleString bad_url = Encode(kTestDomain, "xz", hash, "a.css", "css");
  
    EXPECT_FALSE(TryFetchResource(bad_url));
  }
  
  }  // namespace
  
  }  // namespace net_instaweb
  