| from __future__ import unicode_literals |
| |
| from ..lint import check_file_contents |
| from .base import check_errors |
| import os |
| import pytest |
| import six |
| |
| INTERESTING_FILE_NAMES = { |
| "python": [ |
| "test.py", |
| ], |
| "js": [ |
| "test.js", |
| ], |
| "web-lax": [ |
| "test.htm", |
| "test.html", |
| ], |
| "web-strict": [ |
| "test.svg", |
| "test.xht", |
| "test.xhtml", |
| ], |
| } |
| |
| def check_with_files(input_bytes): |
| return { |
| filename: (check_file_contents("", filename, six.BytesIO(input_bytes), False), kind) |
| for (filename, kind) in |
| ( |
| (os.path.join("html", filename), kind) |
| for (kind, filenames) in INTERESTING_FILE_NAMES.items() |
| for filename in filenames |
| ) |
| } |
| |
| |
| def test_trailing_whitespace(): |
| error_map = check_with_files(b"test; ") |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| expected = [("TRAILING WHITESPACE", "Whitespace at EOL", filename, 1)] |
| if kind == "web-strict": |
| expected.append(("PARSE-FAILED", "Unable to parse file", filename, None)) |
| assert errors == expected |
| |
| |
| def test_indent_tabs(): |
| error_map = check_with_files(b"def foo():\n\x09pass") |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| expected = [("INDENT TABS", "Tabs used for indentation", filename, 2)] |
| if kind == "web-strict": |
| expected.append(("PARSE-FAILED", "Unable to parse file", filename, None)) |
| assert errors == expected |
| |
| |
| def test_cr_not_at_eol(): |
| error_map = check_with_files(b"line1\rline2\r") |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| expected = [("CR AT EOL", "CR character in line separator", filename, 1)] |
| if kind == "web-strict": |
| expected.append(("PARSE-FAILED", "Unable to parse file", filename, None)) |
| assert errors == expected |
| |
| |
| def test_cr_at_eol(): |
| error_map = check_with_files(b"line1\r\nline2\r\n") |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| expected = [ |
| ("CR AT EOL", "CR character in line separator", filename, 1), |
| ("CR AT EOL", "CR character in line separator", filename, 2), |
| ] |
| if kind == "web-strict": |
| expected.append(("PARSE-FAILED", "Unable to parse file", filename, None)) |
| assert errors == expected |
| |
| |
| def test_w3c_test_org(): |
| error_map = check_with_files(b"import('http://www.w3c-test.org/')") |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| expected = [("W3C-TEST.ORG", "External w3c-test.org domain used", filename, 1)] |
| if kind == "python": |
| expected.append(("PARSE-FAILED", "Unable to parse file", filename, 1)) |
| elif kind == "web-strict": |
| expected.append(("PARSE-FAILED", "Unable to parse file", filename, None)) |
| assert errors == expected |
| |
| |
| def test_webidl2_js(): |
| error_map = check_with_files(b"<script src=/resources/webidl2.js>") |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| expected = [("WEBIDL2.JS", "Legacy webidl2.js script used", filename, 1)] |
| if kind == "python": |
| expected.append(("PARSE-FAILED", "Unable to parse file", filename, 1)) |
| elif kind == "web-strict": |
| expected.append(("PARSE-FAILED", "Unable to parse file", filename, None)) |
| assert errors == expected |
| |
| |
| def test_console(): |
| error_map = check_with_files(b"<script>\nconsole.log('error');\nconsole.error ('log')\n</script>") |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| if kind in ["web-lax", "web-strict", "js"]: |
| assert errors == [ |
| ("CONSOLE", "Console logging API used", filename, 2), |
| ("CONSOLE", "Console logging API used", filename, 3), |
| ] |
| else: |
| assert errors == [("PARSE-FAILED", "Unable to parse file", filename, 1)] |
| |
| |
| def test_setTimeout(): |
| error_map = check_with_files(b"<script>setTimeout(() => 1, 10)</script>") |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| if kind == "python": |
| assert errors == [("PARSE-FAILED", "Unable to parse file", filename, 1)] |
| else: |
| assert errors == [('SET TIMEOUT', |
| 'setTimeout used; step_timeout should typically be used instead', |
| filename, |
| 1)] |
| |
| |
| def test_meta_timeout(): |
| code = b""" |
| <html xmlns="http://www.w3.org/1999/xhtml"> |
| <meta name="timeout" /> |
| <meta name="timeout" content="short" /> |
| <meta name="timeout" content="long" /> |
| </html> |
| """ |
| error_map = check_with_files(code) |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| if kind in ["web-lax", "web-strict"]: |
| assert errors == [ |
| ("MULTIPLE-TIMEOUT", "More than one meta name='timeout'", filename, None), |
| ("INVALID-TIMEOUT", "Invalid timeout value ", filename, None), |
| ("INVALID-TIMEOUT", "Invalid timeout value short", filename, None), |
| ] |
| elif kind == "python": |
| assert errors == [ |
| ("PARSE-FAILED", "Unable to parse file", filename, 2), |
| ] |
| |
| |
| def test_early_testharnessreport(): |
| code = b""" |
| <html xmlns="http://www.w3.org/1999/xhtml"> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="/resources/testharness.js"></script> |
| </html> |
| """ |
| error_map = check_with_files(code) |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| if kind in ["web-lax", "web-strict"]: |
| assert errors == [ |
| ("EARLY-TESTHARNESSREPORT", "testharnessreport.js script seen before testharness.js script", filename, None), |
| ] |
| elif kind == "python": |
| assert errors == [ |
| ("PARSE-FAILED", "Unable to parse file", filename, 2), |
| ] |
| |
| |
| def test_multiple_testharness(): |
| code = b""" |
| <html xmlns="http://www.w3.org/1999/xhtml"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharness.js"></script> |
| </html> |
| """ |
| error_map = check_with_files(code) |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| if kind in ["web-lax", "web-strict"]: |
| assert errors == [ |
| ("MULTIPLE-TESTHARNESS", "More than one <script src='/resources/testharness.js'>", filename, None), |
| ("MISSING-TESTHARNESSREPORT", "Missing <script src='/resources/testharnessreport.js'>", filename, None), |
| ] |
| elif kind == "python": |
| assert errors == [ |
| ("PARSE-FAILED", "Unable to parse file", filename, 2), |
| ] |
| |
| |
| def test_multiple_testharnessreport(): |
| code = b""" |
| <html xmlns="http://www.w3.org/1999/xhtml"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| </html> |
| """ |
| error_map = check_with_files(code) |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| if kind in ["web-lax", "web-strict"]: |
| assert errors == [ |
| ("MULTIPLE-TESTHARNESSREPORT", "More than one <script src='/resources/testharnessreport.js'>", filename, None), |
| ] |
| elif kind == "python": |
| assert errors == [ |
| ("PARSE-FAILED", "Unable to parse file", filename, 2), |
| ] |
| |
| |
| def test_present_testharnesscss(): |
| code = b""" |
| <html xmlns="http://www.w3.org/1999/xhtml"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <link rel="stylesheet" href="/resources/testharness.css"/> |
| </html> |
| """ |
| error_map = check_with_files(code) |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| if kind in ["web-lax", "web-strict"]: |
| assert errors == [ |
| ("PRESENT-TESTHARNESSCSS", "Explicit link to testharness.css present", filename, None), |
| ] |
| elif kind == "python": |
| assert errors == [ |
| ("PARSE-FAILED", "Unable to parse file", filename, 2), |
| ] |
| |
| |
| def test_testharness_path(): |
| code = b"""\ |
| <html xmlns="http://www.w3.org/1999/xhtml"> |
| <script src="testharness.js"></script> |
| <script src="resources/testharness.js"></script> |
| <script src="../resources/testharness.js"></script> |
| <script src="http://w3c-test.org/resources/testharness.js"></script> |
| </html> |
| """ |
| error_map = check_with_files(code) |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| expected = [("W3C-TEST.ORG", "External w3c-test.org domain used", filename, 5)] |
| if kind == "python": |
| expected.append(("PARSE-FAILED", "Unable to parse file", filename, 1)) |
| elif kind in ["web-lax", "web-strict"]: |
| expected.extend([ |
| ("TESTHARNESS-PATH", "testharness.js script seen with incorrect path", filename, None), |
| ("TESTHARNESS-PATH", "testharness.js script seen with incorrect path", filename, None), |
| ("TESTHARNESS-PATH", "testharness.js script seen with incorrect path", filename, None), |
| ("TESTHARNESS-PATH", "testharness.js script seen with incorrect path", filename, None), |
| ]) |
| assert errors == expected |
| |
| |
| def test_testharnessreport_path(): |
| code = b"""\ |
| <html xmlns="http://www.w3.org/1999/xhtml"> |
| <script src="testharnessreport.js"></script> |
| <script src="resources/testharnessreport.js"></script> |
| <script src="../resources/testharnessreport.js"></script> |
| <script src="http://w3c-test.org/resources/testharnessreport.js"></script> |
| </html> |
| """ |
| error_map = check_with_files(code) |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| expected = [("W3C-TEST.ORG", "External w3c-test.org domain used", filename, 5)] |
| if kind == "python": |
| expected.append(("PARSE-FAILED", "Unable to parse file", filename, 1)) |
| elif kind in ["web-lax", "web-strict"]: |
| expected.extend([ |
| ("TESTHARNESSREPORT-PATH", "testharnessreport.js script seen with incorrect path", filename, None), |
| ("TESTHARNESSREPORT-PATH", "testharnessreport.js script seen with incorrect path", filename, None), |
| ("TESTHARNESSREPORT-PATH", "testharnessreport.js script seen with incorrect path", filename, None), |
| ("TESTHARNESSREPORT-PATH", "testharnessreport.js script seen with incorrect path", filename, None), |
| ]) |
| assert errors == expected |
| |
| |
| def test_not_testharness_path(): |
| code = b"""\ |
| <html xmlns="http://www.w3.org/1999/xhtml"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="resources/webperftestharness.js"></script> |
| </html> |
| """ |
| error_map = check_with_files(code) |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| if kind == "python": |
| assert errors == [ |
| ("PARSE-FAILED", "Unable to parse file", filename, 1), |
| ] |
| else: |
| assert errors == [] |
| |
| |
| @pytest.mark.skipif(six.PY3, reason="Cannot parse print statements from python 3") |
| def test_print_statement(): |
| error_map = check_with_files(b"def foo():\n print 'statement'\n print\n") |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| if kind == "python": |
| assert errors == [ |
| ("PRINT STATEMENT", "Print function used", filename, 2), |
| ("PRINT STATEMENT", "Print function used", filename, 3), |
| ] |
| elif kind == "web-strict": |
| assert errors == [ |
| ("PARSE-FAILED", "Unable to parse file", filename, None), |
| ] |
| else: |
| assert errors == [] |
| |
| |
| def test_print_function(): |
| error_map = check_with_files(b"def foo():\n print('function')\n") |
| |
| for (filename, (errors, kind)) in error_map.items(): |
| check_errors(errors) |
| |
| if kind == "python": |
| assert errors == [ |
| ("PRINT STATEMENT", "Print function used", filename, 2), |
| ] |
| elif kind == "web-strict": |
| assert errors == [ |
| ("PARSE-FAILED", "Unable to parse file", filename, None), |
| ] |
| else: |
| assert errors == [] |
| |
| |
| open_mode_code = """ |
| def first(): |
| return {0}("test.png") |
| |
| def second(): |
| return {0}("test.png", "r") |
| |
| def third(): |
| return {0}("test.png", "rb") |
| |
| def fourth(): |
| return {0}("test.png", encoding="utf-8") |
| |
| def fifth(): |
| return {0}("test.png", mode="rb") |
| """ |
| |
| |
| def test_open_mode(): |
| for method in ["open", "file"]: |
| code = open_mode_code.format(method).encode("utf-8") |
| errors = check_file_contents("", "test.py", six.BytesIO(code), False) |
| check_errors(errors) |
| |
| message = ("File opened without providing an explicit mode (note: " + |
| "binary files must be read with 'b' in the mode flags)") |
| |
| assert errors == [ |
| ("OPEN-NO-MODE", message, "test.py", 3), |
| ("OPEN-NO-MODE", message, "test.py", 12), |
| ] |
| |
| |
| @pytest.mark.parametrize( |
| "filename,css_mode,expect_error", |
| [ |
| ("foo/bar.html", False, False), |
| ("foo/bar.html", True, True), |
| ("css/bar.html", False, True), |
| ("css/bar.html", True, True), |
| ]) |
| def test_css_support_file(filename, css_mode, expect_error): |
| errors = check_file_contents("", filename, six.BytesIO(b""), css_mode) |
| check_errors(errors) |
| |
| if expect_error: |
| assert errors == [ |
| ('SUPPORT-WRONG-DIR', |
| 'Support file not in support directory', |
| filename, |
| None), |
| ] |
| else: |
| assert errors == [] |
| |
| |
| def test_css_missing_file_css_mode(): |
| code = b"""\ |
| <html xmlns="http://www.w3.org/1999/xhtml"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| </html> |
| """ |
| errors = check_file_contents("", "foo/bar.html", six.BytesIO(code), True) |
| check_errors(errors) |
| |
| assert errors == [ |
| ('MISSING-LINK', |
| 'Testcase file must have a link to a spec', |
| "foo/bar.html", |
| None), |
| ] |
| |
| |
| def test_css_missing_file_in_css(): |
| code = b"""\ |
| <html xmlns="http://www.w3.org/1999/xhtml"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| </html> |
| """ |
| errors = check_file_contents("", "css/foo/bar.html", six.BytesIO(code), False) |
| check_errors(errors) |
| |
| assert errors == [ |
| ('MISSING-LINK', |
| 'Testcase file must have a link to a spec', |
| "css/foo/bar.html", |
| None), |
| ] |
| |
| |
| def test_css_missing_file_manual(): |
| errors = check_file_contents("", "css/foo/bar-manual.html", six.BytesIO(b""), False) |
| check_errors(errors) |
| |
| assert errors == [ |
| ('MISSING-LINK', |
| 'Testcase file must have a link to a spec', |
| "css/foo/bar-manual.html", |
| None), |
| ] |
| |
| |
| @pytest.mark.parametrize("filename", [ |
| "foo.worker.js", |
| "foo.any.js", |
| ]) |
| @pytest.mark.parametrize("input,error", [ |
| (b"""//META: timeout=long\n""", None), |
| (b"""// META: timeout=long\n""", None), |
| (b"""// META: timeout=long\n""", None), |
| (b"""// META: script=foo.js\n""", None), |
| (b"""# META:\n""", None), |
| (b"""\n// META: timeout=long\n""", (2, "STRAY-METADATA")), |
| (b""" // META: timeout=long\n""", (1, "INDENTED-METADATA")), |
| (b"""// META: timeout=long\n// META: timeout=long\n""", None), |
| (b"""// META: timeout=long\n\n// META: timeout=long\n""", (3, "STRAY-METADATA")), |
| (b"""// META: timeout=long\n// Start of the test\n// META: timeout=long\n""", (3, "STRAY-METADATA")), |
| (b"""// META:\n""", (1, "BROKEN-METADATA")), |
| (b"""// META: foobar\n""", (1, "BROKEN-METADATA")), |
| (b"""// META: foo=bar\n""", (1, "UNKNOWN-METADATA")), |
| (b"""// META: timeout=bar\n""", (1, "UNKNOWN-TIMEOUT-METADATA")), |
| ]) |
| def test_script_metadata(filename, input, error): |
| errors = check_file_contents("", filename, six.BytesIO(input), False) |
| check_errors(errors) |
| |
| if error is not None: |
| line, kind = error |
| messages = { |
| "STRAY-METADATA": "Metadata comments should start the file", |
| "INDENTED-METADATA": "Metadata comments should start the line", |
| "BROKEN-METADATA": "Metadata comment is not formatted correctly", |
| "UNKNOWN-TIMEOUT-METADATA": "Unexpected value for timeout metadata", |
| "UNKNOWN-METADATA": "Unexpected kind of metadata", |
| } |
| assert errors == [ |
| (kind, |
| messages[kind], |
| filename, |
| line), |
| ] |
| else: |
| assert errors == [] |
| |
| |
| @pytest.mark.parametrize("input,error", [ |
| (b"""#META: timeout=long\n""", None), |
| (b"""# META: timeout=long\n""", None), |
| (b"""# META: timeout=long\n""", None), |
| (b""""// META:"\n""", None), |
| (b"""\n# META: timeout=long\n""", (2, "STRAY-METADATA")), |
| (b""" # META: timeout=long\n""", (1, "INDENTED-METADATA")), |
| (b"""# META: timeout=long\n# META: timeout=long\n""", None), |
| (b"""# META: timeout=long\n\n# META: timeout=long\n""", (3, "STRAY-METADATA")), |
| (b"""# META: timeout=long\n# Start of the test\n# META: timeout=long\n""", (3, "STRAY-METADATA")), |
| (b"""# META:\n""", (1, "BROKEN-METADATA")), |
| (b"""# META: foobar\n""", (1, "BROKEN-METADATA")), |
| (b"""# META: foo=bar\n""", (1, "UNKNOWN-METADATA")), |
| (b"""# META: timeout=bar\n""", (1, "UNKNOWN-TIMEOUT-METADATA")), |
| ]) |
| def test_python_metadata(input, error): |
| filename = "test.py" |
| errors = check_file_contents("", filename, six.BytesIO(input), False) |
| check_errors(errors) |
| |
| if error is not None: |
| line, kind = error |
| messages = { |
| "STRAY-METADATA": "Metadata comments should start the file", |
| "INDENTED-METADATA": "Metadata comments should start the line", |
| "BROKEN-METADATA": "Metadata comment is not formatted correctly", |
| "UNKNOWN-TIMEOUT-METADATA": "Unexpected value for timeout metadata", |
| "UNKNOWN-METADATA": "Unexpected kind of metadata", |
| } |
| assert errors == [ |
| (kind, |
| messages[kind], |
| filename, |
| line), |
| ] |
| else: |
| assert errors == [] |