diff options
Diffstat (limited to '')
80 files changed, 1662 insertions, 0 deletions
diff --git a/tools/lint/test-disable.yml b/tools/lint/test-disable.yml new file mode 100644 index 0000000000..7d93889704 --- /dev/null +++ b/tools/lint/test-disable.yml @@ -0,0 +1,16 @@ +--- +no-comment-disable: + description: > + "Use 'disabled=<reason>' to disable a test instead of a + comment" + include: ['.'] + exclude: + - "**/application.ini" + - "**/l10n.ini" + - dom/canvas/test/webgl-conf/mochitest-errata.ini + - testing/mozbase/manifestparser/tests + - testing/web-platform + - xpcom/tests/unit/data + extensions: ['ini'] + type: regex + payload: ^[ \t]*(#|;)[ \t]*\[ diff --git a/tools/lint/test/conftest.py b/tools/lint/test/conftest.py new file mode 100644 index 0000000000..e9a6b01a2d --- /dev/null +++ b/tools/lint/test/conftest.py @@ -0,0 +1,228 @@ +from __future__ import absolute_import, print_function + +import logging +import os +import sys +from collections import defaultdict + +from mozbuild.base import MozbuildObject +from mozlint.pathutils import findobject +from mozlint.parser import Parser +from mozlint.result import ResultSummary +from mozlog.structuredlog import StructuredLogger +from mozpack import path + +import pytest + +here = path.abspath(path.dirname(__file__)) +build = MozbuildObject.from_environment(cwd=here, virtualenv_name="python-test") + +lintdir = path.dirname(here) +sys.path.insert(0, lintdir) +logger = logging.getLogger("mozlint") + + +def pytest_generate_tests(metafunc): + """Finds, loads and returns the config for the linter name specified by the + LINTER global variable in the calling module. + + This implies that each test file (that uses this fixture) should only be + used to test a single linter. If no LINTER variable is defined, the test + will fail. + """ + if "config" in metafunc.fixturenames: + if not hasattr(metafunc.module, "LINTER"): + pytest.fail( + "'config' fixture used from a module that didn't set the LINTER variable" + ) + + name = metafunc.module.LINTER + config_path = path.join(lintdir, "{}.yml".format(name)) + parser = Parser(build.topsrcdir) + configs = parser.parse(config_path) + config_names = {config["name"] for config in configs} + + marker = metafunc.definition.get_closest_marker("lint_config") + if marker: + config_name = marker.kwargs["name"] + if config_name not in config_names: + pytest.fail(f"lint config {config_name} not present in {name}.yml") + configs = [ + config for config in configs if config["name"] == marker.kwargs["name"] + ] + + ids = [config["name"] for config in configs] + metafunc.parametrize("config", configs, ids=ids) + + +@pytest.fixture(scope="module") +def root(request): + """Return the root directory for the files of the linter under test. + + For example, with LINTER=flake8 this would be tools/lint/test/files/flake8. + """ + if not hasattr(request.module, "LINTER"): + pytest.fail( + "'root' fixture used from a module that didn't set the LINTER variable" + ) + return path.join(here, "files", request.module.LINTER) + + +@pytest.fixture(scope="module") +def paths(root): + """Return a function that can resolve file paths relative to the linter + under test. + + Can be used like `paths('foo.py', 'bar/baz')`. This will return a list of + absolute paths under the `root` files directory. + """ + + def _inner(*paths): + if not paths: + return [root] + return [path.normpath(path.join(root, p)) for p in paths] + + return _inner + + +@pytest.fixture(autouse=True) +def run_setup(config): + """Make sure that if the linter named in the LINTER global variable has a + setup function, it gets called before running the tests. + """ + if "setup" not in config: + return + + func = findobject(config["setup"]) + func(build.topsrcdir, virtualenv_manager=build.virtualenv_manager) + + +@pytest.fixture +def lint(config, root): + """Find and return the 'lint' function for the external linter named in the + LINTER global variable. + + This will automatically pass in the 'config' and 'root' arguments if not + specified. + """ + try: + func = findobject(config["payload"]) + except (ImportError, ValueError): + pytest.fail( + "could not resolve a lint function from '{}'".format(config["payload"]) + ) + + ResultSummary.root = root + + def wrapper(paths, config=config, root=root, collapse_results=False, **lintargs): + logger.setLevel(logging.DEBUG) + lintargs["log"] = logging.LoggerAdapter( + logger, {"lintname": config.get("name"), "pid": os.getpid()} + ) + results = func(paths, config, root=root, **lintargs) + + if isinstance(results, (list, tuple)): + results = sorted(results) + + if not collapse_results: + return results + + ret = defaultdict(list) + for r in results: + ret[r.relpath].append(r) + return ret + + return wrapper + + +@pytest.fixture +def structuredlog_lint(config, root, logger=None): + """Find and return the 'lint' function for the external linter named in the + LINTER global variable. This variant of the lint function is for linters that + use the 'structuredlog' type. + + This will automatically pass in the 'config' and 'root' arguments if not + specified. + """ + try: + func = findobject(config["payload"]) + except (ImportError, ValueError): + pytest.fail( + "could not resolve a lint function from '{}'".format(config["payload"]) + ) + + ResultSummary.root = root + + if not logger: + logger = structured_logger() + + def wrapper( + paths, + config=config, + root=root, + logger=logger, + collapse_results=False, + **lintargs, + ): + lintargs["log"] = logging.LoggerAdapter( + logger, {"lintname": config.get("name"), "pid": os.getpid()} + ) + results = func(paths, config, root=root, logger=logger, **lintargs) + if not collapse_results: + return results + + ret = defaultdict(list) + for r in results: + ret[r.path].append(r) + return ret + + return wrapper + + +@pytest.fixture +def create_temp_file(tmpdir): + def inner(contents, name=None): + name = name or "temp.py" + path = tmpdir.join(name) + path.write(contents) + return path.strpath + + return inner + + +@pytest.fixture +def structured_logger(): + return StructuredLogger("logger") + + +@pytest.fixture +def perfdocs_sample(): + from test_perfdocs import ( + SAMPLE_TEST, + SAMPLE_CONFIG, + temp_dir, + temp_file, + ) + + with temp_dir() as tmpdir: + suitedir = os.path.join(tmpdir, "suite") + perfdocs_dir = os.path.join(tmpdir, "perfdocs") + os.mkdir(perfdocs_dir) + os.mkdir(suitedir) + + with temp_file( + "perftest.ini", tempdir=suitedir, content="[perftest_sample.js]" + ) as tmpmanifest, temp_file( + "perftest_sample.js", tempdir=suitedir, content=SAMPLE_TEST + ) as tmptest, temp_file( + "config.yml", tempdir=perfdocs_dir, content=SAMPLE_CONFIG + ) as tmpconfig, temp_file( + "index.rst", tempdir=perfdocs_dir, content="{documentation}" + ) as tmpindex: + yield { + "top_dir": tmpdir.replace("\\", "\\\\"), + "manifest": tmpmanifest, + "test": tmptest, + "config": tmpconfig, + "index": tmpindex, + } diff --git a/tools/lint/test/files/black/bad.py b/tools/lint/test/files/black/bad.py new file mode 100644 index 0000000000..0a50df4dd9 --- /dev/null +++ b/tools/lint/test/files/black/bad.py @@ -0,0 +1,6 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +print ( + "test" + ) diff --git a/tools/lint/test/files/black/invalid.py b/tools/lint/test/files/black/invalid.py new file mode 100644 index 0000000000..079ecbad30 --- /dev/null +++ b/tools/lint/test/files/black/invalid.py @@ -0,0 +1,4 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +print( diff --git a/tools/lint/test/files/clang-format/bad/bad.cpp b/tools/lint/test/files/clang-format/bad/bad.cpp new file mode 100644 index 0000000000..f08a83f795 --- /dev/null +++ b/tools/lint/test/files/clang-format/bad/bad.cpp @@ -0,0 +1,6 @@ +int main ( ) { + +return 0; + + +} diff --git a/tools/lint/test/files/clang-format/bad/bad2.c b/tools/lint/test/files/clang-format/bad/bad2.c new file mode 100644 index 0000000000..9792e85071 --- /dev/null +++ b/tools/lint/test/files/clang-format/bad/bad2.c @@ -0,0 +1,8 @@ +#include "bad2.h" + + +int bad2() { + int a =2; + return a; + +} diff --git a/tools/lint/test/files/clang-format/bad/bad2.h b/tools/lint/test/files/clang-format/bad/bad2.h new file mode 100644 index 0000000000..a35d49d7e7 --- /dev/null +++ b/tools/lint/test/files/clang-format/bad/bad2.h @@ -0,0 +1 @@ +int bad2(void ); diff --git a/tools/lint/test/files/clang-format/bad/good.cpp b/tools/lint/test/files/clang-format/bad/good.cpp new file mode 100644 index 0000000000..76e8197013 --- /dev/null +++ b/tools/lint/test/files/clang-format/bad/good.cpp @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/tools/lint/test/files/clang-format/good/foo.cpp b/tools/lint/test/files/clang-format/good/foo.cpp new file mode 100644 index 0000000000..76e8197013 --- /dev/null +++ b/tools/lint/test/files/clang-format/good/foo.cpp @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/tools/lint/test/files/clippy/test1/Cargo.toml b/tools/lint/test/files/clippy/test1/Cargo.toml new file mode 100644 index 0000000000..92d5072eca --- /dev/null +++ b/tools/lint/test/files/clippy/test1/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "hello_world" # the name of the package +version = "0.1.0" # the current version, obeying semver +authors = ["Alice <a@example.com>", "Bob <b@example.com>"] + +[[bin]] +name ="good" +path = "good.rs" + +[[bin]] +name ="bad" +path = "bad.rs" + +[[bin]] +name ="bad2" +path = "bad2.rs" + diff --git a/tools/lint/test/files/clippy/test1/bad.rs b/tools/lint/test/files/clippy/test1/bad.rs new file mode 100644 index 0000000000..c403fba603 --- /dev/null +++ b/tools/lint/test/files/clippy/test1/bad.rs @@ -0,0 +1,14 @@ +fn main() { + // Statements here are executed when the compiled binary is called + + // Print text to the console + println!("Hello World!"); + // Clippy detects this as a swap and considers this as an error + let mut a=1; + let mut b=1; + + a = b; + b = a; + + +} diff --git a/tools/lint/test/files/clippy/test1/bad2.rs b/tools/lint/test/files/clippy/test1/bad2.rs new file mode 100644 index 0000000000..a4236a2de7 --- /dev/null +++ b/tools/lint/test/files/clippy/test1/bad2.rs @@ -0,0 +1,17 @@ +fn main() { + // Statements here are executed when the compiled binary is called + + // Print text to the console + println!("Hello World!"); + let mut a; + let mut b=1; + let mut vec = Vec::new(); + vec.push(1); + vec.push(2); + + + for x in 5..10 - 5 { + a = x; + } + + } diff --git a/tools/lint/test/files/clippy/test1/good.rs b/tools/lint/test/files/clippy/test1/good.rs new file mode 100644 index 0000000000..9bcaee67b7 --- /dev/null +++ b/tools/lint/test/files/clippy/test1/good.rs @@ -0,0 +1,6 @@ +fn main() { + // Statements here are executed when the compiled binary is called + + // Print text to the console + println!("Hello World!"); +} diff --git a/tools/lint/test/files/clippy/test2/Cargo.lock b/tools/lint/test/files/clippy/test2/Cargo.lock new file mode 100644 index 0000000000..6b2bc69eeb --- /dev/null +++ b/tools/lint/test/files/clippy/test2/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "hello_world_2" +version = "0.2.0" diff --git a/tools/lint/test/files/clippy/test2/Cargo.toml b/tools/lint/test/files/clippy/test2/Cargo.toml new file mode 100644 index 0000000000..b0ac992088 --- /dev/null +++ b/tools/lint/test/files/clippy/test2/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "hello_world_2" # the name of the package +version = "0.2.0" # the current version, obeying semver +authors = ["Alice <a@example.com>", "Bob <b@example.com>"] + +[[bin]] +name = "fake_lib1" +path = "src/bad_1.rs" diff --git a/tools/lint/test/files/clippy/test2/src/bad_1.rs b/tools/lint/test/files/clippy/test2/src/bad_1.rs new file mode 100644 index 0000000000..2fe0630202 --- /dev/null +++ b/tools/lint/test/files/clippy/test2/src/bad_1.rs @@ -0,0 +1,15 @@ +mod bad_2; + +fn main() { + // Statements here are executed when the compiled binary is called + + // Print text to the console + println!("Hello World!"); + // Clippy detects this as a swap and considers this as an error + let mut a=1; + let mut b=1; + + a = b; + b = a; + +} diff --git a/tools/lint/test/files/clippy/test2/src/bad_2.rs b/tools/lint/test/files/clippy/test2/src/bad_2.rs new file mode 100644 index 0000000000..f77de330b4 --- /dev/null +++ b/tools/lint/test/files/clippy/test2/src/bad_2.rs @@ -0,0 +1,17 @@ +fn foo() { + // Statements here are executed when the compiled binary is called + + // Print text to the console + println!("Hello World!"); + let mut a; + let mut b=1; + let mut vec = Vec::new(); + vec.push(1); + vec.push(2); + + + for x in 5..10 - 5 { + a = x; + } + + } diff --git a/tools/lint/test/files/codespell/ignore.rst b/tools/lint/test/files/codespell/ignore.rst new file mode 100644 index 0000000000..1371d07054 --- /dev/null +++ b/tools/lint/test/files/codespell/ignore.rst @@ -0,0 +1,5 @@ +This is a file with some typos and informations. +But also testing false positive like optin (because this isn't always option) +or stuff related to our coding style like: +aparent (aParent). +but detects mistakes like mozila diff --git a/tools/lint/test/files/eslint/good.js b/tools/lint/test/files/eslint/good.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tools/lint/test/files/eslint/good.js diff --git a/tools/lint/test/files/eslint/import/bad_import.js b/tools/lint/test/files/eslint/import/bad_import.js new file mode 100644 index 0000000000..e2a8ec8de1 --- /dev/null +++ b/tools/lint/test/files/eslint/import/bad_import.js @@ -0,0 +1 @@ +/* import-globals-from notpresent/notpresent.js */ diff --git a/tools/lint/test/files/eslint/nolint/foo.txt b/tools/lint/test/files/eslint/nolint/foo.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tools/lint/test/files/eslint/nolint/foo.txt diff --git a/tools/lint/test/files/eslint/subdir/bad.js b/tools/lint/test/files/eslint/subdir/bad.js new file mode 100644 index 0000000000..9d2dd18f39 --- /dev/null +++ b/tools/lint/test/files/eslint/subdir/bad.js @@ -0,0 +1,2 @@ +// Missing semicolon +let foo = "bar" diff --git a/tools/lint/test/files/file-perm/maybe-shebang/bad.js b/tools/lint/test/files/file-perm/maybe-shebang/bad.js new file mode 100755 index 0000000000..1a0b4c5fd6 --- /dev/null +++ b/tools/lint/test/files/file-perm/maybe-shebang/bad.js @@ -0,0 +1,2 @@ +# Nothing too + diff --git a/tools/lint/test/files/file-perm/maybe-shebang/good.js b/tools/lint/test/files/file-perm/maybe-shebang/good.js new file mode 100755 index 0000000000..8149c0d4f3 --- /dev/null +++ b/tools/lint/test/files/file-perm/maybe-shebang/good.js @@ -0,0 +1,5 @@ +#!/usr/bin/env node + + +# Nothing + diff --git a/tools/lint/test/files/file-perm/no-shebang/bad-shebang.c b/tools/lint/test/files/file-perm/no-shebang/bad-shebang.c new file mode 100755 index 0000000000..7151678efa --- /dev/null +++ b/tools/lint/test/files/file-perm/no-shebang/bad-shebang.c @@ -0,0 +1,2 @@ +#!/bin/bash +int main() { return 0; } diff --git a/tools/lint/test/files/file-perm/no-shebang/bad.c b/tools/lint/test/files/file-perm/no-shebang/bad.c new file mode 100755 index 0000000000..76e8197013 --- /dev/null +++ b/tools/lint/test/files/file-perm/no-shebang/bad.c @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/tools/lint/test/files/file-perm/no-shebang/bad.png b/tools/lint/test/files/file-perm/no-shebang/bad.png Binary files differnew file mode 100755 index 0000000000..db3a5fda7e --- /dev/null +++ b/tools/lint/test/files/file-perm/no-shebang/bad.png diff --git a/tools/lint/test/files/file-perm/no-shebang/good.c b/tools/lint/test/files/file-perm/no-shebang/good.c new file mode 100644 index 0000000000..76e8197013 --- /dev/null +++ b/tools/lint/test/files/file-perm/no-shebang/good.c @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/tools/lint/test/files/file-whitespace/bad-newline.c b/tools/lint/test/files/file-whitespace/bad-newline.c new file mode 100644 index 0000000000..3746a0add3 --- /dev/null +++ b/tools/lint/test/files/file-whitespace/bad-newline.c @@ -0,0 +1,3 @@ +int main() { return 0; } + +
\ No newline at end of file diff --git a/tools/lint/test/files/file-whitespace/bad-windows.c b/tools/lint/test/files/file-whitespace/bad-windows.c new file mode 100644 index 0000000000..70d4c697b9 --- /dev/null +++ b/tools/lint/test/files/file-whitespace/bad-windows.c @@ -0,0 +1,3 @@ +int main(){
+ return 42;
+}
diff --git a/tools/lint/test/files/file-whitespace/bad.c b/tools/lint/test/files/file-whitespace/bad.c new file mode 100644 index 0000000000..4309b1f55d --- /dev/null +++ b/tools/lint/test/files/file-whitespace/bad.c @@ -0,0 +1,3 @@ +int main() { +return 0; +} diff --git a/tools/lint/test/files/file-whitespace/bad.js b/tools/lint/test/files/file-whitespace/bad.js new file mode 100755 index 0000000000..3441696ef1 --- /dev/null +++ b/tools/lint/test/files/file-whitespace/bad.js @@ -0,0 +1,3 @@ +# Nothing too + + diff --git a/tools/lint/test/files/file-whitespace/good.c b/tools/lint/test/files/file-whitespace/good.c new file mode 100644 index 0000000000..76e8197013 --- /dev/null +++ b/tools/lint/test/files/file-whitespace/good.c @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/tools/lint/test/files/file-whitespace/good.js b/tools/lint/test/files/file-whitespace/good.js new file mode 100755 index 0000000000..8149c0d4f3 --- /dev/null +++ b/tools/lint/test/files/file-whitespace/good.js @@ -0,0 +1,5 @@ +#!/usr/bin/env node + + +# Nothing + diff --git a/tools/lint/test/files/flake8/.flake8 b/tools/lint/test/files/flake8/.flake8 new file mode 100644 index 0000000000..1933432319 --- /dev/null +++ b/tools/lint/test/files/flake8/.flake8 @@ -0,0 +1,4 @@ +[flake8] +max-line-length = 100 +exclude = + subdir/exclude, diff --git a/tools/lint/test/files/flake8/bad.py b/tools/lint/test/files/flake8/bad.py new file mode 100644 index 0000000000..9d9751c7eb --- /dev/null +++ b/tools/lint/test/files/flake8/bad.py @@ -0,0 +1,5 @@ +# Unused import +import distutils + +print("This is a line that is over 80 characters but under 100. It shouldn't fail.") +print("This is a line that is over not only 80, but 100 characters. It should most certainly cause a failure.") diff --git a/tools/lint/test/files/flake8/custom/.flake8 b/tools/lint/test/files/flake8/custom/.flake8 new file mode 100644 index 0000000000..cfe68833f2 --- /dev/null +++ b/tools/lint/test/files/flake8/custom/.flake8 @@ -0,0 +1,4 @@ +[flake8] +max-line-length=110 +ignore= + F401 diff --git a/tools/lint/test/files/flake8/custom/good.py b/tools/lint/test/files/flake8/custom/good.py new file mode 100644 index 0000000000..7f9121a2ba --- /dev/null +++ b/tools/lint/test/files/flake8/custom/good.py @@ -0,0 +1,5 @@ +# Unused import +import distutils + +print("This is a line that is over 80 characters but under 100. It shouldn't fail.") +print("This is a line that is over not only 80, but 100 characters. It should also not cause a failure.") diff --git a/tools/lint/test/files/flake8/ext/bad.configure b/tools/lint/test/files/flake8/ext/bad.configure new file mode 100644 index 0000000000..8214ebb3c0 --- /dev/null +++ b/tools/lint/test/files/flake8/ext/bad.configure @@ -0,0 +1,2 @@ +# unused import +import os diff --git a/tools/lint/test/files/flake8/subdir/exclude/bad.py b/tools/lint/test/files/flake8/subdir/exclude/bad.py new file mode 100644 index 0000000000..9d9751c7eb --- /dev/null +++ b/tools/lint/test/files/flake8/subdir/exclude/bad.py @@ -0,0 +1,5 @@ +# Unused import +import distutils + +print("This is a line that is over 80 characters but under 100. It shouldn't fail.") +print("This is a line that is over not only 80, but 100 characters. It should most certainly cause a failure.") diff --git a/tools/lint/test/files/flake8/subdir/exclude/exclude_subdir/bad.py b/tools/lint/test/files/flake8/subdir/exclude/exclude_subdir/bad.py new file mode 100644 index 0000000000..9d9751c7eb --- /dev/null +++ b/tools/lint/test/files/flake8/subdir/exclude/exclude_subdir/bad.py @@ -0,0 +1,5 @@ +# Unused import +import distutils + +print("This is a line that is over 80 characters but under 100. It shouldn't fail.") +print("This is a line that is over not only 80, but 100 characters. It should most certainly cause a failure.") diff --git a/tools/lint/test/files/license/.eslintrc.js b/tools/lint/test/files/license/.eslintrc.js new file mode 100644 index 0000000000..0449fdfa33 --- /dev/null +++ b/tools/lint/test/files/license/.eslintrc.js @@ -0,0 +1,5 @@ + +// Dot file to verify that it works +// without license + +"use strict"; diff --git a/tools/lint/test/files/license/bad.c b/tools/lint/test/files/license/bad.c new file mode 100644 index 0000000000..76e8197013 --- /dev/null +++ b/tools/lint/test/files/license/bad.c @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/tools/lint/test/files/license/bad.js b/tools/lint/test/files/license/bad.js new file mode 100644 index 0000000000..5de1a72f1f --- /dev/null +++ b/tools/lint/test/files/license/bad.js @@ -0,0 +1,6 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Pulic Unknown License as published by + * the Free Software Foundation, version 3. + * + */ diff --git a/tools/lint/test/files/license/good-other.h b/tools/lint/test/files/license/good-other.h new file mode 100644 index 0000000000..fb915e9b26 --- /dev/null +++ b/tools/lint/test/files/license/good-other.h @@ -0,0 +1,9 @@ +/* +Permission to use, copy, modify, distribute and sell this software +and its documentation for any purpose is hereby granted without fee, +provided that the above copyright notice appear in all copies and +that both that copyright notice and this permission notice appear +in supporting documentation. Samphan Raruenrom makes no +representations about the suitability of this software for any +purpose. It is provided "as is" without express or implied warranty. +*/ diff --git a/tools/lint/test/files/license/good.c b/tools/lint/test/files/license/good.c new file mode 100644 index 0000000000..d1a6827fb1 --- /dev/null +++ b/tools/lint/test/files/license/good.c @@ -0,0 +1,8 @@ + +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +int main() { return 0; } diff --git a/tools/lint/test/files/license/good.js b/tools/lint/test/files/license/good.js new file mode 100644 index 0000000000..d10ae3a8d5 --- /dev/null +++ b/tools/lint/test/files/license/good.js @@ -0,0 +1,7 @@ +#!/usr/bin/env node +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +# Nothing + diff --git a/tools/lint/test/files/lintpref/bad.js b/tools/lint/test/files/lintpref/bad.js new file mode 100644 index 0000000000..ab55ba5dad --- /dev/null +++ b/tools/lint/test/files/lintpref/bad.js @@ -0,0 +1,2 @@ +// Real test pref, matching value. +pref("dom.webidl.test1", true); diff --git a/tools/lint/test/files/lintpref/good.js b/tools/lint/test/files/lintpref/good.js new file mode 100644 index 0000000000..0bf81c8f58 --- /dev/null +++ b/tools/lint/test/files/lintpref/good.js @@ -0,0 +1,6 @@ +// Fake prefs. +pref("foo.bar", 1); +pref("foo.baz", "1.234"); + +// Real test pref, different value. +pref("dom.webidl.test1", false); diff --git a/tools/lint/test/files/pylint/bad.py b/tools/lint/test/files/pylint/bad.py new file mode 100644 index 0000000000..61b69a49cf --- /dev/null +++ b/tools/lint/test/files/pylint/bad.py @@ -0,0 +1,5 @@ +def foo(): + useless_var = 1 + useless_var = true + return "true" + print("unreachable")
\ No newline at end of file diff --git a/tools/lint/test/files/pylint/good.py b/tools/lint/test/files/pylint/good.py new file mode 100644 index 0000000000..c867dc66ec --- /dev/null +++ b/tools/lint/test/files/pylint/good.py @@ -0,0 +1,3 @@ +def foo(): + a = 1 + 1 + return a diff --git a/tools/lint/test/files/rst/.dotfile.rst b/tools/lint/test/files/rst/.dotfile.rst new file mode 100644 index 0000000000..be24e1d161 --- /dev/null +++ b/tools/lint/test/files/rst/.dotfile.rst @@ -0,0 +1,11 @@ +============ +Coding style +========== + +foo bar +~~~~~ + + +This file has error but should not be there +as we don't analyze dot files + diff --git a/tools/lint/test/files/rst/bad.rst b/tools/lint/test/files/rst/bad.rst new file mode 100644 index 0000000000..c9b60f613e --- /dev/null +++ b/tools/lint/test/files/rst/bad.rst @@ -0,0 +1,20 @@ +============ +Coding style +============ + + +This document attempts to explain the basic styles and patterns used in +the Mozilla codebase. New code should try to conform to these standards, +so it is as easy to maintain as existing code. There are exceptions, but +it's still important to know the rules! + + +Whitespace +~~~~~~~~ + +Line length +~~~~~~~~~~~ + +Line length +~~~~~~~~~~~ + diff --git a/tools/lint/test/files/rst/bad2.rst b/tools/lint/test/files/rst/bad2.rst new file mode 100644 index 0000000000..81c35dde06 --- /dev/null +++ b/tools/lint/test/files/rst/bad2.rst @@ -0,0 +1,4 @@ +==== +Test +=== + diff --git a/tools/lint/test/files/rst/good.rst b/tools/lint/test/files/rst/good.rst new file mode 100644 index 0000000000..fd12da85d3 --- /dev/null +++ b/tools/lint/test/files/rst/good.rst @@ -0,0 +1,11 @@ +============ +Coding style +============ + + +This document attempts to explain the basic styles and patterns used in +the Mozilla codebase. New code should try to conform to these standards, +so it is as easy to maintain as existing code. There are exceptions, but +it's still important to know the rules! + + diff --git a/tools/lint/test/files/rustfmt/subdir/bad.rs b/tools/lint/test/files/rustfmt/subdir/bad.rs new file mode 100644 index 0000000000..fb1746fafd --- /dev/null +++ b/tools/lint/test/files/rustfmt/subdir/bad.rs @@ -0,0 +1,16 @@ +fn main() { + // Statements here are executed when the compiled binary is called + + // Print text to the console + println!("Hello World!"); + // Clippy detects this as a swap and considers this as an error + let mut a = + 1; + let mut b=1; + + a = + b; + b = a; + + +} diff --git a/tools/lint/test/files/rustfmt/subdir/bad2.rs b/tools/lint/test/files/rustfmt/subdir/bad2.rs new file mode 100644 index 0000000000..a4236a2de7 --- /dev/null +++ b/tools/lint/test/files/rustfmt/subdir/bad2.rs @@ -0,0 +1,17 @@ +fn main() { + // Statements here are executed when the compiled binary is called + + // Print text to the console + println!("Hello World!"); + let mut a; + let mut b=1; + let mut vec = Vec::new(); + vec.push(1); + vec.push(2); + + + for x in 5..10 - 5 { + a = x; + } + + } diff --git a/tools/lint/test/files/rustfmt/subdir/good.rs b/tools/lint/test/files/rustfmt/subdir/good.rs new file mode 100644 index 0000000000..9bcaee67b7 --- /dev/null +++ b/tools/lint/test/files/rustfmt/subdir/good.rs @@ -0,0 +1,6 @@ +fn main() { + // Statements here are executed when the compiled binary is called + + // Print text to the console + println!("Hello World!"); +} diff --git a/tools/lint/test/files/shellcheck/bad.sh b/tools/lint/test/files/shellcheck/bad.sh new file mode 100644 index 0000000000..b2eb195558 --- /dev/null +++ b/tools/lint/test/files/shellcheck/bad.sh @@ -0,0 +1,3 @@ +#!/bin/sh +hello="Hello world" +echo $1 diff --git a/tools/lint/test/files/shellcheck/good.sh b/tools/lint/test/files/shellcheck/good.sh new file mode 100644 index 0000000000..e61d501955 --- /dev/null +++ b/tools/lint/test/files/shellcheck/good.sh @@ -0,0 +1,2 @@ +#!/bin/sh +echo "Hello world" diff --git a/tools/lint/test/files/yaml/.yamllint b/tools/lint/test/files/yaml/.yamllint new file mode 100644 index 0000000000..4f11bbd6c5 --- /dev/null +++ b/tools/lint/test/files/yaml/.yamllint @@ -0,0 +1,6 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +# Explicity default .yamllint to isolate tests from tree-wide yamlint config. +--- +extends: default diff --git a/tools/lint/test/files/yaml/bad.yml b/tools/lint/test/files/yaml/bad.yml new file mode 100644 index 0000000000..195ac7b030 --- /dev/null +++ b/tools/lint/test/files/yaml/bad.yml @@ -0,0 +1,8 @@ +--- +yamllint: + description: YAML linteraaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax + include: + - .cron.yml + - browser/config/ + - wrong + application:bar diff --git a/tools/lint/test/files/yaml/good.yml b/tools/lint/test/files/yaml/good.yml new file mode 100644 index 0000000000..b30941b797 --- /dev/null +++ b/tools/lint/test/files/yaml/good.yml @@ -0,0 +1,6 @@ +--- +yamllint: + description: YAML linter + include: + - .cron.yml + - browser/config/ diff --git a/tools/lint/test/python.ini b/tools/lint/test/python.ini new file mode 100644 index 0000000000..5acd0a1e81 --- /dev/null +++ b/tools/lint/test/python.ini @@ -0,0 +1,34 @@ +[DEFAULT] +subsuite = mozlint +skip-if = python == 2 + +[test_black.py] +requirements = tools/lint/python/black_requirements.txt +skip-if = os == "mac" # pip unable to find black +[test_eslint.py] +[test_flake8.py] +requirements = tools/lint/python/flake8_requirements.txt +skip-if = os == "mac" # pip unable to find 'flake8==3.5.0' +[test_file_perm.py] +skip-if = os == "win" +[test_file_whitespace.py] +[test_file_license.py] +[test_lintpref.py] +[test_shellcheck.py] +[test_rst.py] +requirements = tools/lint/rst/requirements.txt +skip-if = os == "mac" # pip unable to install +[test_codespell.py] +skip-if = os == "win" || os == "mac" # codespell installed on Linux +[test_yaml.py] +[test_clippy.py] +skip-if = os == "win" || os == "mac" # only installed on Linux +[test_rustfmt.py] +skip-if = os == "win" || os == "mac" # only installed on Linux +[test_clang_format.py] +skip-if = os == "win" || os == "mac" # only installed on Linux +[test_perfdocs.py] +[test_pylint.py] +skip-if = os == "win" || os == "mac" # only installed on linux +requirements = tools/lint/python/pylint_requirements.txt + diff --git a/tools/lint/test/test_black.py b/tools/lint/test/test_black.py new file mode 100644 index 0000000000..a5482c6e11 --- /dev/null +++ b/tools/lint/test/test_black.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import mozunit + +LINTER = "black" + + +def test_lint_black(lint, paths): + results = lint(paths()) + assert len(results) == 2 + + print(results) + assert results[0].level == "error" + assert results[0].relpath == "bad.py" + + assert "EOF" in results[1].message + assert results[1].level == "error" + assert results[1].relpath == "invalid.py" + + +if __name__ == "__main__": + mozunit.main() diff --git a/tools/lint/test/test_clang_format.py b/tools/lint/test/test_clang_format.py new file mode 100644 index 0000000000..a04fde6267 --- /dev/null +++ b/tools/lint/test/test_clang_format.py @@ -0,0 +1,48 @@ +import mozunit + +from conftest import build + +LINTER = "clang-format" + + +def test_good(lint, config, paths): + results = lint(paths("good/"), root=build.topsrcdir, use_filters=False) + print(results) + assert len(results) == 0 + + +def test_basic(lint, config, paths): + results = lint(paths("bad/bad.cpp"), root=build.topsrcdir, use_filters=False) + print(results) + assert len(results) >= 1 + + assert "Reformat C/C++" in results[0].message + assert results[0].level == "warning" + assert results[0].lineno == 1 + assert results[0].column == 4 + assert "bad.cpp" in results[0].path + assert "int main ( ) {" in results[0].diff + + +def test_dir(lint, config, paths): + results = lint(paths("bad/"), root=build.topsrcdir, use_filters=False) + print(results) + assert len(results) >= 4 + + assert "Reformat C/C++" in results[0].message + assert results[0].level == "warning" + assert results[0].lineno == 1 + assert results[0].column == 4 + assert "bad.cpp" in results[0].path + assert "int main ( ) {" in results[0].diff + + assert "Reformat C/C++" in results[5].message + assert results[5].level == "warning" + assert results[5].lineno == 1 + assert results[5].column == 18 + assert "bad2.c" in results[5].path + assert "#include" in results[5].diff + + +if __name__ == "__main__": + mozunit.main() diff --git a/tools/lint/test/test_clippy.py b/tools/lint/test/test_clippy.py new file mode 100644 index 0000000000..553aba8876 --- /dev/null +++ b/tools/lint/test/test_clippy.py @@ -0,0 +1,103 @@ +import mozunit +import os + +LINTER = "clippy" + + +def test_basic(lint, config, paths): + results = lint(paths("test1/")) + print(results) + assert len(results) > 7 + + assert ( + "is never read" in results[0].message or "but never used" in results[0].message + ) + assert results[0].level == "warning" + assert results[0].lineno == 7 + assert results[0].column == 9 + assert results[0].rule == "unused_assignments" + assert results[0].relpath == "test1/bad.rs" + assert "tools/lint/test/files/clippy/test1/bad.rs" in results[0].path + + assert "this looks like you are trying to swap `a` and `b`" in results[1].message + assert results[1].level == "error" + assert results[1].relpath == "test1/bad.rs" + assert results[1].rule == "clippy::almost_swapped" + assert "or maybe you should use `std::mem::replace`" in results[1].hint + + assert "value assigned to `b` is never read" in results[2].message + assert results[2].level == "warning" + assert results[2].relpath == "test1/bad.rs" + + assert "variable does not need to be mutable" in results[5].message + assert results[5].relpath == "test1/bad2.rs" + assert results[5].rule == "unused_mut" + + assert "this range is empty so" in results[6].message + assert results[6].level == "error" + assert results[6].relpath == "test1/bad2.rs" + assert results[6].rule == "clippy::reversed_empty_ranges" + + +def test_error(lint, config, paths): + results = lint(paths("test1/Cargo.toml")) + # Should fail. We don't accept Cargo.toml as input + assert results == 1 + + +def test_file_and_path_provided(lint, config, paths): + results = lint(paths("./test2/src/bad_1.rs", "test1/")) + # even if clippy analyzed it + # we should not have anything from bad_2.rs + # as mozlint is filtering out the file + print(results) + assert len(results) > 16 + assert "value assigned to `a` is never read" in results[0].message + assert results[0].level == "warning" + assert results[0].lineno == 7 + assert results[0].column == 9 + assert results[0].rule == "unused_assignments" + assert results[0].relpath == "test1/bad.rs" + assert "tools/lint/test/files/clippy/test1/bad.rs" in results[0].path + assert "value assigned to `a` is never read" in results[0].message + assert results[8].level == "warning" + assert results[8].lineno == 1 + assert results[8].column == 4 + assert results[8].rule == "dead_code" + assert results[8].relpath == "test2/src/bad_1.rs" + assert "tools/lint/test/files/clippy/test2/src/bad_1.rs" in results[8].path + for r in results: + assert "bad_2.rs" not in r.relpath + + +def test_file_provided(lint, config, paths): + results = lint(paths("./test2/src/bad_1.rs")) + # even if clippy analyzed it + # we should not have anything from bad_2.rs + # as mozlint is filtering out the file + print(results) + assert len(results) > 8 + assert results[0].level == "warning" + assert results[0].lineno == 1 + assert results[0].column == 4 + assert results[0].rule == "dead_code" + assert results[0].relpath == "test2/src/bad_1.rs" + assert "tools/lint/test/files/clippy/test2/src/bad_1.rs" in results[0].path + for r in results: + assert "bad_2.rs" not in r.relpath + + +def test_cleanup(lint, paths, root): + # If Cargo.lock does not exist before clippy run, delete it + lint(paths("test1/")) + assert not os.path.exists(os.path.join(root, "test1/target/")) + assert not os.path.exists(os.path.join(root, "test1/Cargo.lock")) + + # If Cargo.lock exists before clippy run, keep it after cleanup + lint(paths("test2/")) + assert not os.path.exists(os.path.join(root, "test2/target/")) + assert os.path.exists(os.path.join(root, "test2/Cargo.lock")) + + +if __name__ == "__main__": + mozunit.main() diff --git a/tools/lint/test/test_codespell.py b/tools/lint/test/test_codespell.py new file mode 100644 index 0000000000..41912e01a7 --- /dev/null +++ b/tools/lint/test/test_codespell.py @@ -0,0 +1,24 @@ +from __future__ import absolute_import, print_function + +import mozunit + +LINTER = "codespell" + + +def test_lint_codespell(lint, paths): + results = lint(paths()) + assert len(results) == 2 + + assert results[0].message == "informations ==> information" + assert results[0].level == "error" + assert results[0].lineno == 1 + assert results[0].relpath == "ignore.rst" + + assert results[1].message == "mozila ==> mozilla" + assert results[1].level == "error" + assert results[1].lineno == 5 + assert results[1].relpath == "ignore.rst" + + +if __name__ == "__main__": + mozunit.main() diff --git a/tools/lint/test/test_eslint.py b/tools/lint/test/test_eslint.py new file mode 100644 index 0000000000..01456a4063 --- /dev/null +++ b/tools/lint/test/test_eslint.py @@ -0,0 +1,30 @@ +import mozunit + +from conftest import build + +LINTER = "eslint" + + +def test_lint_with_global_exclude(lint, config, paths): + config["exclude"] = ["subdir", "import"] + results = lint(paths(), config=config, root=build.topsrcdir) + assert len(results) == 0 + + +def test_no_files_to_lint(lint, config, paths): + # A directory with no files to lint. + results = lint(paths("nolint"), root=build.topsrcdir) + assert results == [] + + # Errors still show up even when a directory with no files is passed in. + results = lint(paths("nolint", "subdir/bad.js"), root=build.topsrcdir) + assert len(results) == 1 + + +def test_bad_import(lint, config, paths): + results = lint(paths("import"), config=config, root=build.topsrcdir) + assert results == 1 + + +if __name__ == "__main__": + mozunit.main() diff --git a/tools/lint/test/test_file_license.py b/tools/lint/test/test_file_license.py new file mode 100644 index 0000000000..b7f0efac60 --- /dev/null +++ b/tools/lint/test/test_file_license.py @@ -0,0 +1,25 @@ +from __future__ import absolute_import, print_function + +import mozunit + +LINTER = "license" + + +def test_lint_license(lint, paths): + results = lint(paths()) + print(results) + assert len(results) == 3 + + assert ".eslintrc.js" in results[0].relpath + + assert "No matching license strings" in results[1].message + assert results[1].level == "error" + assert "bad.c" in results[1].relpath + + assert "No matching license strings" in results[2].message + assert results[2].level == "error" + assert "bad.js" in results[2].relpath + + +if __name__ == "__main__": + mozunit.main() diff --git a/tools/lint/test/test_file_perm.py b/tools/lint/test/test_file_perm.py new file mode 100644 index 0000000000..df5de9adb8 --- /dev/null +++ b/tools/lint/test/test_file_perm.py @@ -0,0 +1,39 @@ +from __future__ import absolute_import, print_function + +import pytest + +import mozunit + +LINTER = "file-perm" + + +@pytest.mark.lint_config(name="file-perm") +def test_lint_file_perm(lint, paths): + results = lint(paths("no-shebang"), collapse_results=True) + print(results) + + assert results.keys() == { + "no-shebang/bad.c", + "no-shebang/bad-shebang.c", + "no-shebang/bad.png", + } + + for path, issues in results.items(): + for issue in issues: + assert "permissions on a source" in issue.message + assert issue.level == "error" + + +@pytest.mark.lint_config(name="maybe-shebang-file-perm") +def test_lint_shebang_file_perm(config, lint, paths): + results = lint(paths("maybe-shebang")) + print(results) + assert len(results) == 1 + + assert "permissions on a source" in results[0].message + assert results[0].level == "error" + assert results[0].relpath == "maybe-shebang/bad.js" + + +if __name__ == "__main__": + mozunit.main() diff --git a/tools/lint/test/test_file_whitespace.py b/tools/lint/test/test_file_whitespace.py new file mode 100644 index 0000000000..b6fa79799b --- /dev/null +++ b/tools/lint/test/test_file_whitespace.py @@ -0,0 +1,37 @@ +from __future__ import absolute_import, print_function + +import mozunit + +LINTER = "file-whitespace" + + +def test_lint_file_whitespace(lint, paths): + results = lint(paths()) + print(results) + assert len(results) == 5 + + assert "File does not end with newline character" in results[1].message + assert results[1].level == "error" + assert "bad-newline.c" in results[1].relpath + + assert "Empty Lines at end of file" in results[0].message + assert results[0].level == "error" + assert "bad-newline.c" in results[0].relpath + + assert "Windows line return" in results[2].message + assert results[2].level == "error" + assert "bad-windows.c" in results[2].relpath + + assert "Trailing whitespace" in results[3].message + assert results[3].level == "error" + assert "bad.c" in results[3].relpath + assert results[3].lineno == 1 + + assert "Trailing whitespace" in results[4].message + assert results[4].level == "error" + assert "bad.c" in results[4].relpath + assert results[4].lineno == 2 + + +if __name__ == "__main__": + mozunit.main() diff --git a/tools/lint/test/test_flake8.py b/tools/lint/test/test_flake8.py new file mode 100644 index 0000000000..66befab4e7 --- /dev/null +++ b/tools/lint/test/test_flake8.py @@ -0,0 +1,109 @@ +import os + +import mozunit + +LINTER = "flake8" + + +def test_lint_single_file(lint, paths): + results = lint(paths("bad.py")) + assert len(results) == 2 + assert results[0].rule == "F401" + assert results[1].rule == "E501" + assert results[1].lineno == 5 + + # run lint again to make sure the previous results aren't counted twice + results = lint(paths("bad.py")) + assert len(results) == 2 + + +def test_lint_custom_config_ignored(lint, paths): + results = lint(paths("custom")) + assert len(results) == 2 + + results = lint(paths("custom/good.py")) + assert len(results) == 2 + + +def test_lint_fix(lint, create_temp_file): + contents = """ +import distutils + +def foobar(): + pass +""".lstrip() + + path = create_temp_file(contents, name="bad.py") + results = lint([path]) + assert len(results) == 2 + + # Make sure the missing blank line is fixed, but the unused import isn't. + results = lint([path], fix=True) + assert len(results) == 1 + + # Also test with a directory + path = os.path.dirname(create_temp_file(contents, name="bad2.py")) + results = lint([path], fix=True) + # There should now be two files with 2 combined errors + assert len(results) == 2 + assert all(r.rule != "E501" for r in results) + + +def test_lint_fix_uses_config(lint, create_temp_file): + contents = """ +foo = ['A list of strings', 'that go over 80 characters', 'to test if autopep8 fixes it'] +""".lstrip() + + path = create_temp_file(contents, name="line_length.py") + lint([path], fix=True) + + # Make sure autopep8 reads the global config under lintargs['root']. If it + # didn't, then the line-length over 80 would get fixed. + with open(path, "r") as fh: + assert fh.read() == contents + + +def test_lint_excluded_file(lint, paths, config): + # First file is globally excluded, second one is from .flake8 config. + files = paths("bad.py", "subdir/exclude/bad.py", "subdir/exclude/exclude_subdir") + config["exclude"] = paths("bad.py") + results = lint(files, config) + print(results) + assert len(results) == 0 + + # Make sure excludes also apply when running from a different cwd. + cwd = paths("subdir")[0] + os.chdir(cwd) + + results = lint(paths("subdir/exclude")) + print(results) + assert len(results) == 0 + + +def test_lint_excluded_file_with_glob(lint, paths, config): + config["exclude"] = paths("ext/*.configure") + + files = paths("ext") + results = lint(files, config) + print(results) + assert len(results) == 0 + + files = paths("ext/bad.configure") + results = lint(files, config) + print(results) + assert len(results) == 0 + + +def test_lint_excluded_file_with_no_filter(lint, paths, config): + results = lint(paths("subdir/exclude"), use_filters=False) + print(results) + assert len(results) == 4 + + +def test_lint_uses_custom_extensions(lint, paths): + assert len(lint(paths("ext"))) == 1 + assert len(lint(paths("ext/bad.configure"))) == 1 + + +if __name__ == "__main__": + mozunit.main() diff --git a/tools/lint/test/test_lintpref.py b/tools/lint/test/test_lintpref.py new file mode 100644 index 0000000000..4fb3b8d23c --- /dev/null +++ b/tools/lint/test/test_lintpref.py @@ -0,0 +1,18 @@ +from __future__ import absolute_import, print_function + +import mozunit + +LINTER = "lintpref" + + +def test_lintpref(lint, paths): + results = lint(paths()) + assert len(results) == 1 + assert results[0].level == "error" + assert 'pref("dom.webidl.test1", true);' in results[0].message + assert "bad.js" in results[0].relpath + assert results[0].lineno == 2 + + +if __name__ == "__main__": + mozunit.main() diff --git a/tools/lint/test/test_perfdocs.py b/tools/lint/test/test_perfdocs.py new file mode 100644 index 0000000000..f977330fda --- /dev/null +++ b/tools/lint/test/test_perfdocs.py @@ -0,0 +1,423 @@ +import contextlib +import mock +import os +import pytest +import shutil +import tempfile + +import mozunit + +LINTER = "perfdocs" + + +class PerfDocsLoggerMock: + LOGGER = None + PATHS = [] + FAILED = True + + +""" +This is a sample mozperftest test that we use for testing +the verification process. +""" +SAMPLE_TEST = """ +"use strict"; + +async function setUp(context) { + context.log.info("setUp example!"); +} + +async function test(context, commands) { + context.log.info("Test with setUp/tearDown example!"); + await commands.measure.start("https://www.sitespeed.io/"); + await commands.measure.start("https://www.mozilla.org/en-US/"); +} + +async function tearDown(context) { + context.log.info("tearDown example!"); +} + +module.noexport = {}; + +module.exports = { + setUp, + tearDown, + test, + owner: "Performance Testing Team", + name: "Example", + description: "The description of the example test.", + longDescription: ` + This is a longer description of the test perhaps including information + about how it should be run locally or links to relevant information. + ` +}; +""" + + +SAMPLE_CONFIG = """ +name: mozperftest +manifest: None +static-only: False +suites: + suite: + description: "Performance tests from the 'suite' folder." + tests: + Example: "" +""" + + +DYNAMIC_SAMPLE_CONFIG = """ +name: {} +manifest: None +static-only: False +suites: + suite: + description: "Performance tests from the 'suite' folder." + tests: + Example: "" +""" + + +@contextlib.contextmanager +def temp_file(name="temp", tempdir=None, content=None): + if tempdir is None: + tempdir = tempfile.mkdtemp() + path = os.path.join(tempdir, name) + if content is not None: + with open(path, "w") as f: + f.write(content) + try: + yield path + finally: + try: + shutil.rmtree(tempdir) + except FileNotFoundError: + pass + + +@contextlib.contextmanager +def temp_dir(): + tempdir = tempfile.mkdtemp() + try: + yield tempdir + finally: + try: + shutil.rmtree(tempdir) + except FileNotFoundError: + pass + + +def setup_sample_logger(logger, structured_logger, top_dir): + from perfdocs.logger import PerfDocLogger + + PerfDocLogger.LOGGER = structured_logger + PerfDocLogger.PATHS = ["perfdocs"] + PerfDocLogger.TOP_DIR = top_dir + + import perfdocs.verifier as vf + import perfdocs.gatherer as gt + import perfdocs.generator as gn + + gt.logger = logger + vf.logger = logger + gn.logger = logger + + +@mock.patch("perfdocs.generator.Generator") +@mock.patch("perfdocs.verifier.Verifier") +@mock.patch("perfdocs.logger.PerfDocLogger", new=PerfDocsLoggerMock) +def test_perfdocs_start_and_fail(verifier, generator, structured_logger, config, paths): + from perfdocs.perfdocs import run_perfdocs + + with temp_file("bad", content="foo") as temp: + run_perfdocs(config, logger=structured_logger, paths=[temp], generate=False) + assert PerfDocsLoggerMock.LOGGER == structured_logger + assert PerfDocsLoggerMock.PATHS == [temp] + assert PerfDocsLoggerMock.FAILED + + assert verifier.call_count == 1 + assert mock.call().validate_tree() in verifier.mock_calls + assert generator.call_count == 0 + + +@mock.patch("perfdocs.generator.Generator") +@mock.patch("perfdocs.verifier.Verifier") +@mock.patch("perfdocs.logger.PerfDocLogger", new=PerfDocsLoggerMock) +def test_perfdocs_start_and_pass(verifier, generator, structured_logger, config, paths): + from perfdocs.perfdocs import run_perfdocs + + PerfDocsLoggerMock.FAILED = False + with temp_file("bad", content="foo") as temp: + run_perfdocs(config, logger=structured_logger, paths=[temp], generate=False) + assert PerfDocsLoggerMock.LOGGER == structured_logger + assert PerfDocsLoggerMock.PATHS == [temp] + assert not PerfDocsLoggerMock.FAILED + + assert verifier.call_count == 1 + assert mock.call().validate_tree() in verifier.mock_calls + assert generator.call_count == 1 + assert mock.call().generate_perfdocs() in generator.mock_calls + + +@mock.patch("perfdocs.logger.PerfDocLogger", new=PerfDocsLoggerMock) +def test_perfdocs_bad_paths(structured_logger, config, paths): + from perfdocs.perfdocs import run_perfdocs + + with pytest.raises(Exception): + run_perfdocs(config, logger=structured_logger, paths=["bad"], generate=False) + + +@mock.patch("perfdocs.logger.PerfDocLogger") +def test_perfdocs_verification(logger, structured_logger, perfdocs_sample): + top_dir = perfdocs_sample["top_dir"] + setup_sample_logger(logger, structured_logger, top_dir) + + from perfdocs.verifier import Verifier + + verifier = Verifier(top_dir) + verifier.validate_tree() + + # Make sure that we had no warnings + assert logger.warning.call_count == 0 + assert logger.log.call_count == 1 + assert len(logger.mock_calls) == 1 + + +@mock.patch("perfdocs.logger.PerfDocLogger") +def test_perfdocs_verifier_validate_yaml_pass( + logger, structured_logger, perfdocs_sample +): + top_dir = perfdocs_sample["top_dir"] + yaml_path = perfdocs_sample["config"] + setup_sample_logger(logger, structured_logger, top_dir) + + from perfdocs.verifier import Verifier + + valid = Verifier(top_dir).validate_yaml(yaml_path) + + assert valid + + +@mock.patch("perfdocs.verifier.jsonschema") +@mock.patch("perfdocs.logger.PerfDocLogger") +def test_perfdocs_verifier_invalid_yaml( + logger, jsonschema, structured_logger, perfdocs_sample +): + top_dir = perfdocs_sample["top_dir"] + yaml_path = perfdocs_sample["config"] + setup_sample_logger(logger, structured_logger, top_dir) + + from perfdocs.verifier import Verifier + + jsonschema.validate = mock.Mock(side_effect=Exception("Schema/ValidationError")) + verifier = Verifier("top_dir") + valid = verifier.validate_yaml(yaml_path) + + expected = ("YAML ValidationError: Schema/ValidationError", yaml_path) + args, _ = logger.warning.call_args + + assert logger.warning.call_count == 1 + assert args == expected + assert not valid + + +@mock.patch("perfdocs.logger.PerfDocLogger") +def test_perfdocs_verifier_validate_rst_pass( + logger, structured_logger, perfdocs_sample +): + top_dir = perfdocs_sample["top_dir"] + rst_path = perfdocs_sample["index"] + setup_sample_logger(logger, structured_logger, top_dir) + + from perfdocs.verifier import Verifier + + valid = Verifier(top_dir).validate_rst_content(rst_path) + + assert valid + + +@mock.patch("perfdocs.logger.PerfDocLogger") +def test_perfdocs_verifier_invalid_rst(logger, structured_logger, perfdocs_sample): + top_dir = perfdocs_sample["top_dir"] + rst_path = perfdocs_sample["index"] + setup_sample_logger(logger, structured_logger, top_dir) + + # Replace the target string to invalid Keyword for test + with open(rst_path, "r") as file: + filedata = file.read() + + filedata = filedata.replace("documentation", "Invalid Keyword") + + with open(rst_path, "w") as file: + file.write(filedata) + + from perfdocs.verifier import Verifier + + verifier = Verifier("top_dir") + valid = verifier.validate_rst_content(rst_path) + + expected = ( + "Cannot find a '{documentation}' entry in the given index file", + rst_path, + ) + args, _ = logger.warning.call_args + + assert logger.warning.call_count == 1 + assert args == expected + assert not valid + + +@mock.patch("perfdocs.logger.PerfDocLogger") +def test_perfdocs_verifier_validate_descriptions_pass( + logger, structured_logger, perfdocs_sample +): + top_dir = perfdocs_sample["top_dir"] + setup_sample_logger(logger, structured_logger, top_dir) + + from perfdocs.verifier import Verifier + + verifier = Verifier(top_dir) + verifier._check_framework_descriptions(verifier._gatherer.perfdocs_tree[0]) + + assert logger.warning.call_count == 0 + assert logger.log.call_count == 1 + assert len(logger.mock_calls) == 1 + + +@mock.patch("perfdocs.logger.PerfDocLogger") +def test_perfdocs_verifier_not_existing_suite_in_test_list( + logger, structured_logger, perfdocs_sample +): + top_dir = perfdocs_sample["top_dir"] + manifest_path = perfdocs_sample["manifest"] + setup_sample_logger(logger, structured_logger, top_dir) + + from perfdocs.verifier import Verifier + + verifier = Verifier(top_dir) + os.remove(manifest_path) + verifier._check_framework_descriptions(verifier._gatherer.perfdocs_tree[0]) + + expected = ( + "Could not find an existing suite for suite - bad suite name?", + perfdocs_sample["config"], + ) + args, _ = logger.warning.call_args + + assert logger.warning.call_count == 1 + assert args == expected + + +@mock.patch("perfdocs.logger.PerfDocLogger") +def test_perfdocs_verifier_not_existing_tests_in_suites( + logger, structured_logger, perfdocs_sample +): + top_dir = perfdocs_sample["top_dir"] + setup_sample_logger(logger, structured_logger, top_dir) + + with open(perfdocs_sample["config"], "r") as file: + filedata = file.read() + filedata = filedata.replace("Example", "DifferentName") + with open(perfdocs_sample["config"], "w") as file: + file.write(filedata) + + from perfdocs.verifier import Verifier + + verifier = Verifier(top_dir) + verifier._check_framework_descriptions(verifier._gatherer.perfdocs_tree[0]) + + expected = [ + "Could not find an existing test for DifferentName - bad test name?", + "Could not find a test description for Example", + ] + + assert logger.warning.call_count == 2 + for i, call in enumerate(logger.warning.call_args_list): + args, _ = call + assert args[0] == expected[i] + + +@mock.patch("perfdocs.logger.PerfDocLogger") +def test_perfdocs_verifier_invalid_dir(logger, structured_logger, perfdocs_sample): + top_dir = perfdocs_sample["top_dir"] + setup_sample_logger(logger, structured_logger, top_dir) + + from perfdocs.verifier import Verifier + + verifier = Verifier("invalid_path") + with pytest.raises(Exception) as exceinfo: + verifier.validate_tree() + + assert str(exceinfo.value) == "No valid perfdocs directories found" + + +@mock.patch("perfdocs.logger.PerfDocLogger") +def test_perfdocs_verifier_file_invalidation( + logger, structured_logger, perfdocs_sample +): + top_dir = perfdocs_sample["top_dir"] + setup_sample_logger(logger, structured_logger, top_dir) + + from perfdocs.verifier import Verifier + + Verifier.validate_yaml = mock.Mock(return_value=False) + verifier = Verifier(top_dir) + with pytest.raises(Exception): + verifier.validate_tree() + + # Check if "File validation error" log is called + # and Called with a log inside perfdocs_tree(). + assert logger.log.call_count == 2 + assert len(logger.mock_calls) == 2 + + +@mock.patch("perfdocs.logger.PerfDocLogger") +def test_perfdocs_framework_gatherers(logger, structured_logger, perfdocs_sample): + top_dir = perfdocs_sample["top_dir"] + setup_sample_logger(logger, structured_logger, top_dir) + + # Check to make sure that every single framework + # gatherer that has been implemented produces a test list + # in every suite that contains a test with an associated + # manifest. + from perfdocs.gatherer import frameworks + + for framework, gatherer in frameworks.items(): + with open(perfdocs_sample["config"], "w") as f: + f.write(DYNAMIC_SAMPLE_CONFIG.format(framework)) + + fg = gatherer(perfdocs_sample["config"], top_dir) + if getattr(fg, "get_test_list", None) is None: + # Skip framework gatherers that have not + # implemented a method to build a test list. + continue + + # Setup some framework-specific things here if needed + if framework == "raptor": + fg._manifest_path = perfdocs_sample["manifest"] + fg._get_subtests_from_ini = mock.Mock() + fg._get_subtests_from_ini.return_value = { + "Example": perfdocs_sample["manifest"] + } + + for suite, suitetests in fg.get_test_list().items(): + assert suite == "suite" + for test, manifest in suitetests.items(): + assert test == "Example" + assert manifest == perfdocs_sample["manifest"] + + +def test_perfdocs_logger_failure(config, paths): + from perfdocs.logger import PerfDocLogger + + PerfDocLogger.LOGGER = None + with pytest.raises(Exception): + PerfDocLogger() + + PerfDocLogger.PATHS = [] + with pytest.raises(Exception): + PerfDocLogger() + + +if __name__ == "__main__": + mozunit.main() diff --git a/tools/lint/test/test_pylint.py b/tools/lint/test/test_pylint.py new file mode 100644 index 0000000000..6ee2217089 --- /dev/null +++ b/tools/lint/test/test_pylint.py @@ -0,0 +1,24 @@ +import mozunit + +LINTER = "pylint" + + +def test_lint_single_file(lint, paths): + results = lint(paths("bad.py")) + assert len(results) == 3 + assert results[1].rule == "E0602" + assert results[2].rule == "W0101" + assert results[2].lineno == 5 + + # run lint again to make sure the previous results aren't counted twice + results = lint(paths("bad.py")) + assert len(results) == 3 + + +def test_lint_single_file_good(lint, paths): + results = lint(paths("good.py")) + assert len(results) == 0 + + +if __name__ == "__main__": + mozunit.main() diff --git a/tools/lint/test/test_rst.py b/tools/lint/test/test_rst.py new file mode 100644 index 0000000000..532ac332d6 --- /dev/null +++ b/tools/lint/test/test_rst.py @@ -0,0 +1,25 @@ +import pytest +import mozunit +from mozfile import which + +LINTER = "rst" +pytestmark = pytest.mark.skipif( + not which("rstcheck"), reason="rstcheck is not installed" +) + + +def test_basic(lint, paths): + results = lint(paths()) + assert len(results) == 2 + + assert "Title underline too short" in results[0].message + assert results[0].level == "error" + assert results[0].relpath == "bad.rst" + + assert "Title overline & underline mismatch" in results[1].message + assert results[1].level == "error" + assert results[1].relpath == "bad2.rst" + + +if __name__ == "__main__": + mozunit.main() diff --git a/tools/lint/test/test_rustfmt.py b/tools/lint/test/test_rustfmt.py new file mode 100644 index 0000000000..4eaf333825 --- /dev/null +++ b/tools/lint/test/test_rustfmt.py @@ -0,0 +1,44 @@ +import mozunit + + +LINTER = "rustfmt" + + +def test_good(lint, config, paths): + results = lint(paths("subdir/good.rs")) + print(results) + assert len(results) == 0 + + +def test_basic(lint, config, paths): + results = lint(paths("subdir/bad.rs")) + print(results) + assert len(results) >= 1 + + assert "Reformat rust" in results[0].message + assert results[0].level == "warning" + assert results[0].lineno == 4 + assert "bad.rs" in results[0].path + assert "Print text to the console" in results[0].diff + + +def test_dir(lint, config, paths): + results = lint(paths("subdir/")) + print(results) + assert len(results) >= 4 + + assert "Reformat rust" in results[0].message + assert results[0].level == "warning" + assert results[0].lineno == 4 + assert "bad.rs" in results[0].path + assert "Print text to the console" in results[0].diff + + assert "Reformat rust" in results[1].message + assert results[1].level == "warning" + assert results[1].lineno == 4 + assert "bad2.rs" in results[1].path + assert "Print text to the console" in results[1].diff + + +if __name__ == "__main__": + mozunit.main() diff --git a/tools/lint/test/test_shellcheck.py b/tools/lint/test/test_shellcheck.py new file mode 100644 index 0000000000..aa6fdc4494 --- /dev/null +++ b/tools/lint/test/test_shellcheck.py @@ -0,0 +1,26 @@ +import pytest +import mozunit +from mozfile import which + +LINTER = "shellcheck" +pytestmark = pytest.mark.skipif( + not which("shellcheck"), reason="shellcheck is not installed" +) + + +def test_basic(lint, paths): + results = lint(paths()) + print(results) + assert len(results) == 2 + + assert "hello appears unused" in results[0].message + assert results[0].level == "error" + assert results[0].relpath == "bad.sh" + + assert "Double quote to prevent" in results[1].message + assert results[1].level == "error" + assert results[1].relpath == "bad.sh" + + +if __name__ == "__main__": + mozunit.main() diff --git a/tools/lint/test/test_yaml.py b/tools/lint/test/test_yaml.py new file mode 100644 index 0000000000..63b678d152 --- /dev/null +++ b/tools/lint/test/test_yaml.py @@ -0,0 +1,28 @@ +import mozunit + +LINTER = "yaml" + + +def test_basic(lint, paths): + results = lint(paths()) + + assert len(results) == 3 + + assert "line too long (122 > 80 characters)" in results[0].message + assert results[0].level == "error" + assert "bad.yml" in results[0].relpath + assert results[0].lineno == 3 + + assert "wrong indentation: expected 4 but found 8" in results[1].message + assert results[1].level == "error" + assert "bad.yml" in results[1].relpath + assert results[0].lineno == 3 + + assert "could not find expected" in results[2].message + assert results[2].level == "error" + assert "bad.yml" in results[2].relpath + assert results[2].lineno == 9 + + +if __name__ == "__main__": + mozunit.main() |