Skip to content

Commit

Permalink
✅ Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
yosh-matsuda committed Sep 1, 2024
1 parent 953aa65 commit a6bcbb1
Show file tree
Hide file tree
Showing 8 changed files with 394 additions and 6 deletions.
5 changes: 3 additions & 2 deletions gitlab_cq/mypy.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import json
from itertools import dropwhile
from pathlib import Path
from typing import Literal, TypedDict

Expand All @@ -20,7 +21,7 @@ class _MypyOutputJson(TypedDict):
def parse(linter_output: str) -> list[GitLabCodeQuality.Issue]:
gitlab_code_quality: list[GitLabCodeQuality.Issue] = []
try:
for line in linter_output.splitlines():
for line in dropwhile(lambda line: not line.startswith("{"), linter_output.splitlines()):
# skip empty lines
if not line:
continue
Expand Down Expand Up @@ -48,6 +49,6 @@ def parse(linter_output: str) -> list[GitLabCodeQuality.Issue]:
GitLabCodeQuality.add_fingerprint(issue)
gitlab_code_quality.append(issue)
except json.JSONDecodeError as e:
raise ValueError("Hint: argument `--output=json` is required\noutput:\n" + linter_output) from e
raise ValueError(e.msg + "\nHint: argument `--output=json` is required\nOutput:\n" + linter_output) from e

return gitlab_code_quality
12 changes: 10 additions & 2 deletions gitlab_cq/pyright.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import json
import re
from pathlib import Path
from typing import Literal, TypedDict

Expand Down Expand Up @@ -41,9 +42,16 @@ class _PyrightOutputJson(TypedDict):


def parse(linter_output: str) -> list[GitLabCodeQuality.Issue]:
# extract JSON body
match = re.search(r"(^{.*|(?<=\n){.*)$", linter_output, re.DOTALL)
if not match:
raise ValueError(
"No JSON body found in the output\n" "Hint: argument `--outputjson` is required\nOutput:\n" + linter_output
)

gitlab_code_quality: list[GitLabCodeQuality.Issue] = []
try:
pyright_output_json: _PyrightOutputJson = json.loads(linter_output)
pyright_output_json: _PyrightOutputJson = json.loads(match.group(0))
for obj in pyright_output_json["generalDiagnostics"]:
issue: GitLabCodeQuality.Issue = {
"type": "issue",
Expand All @@ -69,6 +77,6 @@ def parse(linter_output: str) -> list[GitLabCodeQuality.Issue]:
GitLabCodeQuality.add_fingerprint(issue)
gitlab_code_quality.append(issue)
except json.JSONDecodeError as e:
raise ValueError("Hint: argument `--outputjson` is required\noutput:\n" + linter_output) from e
raise ValueError(e.msg + "\nHint: argument `--outputjson` is required\nOutput:\n" + linter_output) from e

return gitlab_code_quality
13 changes: 11 additions & 2 deletions gitlab_cq/ruff.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import json
import re
from pathlib import Path
from typing import TypedDict

Expand Down Expand Up @@ -37,9 +38,17 @@ class _RuffOutputJson(TypedDict):


def parse(linter_output: str) -> list[GitLabCodeQuality.Issue]:
# extract JSON body
match = re.search(r"(^\[.*|(?<=\n)\[.*)$", linter_output, re.DOTALL)
if not match:
raise ValueError(
"No JSON body found in the output\n"
"Hint: argument `--output-format json` is required and do not set `--output`\nOutput:\n" + linter_output
)

gitlab_code_quality: list[GitLabCodeQuality.Issue] = []
try:
ruff_output_json: list[_RuffOutputJson] = json.loads(linter_output)
ruff_output_json: list[_RuffOutputJson] = json.loads(match.group())
for obj in ruff_output_json:
issue: GitLabCodeQuality.Issue = {
"type": "issue",
Expand All @@ -60,7 +69,7 @@ def parse(linter_output: str) -> list[GitLabCodeQuality.Issue]:
gitlab_code_quality.append(issue)
except json.JSONDecodeError as e:
raise ValueError(
"Hint: argument `--output-format json` is required and do not set `--output`\noutput:\n" + linter_output
"Hint: argument `--output-format json` is required and do not set `--output`\nOutput:\n" + linter_output
) from e

return gitlab_code_quality
18 changes: 18 additions & 0 deletions test/ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
extend = "../pyproject.toml"

[lint.per-file-ignores]
"*.py" = [
# category
"D", # pydocstyle
"S", # flake8-bandit
"ANN", # flake8-annotations
"N", # pep8-naming

# rules
"B017", # assert-raises-exception
"PT011", # pytest-raises-too-broad
"PLR0915", # too-many-statements
"PLR2004", # magic-value-comparison
"PLW2901", # redefined-loop-name
"PLR0912", # too-many-branches
]
98 changes: 98 additions & 0 deletions test/test_gcc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING, Final

import pytest
from gitlab_cq.gcc import parse

if TYPE_CHECKING:
from gitlab_cq import GitLabCodeQuality

gcc_output_str: Final[str] = """{file_path}:453:17: warning: 'switch' missing 'default' label [-Wswitch-default]
453 | switch (xxx)
| ^
1 warning generated.
{file_path}:41:28: warning: implicit conversion changes signedness: 'int' to 'size_type' (aka 'unsigned long') [-Wsign-conversion]
41 | EXPECT_TRUE(xxx[i].size() == N - 2);
| ~~~~~~ ^
{file_path}:1823:50: note: expanded from macro 'EXPECT_TRUE'
1823 | #define EXPECT_TRUE(condition) GTEST_EXPECT_TRUE(condition)
| ^~~~~~~~~
{file_path}:1808:23: note: expanded from macro 'GTEST_EXPECT_TRUE'
1808 | GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
| ^~~~~~~~~
{file_path}:1453:38: note: expanded from macro 'GTEST_TEST_BOOLEAN_'
1453 | ::testing::AssertionResult(expression)) \
| ^~~~~~~~~~
{file_path}:39:24: warning: comparison of integers of different signs: 'int' and 'IndexType' (aka 'unsigned int') [-Wsign-compare]
39 | for (auto i = 1; i < N - 1; i++)
| ~ ^ ~~~~~
2 warnings generated.
In file included from {file_path}:3:
In file included from {file_path}:4:
{file_path}:453:17: warning: 'switch' missing 'default' label [-Wswitch-default]
453 | switch (xxx)
| ^
1 warning generated.
""".replace("{file_path}", str(Path(__file__).resolve()))


gcc_issues: Final[list[GitLabCodeQuality.Issue]] = [
{
"type": "issue",
"check_name": "gcc: -Wswitch-default",
"description": "'switch' missing 'default' label",
"categories": ["Bug Risk"],
"location": {
"path": str(Path(__file__).relative_to(Path.cwd())),
"positions": {"begin": {"line": 453, "column": 17}, "end": {"line": 453, "column": 17}},
},
"severity": "major",
"fingerprint": "9c4775cae5499784d55682db1d384ced",
},
{
"type": "issue",
"check_name": "gcc: -Wsign-conversion",
"description": "implicit conversion changes signedness: 'int' to 'size_type' (aka 'unsigned long')",
"categories": ["Bug Risk"],
"location": {
"path": str(Path(__file__).relative_to(Path.cwd())),
"positions": {"begin": {"line": 41, "column": 28}, "end": {"line": 41, "column": 28}},
},
"severity": "major",
"fingerprint": "ce05830ad965dc8ab362c5ff5ef41655",
},
{
"type": "issue",
"check_name": "gcc: -Wsign-compare",
"description": "comparison of integers of different signs: 'int' and 'IndexType' (aka 'unsigned int')",
"categories": ["Bug Risk"],
"location": {
"path": str(Path(__file__).relative_to(Path.cwd())),
"positions": {"begin": {"line": 39, "column": 24}, "end": {"line": 39, "column": 24}},
},
"severity": "major",
"fingerprint": "1598d3db95670684e5ffa77509e5a7e9",
},
{
"type": "issue",
"check_name": "gcc: -Wswitch-default",
"description": "'switch' missing 'default' label",
"categories": ["Bug Risk"],
"location": {
"path": str(Path(__file__).relative_to(Path.cwd())),
"positions": {"begin": {"line": 453, "column": 17}, "end": {"line": 453, "column": 17}},
},
"severity": "major",
"fingerprint": "9c4775cae5499784d55682db1d384ced",
},
]


def test_gcc_empty() -> None:
assert parse("", "gcc", "major") == []


def test_gcc() -> None:
assert parse(gcc_output_str, "gcc", "major") == gcc_issues
73 changes: 73 additions & 0 deletions test/test_mypy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING, Final

from gitlab_cq.mypy import parse

if TYPE_CHECKING:
from gitlab_cq import GitLabCodeQuality

mypy_json_str: Final[
str
] = """{"file": "{file_path}", "line": 898, "column": 16, "message": "List comprehension has incompatible type List[Result | None]; expected List[Promise]", "hint": null, "code": "misc", "severity": "error"}
{"file": "{file_path}", "line": 890, "column": 16, "message": "List comprehension has incompatible type List[Result | None]; expected List[Promise]", "hint": null, "code": "misc", "severity": "error"}
{"file": "{file_path}", "line": 891, "column": 16, "message": "List comprehension has incompatible type List[Result | None]; expected List[Promise]", "hint": null, "code": "misc", "severity": "error"}
""".replace("{file_path}", str(Path(__file__).resolve().relative_to(Path.cwd())))


mypy_issues: Final[list[GitLabCodeQuality.Issue]] = [
{
"type": "issue",
"check_name": "mypy: misc",
"description": "List comprehension has incompatible type List[Result | None]; expected List[Promise]",
"categories": ["Style"],
"location": {
"path": str(Path(__file__).relative_to(Path.cwd())),
"positions": {"begin": {"line": 898, "column": 16}, "end": {"line": 898, "column": 16}},
},
"severity": "minor",
"fingerprint": "d1776315ccd7cedf0995551a826b22d7",
},
{
"type": "issue",
"check_name": "mypy: misc",
"description": "List comprehension has incompatible type List[Result | None]; expected List[Promise]",
"categories": ["Style"],
"location": {
"path": str(Path(__file__).relative_to(Path.cwd())),
"positions": {"begin": {"line": 890, "column": 16}, "end": {"line": 890, "column": 16}},
},
"severity": "minor",
"fingerprint": "c3c3bdd0e7dc6c09a199dd83964b4472",
},
{
"type": "issue",
"check_name": "mypy: misc",
"description": "List comprehension has incompatible type List[Result | None]; expected List[Promise]",
"categories": ["Style"],
"location": {
"path": str(Path(__file__).relative_to(Path.cwd())),
"positions": {"begin": {"line": 891, "column": 16}, "end": {"line": 891, "column": 16}},
},
"severity": "minor",
"fingerprint": "251d5a6119a57c83dec7c7d970c51e93",
},
]

mypy_json_str_with_warn = (
""" * Install prebuilt node (22.7.0) ../root/.local/share/virtualenvs/amplify-WmOJONDV/lib/python3.12/site-packages/nodeenv.py:639: DeprecationWarning: Python 3.14 will, by default, filter extracted tar archives and reject files or modify their metadata. Use the filter argument to control this behavior.
archive.extractall(src_dir, extract_list)
... done.
"""
+ mypy_json_str
)


def test_mypy_empty() -> None:
assert parse("") == []


def test_mypy() -> None:
assert parse(mypy_json_str) == mypy_issues
assert parse(mypy_json_str_with_warn) == mypy_issues
94 changes: 94 additions & 0 deletions test/test_pyright.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING, Final

import pytest
from gitlab_cq.pyright import parse

if TYPE_CHECKING:
from gitlab_cq import GitLabCodeQuality

pyright_json_str: Final[str] = """{
"version": "1.1.378",
"time": "1725119710426",
"generalDiagnostics": [
{
"file": "{file_path}",
"severity": "error",
"message": "Overload 5 for \\"Test\\" will never be used because its parameters overlap overload 4",
"range": {
"start": {
"line": 6763,
"character": 8
},
"end": {
"line": 6763,
"character": 18
}
},
"rule": "reportOverlappingOverload"
}
],
"summary": {
"filesAnalyzed": 1,
"errorCount": 1,
"warningCount": 0,
"informationCount": 0,
"timeInSec": 7.561
}
}
""".replace("{file_path}", str(Path(__file__).resolve()))

pyright_issues: Final[list[GitLabCodeQuality.Issue]] = [
{
"type": "issue",
"check_name": "Pyright: reportOverlappingOverload",
"description": 'Overload 5 for "Test" will never be used because its parameters overlap overload 4',
"content": {
"body": "[reportOverlappingOverload](https://github.com/microsoft/pyright/blob/main/docs/configuration.md#reportOverlappingOverload)"
},
"categories": ["Style"],
"location": {
"path": str(Path(__file__).relative_to(Path.cwd())),
"positions": {"begin": {"line": 6763, "column": 8}, "end": {"line": 6763, "column": 18}},
},
"severity": "minor",
"fingerprint": "7bcb5e22c26fff8251f5e3eacfeaf269",
}
]

pyright_json_empty: Final[str] = """{
"version": "1.1.378",
"time": "1725158437381",
"generalDiagnostics": [],
"summary": {
"filesAnalyzed": 1,
"errorCount": 0,
"warningCount": 0,
"informationCount": 0,
"timeInSec": 0.628
}
}"""

pyright_json_str_with_warn = (
""" * Install prebuilt node (22.7.0) ../root/.local/share/virtualenvs/amplify-WmOJONDV/lib/python3.12/site-packages/nodeenv.py:639: DeprecationWarning: Python 3.14 will, by default, filter extracted tar archives and reject files or modify their metadata. Use the filter argument to control this behavior.
archive.extractall(src_dir, extract_list)
... done.
"""
+ pyright_json_str
)


def test_pyright_empty() -> None:
with pytest.raises(ValueError):
parse("")


def test_pyright_no_error() -> None:
assert parse(pyright_json_empty) == []


def test_pyright() -> None:
assert parse(pyright_json_str) == pyright_issues
assert parse(pyright_json_str_with_warn) == pyright_issues
Loading

0 comments on commit a6bcbb1

Please sign in to comment.