1

Regenerate nvim config

This commit is contained in:
2024-06-02 03:29:20 +02:00
parent 75eea0c030
commit ef2e28883d
5576 changed files with 604886 additions and 503 deletions

View File

@ -0,0 +1,30 @@
describe('linter.ansible_lint', function()
it('can parse the output', function()
local parser = require('lint.linters.ansible_lint').parser
local result = parser([[
WARNING Listing 4 violation(s) that are fatal
playbooks/roles/vim/tasks/common.yml:14: git-latest Git checkouts must contain explicit version
playbooks/roles/vim/tasks/common.yml:39: git-latest Git checkouts must contain explicit version
playbooks/roles/vim/tasks/common.yml:52: git-latest Git checkouts must contain explicit version
playbooks/roles/vim/tasks/common.yml:65: no-changed-when Commands should not change things if nothing needs doing
You can skip specific rules or tags by adding them to your configuration file:
# .ansible-lint
warn_list: # or 'skip_list' to silence them completely
- git-latest # Git checkouts must contain explicit version
- no-changed-when # Commands should not change things if nothing needs doing
Finished with 4 failure(s), 0 warning(s) on 1 files.
]])
assert.are.same(4, #result)
local expected = {
source = 'ansible-lint',
message = 'git-latest Git checkouts must contain explicit version',
lnum = 13,
col = 0,
end_lnum = 13,
end_col = 0,
severity = vim.diagnostic.severity.INFO,
}
assert.are.same(expected, result[1])
end)
end)

View File

@ -0,0 +1,6 @@
#!/usr/bin/env python
import sys
sys.stdout.write('tests/both.py:10: foo\n')
sys.stderr.write('tests/both.py:20: bar\n')

View File

@ -0,0 +1,47 @@
describe('linter.checkpatch', function()
it('can parse the output', function()
local parser = require('lint.linters.checkpatch').parser
local bufnr = vim.uri_to_bufnr('file:///foo.c')
local result = parser([[
/foo.c:6: CHECK: Please don't use multiple blank lines
/foo.c:8: WARNING: Missing a blank line after declarations
/foo.c:10: ERROR: switch and case should be at the same indent
]], bufnr)
assert.are.same(3, #result)
local expected_info = {
source = 'checkpatch',
message = 'Please don\'t use multiple blank lines',
lnum = 5,
col = 0,
end_lnum = 5,
end_col = 0,
severity = vim.diagnostic.severity.INFO,
}
assert.are.same(expected_info, result[1])
local expected_warning = {
source = 'checkpatch',
message = 'Missing a blank line after declarations',
lnum = 7,
col = 0,
end_lnum = 7,
end_col = 0,
severity = vim.diagnostic.severity.WARN,
}
assert.are.same(expected_warning, result[2])
local expected_error = {
source = 'checkpatch',
message = 'switch and case should be at the same indent',
lnum = 9,
col = 0,
end_lnum = 9,
end_col = 0,
severity = vim.diagnostic.severity.ERROR,
}
assert.are.same(expected_error, result[3])
end)
end)

View File

@ -0,0 +1,44 @@
describe('linter.checkstyle', function()
it('can parse the output', function()
local parser = require('lint.linters.checkstyle').parser
local result = parser([[
Starting audit...
[WARN] src/main/java/com/foo/bar/ApiClient.java:75:1: 'member def modifier' has incorrect indentation level 0, expected level should be 2. [Indentation]
[ERROR] src/main/java/com/foo/bar/ApiClient.java:187: Line is longer than 120 characters (found 143). [LineLength]
[ERROR] src/main/java/com/foo/bar/ApiClient.java:6:3: 'method def' child has incorrect indentation level 2, expected level should be 4. [Indentation]
Audit done.
Checkstyle ends with 1 errors.
]])
assert.are.same(3, #result)
local expected = {
source = 'checkstyle',
message = "'method def' child has incorrect indentation level 2, expected level should be 4. [Indentation]",
lnum = 5,
col = 2,
end_lnum = 5,
end_col = 2,
severity = vim.diagnostic.severity.ERROR,
}
assert.are.same(expected, result[3])
expected = {
source = 'checkstyle',
message = "Line is longer than 120 characters (found 143). [LineLength]",
lnum = 186,
col = 0,
end_lnum = 186,
end_col = 0,
severity = vim.diagnostic.severity.ERROR,
}
assert.are.same(expected, result[2])
expected = {
source = 'checkstyle',
message = "'member def modifier' has incorrect indentation level 0, expected level should be 2. [Indentation]",
lnum = 74,
col = 0,
end_lnum = 74,
end_col = 0,
severity = vim.diagnostic.severity.WARN
}
assert.are.same(expected, result[1])
end)
end)

View File

@ -0,0 +1,21 @@
describe("linter.commitlint", function()
it("can parse the output", function()
local parser = require("lint.linters.commitlint").parser
local result = parser([[
⧗ input: foo: commitlint parser
✖ type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test] [type-enum]
✖ found 1 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
]])
assert.are.same(1, #result)
local expected = {
source = "commitlint",
message = "type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test] [type-enum]",
lnum = 0,
col = 0,
severity = vim.diagnostic.severity.ERROR,
}
assert.are.same(expected, result[1])
end)
end)

View File

@ -0,0 +1,50 @@
local function get_num_handles()
local pid = vim.fn.getpid()
local output = vim.fn.system({"lsof", "-p"}, tostring(pid))
local lines = vim.split(output, "\n", { plain = true })
return #lines
end
describe('compiler', function()
it('reads errors from both stdout and stderr', function()
local a = vim.api
local bufnr = a.nvim_create_buf(true, true)
a.nvim_buf_set_name(bufnr, "tests/both.py")
a.nvim_set_current_buf(bufnr)
a.nvim_buf_set_option(bufnr, 'errorformat', '%f:%l: %m')
a.nvim_buf_set_option(bufnr, 'makeprg', 'python tests/both.py')
local handles = get_num_handles()
require('lint').try_lint('compiler')
vim.wait(5000, function() return next(vim.diagnostic.get(bufnr)) ~= nil end)
assert.are.same(handles, get_num_handles(), "shouldn't leak any handles")
local result = vim.diagnostic.get(bufnr)
for _, d in pairs(result) do
d.namespace = nil
end
local expected = {
{
bufnr = bufnr,
col = 0,
end_col = 0,
lnum = 9,
end_lnum = 9,
message = 'foo',
severity = 1,
},
{
bufnr = bufnr,
col = 0,
end_col = 0,
end_lnum = 19,
lnum = 19,
message = 'bar',
severity = 1,
},
}
assert.are.same(expected, result)
end)
end)

View File

@ -0,0 +1,41 @@
describe('linter.cppcheck', function()
it('can parse the output', function()
local parser = require('lint.linters.cppcheck').parser
local bufnr = vim.uri_to_bufnr('file:///foo.cpp')
local result = parser([[
/foo.cpp:46:7: [unusedVariable] style: Unused variable: fd
/foo.cpp:366:3: [postfixOperator] performance: Prefer prefix ++/-- operators for non-primitive types.
/foo.cpp:46:{column}: [unusedVariable] style: Unused variable: fd
/foo.cpp:366:{column}: [postfixOperator] performance: Prefer prefix ++/-- operators for non-primitive types.
]], bufnr)
assert.are.same(4, #result)
local expected_1_88 = {
code = 'unusedVariable',
source = 'cppcheck',
message = 'Unused variable: fd',
lnum = 45,
col = 6,
end_lnum = 45,
end_col = 6,
severity = vim.diagnostic.severity.INFO,
user_data = { lsp = { code = 'unusedVariable' } },
}
assert.are.same(expected_1_88, result[1])
local expected_1_34 = {
code = 'unusedVariable',
source = 'cppcheck',
user_data = { lsp = { code = 'unusedVariable' } },
message = 'Unused variable: fd',
lnum = 45,
col = 0,
end_lnum = 45,
end_col = 0,
severity = vim.diagnostic.severity.INFO,
}
assert.are.same(expected_1_34, result[3])
end)
end)

View File

@ -0,0 +1,37 @@
describe('linter.credo', function()
it('can parse the output', function()
local parser = require('lint.linters.credo').parser
-- taken from example screenshot from credo's documentation https://hexdocs.pm/credo/overview.html
-- 3rd record shouldn't get picked up because there is no file/line information
local result = parser([[
[R] → stdin:1:11 Unless conditions should avoid having an `else` block.
[W] ↗ stdin:9:5 Use `reraise` inside a rescue block to preserve the original stacktrace.
[W] ↗ Exception modules should be named consistently. It seems your strategy is to have `Error` ....
]])
assert.are.same(2, #result)
local expected_error = {
col = 10,
end_col = 10,
lnum = 0,
end_lnum = 0,
severity = 1,
message = 'Unless conditions should avoid having an `else` block.',
source = 'credo',
}
assert.are.same(expected_error, result[1])
expected_error = {
col = 4,
end_col = 4,
lnum = 8,
end_lnum = 8,
severity = 2,
message = 'Use `reraise` inside a rescue block to preserve the original stacktrace.',
source = 'credo',
}
assert.are.same(expected_error, result[2])
end)
end)

View File

@ -0,0 +1,58 @@
describe('linter.cspell', function()
it('can parse cspell output', function()
local parser = require('lint.linters.cspell').parser
local bufnr = vim.uri_to_bufnr('file:///foo.txt')
local result = parser([[
/:259:8 - Unknown word (langserver)
/:272:19 - Unknown word (noplugin)
/:278:19 - Unknown word (noplugin)
/:321:2 - Unknown word (checkstyle)
]], bufnr)
assert.are.same(4, #result)
local expected_1 = {
source = 'cspell',
message = 'Unknown word (langserver)',
lnum = 258,
col = 7,
end_lnum = 258,
end_col = 17,
severity = vim.diagnostic.severity.INFO,
}
assert.are.same(expected_1, result[1])
local expected_2 = {
source = 'cspell',
message = 'Unknown word (noplugin)',
lnum = 271,
col = 18,
end_lnum = 271,
end_col = 26,
severity = vim.diagnostic.severity.INFO,
}
assert.are.same(expected_2, result[2])
local expected_3 = {
source = 'cspell',
message = 'Unknown word (noplugin)',
lnum = 277,
col = 18,
end_lnum = 277,
end_col = 26,
severity = vim.diagnostic.severity.INFO,
}
assert.are.same(expected_3, result[3])
local expected_4 = {
source = 'cspell',
message = 'Unknown word (checkstyle)',
lnum = 320,
col = 1,
end_lnum = 320,
end_col = 11,
severity = vim.diagnostic.severity.INFO,
}
assert.are.same(expected_4, result[4])
end)
end)

View File

@ -0,0 +1,23 @@
describe("linter.deadnix", function()
it("can parse the output", function()
local parser = require("lint.linters.deadnix").parser
local result = parser(
[[
{"file":"flake.nix","results":[{"column":17,"endColumn":21,"line":25,"message":"Unused lambda argument: prev"}]}
]],
vim.api.nvim_get_current_buf()
)
assert.are.same(1, #result)
local expected_diagnostics = {
lnum = 24,
end_lnum = 24,
col = 16,
end_col = 21,
message = "Unused lambda argument: prev",
severity = vim.diagnostic.severity.WARN,
}
assert.are.same(expected_diagnostics, result[1])
end)
end)

View File

@ -0,0 +1,175 @@
describe("linter.eslint_d", function()
it("should ignore empty output", function()
local parser = require("lint.linters.eslint_d").parser
assert.are.same({}, parser("", vim.api.nvim_get_current_buf()))
assert.are.same({}, parser(" ", vim.api.nvim_get_current_buf()))
end)
it('should gracefully handle invalid JSON', function()
local parser = require("lint.linters.eslint_d").parser
local json = '{]'
local result = parser(json)
local expected = {
{
lnum = 0,
col = 0,
message = "Could not parse linter output due to: Expected object key string but found T_ARR_END at character 2\noutput: {]",
source = "eslint_d",
}
}
assert.are.same(expected, result)
end)
it('uses 0 defaults for missing line/column', function()
local parser = require("lint.linters.eslint_d").parser
local json = '[{ "messages": [' ..
-- Valid JSON diagnostic.
[[
{
"column": 4,
"endColumn": 8,
"line": 1,
"endLine": 1,
"message": "foo message",
"ruleId": "foo",
"severity": 2
},
]] ..
-- JSON diagnostic with missing line.
[[
{
"column": 4,
"endColumn": 8,
"endLine": 1,
"message": "bar message",
"ruleId": "bar",
"severity": 2
},
]] ..
-- JSON diagnostic with missing column.
[[
{
"endColumn": 8,
"line": 1,
"endLine": 1,
"message": "baz message",
"ruleId": "baz",
"severity": 2
}
]] .. ']}]'
local result = parser(json)
assert.are.same(3, #result)
assert.are.same(0, result[2].lnum)
assert.are.same(0, result[3].col)
end)
it('should show fatal diagnostics on the first line', function()
local parser = require("lint.linters.eslint_d").parser
local json = '[{ "messages": [ { "fatal": true, "message": "fatal", "severity": 2 } ]}]'
local result = parser(json)
assert.are.same(1, #result)
assert.are.same({
col = 0,
lnum = 0,
message = "fatal",
severity = vim.diagnostic.severity.ERROR,
source = "eslint_d"
}, result[1])
end)
it('should parse valid diagnostics', function()
local parser = require("lint.linters.eslint_d").parser
local json = '[{ "messages": [' ..
-- JSON diagnostic with warn severity.
[[
{
"column": 4,
"endColumn": 8,
"line": 12,
"endLine": 14,
"message": "foo message",
"ruleId": "foo",
"severity": 1
},
]] ..
-- JSON diagnostic with error serverity.
[[
{
"column": 16,
"endColumn": 78,
"line": 8,
"endLine": 8,
"message": "bar message",
"ruleId": "bar",
"severity": 2
},
]] ..
-- JSON diagnostic without ruleId.
[[
{
"column": 40,
"endColumn": 78,
"line": 122,
"endLine": 124,
"message": "baz message",
"severity": 2
},
]] ..
-- JSON diagnostic without end values,
[[
{
"column": 36,
"line": 92,
"message": "qux message",
"ruleId": "qux",
"severity": 2
}
]] .. ']}]'
local result = parser(json)
assert.are.same(4, #result)
assert.are.same({
code = "foo",
col = 3,
end_col = 7,
end_lnum = 13,
lnum = 11,
message = "foo message",
severity = vim.diagnostic.severity.WARN,
source = "eslint_d"
}, result[1])
assert.are.same({
code = "bar",
col = 15,
end_col = 77,
end_lnum = 7,
lnum = 7,
message = "bar message",
severity = vim.diagnostic.severity.ERROR,
source = "eslint_d"
}, result[2])
assert.are.same({
col = 39,
end_col = 77,
end_lnum = 123,
lnum = 121,
message = "baz message",
severity = vim.diagnostic.severity.ERROR,
source = "eslint_d"
}, result[3])
assert.are.same({
code = "qux",
col = 35,
lnum = 91,
message = "qux message",
severity = vim.diagnostic.severity.ERROR,
source = "eslint_d"
}, result[4])
end)
end)

View File

@ -0,0 +1,47 @@
describe("linter.hlint", function()
it("can parse an error", function()
-- Main.hs
--
-- (first
--
-- hlint Main.hs --json
local parser = require("lint.linters.hlint").parser
local result = parser([[
[{"module":[],"decl":[],"severity":"Error","hint":"Parse error: possibly incorrect indentation or mismatched brackets","file":"Main.hs","startLine":2,"startColumn":1,"endLine":2,"endColumn":1,"from":" (first\n> \n","to":null,"note":[],"refactorings":"[]"}]
]])
assert.are.same(#result, 1)
local expected = {
lnum = 2,
col = 1,
end_lnum = 2,
end_col = 1,
severity = vim.diagnostic.severity.ERROR,
source = "hlint",
message = "Parse error: possibly incorrect indentation or mismatched brackets",
}
assert.are.same(result[1], expected)
end)
it("can parse a warning", function()
-- Main.hs
--
-- concat (map f x)
--
-- hlint Main.hs --json
local parser = require("lint.linters.hlint").parser
local result = parser([[
[{"module":["Main"],"decl":[],"severity":"Warning","hint":"Use concatMap","file":"Main.hs","startLine":1,"startColumn":1,"endLine":1,"endColumn":17,"from":"concat (map f x)","to":"concatMap f x","note":[],"refactorings":"[Replace {rtype = Expr, pos = SrcSpan {startLine = 1, startCol = 1, endLine = 1, endCol = 17}, subts = [(\"f\",SrcSpan {startLine = 1, startCol = 13, endLine = 1, endCol = 14}),(\"x\",SrcSpan {startLine = 1, startCol = 15, endLine = 1, endCol = 16})], orig = \"concatMap f x\"}]"}]
]])
assert.are.same(#result, 1)
local expected = {
lnum = 1,
col = 1,
end_lnum = 1,
end_col = 17,
severity = vim.diagnostic.severity.WARN,
source = "hlint",
message = "Use concatMap: concatMap f x",
}
assert.are.same(result[1], expected)
end)
end)

View File

@ -0,0 +1,32 @@
describe('linter.jsonlint', function()
it('can parse the output', function()
local parser = require('lint.linters.jsonlint').parser
local result = parser([[
/some/folder/test.json: line 3, col 7, found: 'INVALID' - expected: 'EOF', '}', ':', ',', ']'.
test.json: line 5003, col 11, found: 'INVALID' - expected: 'STRING'.
]], vim.api.nvim_get_current_buf())
assert.are.same(2, #result)
local expected = {
lnum = 2,
end_lnum = 2,
col = 6,
end_col = 6,
message = "found: 'INVALID' - expected: 'EOF', '}', ':', ',', ']'.",
severity = vim.diagnostic.severity.ERROR,
source = "jsonlint",
}
assert.are.same(expected, result[1])
expected = {
lnum = 5002,
end_lnum = 5002,
col = 10,
end_col = 10,
message = "found: 'INVALID' - expected: 'STRING'.",
severity = vim.diagnostic.severity.ERROR,
source = "jsonlint",
}
assert.are.same(expected, result[2])
end)
end)

View File

@ -0,0 +1,55 @@
describe('lint', function()
local a = vim.api
local bufnr = a.nvim_create_buf(true, true)
a.nvim_buf_set_option(bufnr, 'filetype', 'ansible.yaml')
local lint = require('lint')
it('resolves all linters for compound filetypes', function()
lint.linters_by_ft = {
ansible = {'ansible-lint'},
yaml = {'yamllint'},
}
local names = lint._resolve_linter_by_ft('ansible.yaml')
local expected = {'ansible-lint', 'yamllint'}
table.sort(names, function(x, y) return x < y end)
assert.are.same(expected, names)
end)
it('deduplicates linters for compound filetypes', function()
lint.linters_by_ft = {
ansible = {'ansible-lint','yamllint'},
yaml = {'yamllint'},
}
local names = lint._resolve_linter_by_ft('ansible.yaml')
local expected = {'ansible-lint', 'yamllint'}
table.sort(names, function(x, y) return x < y end)
assert.are.same(expected, names)
end)
it("get_running returns running linter", function()
local linter = {
name = "dummy",
cmd = "python",
args = {"tests/loop.py"},
parser = require("lint.parser").from_errorformat("%f:%l: %m")
}
lint.linters.dummy = linter
local orig_lint = lint.lint
---@type lint.LintProc
local captured_proc
---@diagnostic disable-next-line: duplicate-set-field
lint.lint = function(...)
captured_proc = assert(orig_lint(...))
return captured_proc
end
lint.try_lint("dummy")
assert.are.same({"dummy"}, lint.get_running())
assert(captured_proc)
captured_proc:cancel()
vim.wait(500, function() return #lint.get_running() == 0 end)
assert.are.same({}, lint.get_running())
assert.is_false(captured_proc.handle:is_active())
end)
end)

View File

@ -0,0 +1,8 @@
import asyncio
async def sleep():
while True:
await asyncio.sleep(1)
asyncio.run(sleep())

View File

@ -0,0 +1,33 @@
describe('linter.markdownlint', function()
it('can parse the output', function()
local parser = require('lint.linters.markdownlint').parser
local result = parser([[
README.md:35 MD022/blanks-around-headings/blanks-around-headers Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] [Context: "## What's in this repo?"]
README.md:36 MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "- `dotfiles`"]
README.md:47:81 MD013/line-length Line length [Expected: 80; Actual: 114]
README.md:55:81 MD013/line-length Line length [Expected: 80; Actual: 244]
]])
assert.are.same(4, #result)
local expected = {
source = 'markdownlint',
message = 'MD022/blanks-around-headings/blanks-around-headers Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] [Context: "## What\'s in this repo?"]',
lnum = 34,
col = 0,
end_lnum = 34,
end_col = 0,
severity = vim.diagnostic.severity.WARN,
}
assert.are.same(expected, result[1])
expected = {
source = 'markdownlint',
message = 'MD013/line-length Line length [Expected: 80; Actual: 114]',
lnum = 46,
col = 80,
end_lnum = 46,
end_col = 80,
severity = vim.diagnostic.severity.WARN,
}
assert.are.same(expected, result[3])
end)
end)

View File

@ -0,0 +1,50 @@
describe("linter.markuplint", function()
it("can parse the output", function()
local parser = require("lint.linters.markuplint").parser
local output = [[
[
{
"severity": "error",
"message": "The value of the \"id\" attribute is duplicated",
"line": 12,
"col": 8,
"raw": "id=\"root\"",
"ruleId": "id-duplication",
"filePath": "/test/index.html"
},
{
"severity": "warning",
"message": "Require the \"h1\" element",
"line": 1,
"col": 1,
"raw": "<",
"ruleId": "required-h1",
"filePath": "/test/index.html"
}
]
]]
local result = parser(output)
assert.are.same(2, #result)
local expected = {
lnum = 11,
col = 7,
message = 'The value of the "id" attribute is duplicated',
severity = vim.diagnostic.severity.ERROR,
code = "id-duplication",
source = "markuplint",
}
assert.are.same(expected, result[1])
expected = {
lnum = 0,
col = 0,
message = 'Require the "h1" element',
severity = vim.diagnostic.severity.WARN,
code = "required-h1",
source = "markuplint",
}
assert.are.same(expected, result[2])
end)
end)

View File

@ -0,0 +1,2 @@
set rtp=.,../plenary.nvim,$VIMRUNTIME
runtime! plugin/plenary.vim

View File

@ -0,0 +1,47 @@
describe('linter.mlint', function()
it('can parse the output', function()
local parser = require('lint.linters.mlint').parser
local result = parser([[
L 1 (C 1): SCABE: ML5: The McCabe cyclomatic complexity is 2.
L 1 (C 7-9): CLALL: ML0: Using 'clear' with the 'all' option usually decreases code performance and is often unnecessary.
L 7 (C 18): ACABE: ML5: The McCabe cyclomatic complexity of the anonymous function on line 7 is 1.
L 10 (C 48-51): SYNER: ML3: Parse error at recs: usage might be invalid MATLAB syntax.
L 13 (C 1-3): RESWD: ML3: Invalid use of a reserved word.
]], vim.api.nvim_get_current_buf())
assert.are.same(5, #result)
local expected = {
source = 'mlint',
message = 'The McCabe cyclomatic complexity is 2.',
severity = vim.diagnostic.severity.HINT,
lnum = 0,
col = 0,
end_lnum = 0,
end_col = 0,
code = 'SCABE',
user_data = {
lsp = {
code = 'SCABE',
}
},
}
assert.are.same(expected, result[1])
expected = {
source = 'mlint',
message = 'Parse error at recs: usage might be invalid MATLAB syntax.',
severity = vim.diagnostic.severity.ERROR,
lnum = 9,
col = 47,
end_lnum = 9,
end_col = 50,
code = 'SYNER',
user_data = {
lsp = {
code = 'SYNER',
},
},
}
assert.are.same(expected, result[4])
end)
end)

View File

@ -0,0 +1,35 @@
describe('linter.mypy', function()
it('can parse the output', function()
local parser = require('lint.linters.mypy').parser
local bufnr = vim.uri_to_bufnr('file:///foo.py')
local output = [[
/foo.py:10:15:10:20: error: Incompatible return value type (got "str", expected "bool")
/foo.py:20:25:20:30: error: Argument 1 to "foo" has incompatible type "str"; expected "int"
]]
local result = parser(output, bufnr)
assert.are.same(2, #result)
local expected_error = {
source = 'mypy',
message = 'Incompatible return value type (got "str", expected "bool")',
lnum = 9,
col = 14,
end_lnum = 9,
end_col = 20,
severity = vim.diagnostic.severity.ERROR,
}
assert.are.same(expected_error, result[1])
local expected_warning = {
source = 'mypy',
message = 'Argument 1 to "foo" has incompatible type "str"; expected "int"',
lnum = 19,
col = 24,
end_lnum = 19,
end_col = 30,
severity = vim.diagnostic.severity.ERROR,
}
assert.are.same(expected_warning, result[2])
end)
end)

View File

@ -0,0 +1,53 @@
describe('linter.oelint-adv', function()
it('can parse the output', function()
local parser = require('lint.linters.oelint-adv').parser
local bufnr = vim.uri_to_bufnr('file:///foo.bb')
local result = parser([[
/foo.bb:1:error:oelint.var.mandatoryvar.HOMEPAGE:Variable 'HOMEPAGE' should be set
/foo.bb:1:info:oelint.var.suggestedvar.CVE_PRODUCT:Variable 'CVE_PRODUCT' should be set
/foo.bb:2:warning:oelint.vars.spacesassignment:Suggest spaces around variable assignment. E.g. 'FOO = "BAR"'
]], bufnr)
assert.are.same(3, #result)
local expected_error = {
code = 'oelint.var.mandatoryvar.HOMEPAGE',
source = 'oelint-adv',
message = 'Variable \'HOMEPAGE\' should be set',
lnum = 0,
col = 0,
end_lnum = 0,
end_col = 0,
severity = vim.diagnostic.severity.ERROR,
user_data = { lsp = { code = 'oelint.var.mandatoryvar.HOMEPAGE' } },
}
assert.are.same(expected_error, result[1])
local expected_info = {
code = 'oelint.var.suggestedvar.CVE_PRODUCT',
source = 'oelint-adv',
message = 'Variable \'CVE_PRODUCT\' should be set',
lnum = 0,
col = 0,
end_lnum = 0,
end_col = 0,
severity = vim.diagnostic.severity.INFO,
user_data = { lsp = { code = 'oelint.var.suggestedvar.CVE_PRODUCT' } },
}
assert.are.same(expected_info, result[2])
local expected_warning = {
code = 'oelint.vars.spacesassignment',
source = 'oelint-adv',
message = 'Suggest spaces around variable assignment. E.g. \'FOO = "BAR"\'',
lnum = 1,
col = 0,
end_lnum = 1,
end_col = 0,
severity = vim.diagnostic.severity.WARN,
user_data = { lsp = { code = 'oelint.vars.spacesassignment' } },
}
assert.are.same(expected_warning, result[3])
end)
end)

View File

@ -0,0 +1,76 @@
describe('from_errorformat', function()
it('Parses single-line errorformat', function()
local efm = '%f:%l:%c:%t:%n:%m'
local skeleton = { source = 'test_case' }
local parser = require('lint.parser').from_errorformat(efm, skeleton)
local output = [[
dir1/file1.txt:10:15:E:200:Big mistake
dir2/file2.txt:20:25:W:300:Bigger mistake
]]
local result = parser(output)
local expected = {
{
message = 'Big mistake',
lnum = 9,
col = 14,
end_lnum = 9,
end_col = 14,
severity = vim.diagnostic.severity.ERROR,
source = 'test_case',
},
{
message = 'Bigger mistake',
lnum = 19,
col = 24,
end_lnum = 19,
end_col = 24,
severity = vim.diagnostic.severity.WARN,
source = 'test_case',
},
}
assert.are.same(expected, result)
end)
it('Strips newlines and whitespace from error message', function()
-- NOTE: %m on subsequent line of multi-line emf will include a starting
-- newline character
local parser = require('lint.parser').from_errorformat('%W%l,%Z%m')
assert.equals('Big Mistake', parser('10\n \t Big Mistake \t \n')[1].message)
end)
end)
describe('from_pattern', function()
it('Uses source from defaults', function()
local pattern = '(.*):(%d+):(%d+) (.*)'
local groups = { '_', 'lnum', 'col', 'message' }
local defaults = { source = 'test_case' }
local severity_map = nil
local parser = require('lint.parser').from_pattern(pattern, groups, severity_map, defaults)
local output = [[
foo:10:13 Big mistake
bar:209:14 Bigger mistake
]]
local result = parser(output, 0)
local expected = {
{
message = 'Big mistake',
lnum = 9,
col = 12,
end_lnum = 9,
end_col = 12,
severity = vim.diagnostic.severity.ERROR,
source = 'test_case',
},
{
message = 'Bigger mistake',
lnum = 208,
col = 13,
end_lnum = 208,
end_col = 13,
severity = vim.diagnostic.severity.ERROR,
source = 'test_case',
},
}
assert.are.same(expected, result)
end)
end)

View File

@ -0,0 +1,116 @@
describe('linter.php', function()
it("doesn't error on empty output", function()
local parser = require('lint.linters.php').parser
parser('')
parser(' ')
end)
it("handles the default output when there are no errors or warnings", function()
local parser = require('lint.linters.php').parser
local result = parser('No syntax errors detected in Standard input code')
assert.are.same(0, #result)
end)
it("handles warnings in the output", function()
local parser = require('lint.linters.php').parser
local result = parser([[
Warning: The use statement with non-compound name 'Foo' has no effect in Standard input code on line 3
Warning: The use statement with non-compound name 'Bar' has no effect in Standard input code on line 4
No syntax errors detected in Standard input code
]])
assert.are.same(2, #result)
local expected = {
lnum = 2,
end_lnum = 2,
col = 0,
end_col = 0,
message = 'The use statement with non-compound name \'Foo\' has no effect',
source = 'php',
severity = vim.diagnostic.severity.WARN
}
assert.are.same(expected, result[1])
expected = {
lnum = 3,
end_lnum = 3,
col = 0,
end_col = 0,
message = 'The use statement with non-compound name \'Bar\' has no effect',
source = 'php',
severity = vim.diagnostic.severity.WARN
}
assert.are.same(expected, result[2])
end)
it("handles a parse error in the output", function()
local parser = require('lint.linters.php').parser
local result = parser([[
Parse error: syntax error, unexpected token "function", expecting "," or ";" in Standard input code on line 9
Errors parsing Standard input code
]])
assert.are.same(1, #result)
local expected = {
lnum = 8,
end_lnum = 8,
col = 0,
end_col = 0,
message = 'syntax error, unexpected token "function", expecting "," or ";"',
source = 'php',
severity = vim.diagnostic.severity.ERROR
}
assert.are.same(expected, result[1])
end)
it("handles a fatal error in the output", function()
local parser = require('lint.linters.php').parser
local result = parser([[
Fatal error: Unparenthesized `a ? b : c ? d : e` is not supported. Use either `(a ? b : c) ? d : e` or `a ? b : (c ? d : e)` in Standard input code on line 3
Errors parsing Standard input code
]])
assert.are.same(1, #result)
local expected = {
lnum = 2,
end_lnum = 2,
col = 0,
end_col = 0,
message = 'Unparenthesized `a ? b : c ? d : e` is not supported. Use either `(a ? b : c) ? d : e` or `a ? b : (c ? d : e)`',
source = 'php',
severity = vim.diagnostic.severity.ERROR
}
assert.are.same(expected, result[1])
end)
it("handles a deprecation notices in the output", function()
local parser = require('lint.linters.php').parser
local result = parser([[
Deprecated: Optional parameter $a declared before required parameter $b is implicitly treated as a required parameter in Standard input code on line 3
No syntax errors detected in Standard input code
]])
assert.are.same(1, #result)
local expected = {
lnum = 2,
end_lnum = 2,
col = 0,
end_col = 0,
message = 'Optional parameter $a declared before required parameter $b is implicitly treated as a required parameter',
source = 'php',
severity = vim.diagnostic.severity.INFO
}
assert.are.same(expected, result[1])
end)
end)

View File

@ -0,0 +1,101 @@
describe('linter.phpcs', function()
it('ignores empty output', function()
local parser = require('lint.linters.phpcs').parser
assert.are.same({}, parser('', vim.api.nvim_get_current_buf()))
assert.are.same({}, parser(' ', vim.api.nvim_get_current_buf()))
end)
it('parses json output correctly', function()
local parser = require('lint.linters.phpcs').parser
-- json sourced from sample here: https://github.com/squizlabs/PHP_CodeSniffer/wiki/Reporting#printing-a-json-report
-- slightly modified for STDIN result format
local result = parser([[
{
"totals": {
"errors": 4,
"warnings": 1,
"fixable": 3
},
"files": {
"STDIN": {
"errors": 4,
"warnings": 1,
"messages": [
{
"message": "Missing file doc comment",
"source": "PEAR.Commenting.FileComment.Missing",
"severity": 5,
"type": "ERROR",
"line": 2,
"column": 1,
"fixable": false
},
{
"message": "TRUE, FALSE and NULL must be lowercase; expected \"false\" but found \"FALSE\"",
"source": "Generic.PHP.LowerCaseConstant.Found",
"severity": 5,
"type": "ERROR",
"line": 4,
"column": 12,
"fixable": true
},
{
"message": "Line indented incorrectly; expected at least 4 spaces, found 1",
"source": "PEAR.WhiteSpace.ScopeIndent.Incorrect",
"severity": 5,
"type": "ERROR",
"line": 6,
"column": 2,
"fixable": true
},
{
"message": "Missing function doc comment",
"source": "PEAR.Commenting.FunctionComment.Missing",
"severity": 5,
"type": "ERROR",
"line": 9,
"column": 1,
"fixable": false
},
{
"message": "Inline control structures are discouraged",
"source": "Generic.ControlStructures.InlineControlStructure.Discouraged",
"severity": 5,
"type": "WARNING",
"line": 11,
"column": 5,
"fixable": true
}
]
}
}
}
]], vim.api.nvim_get_current_buf())
assert.are.same(5, #result)
local expected = {
lnum = 1,
end_lnum = 1,
col = 0,
end_col = 0,
message = 'Missing file doc comment',
code = 'PEAR.Commenting.FileComment.Missing',
source = 'phpcs',
severity = vim.diagnostic.severity.ERROR
}
assert.are.same(expected, result[1])
expected = {
lnum = 10,
end_lnum = 10,
col = 4,
end_col = 4,
message = 'Inline control structures are discouraged',
code = 'Generic.ControlStructures.InlineControlStructure.Discouraged',
source = 'phpcs',
severity = vim.diagnostic.severity.WARN,
}
assert.are.same(expected, result[5])
end)
end)

View File

@ -0,0 +1,114 @@
describe('linter.phpmd', function()
it("doesn't error on empty output", function()
local parser = require('lint.linters.phpmd').parser
parser('')
parser(' ')
end)
it("handles the default JSON output when there are no coding violations", function()
local parser = require('lint.linters.phpmd').parser
local result = parser([[
{
"version": "@package_version@",
"package": "phpmd",
"timestamp": "2023-07-12T20:09:31+01:00",
"files": []
}
]], vim.api.nvim_get_current_buf())
assert.are.same(0, #result)
end)
it('parses json output correctly', function()
local parser = require('lint.linters.phpmd').parser
local result = parser([[
{
"version": "@package_version@",
"package": "phpmd",
"timestamp": "2023-07-12T19:28:55+01:00",
"files": [
{
"file": "php:\/\/stdin",
"violations": [
{
"beginLine": 11,
"endLine": 326,
"package": "Acme\\Example",
"function": null,
"class": "MyClass",
"method": null,
"description": "The class MyClass has an overall complexity of 61 which is very high. The configured complexity threshold is 50.",
"rule": "ExcessiveClassComplexity",
"ruleSet": "Code Size Rules",
"externalInfoUrl": "https:\/\/phpmd.org\/rules\/codesize.html#excessiveclasscomplexity",
"priority": 3
},
{
"beginLine": 125,
"endLine": 152,
"package": "Acme\\Example",
"function": null,
"class": "MyClass",
"method": "myMethod",
"description": "The method myMethod() has a Cyclomatic Complexity of 10. The configured cyclomatic complexity threshold is 10.",
"rule": "CyclomaticComplexity",
"ruleSet": "Code Size Rules",
"externalInfoUrl": "https:\/\/phpmd.org\/rules\/codesize.html#cyclomaticcomplexity",
"priority": 3
},
{
"beginLine": 146,
"endLine": 146,
"package": null,
"function": null,
"class": null,
"method": null,
"description": "Avoid excessively long variable names like $thisIsAVeryLongVariableName. Keep variable name length under 20.",
"rule": "LongVariable",
"ruleSet": "Naming Rules",
"externalInfoUrl": "https:\/\/phpmd.org\/rules\/naming.html#longvariable",
"priority": 3
}
]
}
]
}
]], vim.api.nvim_get_current_buf())
assert.are.same(3, #result)
local expected = {
lnum = 10,
end_lnum = 325,
col = 0,
end_col = 0,
message = 'The class MyClass has an overall complexity of 61 which is very high. The configured complexity threshold is 50.',
code = 'ExcessiveClassComplexity',
source = 'phpmd',
severity = vim.diagnostic.severity.INFO
}
assert.are.same(expected, result[1])
expected = {
lnum = 124,
end_lnum = 151,
col = 0,
end_col = 0,
message = 'The method myMethod() has a Cyclomatic Complexity of 10. The configured cyclomatic complexity threshold is 10.',
code = 'CyclomaticComplexity',
source = 'phpmd',
severity = vim.diagnostic.severity.INFO,
}
assert.are.same(expected, result[2])
expected = {
lnum = 145,
end_lnum = 145,
col = 0,
end_col = 0,
message = 'Avoid excessively long variable names like $thisIsAVeryLongVariableName. Keep variable name length under 20.',
code = 'LongVariable',
source = 'phpmd',
severity = vim.diagnostic.severity.INFO,
}
assert.are.same(expected, result[3])
end)
end)

View File

@ -0,0 +1,60 @@
local bufnr = vim.uri_to_bufnr('file:///test.php')
describe('linter.phpstan', function()
it("doesn't error on empty output", function()
local parser = require('lint.linters.phpstan').parser
parser('')
parser(' ')
end)
it('parses json output correctly', function()
local parser = require('lint.linters.phpstan').parser
local result = parser(
[[
{
"totals": {
"errors": 0,
"file_errors": 2
},
"files": {
"/test.php": {
"errors": 2,
"messages": [
{
"message": "Ignored error pattern",
"line": null,
"ignorable": false
},
{
"message": "Property never read, only written.",
"line": 6,
"ignorable": true,
"tip": "See: https://phpstan.org/developing-extensions/always-read-written-properties"
}
]
}
},
"errors": []
}
]],
bufnr
)
local expected = {
{
col = 0,
lnum = 0,
message = 'Ignored error pattern',
source = 'phpstan',
},
{
col = 0,
lnum = 5,
message = 'Property never read, only written.',
source = 'phpstan',
},
}
assert.are.same(expected, result)
end)
end)

View File

@ -0,0 +1,85 @@
describe('linter.prisma-lint', function()
it('can parse prisma-lint output', function()
local parser = require('lint.linters.prisma-lint').parser
local result = parser([[
{
"violations": [
{
"ruleName": "model-name-mapping-snake-case",
"message": "Model name must be mapped to \"user\".",
"fileName": "/example/invalid-simple.prisma",
"location": {
"startLine": 1,
"startColumn": 1,
"endLine": 1,
"endColumn": 12
}
},
{
"ruleName": "require-field",
"message": "Missing required fields: \"createdAt\".",
"fileName": "/example/invalid-simple.prisma",
"location": {
"startLine": 1,
"startColumn": 1,
"endLine": 1,
"endColumn": 12
}
},
{
"ruleName": "field-name-mapping-snake-case",
"message": "Field name must be mapped to snake case.",
"fileName": "/example/invalid-simple.prisma",
"location": {
"startLine": 3,
"startColumn": 3,
"endLine": 3,
"endColumn": 8
}
}
]
}
]])
assert.are.same(3, #result)
local expected_1 = {
source = 'prisma-lint',
lnum = 0,
col = 0,
end_lnum = 0,
end_col = 12,
severity = vim.diagnostic.severity.ERROR,
message = 'Model name must be mapped to "user".',
code = 'model-name-mapping-snake-case',
}
assert.are.same(expected_1, result[1])
local expected_2 = {
source = 'prisma-lint',
lnum = 0,
col = 0,
end_lnum = 0,
end_col = 12,
severity = vim.diagnostic.severity.ERROR,
message = 'Missing required fields: "createdAt".',
code = 'require-field',
}
assert.are.same(expected_2, result[2])
local expected_3 = {
source = 'prisma-lint',
lnum = 2,
col = 2,
end_lnum = 2,
end_col = 8,
severity = vim.diagnostic.severity.ERROR,
message = 'Field name must be mapped to snake case.',
code = 'field-name-mapping-snake-case',
}
assert.are.same(expected_3, result[3])
end)
end)

View File

@ -0,0 +1,50 @@
describe('linter.pycodestyle', function()
it("doesn't error on empty output", function()
local parser = require('lint.linters.pycodestyle').parser
parser('', vim.api.nvim_get_current_buf())
parser(' ', vim.api.nvim_get_current_buf())
end)
it('can parse the output', function()
local parser = require('lint.linters.pycodestyle').parser
local result = parser([[
test.py:26:1:E302:expected 2 blank lines, found 1
test.py:37:80:E501:line too long (88 > 79 characters)
test.py:69:48:W291:trailing whitespace
test.py:411:13:E128:continuation line under-indented for visual indent
]], vim.api.nvim_get_current_buf())
assert.are.same(4, #result)
local expected_error = {
code = 'E302',
source = 'pycodestyle',
message = 'expected 2 blank lines, found 1',
lnum = 25,
col = 0,
end_lnum = 25,
end_col = 0,
severity = vim.diagnostic.severity.WARN,
user_data = {
lsp = {
code = 'E302',
}
}
}
assert.are.same(expected_error, result[1])
local expected_warning = {
code = 'W291',
source = 'pycodestyle',
message = 'trailing whitespace',
lnum = 68,
end_lnum = 68,
col = 47,
end_col = 47,
severity = vim.diagnostic.severity.WARN,
user_data = {
lsp = {
code = 'W291',
}
}
}
assert.are.same(expected_warning, result[3])
end)
end)

View File

@ -0,0 +1,41 @@
describe('linter.pydocstyle', function()
it("doesn't error on empty output", function()
local parser = require('lint.linters.pydocstyle').parser
parser('')
parser(' ')
end)
it('can parse the output', function()
local parser = require('lint.linters.pydocstyle').parser
local result = parser([[
test.py:10 in public class `Foo`:
D200: One-line docstring should fit on one line with quotes (found 3)
test.py:20 in public class `Bar`:
D208: Docstring is over-indented
]]
)
assert.are.same(2, #result)
local expected_error = {
source = 'pydocstyle',
message = 'One-line docstring should fit on one line with quotes (found 3)',
lnum = 9,
col = 0,
end_lnum = 9,
end_col = 0,
severity = vim.diagnostic.severity.HINT,
}
assert.are.same(expected_error, result[1])
local expected_warning = {
source = 'pydocstyle',
message = 'Docstring is over-indented',
lnum = 19,
col = 0,
end_lnum = 19,
end_col = 0,
severity = vim.diagnostic.severity.HINT,
}
assert.are.same(expected_warning, result[2])
end)
end)

View File

@ -0,0 +1,130 @@
describe('linter.pylint', function()
it('can parse pylint output', function()
local parser = require('lint.linters.pylint').parser
local bufnr = vim.uri_to_bufnr('file:///two.py')
local result = parser([[
[
{
"type": "warning",
"module": "two",
"obj": "",
"line": 4,
"column": 0,
"path": "/two.py",
"symbol": "bad-indentation",
"message": "Bad indentation. Found 2 spaces, expected 4",
"message-id": "W0311"
},
{
"type": "convention",
"module": "two",
"obj": "",
"line": 1,
"column": 0,
"path": "/two.py",
"symbol": "missing-module-docstring",
"message": "Missing module docstring",
"message-id": "C0114"
},
{
"type": "refactor",
"module": "two",
"obj": "",
"line": 3,
"column": 3,
"path": "/two.py",
"symbol": "comparison-with-itself",
"message": "Redundant comparison - 1 == 1",
"message-id": "R0124"
},
{
"type": "warning",
"module": "two",
"obj": "",
"line": 5,
"column": 4,
"endLine": 5,
"endColumn": 8,
"path": "/two.py",
"symbol": "unused-variable",
"message": "Unused variable 'test'",
"message-id": "W0612"
}
]
]], bufnr)
assert.are.same(4, #result)
local expected_1 = {
source = 'pylint',
message = 'Bad indentation. Found 2 spaces, expected 4 (bad-indentation)',
lnum = 3,
col = 0,
end_lnum = 3,
end_col = 0,
severity = vim.diagnostic.severity.WARN,
code = 'W0311',
user_data = {
lsp = {
code = 'W0311',
},
},
}
assert.are.same(expected_1, result[1])
local expected_2 = {
source = 'pylint',
message = 'Missing module docstring (missing-module-docstring)',
lnum = 0,
col = 0,
end_lnum = 0,
end_col = 0,
severity = vim.diagnostic.severity.HINT,
code = 'C0114',
user_data = {
lsp = {
code = 'C0114',
},
},
}
assert.are.same(expected_2, result[2])
local expected_3 = {
source = 'pylint',
message = 'Redundant comparison - 1 == 1 (comparison-with-itself)',
lnum = 2,
col = 3,
end_lnum = 2,
end_col = 3,
severity = vim.diagnostic.severity.INFO,
code = 'R0124',
user_data = {
lsp = {
code = 'R0124',
},
},
}
assert.are.same(expected_3, result[3])
local expected_4 = {
source = 'pylint',
message = "Unused variable 'test' (unused-variable)",
lnum = 4,
col = 4,
end_lnum = 4,
end_col = 8,
severity = vim.diagnostic.severity.WARN,
code = 'W0612',
user_data = {
lsp = {
code = 'W0612',
},
},
}
assert.are.same(expected_4, result[4])
end)
end)

View File

@ -0,0 +1,33 @@
describe('linter.rpmspec', function()
it('can parse the output', function()
local parser = require('lint.linters.rpmspec').parser
local bufnr = vim.uri_to_bufnr('file:///foo.spec')
local output = [[
warning: Macro expanded in comment on line 1: %{?fedora}
error: line 2: Unknown tag: %if
]]
local result = parser(output, bufnr)
assert.are.same(2, #result)
local expected_warning_1 = {
col = 0,
end_col = 0,
end_lnum = 0,
lnum = 0,
message = 'Macro expanded in comment',
severity = vim.diagnostic.severity.WARN,
source = 'rpmspec',
}
local expected_error_1 = {
col = 0,
end_col = 0,
end_lnum = 1,
lnum = 1,
message = 'Unknown tag: %if',
severity = vim.diagnostic.severity.ERROR,
source = 'rpmspec',
}
assert.are.same(expected_error_1, result[1])
assert.are.same(expected_warning_1, result[2])
end)
end)

View File

@ -0,0 +1,82 @@
describe('linter.rubocop', function()
it('can parse rubocop output', function()
local parser = require('lint.linters.rubocop').parser
local result = parser([[
{
"files": [
{
"path": "test.rb",
"offenses": [
{
"severity": "convention",
"message": "Use snake_case for method names.",
"cop_name": "Naming/MethodName",
"corrected": false,
"correctable": false,
"location": {
"start_line": 3,
"start_column": 5,
"last_line": 3,
"last_column": 11,
"length": 7,
"line": 3,
"column": 5
}
},
{
"severity": "warning",
"message": "Useless assignment to variable - `foo`.",
"cop_name": "Lint/UselessAssignment",
"corrected": false,
"correctable": false,
"location": {
"start_line": 4,
"start_column": 3,
"last_line": 4,
"last_column": 5,
"length": 3,
"line": 4,
"column": 3
}
}
]
}
]
}
]])
assert.are.same(2, #result)
local expected_1 = {
source = 'rubocop',
lnum = 2,
col = 4,
end_lnum = 2,
end_col = 11,
severity = vim.diagnostic.severity.HINT,
message = 'Use snake_case for method names.',
code = 'Naming/MethodName',
}
assert.are.same(expected_1, result[1])
local expected_2 = {
source = 'rubocop',
lnum = 3,
col = 2,
end_lnum = 3,
end_col = 5,
severity = vim.diagnostic.severity.WARN,
message = 'Useless assignment to variable - `foo`.',
code = 'Lint/UselessAssignment',
}
assert.are.same(expected_2, result[2])
end)
it('can handle rubocop excluding the file', function()
local parser = require('lint.linters.rubocop').parser
local result = parser([[{ "files": [] }]])
assert.are.same(0, #result)
end)
end)

View File

@ -0,0 +1,50 @@
describe('linter.ruby', function()
it('can parse the output', function()
local parser = require('lint.linters.ruby').parser
local bufnr = vim.uri_to_bufnr('file:///foo.rb')
local output = [[
/foo.rb:2: warning: key :bar is duplicated and overwritten on line 2
/foo.rb:2: warning: unused literal ignored
/foo.rb:3: syntax error, unexpected end-of-input
]]
local result = parser(output, bufnr)
assert.are.same(3, #result)
local expected_warning_1 = {
col = 0,
end_col = 0,
end_lnum = 1,
lnum = 1,
message = 'key :bar is duplicated and overwritten on line 2',
severity = 2,
source = 'ruby',
}
assert.are.same(expected_warning_1, result[1])
local expected_warning_2 = {
col = 0,
end_col = 0,
end_lnum = 1,
lnum = 1,
message = 'unused literal ignored',
severity = 2,
source = 'ruby',
}
assert.are.same(expected_warning_2, result[2])
local expected_error_1 = {
col = 0,
end_col = 0,
end_lnum = 2,
lnum = 2,
message = 'unexpected end-of-input',
severity = 1,
source = 'ruby',
}
assert.are.same(expected_error_1, result[3])
end)
end)

View File

@ -0,0 +1,84 @@
describe("linter.saltlint", function()
it("can parse the output", function()
local parser = require("lint.linters.saltlint").parser
local result = parser([[
[
{
"id": "903",
"message": "State 'virt.reverted' is deprecated since SaltStack version '2016.3.0'",
"filename": "/tmp/tmpybp_b5bk.sls",
"linenumber": 3,
"line": " virt.reverted:",
"severity": "HIGH"
},
{
"id": "217",
"message": "\"requires\" looks like a typo. Did you mean \"require\"?",
"filename": "/tmp/tmpybp_b5bk.sls",
"linenumber": 9,
"line": " - requires: foo",
"severity": "LOW"
},
{
"id": "201",
"message": "Trailing whitespace",
"filename": "/tmp/tmpybp_b5bk.sls",
"linenumber": 12,
"line": " file.create: ",
"severity": "INFO"
},
{
"id": "204",
"message": "Lines should be no longer than 160 chars",
"filename": "/tmp/tmpybp_b5bk.sls",
"linenumber": 17,
"line": " - name: test_long_line_aaa...",
"severity": "VERY_LOW"
}
]
]])
-- Count of results
assert.are.same(4, #result)
-- JSON diagnostic with HIGH severity
assert.are.same({
lnum = 2,
col = 1,
severity = vim.diagnostic.severity.ERROR,
message = "State 'virt.reverted' is deprecated since SaltStack version '2016.3.0'",
source = "salt-lint",
code = "903",
}, result[1])
-- JSON diagnostic with LOW severity
assert.are.same({
lnum = 8,
col = 1,
severity = vim.diagnostic.severity.WARN,
message = '"requires" looks like a typo. Did you mean "require"?',
source = "salt-lint",
code = "217",
}, result[2])
-- JSON diagnostic with INFO severity
assert.are.same({
lnum = 11,
col = 1,
severity = vim.diagnostic.severity.INFO,
message = "Trailing whitespace",
source = "salt-lint",
code = "201",
}, result[3])
-- JSON diagnostic with VERY_LOW severity
assert.are.same({
lnum = 16,
col = 1,
severity = vim.diagnostic.severity.WARN,
message = "Lines should be no longer than 160 chars",
source = "salt-lint",
code = "204",
}, result[4])
end)
end)

View File

@ -0,0 +1,81 @@
describe("linter.snyk_iac", function()
it("Parses output sample", function()
local parser = require("lint.linters.snyk_iac").parser
local bufnr = vim.uri_to_bufnr("file:///main.tf")
local output = [[
{
"meta": {
"isPrivate": true,
"isLicensesEnabled": false,
"ignoreSettings": {
"adminOnly": true,
"reasonRequired": true,
"disregardFilesystemIgnores": false
},
"org": "",
"orgPublicId": "",
"policy": ""
},
"filesystemPolicy": false,
"vulnerabilities": [],
"dependencyCount": 0,
"licensesPolicy": null,
"ignoreSettings": null,
"targetFile": "main.tf",
"projectName": "example-tf",
"org": "",
"policy": "",
"isPrivate": true,
"targetFilePath": "/home/Projects/tmp/example-tf/main.tf",
"packageManager": "terraformconfig",
"path": "main.tf",
"projectType": "terraformconfig",
"ok": false,
"infrastructureAsCodeIssues": [
{
"id": "SNYK-CC-TF-119",
"title": "IAM Policy grants full administrative rights",
"severity": "medium",
"isIgnored": false,
"subType": "IAM",
"documentation": "https://security.snyk.io/rules/cloud/SNYK-CC-TF-119",
"isGeneratedByCustomRule": false,
"issue": "The IAM Policy grants all permissions to all resources",
"impact": "Any identity with this policy will have full administrative rights in the account",
"resolve": "Set `Actions` and `Resources` attributes to limited subset, e.g `Actions: ['s3:Create*']`",
"remediation": {
"cloudformation": "Set `Actions` and `Resources` attributes to limited subset, e.g `Actions: ['s3:Create*']`",
"terraform": "Set `Actions` and `Resources` attributes to limited subset, e.g `Actions: ['s3:Create*']`"
},
"lineNumber": 20,
"iacDescription": {
"issue": "The IAM Policy grants all permissions to all resources",
"impact": "Any identity with this policy will have full administrative rights in the account",
"resolve": "Set `Actions` and `Resources` attributes to limited subset, e.g `Actions: ['s3:Create*']`"
},
"publicId": "SNYK-CC-TF-119",
"msg": "data.aws_iam_policy_document[foo]",
"references": [
"https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html"
],
"path": ["data", "aws_iam_policy_document[foo]"],
"compliance": []
}
]
}
]]
local result = parser(output, bufnr)
local expected = {
{
source = "snyk",
message = "IAM Policy grants full administrative rights - The IAM Policy grants all permissions to all resources - Any identity with this policy will have full administrative rights in the account",
lnum = 19,
end_lnum = 19,
col = 0,
severity = vim.diagnostic.severity.WARN,
code = "SNYK-CC-TF-119",
},
}
assert.are.same(expected, result)
end)
end)

View File

@ -0,0 +1,79 @@
describe('linter.sqlfluff', function()
it('multi-line output from sqlfluff', function()
local parser = require('lint.linters.sqlfluff').parser
local bufnr = vim.uri_to_bufnr('file:///non-existent.sql')
-- actual output I got from running sqlfluff
local result = parser([[
[{"filepath": "stdin", "violations": [{"start_line_no": 68, "start_line_pos": 1, "code": "L003", "description": "Expected 1 indentation, found 0 [compared to line 52]"}, {"start_line_no": 68, "start_line_pos": 1, "code": "L013", "description": "Column expression without alias. Use explicit `AS` clause."}]}]
]], bufnr)
assert.are.same(2, #result)
local expected = {}
expected[1] = {
source = 'sqlfluff',
message = 'Expected 1 indentation, found 0 [compared to line 52]',
lnum = 67, -- mind the line indexing
col = 0, -- mind the column indexing
severity = vim.diagnostic.severity.ERROR,
user_data = {lsp = {code = 'L003'}},
}
assert.are.same(expected[1], result[1])
expected[2] = {
source = 'sqlfluff',
message = 'Column expression without alias. Use explicit `AS` clause.',
lnum = 67,
col = 0,
severity = vim.diagnostic.severity.ERROR,
user_data = {lsp = {code = 'L013'}},
}
assert.are.same(expected[2], result[2])
end)
it('multi-line output from sqlfluff: old version', function()
local parser = require('lint.linters.sqlfluff').parser
local bufnr = vim.uri_to_bufnr('file:///non-existent.sql')
-- actual output I got from running sqlfluff
local result = parser([[
[{"filepath": "stdin", "violations": [{"line_no": 68, "line_pos": 1, "code": "L003", "description": "Expected 1 indentation, found 0 [compared to line 52]"}, {"line_no": 68, "line_pos": 1, "code": "L013", "description": "Column expression without alias. Use explicit `AS` clause."}]}]
]], bufnr)
assert.are.same(2, #result)
local expected = {}
expected[1] = {
source = 'sqlfluff',
message = 'Expected 1 indentation, found 0 [compared to line 52]',
lnum = 67, -- mind the line indexing
col = 0, -- mind the column indexing
severity = vim.diagnostic.severity.ERROR,
user_data = {lsp = {code = 'L003'}},
}
assert.are.same(expected[1], result[1])
expected[2] = {
source = 'sqlfluff',
message = 'Column expression without alias. Use explicit `AS` clause.',
lnum = 67,
col = 0,
severity = vim.diagnostic.severity.ERROR,
user_data = {lsp = {code = 'L013'}},
}
assert.are.same(expected[2], result[2])
end)
it("bad CLI args end in non-json output", function()
local parser = require('lint.linters.sqlfluff').parser
local bufnr = vim.uri_to_bufnr('file:///non-existent.sql')
-- when problems are encountered, sqlfluff will report with plain text and
-- not a formatted json
local status, _ = pcall(parser, [[
Error: Unknown dialect 'postgresql'
]], bufnr)
-- not breaking should be enough, the parsing error is reported as "problem in first line-col"
assert(status)
end)
end)

View File

@ -0,0 +1,47 @@
describe('linter.staticcheck', function()
it('can parse the output', function()
local parser = require('lint.linters.staticcheck').parser
local bufnr = vim.uri_to_bufnr('file:///main.go')
local result = parser([[
{"code":"S1001","severity":"error","location":{"file":"/main.go","line":8,"column":2},"end":{"file":"/main.go","line":8,"column":23},"message":"should use copy() instead of a loop"}
{"code":"S1002","severity":"error","location":{"file":"/main.go","line":13,"column":5},"end":{"file":"/main.go","line":13,"column":14},"message":"should omit comparison to bool constant, can be simplified to x"}
{"code":"S1002","severity":"error","location":{"file":"/sub.go","line":7,"column":5},"end":{"file":"/sub.go","line":7,"column":14},"message":"should omit comparison to bool constant, can be simplified to y"}
]], bufnr)
assert.are.same(2, #result)
local expected_error_1 = {
code = 'S1001',
col = 1,
end_col = 22,
end_lnum = 7,
lnum = 7,
message = 'should use copy() instead of a loop',
severity = 1,
user_data = {
lsp = {
code = 'S1001'
}
}
}
assert.are.same(expected_error_1, result[1])
local expected_error_2 = {
code = 'S1002',
col = 4,
end_col = 13,
end_lnum = 12,
lnum = 12,
message = 'should omit comparison to bool constant, can be simplified to x',
severity = 1,
user_data = {
lsp = {
code = 'S1002'
}
}
}
assert.are.same(expected_error_2, result[2])
end)
end)

View File

@ -0,0 +1,47 @@
describe("linter.swiftlint", function()
it("doesn't error on empty output", function()
local parser = require("lint.linters.swiftlint")().parser
parser("", vim.api.nvim_get_current_buf())
parser(" ", vim.api.nvim_get_current_buf())
end)
it("can parse the output", function()
local parser = require("lint.linters.swiftlint")().parser
local result = parser(
[[
Linting 'File1.swift' (217/1344)
Linting 'File2.swift' (218/1344)
Linting 'MainViewModel.swift' (219/1344)
/path/to/file/MainViewModel.swift:652:12: warning: File Length Violation: File should contain 500 lines or less: currently contains 652 (file_length)
Linting 'DetailsViewModel.swift.swift' (217/1344)
/path/to/file/DetailsViewModel.swift:12:14: error: Type Body Length Violation: Type body should span 700 lines or less excluding comments and whitespace: currently spans 738 lines (type_body_length)
Linting 'File3.swift' (218/1344)
Linting 'File4.swift' (219/1344)
]],
vim.api.nvim_get_current_buf()
)
assert.are.same(2, #result)
local expected_warning = {
source = "swiftlint",
message = "File Length Violation: File should contain 500 lines or less: currently contains 652 (file_length)",
lnum = 651,
end_lnum = 651,
col = 11,
end_col = 11,
severity = vim.diagnostic.severity.WARN,
}
assert.are.same(expected_warning, result[1])
local expected_error = {
source = "swiftlint",
message = "Type Body Length Violation: Type body should span 700 lines or less excluding comments and whitespace: currently spans 738 lines (type_body_length)",
lnum = 11,
end_lnum = 11,
col = 13,
end_col = 13,
severity = vim.diagnostic.severity.ERROR,
}
assert.are.same(expected_error, result[2])
end)
end)

View File

@ -0,0 +1,53 @@
describe('linter.systemdlint', function()
it('can parse the output', function()
local parser = require('lint.linters.systemdlint').parser
local bufnr = vim.uri_to_bufnr('file:///foo.service')
local result = parser([[
/foo.service:1:error:ExecNotFound:Command referenced not found
/foo.service:2:info:NoFailureCheck:Return-code check is disabled. Errors are not reported
/foo.service:3:warning:ReferencedUnitNotFound:The Unit 'bar.service' referenced was not found in filesystem
]], bufnr)
assert.are.same(3, #result)
local expected_error = {
code = 'ExecNotFound',
source = 'systemdlint',
message = 'Command referenced not found',
lnum = 0,
col = 0,
end_lnum = 0,
end_col = 0,
severity = vim.diagnostic.severity.ERROR,
user_data = { lsp = { code = 'ExecNotFound' } },
}
assert.are.same(expected_error, result[1])
local expected_info = {
code = 'NoFailureCheck',
source = 'systemdlint',
message = 'Return-code check is disabled. Errors are not reported',
lnum = 1,
col = 0,
end_lnum = 1,
end_col = 0,
severity = vim.diagnostic.severity.INFO,
user_data = { lsp = { code = 'NoFailureCheck' } },
}
assert.are.same(expected_info, result[2])
local expected_warning = {
code = 'ReferencedUnitNotFound',
source = 'systemdlint',
message = 'The Unit \'bar.service\' referenced was not found in filesystem',
lnum = 2,
col = 0,
end_lnum = 2,
end_col = 0,
severity = vim.diagnostic.severity.WARN,
user_data = { lsp = { code = 'ReferencedUnitNotFound' } },
}
assert.are.same(expected_warning, result[3])
end)
end)

View File

@ -0,0 +1,22 @@
describe('linter.tflint', function()
it('Parses output sample', function()
local parser = require('lint.linters.tflint').parser
local bufnr = vim.uri_to_bufnr('file:///main.tf')
local result = parser(
[[{"issues":[{"rule":{"name":"terraform_required_providers","severity":"warning","link":"https://github.com/terraform-linters/tflint-ruleset-terraform/blob/v0.2.2/docs/rules/terraform_required_providers.md"},"message":"Missing version constraint for provider \"aws\" in \"required_providers\"","range":{"filename":"main.tf","start":{"line":19,"column":1},"end":{"line":19,"column":15}},"callers":[]}],"errors":[]}]],
bufnr
)
assert.are.same(1, #result)
local expected = {
source = 'tflint',
message = 'Missing version constraint for provider "aws" in "required_providers" (terraform_required_providers)\nReference: https://github.com/terraform-linters/tflint-ruleset-terraform/blob/v0.2.2/docs/rules/terraform_required_providers.md',
lnum = 19,
col = 1,
end_lnum = 19,
end_col = 15,
severity = vim.diagnostic.severity.WARN,
}
assert.are.same(expected, result[1])
end)
end)

View File

@ -0,0 +1,49 @@
describe('linter.tfsec', function()
it('Parses output sample', function()
local parser = require('lint.linters.tfsec').parser
local bufnr = vim.uri_to_bufnr('file:///main.tf')
local output = [[
{
"results": [
{
"rule_id": "AVD-AWS-0065",
"long_id": "aws-kms-auto-rotate-keys",
"rule_description": "A KMS key is not configured to auto-rotate.",
"rule_provider": "aws",
"rule_service": "kms",
"impact": "Long life KMS keys increase the attack surface when compromised",
"resolution": "Configure KMS key to auto rotate",
"links": [
"https://aquasecurity.github.io/tfsec/v1.28.1/checks/aws/kms/auto-rotate-keys/",
"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key#enable_key_rotation"
],
"description": "Key does not have rotation enabled.",
"severity": "MEDIUM",
"warning": false,
"status": 0,
"resource": "aws_kms_key.key",
"location": {
"filename": "/main.tf",
"start_line": 1,
"end_line": 4
}
}
]
}
]]
local result = parser(output, bufnr)
local expected = {
{
source = 'tfsec',
message = "Key does not have rotation enabled. Long life KMS keys increase the attack surface when compromised",
lnum = 0,
end_lnum = 3,
col = 1,
end_col = 4,
severity = vim.diagnostic.severity.WARN,
code = "AVD-AWS-0065",
}
}
assert.are.same(expected, result)
end)
end)

View File

@ -0,0 +1,101 @@
describe("linter.trivy", function()
it("Parses output sample", function()
local parser = require("lint.linters.trivy").parser
local bufnr = vim.uri_to_bufnr("file:///main.tf")
local output = [[
{
"SchemaVersion": 2,
"ArtifactName": "main.tf",
"ArtifactType": "filesystem",
"Metadata": {
"ImageConfig": {
"architecture": "",
"created": "0001-01-01T00:00:00Z",
"os": "",
"rootfs": {
"type": "",
"diff_ids": null
},
"config": {}
}
},
"Results": [
{
"Target": ".",
"Class": "config",
"Type": "terraform",
"MisconfSummary": {
"Successes": 1,
"Failures": 0,
"Exceptions": 0
}
},
{
"Target": "main.tf",
"Class": "config",
"Type": "terraform",
"MisconfSummary": {
"Successes": 0,
"Failures": 1,
"Exceptions": 0
},
"Misconfigurations": [
{
"Type": "Terraform Security Check",
"ID": "AVD-AWS-0065",
"AVDID": "AVD-AWS-0065",
"Title": "A KMS key is not configured to auto-rotate.",
"Description": "You should configure your KMS keys to auto rotate to maintain security and defend against compromise.",
"Message": "Key does not have rotation enabled.",
"Query": "data..",
"Resolution": "Configure KMS key to auto rotate",
"Severity": "MEDIUM",
"PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-0065",
"References": [
"https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html",
"https://avd.aquasec.com/misconfig/avd-aws-0065"
],
"Status": "FAIL",
"Layer": {},
"CauseMetadata": {
"Resource": "aws_kms_key.foo",
"Provider": "AWS",
"Service": "kms",
"StartLine": 15,
"EndLine": 15,
"Code": {
"Lines": [
{
"Number": 15,
"Content": "resource \"aws_kms_key\" \"foo\" {}",
"IsCause": true,
"Annotation": "",
"Truncated": false,
"FirstCause": true,
"LastCause": true
}
]
}
}
}
]
}
]
}
]]
local result = parser(output, bufnr)
local expected = {
{
source = "trivy",
message = "A KMS key is not configured to auto-rotate. You should configure your KMS keys to auto rotate to maintain security and defend against compromise.",
lnum = 14,
end_lnum = 14,
col = 15,
end_col = 15,
severity = vim.diagnostic.severity.WARN,
code = "AVD-AWS-0065",
},
}
assert.are.same(expected, result)
end)
end)

View File

@ -0,0 +1,39 @@
describe('linter.twigcs', function()
it('ignores empty output', function()
local parser = require('lint.linters.twigcs').parser
assert.are.same({}, parser('', vim.api.nvim_get_current_buf()))
assert.are.same({}, parser(' ', vim.api.nvim_get_current_buf()))
end)
it('parses emacs output correctly', function()
local parser = require('lint.linters.twigcs').parser
local result = parser([[/Users/dblanken/code/testcode/test.twig:2:15: error - Should have 1 argument.
/Users/dblanken/code/testcode/test.twig:4:2: error - Another problem.]])
assert.are.same(2, #result)
local expected = {
col = 14,
end_col = 14,
lnum = 1,
end_lnum = 1,
message = 'Should have 1 argument.',
severity = 1,
source = 'twigcs',
}
assert.are.same(expected, result[1])
expected = {
col = 1,
end_col = 1,
lnum = 3,
end_lnum = 3,
message = 'Another problem.',
severity = 1,
source = 'twigcs',
}
assert.are.same(expected, result[2])
end)
end)

View File

@ -0,0 +1,22 @@
describe("lint.util", function()
local util = require("lint.util")
it("wrap can re-map diagnostics from lint result", function()
local cspell = require("lint.linters.cspell")
local custom_cspell = util.wrap(cspell, function(diagnostic)
diagnostic.severity = vim.diagnostic.severity.HINT
return diagnostic
end)
local output = [[
/:259:8 - Unknown word (langserver)
/:272:19 - Unknown word (noplugin)
]]
local bufnr = vim.uri_to_bufnr('file:///foo.txt')
local orig_result = cspell.parser(output, bufnr)
local result = custom_cspell.parser(output, bufnr)
assert.are.same(#result, 2)
assert.are.same(#orig_result, 2)
assert.are.same(result[1].severity, vim.diagnostic.severity.HINT)
assert.are.same(orig_result[1].severity, vim.diagnostic.severity.INFO)
end)
end)

View File

@ -0,0 +1,8 @@
describe('linter.vale', function()
it("doesn't error on empty output", function()
local parser = require('lint.linters.vale').parser
parser('')
parser(' ')
end)
end)

View File

@ -0,0 +1,70 @@
describe('linter.verilator', function()
it('can parse the output', function()
local parser = require('lint.linters.verilator').parser
local result = parser([[
%Warning-DECLFILENAME: t.v:24:8: Filename 't' does not match MODULE name: 'uart'
24 | module uart
| ^~~~
... For warning description see https://verilator.org/warn/DECLFILENAME?v=5.006
... Use "/* verilator lint_off DECLFILENAME */" and lint_on around source to disable this message.
%Warning-PINCONNECTEMPTY: t.v:48:4: Cell pin connected by name with empty reference: 'wr_rst_busy'
48 | .wr_rst_busy(),
| ^~~~~~~~~~~
%Warning-PINCONNECTEMPTY: t.v:49:4: Cell pin connected by name with empty reference: 'rd_rst_busy'
49 | .rd_rst_busy(),
| ^~~~~~~~~~~
%Error: t.v:45:4: Cannot find file containing module: 'tx_fifo'
45 | tx_fifo
| ^~~~~~~
%Error: t.v:45:4: This may be because there's no search path specified with -I<dir>.
45 | tx_fifo
| ^~~~~~~
... Looked in:
tx_fifo
tx_fifo.v
tx_fifo.sv
obj_dir/tx_fifo
obj_dir/tx_fifo.v
obj_dir/tx_fifo.sv
%Error: t.v:64:2: Cannot find file containing module: 'uart_tx'
64 | uart_tx
| ^~~~~~~
%Error: Exiting due to 3 error(s), 3 warning(s)
]], vim.api.nvim_get_current_buf())
assert.are.same(6, #result)
local expected = {
source = 'verilator',
message = 'Filename \'t\' does not match MODULE name: \'uart\'',
severity = vim.diagnostic.severity.WARN,
lnum = 23,
col = 7,
end_lnum = 23,
end_col = 7,
code = 'DECLFILENAME',
user_data = {
lsp = {
code = 'DECLFILENAME',
}
},
}
assert.are.same(expected, result[1])
expected = {
source = 'verilator',
message = 'This may be because there\'s no search path specified with -I<dir>.',
severity = vim.diagnostic.severity.ERROR,
lnum = 44,
col = 3,
end_lnum = 44,
end_col = 3,
code = '',
user_data = {
lsp = {
code = '',
},
},
}
assert.are.same(expected, result[5])
end)
end)