summaryrefslogtreecommitdiffstats
path: root/tests/lint_tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests/lint_tests')
-rw-r--r--tests/lint_tests/conftest.py41
-rw-r--r--tests/lint_tests/lint_tutil.py60
-rw-r--r--tests/lint_tests/test_lint_changelog.py90
-rw-r--r--tests/lint_tests/test_lint_dctrl.py143
-rw-r--r--tests/lint_tests/test_lint_debputy.py207
5 files changed, 480 insertions, 61 deletions
diff --git a/tests/lint_tests/conftest.py b/tests/lint_tests/conftest.py
index d08f5ca..2c54eb7 100644
--- a/tests/lint_tests/conftest.py
+++ b/tests/lint_tests/conftest.py
@@ -1,16 +1,22 @@
import pytest
+from debian.debian_support import DpkgArchTable
-from debputy.lsp.lsp_features import lsp_set_plugin_features
-from debputy.plugin.api.feature_set import PluginProvidedFeatureSet
+from debputy._deb_options_profiles import DebBuildOptionsAndProfiles
+from debputy.architecture_support import DpkgArchitectureBuildProcessValuesTable
+from debputy.packages import DctrlParser
from debputy.util import setup_logging
try:
from lsprotocol.types import Diagnostic
+ from debputy.lsp.spellchecking import disable_spellchecking
HAS_LSPROTOCOL = True
except ImportError:
HAS_LSPROTOCOL = False
+ def disable_spellchecking() -> None:
+ pass
+
@pytest.fixture(scope="session", autouse=True)
def enable_logging() -> None:
@@ -19,12 +25,25 @@ def enable_logging() -> None:
setup_logging(reconfigure_logging=True)
-@pytest.fixture(autouse=True)
-def setup_feature_set(
- debputy_plugin_feature_set: PluginProvidedFeatureSet,
-) -> None:
- lsp_set_plugin_features(debputy_plugin_feature_set)
- try:
- yield
- finally:
- lsp_set_plugin_features(None)
+@pytest.fixture(scope="session", autouse=True)
+def disable_spellchecking_fixture() -> None:
+ # CI/The buildd does not install relevant, so this is mostly about ensuring
+ # consistent behavior between clean and "unclean" build/test environments
+ disable_spellchecking()
+
+
+@pytest.fixture
+def lint_dctrl_parser(
+ dpkg_arch_query: DpkgArchTable,
+ amd64_dpkg_architecture_variables: DpkgArchitectureBuildProcessValuesTable,
+ no_profiles_or_build_options: DebBuildOptionsAndProfiles,
+) -> DctrlParser:
+ return DctrlParser(
+ frozenset(),
+ frozenset(),
+ True,
+ True,
+ amd64_dpkg_architecture_variables,
+ dpkg_arch_query,
+ no_profiles_or_build_options,
+ )
diff --git a/tests/lint_tests/lint_tutil.py b/tests/lint_tests/lint_tutil.py
index d4f654c..83b69fd 100644
--- a/tests/lint_tests/lint_tutil.py
+++ b/tests/lint_tests/lint_tutil.py
@@ -1,9 +1,16 @@
import collections
-from typing import List, Optional, Mapping, Any
+from typing import List, Optional, Mapping, Any, Callable
import pytest
-from debputy.linting.lint_util import LinterImpl, LinterPositionCodec
+from debputy.linting.lint_util import (
+ LinterImpl,
+ LinterPositionCodec,
+ LintStateImpl,
+ LintState,
+)
+from debputy.packages import DctrlParser
+from debputy.plugin.api.feature_set import PluginProvidedFeatureSet
try:
from lsprotocol.types import Diagnostic, DiagnosticSeverity
@@ -22,13 +29,48 @@ except ImportError:
LINTER_POSITION_CODEC = LinterPositionCodec()
+class LintWrapper:
+
+ def __init__(
+ self,
+ path: str,
+ handler: Callable[[LintState], Optional[List[Diagnostic]]],
+ debputy_plugin_feature_set: PluginProvidedFeatureSet,
+ dctrl_parser: DctrlParser,
+ ) -> None:
+ self._debputy_plugin_feature_set = debputy_plugin_feature_set
+ self._handler = handler
+ self.dctrl_lines: Optional[List[str]] = None
+ self.path = path
+ self._dctrl_parser = dctrl_parser
+
+ def __call__(self, lines: List[str]) -> Optional[List["Diagnostic"]]:
+ source_package = None
+ binary_packages = None
+ dctrl_lines = self.dctrl_lines
+ if dctrl_lines is not None:
+ source_package, binary_packages = (
+ self._dctrl_parser.parse_source_debian_control(
+ dctrl_lines, ignore_errors=True
+ )
+ )
+ state = LintStateImpl(
+ self._debputy_plugin_feature_set,
+ self.path,
+ lines,
+ source_package,
+ binary_packages,
+ )
+ return check_diagnostics(self._handler(state))
+
+
def requires_levenshtein(func: Any) -> Any:
return pytest.mark.skipif(
not HAS_LEVENSHTEIN, reason="Missing python3-levenshtein"
)(func)
-def _check_diagnostics(
+def check_diagnostics(
diagnostics: Optional[List["Diagnostic"]],
) -> Optional[List["Diagnostic"]]:
if diagnostics:
@@ -37,18 +79,6 @@ def _check_diagnostics(
return diagnostics
-def run_linter(
- path: str, lines: List[str], linter: LinterImpl
-) -> Optional[List["Diagnostic"]]:
- uri = f"file://{path}"
- return _check_diagnostics(linter(uri, path, lines, LINTER_POSITION_CODEC))
-
-
-def exactly_one_diagnostic(diagnostics: Optional[List["Diagnostic"]]) -> "Diagnostic":
- assert diagnostics and len(diagnostics) == 1
- return diagnostics[0]
-
-
def by_range_sort_key(diagnostic: Diagnostic) -> Any:
start_pos = diagnostic.range.start
end_pos = diagnostic.range.end
diff --git a/tests/lint_tests/test_lint_changelog.py b/tests/lint_tests/test_lint_changelog.py
new file mode 100644
index 0000000..25dac0e
--- /dev/null
+++ b/tests/lint_tests/test_lint_changelog.py
@@ -0,0 +1,90 @@
+import textwrap
+from typing import List, Optional
+
+import pytest
+
+from debputy.lsp.lsp_debian_changelog import _lint_debian_changelog
+from debputy.lsp.lsp_debian_control import _lint_debian_control
+from debputy.packages import DctrlParser
+from debputy.plugin.api.feature_set import PluginProvidedFeatureSet
+from lint_tests.lint_tutil import (
+ group_diagnostics_by_severity,
+ requires_levenshtein,
+ LintWrapper,
+)
+
+try:
+ from lsprotocol.types import Diagnostic, DiagnosticSeverity
+except ImportError:
+ pass
+
+
+@pytest.fixture
+def line_linter(
+ debputy_plugin_feature_set: PluginProvidedFeatureSet,
+ lint_dctrl_parser: DctrlParser,
+) -> LintWrapper:
+ return LintWrapper(
+ "/nowhere/debian/changelog",
+ _lint_debian_changelog,
+ debputy_plugin_feature_set,
+ lint_dctrl_parser,
+ )
+
+
+def test_dctrl_lint(line_linter: LintWrapper) -> None:
+ lines = textwrap.dedent(
+ """\
+ foo (0.2) unstable; urgency=medium
+
+ * Renamed to foo
+
+ -- Niels Thykier <niels@thykier.net> Mon, 08 Apr 2024 16:00:00 +0000
+
+ bar (0.2) unstable; urgency=medium
+
+ * Initial release
+
+ -- Niels Thykier <niels@thykier.net> Mon, 01 Apr 2024 00:00:00 +0000
+ """
+ ).splitlines(keepends=True)
+
+ diagnostics = line_linter(lines)
+ print(diagnostics)
+ # Without a control file, this is fine
+ assert not diagnostics
+
+ line_linter.dctrl_lines = textwrap.dedent(
+ """\
+ Source: foo
+
+ Package: something-else
+ """
+ )
+
+ diagnostics = line_linter(lines)
+ print(diagnostics)
+ # Also fine, because d/control and d/changelog agrees
+ assert not diagnostics
+
+ line_linter.dctrl_lines = textwrap.dedent(
+ """\
+ Source: bar
+
+ Package: something-else
+ """
+ )
+
+ diagnostics = line_linter(lines)
+ print(diagnostics)
+ # This should be problematic though
+ assert diagnostics and len(diagnostics) == 1
+ diag = diagnostics[0]
+
+ msg = (
+ "The first entry must use the same source name as debian/control."
+ ' Changelog uses: "foo" while d/control uses: "bar"'
+ )
+ assert diag.severity == DiagnosticSeverity.Error
+ assert diag.message == msg
+ assert f"{diag.range}" == "0:0-0:3"
diff --git a/tests/lint_tests/test_lint_dctrl.py b/tests/lint_tests/test_lint_dctrl.py
index cc2758e..e9a5756 100644
--- a/tests/lint_tests/test_lint_dctrl.py
+++ b/tests/lint_tests/test_lint_dctrl.py
@@ -1,14 +1,15 @@
import textwrap
-from typing import List, Optional, Callable
+from typing import List, Optional
import pytest
from debputy.lsp.lsp_debian_control import _lint_debian_control
+from debputy.packages import DctrlParser
+from debputy.plugin.api.feature_set import PluginProvidedFeatureSet
from lint_tests.lint_tutil import (
- run_linter,
group_diagnostics_by_severity,
requires_levenshtein,
- exactly_one_diagnostic,
+ LintWrapper,
)
try:
@@ -17,20 +18,30 @@ except ImportError:
pass
-TestLinter = Callable[[List[str]], Optional[List["Diagnostic"]]]
+class DctrlLintWrapper(LintWrapper):
+ def __call__(self, lines: List[str]) -> Optional[List["Diagnostic"]]:
+ try:
+ self.dctrl_lines = lines
+ return super().__call__(lines)
+ finally:
+ self.dctrl_lines = None
-@pytest.fixture
-def line_linter() -> TestLinter:
- path = "/nowhere/debian/control"
-
- def _linter(lines: List[str]) -> Optional[List["Diagnostic"]]:
- return run_linter(path, lines, _lint_debian_control)
-
- return _linter
-
-def test_dctrl_lint(line_linter: TestLinter) -> None:
+@pytest.fixture
+def line_linter(
+ debputy_plugin_feature_set: PluginProvidedFeatureSet,
+ lint_dctrl_parser: DctrlParser,
+) -> LintWrapper:
+ return DctrlLintWrapper(
+ "/nowhere/debian/control",
+ _lint_debian_control,
+ debputy_plugin_feature_set,
+ lint_dctrl_parser,
+ )
+
+
+def test_dctrl_lint(line_linter: LintWrapper) -> None:
lines = textwrap.dedent(
"""\
Source: foo
@@ -86,7 +97,7 @@ def test_dctrl_lint(line_linter: TestLinter) -> None:
@requires_levenshtein
-def test_dctrl_lint_typos(line_linter: TestLinter) -> None:
+def test_dctrl_lint_typos(line_linter: LintWrapper) -> None:
lines = textwrap.dedent(
"""\
Source: foo
@@ -109,9 +120,109 @@ def test_dctrl_lint_typos(line_linter: TestLinter) -> None:
diagnostics = line_linter(lines)
print(diagnostics)
- diag = exactly_one_diagnostic(diagnostics)
+ assert diagnostics and len(diagnostics) == 1
+ diag = diagnostics[0]
msg = 'The "Build-Dpends" looks like a typo of "Build-Depends".'
assert diag.message == msg
assert diag.severity == DiagnosticSeverity.Warning
assert f"{diag.range}" == "6:0-6:12"
+
+
+@requires_levenshtein
+def test_dctrl_lint_mx_value_with_typo(line_linter: LintWrapper) -> None:
+ lines = textwrap.dedent(
+ """\
+ Source: foo
+ Standards-Version: 4.5.2
+ Priority: optional
+ Section: devel
+ Maintainer: Jane Developer <jane@example.com>
+ Build-Depends: debhelper-compat (= 13)
+
+ Package: foo
+ # Typo of `all`
+ Architecture: linux-any alle
+ Description: Some very interesting synopsis
+ A very interesting description
+ that spans multiple lines
+ .
+ Just so be clear, this is for a test.
+ """
+ ).splitlines(keepends=True)
+
+ diagnostics = line_linter(lines)
+ print(diagnostics)
+ assert len(diagnostics) == 2
+ by_severity = group_diagnostics_by_severity(diagnostics)
+ assert DiagnosticSeverity.Error in by_severity
+ assert DiagnosticSeverity.Warning in by_severity
+
+ typo_diag = by_severity[DiagnosticSeverity.Warning][0]
+ mx_diag = by_severity[DiagnosticSeverity.Error][0]
+ mx_msg = 'The value "all" cannot be used with other values.'
+ typo_msg = 'It is possible that the value is a typo of "all".'
+ assert mx_diag.message == mx_msg
+ assert typo_diag.message == typo_msg
+ assert f"{mx_diag.range}" == "10:24-10:28"
+ assert f"{typo_diag.range}" == "10:24-10:28"
+
+
+def test_dctrl_lint_mx_value(line_linter: LintWrapper) -> None:
+ lines = textwrap.dedent(
+ """\
+ Source: foo
+ Standards-Version: 4.5.2
+ Priority: optional
+ Section: devel
+ Maintainer: Jane Developer <jane@example.com>
+ Build-Depends: debhelper-compat (= 13)
+
+ Package: foo
+ Architecture: all linux-any
+ Description: Some very interesting synopsis
+ A very interesting description
+ that spans multiple lines
+ .
+ Just so be clear, this is for a test.
+ """
+ ).splitlines(keepends=True)
+
+ diagnostics = line_linter(lines)
+ print(diagnostics)
+ assert diagnostics and len(diagnostics) == 1
+ diag = diagnostics[0]
+
+ msg = 'The value "all" cannot be used with other values.'
+ assert diag.message == msg
+ assert diag.severity == DiagnosticSeverity.Error
+ assert f"{diag.range}" == "8:14-8:17"
+
+ lines = textwrap.dedent(
+ """\
+ Source: foo
+ Standards-Version: 4.5.2
+ Priority: optional
+ Section: devel
+ Maintainer: Jane Developer <jane@example.com>
+ Build-Depends: debhelper-compat (= 13)
+
+ Package: foo
+ Architecture: linux-any any
+ Description: Some very interesting synopsis
+ A very interesting description
+ that spans multiple lines
+ .
+ Just so be clear, this is for a test.
+ """
+ ).splitlines(keepends=True)
+
+ diagnostics = line_linter(lines)
+ print(diagnostics)
+ assert diagnostics and len(diagnostics) == 1
+ diag = diagnostics[0]
+
+ msg = 'The value "any" cannot be used with other values.'
+ assert diag.message == msg
+ assert diag.severity == DiagnosticSeverity.Error
+ assert f"{diag.range}" == "8:24-8:27"
diff --git a/tests/lint_tests/test_lint_debputy.py b/tests/lint_tests/test_lint_debputy.py
index 74977d0..9c30392 100644
--- a/tests/lint_tests/test_lint_debputy.py
+++ b/tests/lint_tests/test_lint_debputy.py
@@ -1,13 +1,12 @@
-import textwrap
from typing import List, Optional, Callable
import pytest
-from debputy.lsp.lsp_debian_debputy_manifest import _lint_debian_debputy_manifest
+from debputy.packages import DctrlParser
+from debputy.plugin.api.feature_set import PluginProvidedFeatureSet
from lint_tests.lint_tutil import (
- run_linter,
- group_diagnostics_by_severity,
requires_levenshtein,
+ LintWrapper,
)
try:
@@ -16,20 +15,20 @@ except ImportError:
pass
-TestLinter = Callable[[List[str]], Optional[List["Diagnostic"]]]
-
-
@pytest.fixture
-def line_linter() -> TestLinter:
- path = "/nowhere/debian/debputy.manifest"
-
- def _linter(lines: List[str]) -> Optional[List["Diagnostic"]]:
- return run_linter(path, lines, _lint_debian_debputy_manifest)
-
- return _linter
-
-
-def test_debputy_lint_unknown_keys(line_linter: TestLinter) -> None:
+def line_linter(
+ debputy_plugin_feature_set: PluginProvidedFeatureSet,
+ lint_dctrl_parser: DctrlParser,
+) -> LintWrapper:
+ return LintWrapper(
+ "/nowhere/debian/debputy.manifest",
+ _lint_debian_debputy_manifest,
+ debputy_plugin_feature_set,
+ lint_dctrl_parser,
+ )
+
+
+def test_debputy_lint_unknown_keys(line_linter: LintWrapper) -> None:
lines = textwrap.dedent(
"""\
manifest-version: 0.1
@@ -85,7 +84,7 @@ def test_debputy_lint_unknown_keys(line_linter: TestLinter) -> None:
@requires_levenshtein
-def test_debputy_lint_unknown_keys_spelling(line_linter: TestLinter) -> None:
+def test_debputy_lint_unknown_keys_spelling(line_linter: LintWrapper) -> None:
lines = textwrap.dedent(
"""\
manifest-version: 0.1
@@ -131,7 +130,7 @@ def test_debputy_lint_unknown_keys_spelling(line_linter: TestLinter) -> None:
assert f"{third_error.range}" == "8:6-8:9"
-def test_debputy_lint_conflicting_keys(line_linter: TestLinter) -> None:
+def test_debputy_lint_conflicting_keys(line_linter: LintWrapper) -> None:
lines = textwrap.dedent(
"""\
manifest-version: 0.1
@@ -179,3 +178,173 @@ def test_debputy_lint_conflicting_keys(line_linter: TestLinter) -> None:
msg = 'The "sources" cannot be used with "source".'
assert fourth_error.message == msg
assert f"{fourth_error.range}" == "9:4-9:11"
+
+
+import textwrap
+from typing import List, Optional, Callable
+
+import pytest
+
+from debputy.lsp.lsp_debian_debputy_manifest import _lint_debian_debputy_manifest
+from debputy.packages import DctrlParser
+from debputy.plugin.api.feature_set import PluginProvidedFeatureSet
+from lint_tests.lint_tutil import (
+ group_diagnostics_by_severity,
+ requires_levenshtein,
+ LintWrapper,
+)
+
+try:
+ from lsprotocol.types import Diagnostic, DiagnosticSeverity
+except ImportError:
+ pass
+
+TestLintWrapper = Callable[[List[str]], Optional[List["Diagnostic"]]]
+
+
+@pytest.fixture
+def line_linter(
+ debputy_plugin_feature_set: PluginProvidedFeatureSet,
+ lint_dctrl_parser: DctrlParser,
+) -> LintWrapper:
+ return LintWrapper(
+ "/nowhere/debian/debputy.manifest",
+ _lint_debian_debputy_manifest,
+ debputy_plugin_feature_set,
+ lint_dctrl_parser,
+ )
+
+
+def test_debputy_lint_unknown_keys(line_linter: LintWrapper) -> None:
+ lines = textwrap.dedent(
+ """\
+ manifest-version: 0.1
+ installations:
+ - install-something:
+ sources:
+ - abc
+ - def
+ - install-docs:
+ source: foo
+ puff: true # Unknown keyword (assuming install-docs)
+ when:
+ negated: cross-compiling
+ - install-docs:
+ source: bar
+ when: ross-compiling # Typo of "cross-compiling"; FIXME not caught
+ packages:
+ foo:
+ blah: qwe # Unknown keyword
+ """
+ ).splitlines(keepends=True)
+
+ diagnostics = line_linter(lines)
+ by_severity = group_diagnostics_by_severity(diagnostics)
+ # This example triggers errors only
+ assert DiagnosticSeverity.Error in by_severity
+
+ assert DiagnosticSeverity.Warning not in by_severity
+ assert DiagnosticSeverity.Hint not in by_severity
+ assert DiagnosticSeverity.Information not in by_severity
+
+ errors = by_severity[DiagnosticSeverity.Error]
+ print(errors)
+ assert len(errors) == 4
+
+ first_error, second_error, third_error, fourth_error = errors
+
+ msg = 'Unknown or unsupported key "install-something".'
+ assert first_error.message == msg
+ assert f"{first_error.range}" == "2:2-2:19"
+
+ msg = 'Unknown or unsupported key "puff".'
+ assert second_error.message == msg
+ assert f"{second_error.range}" == "8:4-8:8"
+
+ msg = 'Unknown or unsupported key "negated".'
+ assert third_error.message == msg
+ assert f"{third_error.range}" == "10:6-10:13"
+
+ msg = 'Unknown or unsupported key "blah".'
+ assert fourth_error.message == msg
+ assert f"{fourth_error.range}" == "16:4-16:8"
+
+
+@requires_levenshtein
+def test_debputy_lint_unknown_keys_spelling(line_linter: LintWrapper) -> None:
+ lines = textwrap.dedent(
+ """\
+ manifest-version: 0.1
+ installations:
+ - install-dcoss: # typo
+ sources:
+ - abc
+ - def
+ puff: true # Unknown keyword (assuming install-docs)
+ when:
+ nut: cross-compiling # Typo of "not"
+ - install-docs:
+ source: bar
+ when: ross-compiling # Typo of "cross-compiling"; FIXME not caught
+ """
+ ).splitlines(keepends=True)
+
+ diagnostics = line_linter(lines)
+ by_severity = group_diagnostics_by_severity(diagnostics)
+ # This example triggers errors only
+ assert DiagnosticSeverity.Error in by_severity
+
+ assert DiagnosticSeverity.Warning not in by_severity
+ assert DiagnosticSeverity.Hint not in by_severity
+ assert DiagnosticSeverity.Information not in by_severity
+
+ errors = by_severity[DiagnosticSeverity.Error]
+ print(errors)
+ assert len(errors) == 3
+
+ first_error, second_error, third_error = errors
+
+ msg = 'Unknown or unsupported key "install-dcoss". It looks like a typo of "install-docs".'
+ assert first_error.message == msg
+ assert f"{first_error.range}" == "2:2-2:15"
+
+ msg = 'Unknown or unsupported key "puff".'
+ assert second_error.message == msg
+ assert f"{second_error.range}" == "6:4-6:8"
+
+ msg = 'Unknown or unsupported key "nut". It looks like a typo of "not".'
+ assert third_error.message == msg
+ assert f"{third_error.range}" == "8:6-8:9"
+
+
+def test_debputy_lint_check_package_names(line_linter: LintWrapper) -> None:
+ lines = textwrap.dedent(
+ """\
+ manifest-version: 0.1
+ packages:
+ unknown-package:
+ binary-version: '1:{{DEB_VERSION_UPSTREAM_REVISION}}'
+ """
+ ).splitlines(keepends=True)
+
+ line_linter.dctrl_lines = None
+ diagnostics = line_linter(lines)
+ print(diagnostics)
+ # Does nothing without a control file
+ assert not diagnostics
+
+ line_linter.dctrl_lines = textwrap.dedent(
+ """\
+ Source: foo
+
+ Package: foo
+ """
+ ).splitlines(keepends=True)
+
+ diagnostics = line_linter(lines)
+ assert diagnostics and len(diagnostics) == 1
+ diag = diagnostics[0]
+
+ msg = 'Unknown package "unknown-package".'
+ assert diag.message == msg
+ assert f"{diag.range}" == "2:4-2:19"