text: from __future__ import unicode_literals
  
  import abc
  import inspect
  import os
  import re
  
  import six
  
  MYPY = False
  if MYPY:
      # MYPY is set to True when run under Mypy.
      from typing import Any, List, Match, Optional, Pattern, Text, Tuple, cast
      Error = Tuple[Text, Text, Text, Optional[int]]
  
  
  def collapse(text):
      # type: (Text) -> Text
      return inspect.cleandoc(str(text)).replace("\n", " ")
  
  
  class Rule(six.with_metaclass(abc.ABCMeta)):
      @abc.abstractproperty
      def name(self):
          # type: () -> Text
          pass
  
      @abc.abstractproperty
      def description(self):
          # type: () -> Text
          pass
  
      to_fix = None  # type: Optional[Text]
  
      @classmethod
      def error(cls, path, context=(), line_no=None):
          # type: (Text, Tuple[Any, ...], Optional[int]) -> Error
          if MYPY:
              name = cast(Text, cls.name)
              description = cast(Text, cls.description)
          else:
              name = cls.name
              description = cls.description
          description = description % context
          return (name, description, path, line_no)
  
  
  class MissingLink(Rule):
      name = "MISSING-LINK"
      description = "Testcase file must have a link to a spec"
      to_fix = """
          Ensure that there is a `<link rel="help" href="[url]">` for the spec.
          `MISSING-LINK` is designed to ensure that the CSS build tool can find
          the tests. Note that the CSS build system is primarily used by
          [test.csswg.org/](http://test.csswg.org/), which doesn't use
          `wptserve`, so `*.any.js` and similar tests won't work there; stick
          with the `.html` equivalent.
      """
  
  
  class PathLength(Rule):
      name = "PATH LENGTH"
      description = "/%s longer than maximum path length (%d > 150)"
      to_fix = "use shorter filename to rename the test file"
  
  
  class FileType(Rule):
      name = "FILE TYPE"
      description = "/%s is an unsupported file type (%s)"
  
  
  class WorkerCollision(Rule):
      name = "WORKER COLLISION"
      description = collapse("""
          path ends with %s which collides with generated tests from %s files
      """)
  
  
  class GitIgnoreFile(Rule):
      name = "GITIGNORE"
      description = ".gitignore found outside the root"
  
  
  class AhemCopy(Rule):
      name = "AHEM COPY"
      description = "Don't add extra copies of Ahem, use /fonts/Ahem.ttf"
  
  
  class AhemSystemFont(Rule):
      name = "AHEM SYSTEM FONT"
      description = "Don't use Ahem as a system font, use /fonts/ahem.css"
  
  
  # TODO: Add tests for this rule
  class IgnoredPath(Rule):
      name = "IGNORED PATH"
      description = collapse("""
          %s matches an ignore filter in .gitignore - please add a .gitignore
          exception
      """)
  
  
  class CSSCollidingTestName(Rule):
      name = "CSS-COLLIDING-TEST-NAME"
      description = "The filename %s in the %s testsuite is shared by: %s"
  
  
  class CSSCollidingRefName(Rule):
      name = "CSS-COLLIDING-REF-NAME"
      description = "The filename %s is shared by: %s"
  
  
  class CSSCollidingSupportName(Rule):
      name = "CSS-COLLIDING-SUPPORT-NAME"
      description = "The filename %s is shared by: %s"
  
  
  class SupportWrongDir(Rule):
      name = "SUPPORT-WRONG-DIR"
      description = "Support file not in support directory"
  
  
  class ParseFailed(Rule):
      name = "PARSE-FAILED"
      description = "Unable to parse file"
      to_fix = """
          examine the file to find the causes of any parse errors, and fix them.
      """
  
  
  class ContentManual(Rule):
      name = "CONTENT-MANUAL"
      description = "Manual test whose filename doesn't end in '-manual'"
  
  
  class ContentVisual(Rule):
      name = "CONTENT-VISUAL"
      description = "Visual test whose filename doesn't end in '-visual'"
  
  
  class AbsoluteUrlRef(Rule):
      name = "ABSOLUTE-URL-REF"
      description = collapse("""
          Reference test with a reference file specified via an absolute URL:
          '%s'
      """)
  
  
  class SameFileRef(Rule):
      name = "SAME-FILE-REF"
      description = "Reference test which points at itself as a reference"
  
  
  class NonexistentRef(Rule):
      name = "NON-EXISTENT-REF"
      description = collapse("""
          Reference test with a non-existent '%s' relationship reference: '%s'
      """)
  
  
  class MultipleTimeout(Rule):
      name = "MULTIPLE-TIMEOUT"
      description = "More than one meta name='timeout'"
      to_fix = """
          ensure each test file has only one instance of a `<meta
          name="timeout"...>` element
      """
  
  
  class InvalidTimeout(Rule):
      name = "INVALID-TIMEOUT"
      description = collapse("""
          Test file with `<meta name='timeout'...>` element that has a `content`
          attribute whose value is not `long`: %s
      """)
      to_fix = "replace the value of the `content` attribute with `long`"
  
  
  class MultipleTestharness(Rule):
      name = "MULTIPLE-TESTHARNESS"
      description = "More than one `<script src='/resources/testharness.js'>`"
      to_fix = """
          ensure each test has only one `<script
          src='/resources/testharnessreport.js'>` instance
      """
  
  
  class MissingTestharnessReport(Rule):
      name = "MISSING-TESTHARNESSREPORT"
      description = "Missing `<script src='/resources/testharnessreport.js'>`"
      to_fix = """
          ensure each test file contains `<script
          src='/resources/testharnessreport.js'>`
      """
  
  
  class MultipleTestharnessReport(Rule):
      name = "MULTIPLE-TESTHARNESSREPORT"
      description = "More than one `<script src='/resources/testharnessreport.js'>`"
  
  
  class VariantMissing(Rule):
      name = "VARIANT-MISSING"
      description = collapse("""
          Test file with a `<meta name='variant'...>` element that's missing a
          `content` attribute
      """)
      to_fix = """
          add a `content` attribute with an appropriate value to the `<meta
          name='variant'...>` element
      """
  
  
  class MalformedVariant(Rule):
      name = "MALFORMED-VARIANT"
      description = collapse("""
          %s `<meta name=variant>` 'content' attribute must be the empty string
          or start with '?' or '#'
      """)
  
  
  class LateTimeout(Rule):
      name = "LATE-TIMEOUT"
      description = "`<meta name=timeout>` seen after testharness.js script"
      description = collapse("""
          Test file with `<meta name='timeout'...>` element after `<script
          src='/resources/testharnessreport.js'>` element
      """)
      to_fix = """
          move the `<meta name="timeout"...>` element to precede the `script`
          element.
      """
  
  
  class EarlyTestharnessReport(Rule):
      name = "EARLY-TESTHARNESSREPORT"
      description = collapse("""
          Test file has an instance of
          `<script src='/resources/testharnessreport.js'>` prior to
          `<script src='/resources/testharness.js'>`
      """)
      to_fix = "flip the order"
  
  
  class EarlyTestdriverVendor(Rule):
      name = "EARLY-TESTDRIVER-VENDOR"
      description = collapse("""
          Test file has an instance of
          `<script src='/resources/testdriver-vendor.js'>` prior to
          `<script src='/resources/testdriver.js'>`
      """)
      to_fix = "flip the order"
  
  
  class MultipleTestdriver(Rule):
      name = "MULTIPLE-TESTDRIVER"
      description = "More than one `<script src='/resources/testdriver.js'>`"
  
  
  class MissingTestdriverVendor(Rule):
      name = "MISSING-TESTDRIVER-VENDOR"
      description = "Missing `<script src='/resources/testdriver-vendor.js'>`"
  
  
  class MultipleTestdriverVendor(Rule):
      name = "MULTIPLE-TESTDRIVER-VENDOR"
      description = "More than one `<script src='/resources/testdriver-vendor.js'>`"
  
  
  class TestharnessPath(Rule):
      name = "TESTHARNESS-PATH"
      description = "testharness.js script seen with incorrect path"
  
  
  class TestharnessReportPath(Rule):
      name = "TESTHARNESSREPORT-PATH"
      description = "testharnessreport.js script seen with incorrect path"
  
  
  class TestdriverPath(Rule):
      name = "TESTDRIVER-PATH"
      description = "testdriver.js script seen with incorrect path"
  
  
  class TestdriverVendorPath(Rule):
      name = "TESTDRIVER-VENDOR-PATH"
      description = "testdriver-vendor.js script seen with incorrect path"
  
  
  class OpenNoMode(Rule):
      name = "OPEN-NO-MODE"
      description = "File opened without providing an explicit mode (note: binary files must be read with 'b' in the mode flags)"
  
  
  class UnknownGlobalMetadata(Rule):
      name = "UNKNOWN-GLOBAL-METADATA"
      description = "Unexpected value for global metadata"
  
  
  class BrokenGlobalMetadata(Rule):
      name = "BROKEN-GLOBAL-METADATA"
      description = "Invalid global metadata: %s"
  
  
  class UnknownTimeoutMetadata(Rule):
      name = "UNKNOWN-TIMEOUT-METADATA"
      description = "Unexpected value for timeout metadata"
  
  
  class UnknownMetadata(Rule):
      name = "UNKNOWN-METADATA"
      description = "Unexpected kind of metadata"
  
  
  class StrayMetadata(Rule):
      name = "STRAY-METADATA"
      description = "Metadata comments should start the file"
  
  
  class IndentedMetadata(Rule):
      name = "INDENTED-METADATA"
      description = "Metadata comments should start the line"
  
  
  class BrokenMetadata(Rule):
      name = "BROKEN-METADATA"
      description = "Metadata comment is not formatted correctly"
  
  
  class TestharnessInOtherType(Rule):
      name = "TESTHARNESS-IN-OTHER-TYPE"
      description = "testharness.js included in a %s test"
  
  
  class DuplicateBasenamePath(Rule):
      name = "DUPLICATE-BASENAME-PATH"
      description = collapse("""
              File has identical basename path (path excluding extension) as
              other file(s) (found extensions: %s)
      """)
      to_fix = "rename files so they have unique basename paths"
  
  
  class TentativeDirectoryName(Rule):
      name = "TENTATIVE-DIRECTORY-NAME"
      description = "Directories for tentative tests must be named exactly 'tentative'"
      to_fix = "rename directory to be called 'tentative'"
  
  
  class Regexp(six.with_metaclass(abc.ABCMeta)):
      @abc.abstractproperty
      def pattern(self):
          # type: () -> bytes
          pass
  
      @abc.abstractproperty
      def name(self):
          # type: () -> Text
          pass
  
      @abc.abstractproperty
      def description(self):
          # type: () -> Text
          pass
  
      file_extensions = None  # type: Optional[List[Text]]
  
      def __init__(self):
          # type: () -> None
          self._re = re.compile(self.pattern)  # type: Pattern[bytes]
  
      def applies(self, path):
          # type: (Text) -> bool
          return (self.file_extensions is None or
                  os.path.splitext(path)[1] in self.file_extensions)
  
      def search(self, line):
          # type: (bytes) -> Optional[Match[bytes]]
          return self._re.search(line)
  
  
  class TabsRegexp(Regexp):
      pattern = b"^\t"
      name = "INDENT TABS"
      description = "Test-file line starts with one or more tab characters"
      to_fix = "use spaces to replace any tab characters at beginning of lines"
  
  
  class CRRegexp(Regexp):
      pattern = b"\r$"
      name = "CR AT EOL"
      description = "Test-file line ends with CR (U+000D) character"
      to_fix = """
          reformat file so each line just has LF (U+000A) line ending (standard,
          cross-platform "Unix" line endings instead of, e.g., DOS line endings).
      """
  
  
  class SetTimeoutRegexp(Regexp):
      pattern = br"setTimeout\s*\("
      name = "SET TIMEOUT"
      file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
      description = "setTimeout used"
      to_fix = """
          replace all `setTimeout(...)` calls with `step_timeout(...)` calls
      """
  
  
  class W3CTestOrgRegexp(Regexp):
      pattern = br"w3c\-test\.org"
      name = "W3C-TEST.ORG"
      description = "Test-file line has the string `w3c-test.org`"
      to_fix = """
          either replace the `w3c-test.org` string with the expression
          `{{host}}:{{ports[http][0]}}` or a generic hostname like `example.org`
      """
  
  
  class WebPlatformTestRegexp(Regexp):
      pattern = br"web\-platform\.test"
      name = "WEB-PLATFORM.TEST"
      description = "Internal web-platform.test domain used"
      to_fix = """
          use [server-side substitution](https://web-platform-tests.org/writing-tests/server-pipes.html#sub),
          along with the [`.sub` filename-flag](https://web-platform-tests.org/writing-tests/file-names.html#test-features),
          to replace web-platform.test with `{{domains[]}}`
      """
  
  
  class Webidl2Regexp(Regexp):
      pattern = br"webidl2\.js"
      name = "WEBIDL2.JS"
      description = "Legacy webidl2.js script used"
  
  
  class ConsoleRegexp(Regexp):
      pattern = br"console\.[a-zA-Z]+\s*\("
      name = "CONSOLE"
      file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
      description = "Test-file line has a `console.*(...)` call"
      to_fix = """
          remove the `console.*(...)` call (and in some cases, consider adding an
          `assert_*` of some kind in place of it)
      """
  
  
  class GenerateTestsRegexp(Regexp):
      pattern = br"generate_tests\s*\("
      name = "GENERATE_TESTS"
      file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
      description = "Test file line has a generate_tests call"
      to_fix = "remove the call and call `test()` a number of times instead"
  
  
  class PrintRegexp(Regexp):
      pattern = br"print(?:\s|\s*\()"
      name = "PRINT STATEMENT"
      file_extensions = [".py"]
      description = collapse("""
          A server-side python support file contains a `print` statement
      """)
      to_fix = """
          remove the `print` statement or replace it with something else that
          achieves the intended effect (e.g., a logging call)
      """
  
  
  class LayoutTestsRegexp(Regexp):
      pattern = br"(eventSender|testRunner|internals)\."
      name = "LAYOUTTESTS APIS"
      file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
      description = "eventSender/testRunner/internals used; these are LayoutTests-specific APIs (WebKit/Blink)"
  
  
  class MissingDepsRegexp(Regexp):
      pattern = br"[^\w]/gen/"
      name = "MISSING DEPENDENCY"
      file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
      description = "Chromium-specific content referenced"
      to_fix = "Reimplement the test to use well-documented testing interfaces"
  
  
  class SpecialPowersRegexp(Regexp):
      pattern = b"SpecialPowers"
      name = "SPECIALPOWERS API"
      file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
      description = "SpecialPowers used; this is gecko-specific and not supported in wpt"
  
  
  class TrailingWhitespaceRegexp(Regexp):
      name = "TRAILING WHITESPACE"
      description = "Whitespace at EOL"
      pattern = b"[ \t\f\v]$"
      to_fix = """Remove trailing whitespace from all lines in the file."""
  
  
  class AssertThrowsRegexp(Regexp):
      pattern = br"[^.]assert_throws\("
      name = "ASSERT_THROWS"
      file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
      description = "Test-file line has an `assert_throws(...)` call"
      to_fix = """Replace with `assert_throws_dom` or `assert_throws_js` or `assert_throws_exactly`"""
  
  
  class PromiseRejectsRegexp(Regexp):
      pattern = br"promise_rejects\("
      name = "PROMISE_REJECTS"
      file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
      description = "Test-file line has a `promise_rejects(...)` call"
      to_fix = """Replace with promise_rejects_dom or promise_rejects_js or `promise_rejects_exactly`"""
  
  
  class AssertPreconditionRegexp(Regexp):
      pattern = br"[^.]assert_precondition\("
      name = "ASSERT-PRECONDITION"
      file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"]
      description = "Test-file line has an `assert_precondition(...)` call"
      to_fix = """Replace with `assert_implements` or `assert_implements_optional`"""
  