text: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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.
   */
  
  // Base class for tests which do rewrites within CSS.
  
  #ifndef NET_INSTAWEB_REWRITER_PUBLIC_CSS_REWRITE_TEST_BASE_H_
  #define NET_INSTAWEB_REWRITER_PUBLIC_CSS_REWRITE_TEST_BASE_H_
  
  #include "base/logging.h"
  #include "net/instaweb/rewriter/public/css_filter.h"
  #include "net/instaweb/rewriter/public/rewrite_options.h"
  #include "pagespeed/kernel/base/statistics.h"
  #include "pagespeed/kernel/base/string.h"
  #include "pagespeed/kernel/base/string_util.h"
  #include "test/net/instaweb/rewriter/rewrite_test_base.h"
  #include "test/pagespeed/kernel/html/html_parse_test_base.h"
  
  namespace net_instaweb {
  
  class ResourceNamer;
  
  class CssRewriteTestBase : public RewriteTestBase {
   protected:
    CssRewriteTestBase() {
      num_blocks_rewritten_ =
          statistics()->GetVariable(CssFilter::kBlocksRewritten);
      num_fallback_rewrites_ =
          statistics()->GetVariable(CssFilter::kFallbackRewrites);
      num_parse_failures_ = statistics()->GetVariable(CssFilter::kParseFailures);
      num_rewrites_dropped_ =
          statistics()->GetVariable(CssFilter::kRewritesDropped);
      total_bytes_saved_ =
          statistics()->GetUpDownCounter(CssFilter::kTotalBytesSaved);
      total_original_bytes_ =
          statistics()->GetVariable(CssFilter::kTotalOriginalBytes);
      num_uses_ = statistics()->GetVariable(CssFilter::kUses);
      num_flatten_imports_charset_mismatch_ =
          statistics()->GetVariable(CssFilter::kCharsetMismatch);
      num_flatten_imports_invalid_url_ =
          statistics()->GetVariable(CssFilter::kInvalidUrl);
      num_flatten_imports_limit_exceeded_ =
          statistics()->GetVariable(CssFilter::kLimitExceeded);
      num_flatten_imports_minify_failed_ =
          statistics()->GetVariable(CssFilter::kMinifyFailed);
      num_flatten_imports_recursion_ =
          statistics()->GetVariable(CssFilter::kRecursion);
      num_flatten_imports_complex_queries_ =
          statistics()->GetVariable(CssFilter::kComplexQueries);
    }
    ~CssRewriteTestBase() override;
  
    void SetUp() override {
      RewriteTestBase::SetUp();
      options()->set_always_rewrite_css(true);
      AddFilter(RewriteOptions::kRewriteCss);
    }
  
    enum ValidationFlags {
      kNoFlags = 0,
  
      kExpectSuccess =
          1 << 0,              // CSS parser succeeds and URL should be rewritten.
      kExpectCached = 1 << 1,  // CSS parser succeeds and URL should be rewritten,
                               // but everything is from the cache so zero stats.
      kExpectNoChange =
          1 << 2,  // CSS parser succeeds but the URL is not rewritten
                   // because we increased the size of contents.
      kExpectFallback = 1 << 3,  // CSS parser fails, fallback succeeds.
      kExpectFailure = 1 << 4,   // CSS parser fails, fallback failed or disabled.
  
      // TODO(sligocki): Explain why we turn off stats check at each use-site.
      kNoStatCheck = 1 << 5,
      // TODO(sligocki): Why would we ever want to clear fetcher?
      kNoClearFetcher = 1 << 6,
      // TODO(sligocki): Explain why we turn off other contexts.
      kNoOtherContexts = 1 << 7,
  
      kLinkCharsetIsUTF8 = 1 << 8,
      kLinkScreenMedia = 1 << 9,
      kLinkPrintMedia = 1 << 10,
  
      kMetaCharsetUTF8 = 1 << 11,
      kMetaCharsetISO88591 = 1 << 12,
      kMetaHttpEquiv = 1 << 13,
      kMetaHttpEquivUnquoted = 1 << 14,
  
      // Flags to the check various import flattening failure statistics.
      kFlattenImportsCharsetMismatch = 1 << 15,
      kFlattenImportsInvalidUrl = 1 << 16,
      kFlattenImportsLimitExceeded = 1 << 17,
      kFlattenImportsMinifyFailed = 1 << 18,
      kFlattenImportsRecursion = 1 << 19,
      kFlattenImportsComplexQueries = 1 << 20,
  
      // Flags to allow methods to know if the HTML is the test input or output.
      kInputHtml = 1 << 21,
      kOutputHtml = 1 << 22,
    };
  
    static bool ExactlyOneTrue(bool a, bool b) { return a ^ b; }
    static bool ExactlyOneTrue(bool a, bool b, bool c) {
      return ExactlyOneTrue(a, ExactlyOneTrue(b, c));
    }
    static bool ExactlyOneTrue(bool a, bool b, bool c, bool d) {
      return ExactlyOneTrue(a, ExactlyOneTrue(b, c, d));
    }
    static bool ExactlyOneTrue(bool a, bool b, bool c, bool d, bool e) {
      return ExactlyOneTrue(a, ExactlyOneTrue(b, c, d, e));
    }
  
    bool FlagSet(int flags, ValidationFlags f) const { return (flags & f) != 0; }
  
    // Sanity check on flags passed in.
    void CheckFlags(int flags) {
      CHECK(ExactlyOneTrue(
          FlagSet(flags, kExpectSuccess), FlagSet(flags, kExpectCached),
          FlagSet(flags, kExpectNoChange), FlagSet(flags, kExpectFallback),
          FlagSet(flags, kExpectFailure)));
    }
  
    // Check that inline CSS gets rewritten correctly.
    bool ValidateRewriteInlineCss(StringPiece id, StringPiece css_input,
                                  StringPiece expected_css_output, int flags);
  
    void GetNamerForCss(StringPiece id, StringPiece expected_css_output,
                        ResourceNamer* namer);
  
    GoogleString ExpectedUrlForNamer(const ResourceNamer& namer);
  
    GoogleString ExpectedUrlForCss(StringPiece id,
                                   StringPiece expected_css_output);
  
    // Check that external CSS gets rewritten correctly.
    void ValidateRewriteExternalCss(StringPiece id, StringPiece css_input,
                                    StringPiece expected_css_output, int flags) {
      ValidateRewriteExternalCssUrl(id, StrCat(kTestDomain, id, ".css"),
                                    css_input, expected_css_output, flags);
    }
  
    void ValidateRewriteExternalCssUrl(StringPiece id, StringPiece css_url,
                                       StringPiece css_input,
                                       StringPiece expected_css_output,
                                       int flags);
  
    // Makes an HTML document with an external CSS link.
    GoogleString MakeHtmlWithExternalCssLink(StringPiece css_url, int flags,
                                             bool insert_debug_message);
  
    // Makes a CSS body with an external image link, with nice indentation.
    GoogleString MakeIndentedCssWithImage(StringPiece image_url);
  
    // Makes a minified CSS body with an external image link.
    GoogleString MakeMinifiedCssWithImage(StringPiece image_url);
  
    // Extract the background image from the css text
    GoogleString ExtractCssBackgroundImage(StringPiece in_css);
  
    void ValidateRewrite(StringPiece id, StringPiece css_input,
                         StringPiece gold_output, int flags) {
      if (ValidateRewriteInlineCss(StrCat(id, "-inline"), css_input, gold_output,
                                   flags)) {
        // Don't run for external CSS unless inline succeeds.
        ValidateRewriteExternalCss(StrCat(id, "-external"), css_input,
                                   gold_output, flags);
      }
    }
  
    void ValidateFailParse(StringPiece id, StringPiece css_input) {
      ValidateRewrite(id, css_input, css_input, kExpectFailure);
    }
  
    // Reset all Variables.
    void ResetStats();
  
    // Validate HTML rewrite as well as checking statistics.
    bool ValidateWithStats(StringPiece id, StringPiece html_input,
                           StringPiece expected_html_output,
                           StringPiece css_input, StringPiece expected_css_output,
                           int flags);
  
    // Helper to test for how we handle trailing junk
    void TestCorruptUrl(const char* junk);
  
    Variable* num_blocks_rewritten_;
    Variable* num_fallback_rewrites_;
    Variable* num_parse_failures_;
    Variable* num_rewrites_dropped_;
    UpDownCounter* total_bytes_saved_;
    Variable* total_original_bytes_;
    Variable* num_uses_;
    Variable* num_flatten_imports_charset_mismatch_;
    Variable* num_flatten_imports_invalid_url_;
    Variable* num_flatten_imports_limit_exceeded_;
    Variable* num_flatten_imports_minify_failed_;
    Variable* num_flatten_imports_recursion_;
    Variable* num_flatten_imports_complex_queries_;
  };
  
  }  // namespace net_instaweb
  
  #endif  // NET_INSTAWEB_REWRITER_PUBLIC_CSS_REWRITE_TEST_BASE_H_
  