tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

rules.py (17952B)


      1 import abc
      2 import inspect
      3 import os
      4 import re
      5 from typing import Any, List, Match, Optional, Pattern, Text, Tuple, cast
      6 
      7 
      8 Error = Tuple[str, str, str, Optional[int]]
      9 
     10 def collapse(text: Text) -> Text:
     11    return inspect.cleandoc(str(text)).replace("\n", " ")
     12 
     13 
     14 class Rule(metaclass=abc.ABCMeta):
     15    @abc.abstractproperty
     16    def name(self) -> Text:
     17        pass
     18 
     19    @abc.abstractproperty
     20    def description(self) -> Text:
     21        pass
     22 
     23    to_fix: Optional[Text] = None
     24 
     25    @classmethod
     26    def error(cls, path: Text, context: Tuple[Any, ...] = (), line_no: Optional[int] = None) -> Error:
     27        name = cast(str, cls.name)
     28        description = cast(str, cls.description) % context
     29        return (name, description, path, line_no)
     30 
     31 
     32 class MissingLink(Rule):
     33    name = "MISSING-LINK"
     34    description = "Testcase file must have a link to a spec"
     35    to_fix = """
     36        Ensure that there is a `<link rel="help" href="[url]">` for the spec.
     37        `MISSING-LINK` is designed to ensure that the CSS build tool can find
     38        the tests. Note that the CSS build system is primarily used by
     39        [test.csswg.org/](http://test.csswg.org/), which doesn't use
     40        `wptserve`, so `*.any.js` and similar tests won't work there; stick
     41        with the `.html` equivalent.
     42    """
     43 
     44 
     45 class PathLength(Rule):
     46    name = "PATH LENGTH"
     47    description = "/%s longer than maximum path length (%d > 150)"
     48    to_fix = "use shorter filename to rename the test file"
     49 
     50 
     51 class FileType(Rule):
     52    name = "FILE TYPE"
     53    description = "/%s is an unsupported file type (%s)"
     54 
     55 
     56 class WorkerCollision(Rule):
     57    name = "WORKER COLLISION"
     58    description = collapse("""
     59        path ends with %s which collides with generated tests from %s files
     60    """)
     61 
     62 
     63 class GitIgnoreFile(Rule):
     64    name = "GITIGNORE"
     65    description = ".gitignore found outside the root"
     66 
     67 
     68 class MojomJSFile(Rule):
     69    name = "MOJOM-JS"
     70    description = "Don't check *.mojom.js files into WPT"
     71    to_fix = """
     72        Check if the file is already included in mojojs.zip:
     73        https://source.chromium.org/chromium/chromium/src/+/master:chrome/tools/build/linux/FILES.cfg
     74        If yes, use `loadMojoResources` from `resources/test-only-api.js` to load
     75        it; if not, contact ecosystem-infra@chromium.org for adding new files
     76        to mojojs.zip.
     77    """
     78 
     79 
     80 class AhemCopy(Rule):
     81    name = "AHEM COPY"
     82    description = "Don't add extra copies of Ahem, use /fonts/Ahem.ttf"
     83 
     84 
     85 class AhemSystemFont(Rule):
     86    name = "AHEM SYSTEM FONT"
     87    description = "Don't use Ahem as a system font, use /fonts/ahem.css"
     88 
     89 
     90 # TODO: Add tests for this rule
     91 class IgnoredPath(Rule):
     92    name = "IGNORED PATH"
     93    description = collapse("""
     94        %s matches an ignore filter in .gitignore - please add a .gitignore
     95        exception
     96    """)
     97 
     98 
     99 class ParseFailed(Rule):
    100    name = "PARSE-FAILED"
    101    description = "Unable to parse file"
    102    to_fix = """
    103        examine the file to find the causes of any parse errors, and fix them.
    104    """
    105 
    106 
    107 class ContentManual(Rule):
    108    name = "CONTENT-MANUAL"
    109    description = "Manual test whose filename doesn't end in '-manual'"
    110 
    111 
    112 class ContentVisual(Rule):
    113    name = "CONTENT-VISUAL"
    114    description = "Visual test whose filename doesn't end in '-visual'"
    115 
    116 
    117 class AbsoluteUrlRef(Rule):
    118    name = "ABSOLUTE-URL-REF"
    119    description = collapse("""
    120        Reference test with a reference file specified via an absolute URL:
    121        '%s'
    122    """)
    123 
    124 
    125 class SameFileRef(Rule):
    126    name = "SAME-FILE-REF"
    127    description = "Reference test which points at itself as a reference"
    128 
    129 
    130 class NonexistentRef(Rule):
    131    name = "NON-EXISTENT-REF"
    132    description = collapse("""
    133        Reference test with a non-existent '%s' relationship reference: '%s'
    134    """)
    135 
    136 
    137 class MultipleTimeout(Rule):
    138    name = "MULTIPLE-TIMEOUT"
    139    description = "More than one meta name='timeout'"
    140    to_fix = """
    141        ensure each test file has only one instance of a `<meta
    142        name="timeout"...>` element
    143    """
    144 
    145 
    146 class InvalidTimeout(Rule):
    147    name = "INVALID-TIMEOUT"
    148    description = collapse("""
    149        Test file with `<meta name='timeout'...>` element that has a `content`
    150        attribute whose value is not `long`: %s
    151    """)
    152    to_fix = "replace the value of the `content` attribute with `long`"
    153 
    154 
    155 class MultipleTestharness(Rule):
    156    name = "MULTIPLE-TESTHARNESS"
    157    description = "More than one `<script src='/resources/testharness.js'>`"
    158    to_fix = """
    159        Ensure each test has only one `<script
    160        src='/resources/testharness.js'>` instance.
    161        For `.js` tests, remove `// META: script=/resources/testharness.js`,
    162        which wptserve already adds to the boilerplate markup.
    163    """
    164 
    165 
    166 class MissingReftestWait(Rule):
    167    name = "MISSING-REFTESTWAIT"
    168    description = "Missing `class=reftest-wait`"
    169    to_fix = """
    170        ensure tests that include reftest-wait.js also use class=reftest-wait on the root element.
    171    """
    172 
    173 
    174 class MissingTestharnessReport(Rule):
    175    name = "MISSING-TESTHARNESSREPORT"
    176    description = "Missing `<script src='/resources/testharnessreport.js'>`"
    177    to_fix = """
    178        ensure each test file contains `<script
    179        src='/resources/testharnessreport.js'>`
    180    """
    181 
    182 
    183 class MultipleTestharnessReport(Rule):
    184    name = "MULTIPLE-TESTHARNESSREPORT"
    185    description = "More than one `<script src='/resources/testharnessreport.js'>`"
    186    to_fix = """
    187        Ensure each test has only one `<script
    188        src='/resources/testharnessreport.js'>` instance.
    189        For `.js` tests, remove `// META: script=/resources/testharnessreport.js`,
    190        which wptserve already adds to the boilerplate markup.
    191    """
    192 
    193 
    194 class VariantMissing(Rule):
    195    name = "VARIANT-MISSING"
    196    description = collapse("""
    197        Test file with a `<meta name='variant'...>` element that's missing a
    198        `content` attribute
    199    """)
    200    to_fix = """
    201        add a `content` attribute with an appropriate value to the `<meta
    202        name='variant'...>` element
    203    """
    204 
    205 
    206 class MalformedVariant(Rule):
    207    name = "MALFORMED-VARIANT"
    208    description = collapse("""
    209        %s must be a non empty string
    210        and start with '?' or '#'
    211    """)
    212 
    213 
    214 class LateTimeout(Rule):
    215    name = "LATE-TIMEOUT"
    216    description = "`<meta name=timeout>` seen after testharness.js script"
    217    description = collapse("""
    218        Test file with `<meta name='timeout'...>` element after `<script
    219        src='/resources/testharnessreport.js'>` element
    220    """)
    221    to_fix = """
    222        move the `<meta name="timeout"...>` element to precede the `script`
    223        element.
    224    """
    225 
    226 
    227 class EarlyTestharnessReport(Rule):
    228    name = "EARLY-TESTHARNESSREPORT"
    229    description = collapse("""
    230        Test file has an instance of
    231        `<script src='/resources/testharnessreport.js'>` prior to
    232        `<script src='/resources/testharness.js'>`
    233    """)
    234    to_fix = "flip the order"
    235 
    236 
    237 class EarlyTestdriverVendor(Rule):
    238    name = "EARLY-TESTDRIVER-VENDOR"
    239    description = collapse("""
    240        Test file has an instance of
    241        `<script src='/resources/testdriver-vendor.js'>` prior to
    242        `<script src='/resources/testdriver.js'>`
    243    """)
    244    to_fix = "flip the order"
    245 
    246 
    247 class MultipleTestdriver(Rule):
    248    name = "MULTIPLE-TESTDRIVER"
    249    description = "More than one `<script src='/resources/testdriver.js'>`"
    250 
    251 
    252 class MissingTestdriverVendor(Rule):
    253    name = "MISSING-TESTDRIVER-VENDOR"
    254    description = "Missing `<script src='/resources/testdriver-vendor.js'>`"
    255 
    256 
    257 class MultipleTestdriverVendor(Rule):
    258    name = "MULTIPLE-TESTDRIVER-VENDOR"
    259    description = "More than one `<script src='/resources/testdriver-vendor.js'>`"
    260 
    261 
    262 class TestharnessPath(Rule):
    263    name = "TESTHARNESS-PATH"
    264    description = "testharness.js script seen with incorrect path"
    265 
    266 
    267 class TestharnessReportPath(Rule):
    268    name = "TESTHARNESSREPORT-PATH"
    269    description = "testharnessreport.js script seen with incorrect path"
    270 
    271 
    272 class TestdriverPath(Rule):
    273    name = "TESTDRIVER-PATH"
    274    description = "testdriver.js script seen with incorrect path"
    275 
    276 
    277 class TestdriverUnsupportedQueryParameter(Rule):
    278    name = "TESTDRIVER-UNSUPPORTED-QUERY-PARAMETER"
    279    description = "testdriver.js script seen with unsupported query parameters"
    280 
    281 
    282 class TestdriverVendorPath(Rule):
    283    name = "TESTDRIVER-VENDOR-PATH"
    284    description = "testdriver-vendor.js script seen with incorrect path"
    285 
    286 
    287 class TestdriverInUnsupportedType(Rule):
    288    name = "TESTDRIVER-IN-UNSUPPORTED-TYPE"
    289    description = "testdriver.js included in a %s test, which doesn't support testdriver.js"
    290 
    291 
    292 class OpenNoMode(Rule):
    293    name = "OPEN-NO-MODE"
    294    description = "File opened without providing an explicit mode (note: binary files must be read with 'b' in the mode flags)"
    295 
    296 
    297 class UnknownGlobalMetadata(Rule):
    298    name = "UNKNOWN-GLOBAL-METADATA"
    299    description = "Unexpected value for global metadata"
    300 
    301 
    302 class BrokenGlobalMetadata(Rule):
    303    name = "BROKEN-GLOBAL-METADATA"
    304    description = "Invalid global metadata: %s"
    305 
    306 
    307 class UnknownTimeoutMetadata(Rule):
    308    name = "UNKNOWN-TIMEOUT-METADATA"
    309    description = "Unexpected value for timeout metadata"
    310 
    311 
    312 class UnknownMetadata(Rule):
    313    name = "UNKNOWN-METADATA"
    314    description = "Unexpected kind of metadata"
    315 
    316 
    317 class StrayMetadata(Rule):
    318    name = "STRAY-METADATA"
    319    description = "Metadata comments should start the file"
    320 
    321 
    322 class IndentedMetadata(Rule):
    323    name = "INDENTED-METADATA"
    324    description = "Metadata comments should start the line"
    325 
    326 
    327 class BrokenMetadata(Rule):
    328    name = "BROKEN-METADATA"
    329    description = "Metadata comment is not formatted correctly"
    330 
    331 
    332 class TestharnessInOtherType(Rule):
    333    name = "TESTHARNESS-IN-OTHER-TYPE"
    334    description = "testharness.js included in a %s test"
    335 
    336 
    337 class ReferenceInOtherType(Rule):
    338    name = "REFERENCE-IN-OTHER-TYPE"
    339    description = "Reference link included in a %s test"
    340 
    341 
    342 class DuplicateBasenamePath(Rule):
    343    name = "DUPLICATE-BASENAME-PATH"
    344    description = collapse("""
    345            File has identical basename path (path excluding extension) as
    346            other file(s) (found extensions: %s)
    347    """)
    348    to_fix = "rename files so they have unique basename paths"
    349 
    350 
    351 class DuplicatePathCaseInsensitive(Rule):
    352    name = "DUPLICATE-CASE-INSENSITIVE-PATH"
    353    description = collapse("""
    354            Path differs from path %s only in case
    355    """)
    356    to_fix = "rename files so they are unique irrespective of case"
    357 
    358 
    359 class TentativeDirectoryName(Rule):
    360    name = "TENTATIVE-DIRECTORY-NAME"
    361    description = "Directories for tentative tests must be named exactly 'tentative'"
    362    to_fix = "rename directory to be called 'tentative'"
    363 
    364 
    365 class InvalidMetaFile(Rule):
    366    name = "INVALID-META-FILE"
    367    description = "The META.yml is not a YAML file with the expected structure"
    368 
    369 
    370 class InvalidWebFeaturesFile(Rule):
    371    name = "INVALID-WEB-FEATURES-FILE"
    372    description = "The WEB_FEATURES.yml file contains an invalid structure"
    373 
    374 
    375 class MissingTestInWebFeaturesFile(Rule):
    376    name = "MISSING-WEB-FEATURES-FILE"
    377    description = collapse("""
    378        The WEB_FEATURES.yml file references a test that does not exist: '%s'
    379    """)
    380 
    381 
    382 EXTENSIONS = {
    383    "html": [".html", ".htm"],
    384    "xhtml": [".xht", ".xhtml"],
    385    "svg": [".svg"],
    386    "js": [".js", ".mjs"],
    387    "python": [".py"]
    388 }
    389 EXTENSIONS["markup"] = EXTENSIONS["html"] + EXTENSIONS["xhtml"] + EXTENSIONS["svg"]
    390 EXTENSIONS["js_all"] = EXTENSIONS["markup"] + EXTENSIONS["js"]
    391 
    392 
    393 class Regexp(metaclass=abc.ABCMeta):
    394    @abc.abstractproperty
    395    def pattern(self) -> bytes:
    396        pass
    397 
    398    @abc.abstractproperty
    399    def name(self) -> Text:
    400        pass
    401 
    402    @abc.abstractproperty
    403    def description(self) -> Text:
    404        pass
    405 
    406    file_extensions: Optional[List[Text]] = None
    407 
    408    def __init__(self) -> None:
    409        self._re: Pattern[bytes] = re.compile(self.pattern)
    410 
    411    def applies(self, path: Text) -> bool:
    412        return (self.file_extensions is None or
    413                os.path.splitext(path)[1] in self.file_extensions)
    414 
    415    def search(self, line: bytes) -> Optional[Match[bytes]]:
    416        return self._re.search(line)
    417 
    418 
    419 class TabsRegexp(Regexp):
    420    pattern = b"^\t"
    421    name = "INDENT TABS"
    422    description = "Test-file line starts with one or more tab characters"
    423    to_fix = "use spaces to replace any tab characters at beginning of lines"
    424 
    425 
    426 class CRRegexp(Regexp):
    427    pattern = b"\r$"
    428    name = "CR AT EOL"
    429    description = "Test-file line ends with CR (U+000D) character"
    430    to_fix = """
    431        reformat file so each line just has LF (U+000A) line ending (standard,
    432        cross-platform "Unix" line endings instead of, e.g., DOS line endings).
    433    """
    434 
    435 
    436 class SetTimeoutRegexp(Regexp):
    437    pattern = br"setTimeout\s*\("
    438    name = "SET TIMEOUT"
    439    file_extensions = EXTENSIONS["js_all"]
    440    description = "setTimeout used"
    441    to_fix = """
    442        replace all `setTimeout(...)` calls with `step_timeout(...)` calls
    443    """
    444 
    445 
    446 class W3CTestOrgRegexp(Regexp):
    447    pattern = br"w3c\-test\.org"
    448    name = "W3C-TEST.ORG"
    449    description = "Test-file line has the string `w3c-test.org`"
    450    to_fix = """
    451        either replace the `w3c-test.org` string with the expression
    452        `{{host}}:{{ports[http][0]}}` or a generic hostname like `example.org`
    453    """
    454 
    455 
    456 class WebPlatformTestRegexp(Regexp):
    457    pattern = br"web\-platform\.test"
    458    name = "WEB-PLATFORM.TEST"
    459    description = "Internal web-platform.test domain used"
    460    to_fix = """
    461        use [server-side substitution](https://web-platform-tests.org/writing-tests/server-pipes.html#sub),
    462        along with the [`.sub` filename-flag](https://web-platform-tests.org/writing-tests/file-names.html#test-features),
    463        to replace web-platform.test with `{{domains[]}}`
    464    """
    465 
    466 
    467 class Webidl2Regexp(Regexp):
    468    pattern = br"webidl2\.js"
    469    name = "WEBIDL2.JS"
    470    description = "Legacy webidl2.js script used"
    471 
    472 
    473 class ConsoleRegexp(Regexp):
    474    pattern = br"console\.[a-zA-Z]+\s*\("
    475    name = "CONSOLE"
    476    file_extensions = EXTENSIONS["js_all"]
    477    description = "Test-file line has a `console.*(...)` call"
    478    to_fix = """
    479        remove the `console.*(...)` call (and in some cases, consider adding an
    480        `assert_*` of some kind in place of it)
    481    """
    482 
    483 
    484 class GenerateTestsRegexp(Regexp):
    485    pattern = br"generate_tests\s*\("
    486    name = "GENERATE_TESTS"
    487    file_extensions = EXTENSIONS["js_all"]
    488    description = "Test file line has a generate_tests call"
    489    to_fix = "remove the call and call `test()` a number of times instead"
    490 
    491 
    492 class PrintRegexp(Regexp):
    493    pattern = br"print(?:\s|\s*\()"
    494    name = "PRINT STATEMENT"
    495    file_extensions = EXTENSIONS["python"]
    496    description = collapse("""
    497        A server-side python support file contains a `print` statement
    498    """)
    499    to_fix = """
    500        remove the `print` statement or replace it with something else that
    501        achieves the intended effect (e.g., a logging call)
    502    """
    503 
    504 
    505 class LayoutTestsRegexp(Regexp):
    506    pattern = br"(eventSender|testRunner|internals)\."
    507    name = "LAYOUTTESTS APIS"
    508    file_extensions = EXTENSIONS["js_all"]
    509    description = "eventSender/testRunner/internals used; these are LayoutTests-specific APIs (WebKit/Blink)"
    510 
    511 
    512 class MissingDepsRegexp(Regexp):
    513    pattern = br"[^\w]/gen/"
    514    name = "MISSING DEPENDENCY"
    515    file_extensions = EXTENSIONS["js_all"]
    516    description = "Chromium-specific content referenced"
    517    to_fix = "Reimplement the test to use well-documented testing interfaces"
    518 
    519 
    520 class SpecialPowersRegexp(Regexp):
    521    pattern = b"SpecialPowers"
    522    name = "SPECIALPOWERS API"
    523    file_extensions = EXTENSIONS["js_all"]
    524    description = "SpecialPowers used; this is gecko-specific and not supported in wpt"
    525 
    526 
    527 class TrailingWhitespaceRegexp(Regexp):
    528    name = "TRAILING WHITESPACE"
    529    description = "Whitespace at EOL"
    530    pattern = b"[ \t\f\v]$"
    531    to_fix = """Remove trailing whitespace from all lines in the file."""
    532 
    533 
    534 class AssertThrowsRegexp(Regexp):
    535    pattern = br"[^.]assert_throws\("
    536    name = "ASSERT_THROWS"
    537    file_extensions = EXTENSIONS["js_all"]
    538    description = "Test-file line has an `assert_throws(...)` call"
    539    to_fix = """Replace with `assert_throws_dom` or `assert_throws_js` or `assert_throws_exactly`"""
    540 
    541 
    542 class PromiseRejectsRegexp(Regexp):
    543    pattern = br"promise_rejects\("
    544    name = "PROMISE_REJECTS"
    545    file_extensions = EXTENSIONS["js_all"]
    546    description = "Test-file line has a `promise_rejects(...)` call"
    547    to_fix = """Replace with promise_rejects_dom or promise_rejects_js or `promise_rejects_exactly`"""
    548 
    549 
    550 class AssertPreconditionRegexp(Regexp):
    551    pattern = br"[^.]assert_precondition\("
    552    name = "ASSERT-PRECONDITION"
    553    file_extensions = EXTENSIONS["js_all"]
    554    description = "Test-file line has an `assert_precondition(...)` call"
    555    to_fix = """Replace with `assert_implements` or `assert_implements_optional`"""
    556 
    557 
    558 class HTMLInvalidSyntaxRegexp(Regexp):
    559    pattern = (br"<(a|abbr|article|audio|b|bdi|bdo|blockquote|body|button|canvas|caption|cite|code|colgroup|data|datalist|dd|del|details|"
    560               br"dfn|dialog|div|dl|dt|em|fieldset|figcaption|figure|footer|form|h[1-6]|head|header|html|i|iframe|ins|kbd|label|legend|li|"
    561               br"main|map|mark|menu|meter|nav|noscript|object|ol|optgroup|option|output|p|picture|pre|progress|q|rp|rt|ruby|s|samp|script|"
    562               br"search|section|select|slot|small|span|strong|style|sub|summary|sup|table|tbody|td|template|textarea|tfoot|th|thead|time|"
    563               br"title|tr|u|ul|var|video)(\s+[^>]+)?\s*/>")
    564    name = "HTML INVALID SYNTAX"
    565    file_extensions = EXTENSIONS["html"]
    566    description = "Test-file line has a non-void HTML tag with /> syntax"
    567    to_fix = """Replace with start tag and end tag"""
    568 
    569 
    570 class TestDriverInternalRegexp(Regexp):
    571    pattern = br"test_driver_internal"
    572    name = "TEST DRIVER INTERNAL"
    573    file_extensions = EXTENSIONS["js_all"]
    574    description = "Test-file uses test_driver_internal API"
    575    to_fix = """Only use test_driver public API"""