diff options
Diffstat (limited to 'python/mozlint/test')
22 files changed, 515 insertions, 0 deletions
diff --git a/python/mozlint/test/__init__.py b/python/mozlint/test/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/mozlint/test/__init__.py diff --git a/python/mozlint/test/conftest.py b/python/mozlint/test/conftest.py new file mode 100644 index 000000000..e171798b0 --- /dev/null +++ b/python/mozlint/test/conftest.py @@ -0,0 +1,42 @@ +# 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 os + +import pytest + +from mozlint import LintRoller + + +here = os.path.abspath(os.path.dirname(__file__)) + + +@pytest.fixture +def lint(request): + lintargs = getattr(request.module, 'lintargs', {}) + return LintRoller(root=here, **lintargs) + + +@pytest.fixture(scope='session') +def filedir(): + return os.path.join(here, 'files') + + +@pytest.fixture(scope='module') +def files(filedir, request): + suffix_filter = getattr(request.module, 'files', ['']) + return [os.path.join(filedir, p) for p in os.listdir(filedir) + if any(p.endswith(suffix) for suffix in suffix_filter)] + + +@pytest.fixture(scope='session') +def lintdir(): + return os.path.join(here, 'linters') + + +@pytest.fixture(scope='module') +def linters(lintdir, request): + suffix_filter = getattr(request.module, 'linters', ['.lint']) + return [os.path.join(lintdir, p) for p in os.listdir(lintdir) + if any(p.endswith(suffix) for suffix in suffix_filter)] diff --git a/python/mozlint/test/files/foobar.js b/python/mozlint/test/files/foobar.js new file mode 100644 index 000000000..d9754d0a2 --- /dev/null +++ b/python/mozlint/test/files/foobar.js @@ -0,0 +1,2 @@ +// Oh no.. we called this variable foobar, bad! +var foobar = "a string"; diff --git a/python/mozlint/test/files/foobar.py b/python/mozlint/test/files/foobar.py new file mode 100644 index 000000000..e1677b3fd --- /dev/null +++ b/python/mozlint/test/files/foobar.py @@ -0,0 +1,2 @@ +# Oh no.. we called this variable foobar, bad! +foobar = "a string" diff --git a/python/mozlint/test/files/no_foobar.js b/python/mozlint/test/files/no_foobar.js new file mode 100644 index 000000000..6b95d646c --- /dev/null +++ b/python/mozlint/test/files/no_foobar.js @@ -0,0 +1,2 @@ +// What a relief +var properlyNamed = "a string"; diff --git a/python/mozlint/test/linters/badreturncode.lint b/python/mozlint/test/linters/badreturncode.lint new file mode 100644 index 000000000..398d51a55 --- /dev/null +++ b/python/mozlint/test/linters/badreturncode.lint @@ -0,0 +1,21 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + + +def lint(files, **lintargs): + return 1 + + +LINTER = { + 'name': "BadReturnCodeLinter", + 'description': "Returns an error code no matter what", + 'include': [ + 'files', + ], + 'type': 'external', + 'extensions': ['.js', '.jsm'], + 'payload': lint, +} diff --git a/python/mozlint/test/linters/explicit_path.lint b/python/mozlint/test/linters/explicit_path.lint new file mode 100644 index 000000000..8c1a88a1f --- /dev/null +++ b/python/mozlint/test/linters/explicit_path.lint @@ -0,0 +1,13 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: + +LINTER = { + 'name': "ExplicitPathLinter", + 'description': "Only lint a specific file name", + 'rule': 'no-foobar', + 'include': [ + 'no_foobar.js', + ], + 'type': 'string', + 'payload': 'foobar', +} diff --git a/python/mozlint/test/linters/external.lint b/python/mozlint/test/linters/external.lint new file mode 100644 index 000000000..dcae419db --- /dev/null +++ b/python/mozlint/test/linters/external.lint @@ -0,0 +1,30 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +from mozlint import result + + +def lint(files, **lintargs): + results = [] + for path in files: + with open(path, 'r') as fh: + for i, line in enumerate(fh.readlines()): + if 'foobar' in line: + results.append(result.from_linter( + LINTER, path=path, lineno=i+1, column=1, rule="no-foobar")) + return results + + +LINTER = { + 'name': "ExternalLinter", + 'description': "It's bad to have the string foobar in js files.", + 'include': [ + 'files', + ], + 'type': 'external', + 'extensions': ['.js', '.jsm'], + 'payload': lint, +} diff --git a/python/mozlint/test/linters/invalid_exclude.lint b/python/mozlint/test/linters/invalid_exclude.lint new file mode 100644 index 000000000..be6d0045c --- /dev/null +++ b/python/mozlint/test/linters/invalid_exclude.lint @@ -0,0 +1,10 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: + +LINTER = { + 'name': "BadExcludeLinter", + 'description': "Has an invalid exclude directive.", + 'exclude': [0, 1], # should be a list of strings + 'type': 'string', + 'payload': 'foobar', +} diff --git a/python/mozlint/test/linters/invalid_extension.lnt b/python/mozlint/test/linters/invalid_extension.lnt new file mode 100644 index 000000000..3cb8153a0 --- /dev/null +++ b/python/mozlint/test/linters/invalid_extension.lnt @@ -0,0 +1,9 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: + +LINTER = { + 'name': "BadExtensionLinter", + 'description': "Has an invalid file extension.", + 'type': 'string', + 'payload': 'foobar', +} diff --git a/python/mozlint/test/linters/invalid_include.lint b/python/mozlint/test/linters/invalid_include.lint new file mode 100644 index 000000000..343d5e195 --- /dev/null +++ b/python/mozlint/test/linters/invalid_include.lint @@ -0,0 +1,10 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: + +LINTER = { + 'name': "BadIncludeLinter", + 'description': "Has an invalid include directive.", + 'include': 'should be a list', + 'type': 'string', + 'payload': 'foobar', +} diff --git a/python/mozlint/test/linters/invalid_type.lint b/python/mozlint/test/linters/invalid_type.lint new file mode 100644 index 000000000..9e5926c5a --- /dev/null +++ b/python/mozlint/test/linters/invalid_type.lint @@ -0,0 +1,9 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: + +LINTER = { + 'name': "BadTypeLinter", + 'description': "Has an invalid type.", + 'type': 'invalid', + 'payload': 'foobar', +} diff --git a/python/mozlint/test/linters/missing_attrs.lint b/python/mozlint/test/linters/missing_attrs.lint new file mode 100644 index 000000000..380512b64 --- /dev/null +++ b/python/mozlint/test/linters/missing_attrs.lint @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: + +LINTER = { + 'name': "MissingAttrsLinter", + 'description': "Missing type and payload", +} diff --git a/python/mozlint/test/linters/missing_definition.lint b/python/mozlint/test/linters/missing_definition.lint new file mode 100644 index 000000000..a84b305d2 --- /dev/null +++ b/python/mozlint/test/linters/missing_definition.lint @@ -0,0 +1,4 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: + +# No LINTER variable diff --git a/python/mozlint/test/linters/raises.lint b/python/mozlint/test/linters/raises.lint new file mode 100644 index 000000000..f17e18733 --- /dev/null +++ b/python/mozlint/test/linters/raises.lint @@ -0,0 +1,19 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +from mozlint.errors import LintException + + +def lint(files, **lintargs): + raise LintException("Oh no something bad happened!") + + +LINTER = { + 'name': "RaisesLinter", + 'description': "Raises an exception", + 'type': 'external', + 'payload': lint, +} diff --git a/python/mozlint/test/linters/regex.lint b/python/mozlint/test/linters/regex.lint new file mode 100644 index 000000000..439cadf36 --- /dev/null +++ b/python/mozlint/test/linters/regex.lint @@ -0,0 +1,15 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: + +LINTER = { + 'name': "RegexLinter", + 'description': "Make sure the string 'foobar' never appears " + "in a js variable file because it is bad.", + 'rule': 'no-foobar', + 'include': [ + '**/*.js', + '**/*.jsm', + ], + 'type': 'regex', + 'payload': 'foobar', +} diff --git a/python/mozlint/test/linters/string.lint b/python/mozlint/test/linters/string.lint new file mode 100644 index 000000000..46bf0e8b8 --- /dev/null +++ b/python/mozlint/test/linters/string.lint @@ -0,0 +1,15 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: + +LINTER = { + 'name': "StringLinter", + 'description': "Make sure the string 'foobar' never appears " + "in browser js files because it is bad.", + 'rule': 'no-foobar', + 'include': [ + '**/*.js', + '**/*.jsm', + ], + 'type': 'string', + 'payload': 'foobar', +} diff --git a/python/mozlint/test/linters/structured.lint b/python/mozlint/test/linters/structured.lint new file mode 100644 index 000000000..e8be8d7b3 --- /dev/null +++ b/python/mozlint/test/linters/structured.lint @@ -0,0 +1,28 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + + +def lint(files, logger, **kwargs): + for path in files: + with open(path, 'r') as fh: + for i, line in enumerate(fh.readlines()): + if 'foobar' in line: + logger.lint_error(path=path, + lineno=i+1, + column=1, + rule="no-foobar") + + +LINTER = { + 'name': "StructuredLinter", + 'description': "It's bad to have the string foobar in js files.", + 'include': [ + 'files', + ], + 'type': 'structured_log', + 'extensions': ['.js', '.jsm'], + 'payload': lint, +} diff --git a/python/mozlint/test/test_formatters.py b/python/mozlint/test/test_formatters.py new file mode 100644 index 000000000..b9e6512b2 --- /dev/null +++ b/python/mozlint/test/test_formatters.py @@ -0,0 +1,90 @@ +# 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/. + +from __future__ import unicode_literals + +import json +import sys +from collections import defaultdict + +import pytest + +from mozlint import ResultContainer +from mozlint import formatters + + +@pytest.fixture +def results(scope='module'): + containers = ( + ResultContainer( + linter='foo', + path='a/b/c.txt', + message="oh no foo", + lineno=1, + ), + ResultContainer( + linter='bar', + path='d/e/f.txt', + message="oh no bar", + hint="try baz instead", + level='warning', + lineno=4, + column=2, + rule="bar-not-allowed", + ), + ResultContainer( + linter='baz', + path='a/b/c.txt', + message="oh no baz", + lineno=4, + source="if baz:", + ), + ) + results = defaultdict(list) + for c in containers: + results[c.path].append(c) + return results + + +def test_stylish_formatter(results): + expected = """ +a/b/c.txt + 1 error oh no foo (foo) + 4 error oh no baz (baz) + +d/e/f.txt + 4:2 warning oh no bar bar-not-allowed (bar) + +\u2716 3 problems (2 errors, 1 warning) +""".strip() + + fmt = formatters.get('stylish', disable_colors=True) + assert expected == fmt(results) + + +def test_treeherder_formatter(results): + expected = """ +TEST-UNEXPECTED-ERROR | a/b/c.txt:1 | oh no foo (foo) +TEST-UNEXPECTED-ERROR | a/b/c.txt:4 | oh no baz (baz) +TEST-UNEXPECTED-WARNING | d/e/f.txt:4:2 | oh no bar (bar-not-allowed) +""".strip() + + fmt = formatters.get('treeherder') + assert expected == fmt(results) + + +def test_json_formatter(results): + fmt = formatters.get('json') + formatted = json.loads(fmt(results)) + + assert set(formatted.keys()) == set(results.keys()) + + slots = ResultContainer.__slots__ + for errors in formatted.values(): + for err in errors: + assert all(s in err for s in slots) + + +if __name__ == '__main__': + sys.exit(pytest.main(['--verbose', __file__])) diff --git a/python/mozlint/test/test_parser.py b/python/mozlint/test/test_parser.py new file mode 100644 index 000000000..e18e7a5a9 --- /dev/null +++ b/python/mozlint/test/test_parser.py @@ -0,0 +1,55 @@ +# 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 os +import sys + +import pytest + +from mozlint.parser import Parser +from mozlint.errors import ( + LinterNotFound, + LinterParseError, +) + + +@pytest.fixture(scope='module') +def parse(lintdir): + parser = Parser() + + def _parse(name): + path = os.path.join(lintdir, name) + return parser(path) + return _parse + + +def test_parse_valid_linter(parse): + lintobj = parse('string.lint') + assert isinstance(lintobj, dict) + assert 'name' in lintobj + assert 'description' in lintobj + assert 'type' in lintobj + assert 'payload' in lintobj + + +@pytest.mark.parametrize('linter', [ + 'invalid_type.lint', + 'invalid_extension.lnt', + 'invalid_include.lint', + 'invalid_exclude.lint', + 'missing_attrs.lint', + 'missing_definition.lint', +]) +def test_parse_invalid_linter(parse, linter): + with pytest.raises(LinterParseError): + parse(linter) + + +def test_parse_non_existent_linter(parse): + with pytest.raises(LinterNotFound): + parse('missing_file.lint') + + +if __name__ == '__main__': + sys.exit(pytest.main(['--verbose', __file__])) diff --git a/python/mozlint/test/test_roller.py b/python/mozlint/test/test_roller.py new file mode 100644 index 000000000..b4b82c346 --- /dev/null +++ b/python/mozlint/test/test_roller.py @@ -0,0 +1,82 @@ +# 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 os +import sys + +import pytest + +from mozlint import ResultContainer +from mozlint.errors import LintersNotConfigured, LintException + + +here = os.path.abspath(os.path.dirname(__file__)) + + +linters = ('string.lint', 'regex.lint', 'external.lint') + + +def test_roll_no_linters_configured(lint, files): + with pytest.raises(LintersNotConfigured): + lint.roll(files) + + +def test_roll_successful(lint, linters, files): + lint.read(linters) + + result = lint.roll(files) + assert len(result) == 1 + assert lint.return_code == 1 + + path = result.keys()[0] + assert os.path.basename(path) == 'foobar.js' + + errors = result[path] + assert isinstance(errors, list) + assert len(errors) == 6 + + container = errors[0] + assert isinstance(container, ResultContainer) + assert container.rule == 'no-foobar' + + +def test_roll_catch_exception(lint, lintdir, files): + lint.read(os.path.join(lintdir, 'raises.lint')) + + # suppress printed traceback from test output + old_stderr = sys.stderr + sys.stderr = open(os.devnull, 'w') + with pytest.raises(LintException): + lint.roll(files) + sys.stderr = old_stderr + + +def test_roll_with_excluded_path(lint, linters, files): + lint.lintargs.update({'exclude': ['**/foobar.js']}) + + lint.read(linters) + result = lint.roll(files) + + assert len(result) == 0 + assert lint.return_code == 0 + + +def test_roll_with_invalid_extension(lint, lintdir, filedir): + lint.read(os.path.join(lintdir, 'external.lint')) + result = lint.roll(os.path.join(filedir, 'foobar.py')) + assert len(result) == 0 + assert lint.return_code == 0 + + +def test_roll_with_failure_code(lint, lintdir, files): + lint.read(os.path.join(lintdir, 'badreturncode.lint')) + + assert lint.return_code is None + result = lint.roll(files) + assert len(result) == 0 + assert lint.return_code == 1 + + +if __name__ == '__main__': + sys.exit(pytest.main(['--verbose', __file__])) diff --git a/python/mozlint/test/test_types.py b/python/mozlint/test/test_types.py new file mode 100644 index 000000000..ee0ea9b63 --- /dev/null +++ b/python/mozlint/test/test_types.py @@ -0,0 +1,50 @@ +# 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 os +import sys + +import pytest + +from mozlint.result import ResultContainer + + +@pytest.fixture +def path(filedir): + def _path(name): + return os.path.join(filedir, name) + return _path + + +@pytest.fixture(params=['string.lint', 'regex.lint', 'external.lint', 'structured.lint']) +def linter(lintdir, request): + return os.path.join(lintdir, request.param) + + +def test_linter_types(lint, linter, files, path): + lint.read(linter) + result = lint.roll(files) + assert isinstance(result, dict) + assert path('foobar.js') in result + assert path('no_foobar.js') not in result + + result = result[path('foobar.js')][0] + assert isinstance(result, ResultContainer) + + name = os.path.basename(linter).split('.')[0] + assert result.linter.lower().startswith(name) + + +def test_no_filter(lint, lintdir, files): + lint.read(os.path.join(lintdir, 'explicit_path.lint')) + result = lint.roll(files) + assert len(result) == 0 + + lint.lintargs['use_filters'] = False + result = lint.roll(files) + assert len(result) == 2 + + +if __name__ == '__main__': + sys.exit(pytest.main(['--verbose', __file__])) |