diff --git a/.coveragerc b/.coveragerc
index faa494f8e6..ddfcbd3348 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -24,5 +24,5 @@ exclude_lines =
if TYPE_CHECKING:
@overload
- # Abstract methods are not exectued during pytest runs
+ # Abstract methods are not executed during pytest runs
raise NotImplementedError()
diff --git a/.pyenchant_pylint_custom_dict.txt b/.pyenchant_pylint_custom_dict.txt
index 80918ac5aa..825ffe1f8c 100644
--- a/.pyenchant_pylint_custom_dict.txt
+++ b/.pyenchant_pylint_custom_dict.txt
@@ -154,6 +154,7 @@ iterables
iteritems
jn
jpg
+json
jx
jython
# class is a reserved word
diff --git a/pylint/testutils/_primer/primer.py b/pylint/testutils/_primer/primer.py
index 48b3433f20..3a911df4bb 100644
--- a/pylint/testutils/_primer/primer.py
+++ b/pylint/testutils/_primer/primer.py
@@ -6,25 +6,13 @@
import argparse
import json
-import sys
-import warnings
-from io import StringIO
-from itertools import chain
from pathlib import Path
-from typing import Dict, List, Union
-import git
-
-from pylint.lint import Run
-from pylint.reporters import JSONReporter
from pylint.testutils._primer import PackageToLint
-
-MAX_GITHUB_COMMENT_LENGTH = 65536
-
-PackageMessages = Dict[str, List[Dict[str, Union[str, int]]]]
-
-GITHUB_CRASH_TEMPLATE_LOCATION = "/home/runner/.cache"
-CRASH_TEMPLATE_INTRO = "There is a pre-filled template"
+from pylint.testutils._primer.primer_command import PrimerCommand
+from pylint.testutils._primer.primer_compare_command import CompareCommand
+from pylint.testutils._primer.primer_prepare_command import PrepareCommand
+from pylint.testutils._primer.primer_run_command import RunCommand
class Primer:
@@ -90,212 +78,16 @@ def __init__(self, primer_directory: Path, json_path: Path) -> None:
self.packages = self._get_packages_to_lint_from_json(json_path)
"""All packages to prime."""
- def run(self) -> None:
if self.config.command == "prepare":
- self._handle_prepare_command()
+ command_class: type[PrimerCommand] = PrepareCommand
if self.config.command == "run":
- self._handle_run_command()
+ command_class = RunCommand
if self.config.command == "compare":
- self._handle_compare_command()
-
- def _handle_prepare_command(self) -> None:
- commit_string = ""
- if self.config.clone:
- for package, data in self.packages.items():
- local_commit = data.lazy_clone()
- print(f"Cloned '{package}' at commit '{local_commit}'.")
- commit_string += local_commit + "_"
- elif self.config.check:
- for package, data in self.packages.items():
- local_commit = git.Repo(data.clone_directory).head.object.hexsha
- print(f"Found '{package}' at commit '{local_commit}'.")
- commit_string += local_commit + "_"
- elif self.config.make_commit_string:
- for package, data in self.packages.items():
- remote_sha1_commit = (
- git.cmd.Git().ls_remote(data.url, data.branch).split("\t")[0]
- )
- print(f"'{package}' remote is at commit '{remote_sha1_commit}'.")
- commit_string += remote_sha1_commit + "_"
- elif self.config.read_commit_string:
- with open(
- self.primer_directory / "commit_string.txt", encoding="utf-8"
- ) as f:
- print(f.read())
-
- if commit_string:
- with open(
- self.primer_directory / "commit_string.txt", "w", encoding="utf-8"
- ) as f:
- f.write(commit_string)
-
- def _handle_run_command(self) -> None:
- packages: PackageMessages = {}
-
- for package, data in self.packages.items():
- output = self._lint_package(data)
- packages[package] = output
- print(f"Successfully primed {package}.")
-
- astroid_errors = []
- other_fatal_msgs = []
- for msg in chain.from_iterable(packages.values()):
- if msg["type"] == "fatal":
- # Remove the crash template location if we're running on GitHub.
- # We were falsely getting "new" errors when the timestamp changed.
- assert isinstance(msg["message"], str)
- if GITHUB_CRASH_TEMPLATE_LOCATION in msg["message"]:
- msg["message"] = msg["message"].rsplit(CRASH_TEMPLATE_INTRO)[0]
- if msg["symbol"] == "astroid-error":
- astroid_errors.append(msg)
- else:
- other_fatal_msgs.append(msg)
-
- with open(
- self.primer_directory
- / f"output_{'.'.join(str(i) for i in sys.version_info[:3])}_{self.config.type}.txt",
- "w",
- encoding="utf-8",
- ) as f:
- json.dump(packages, f)
-
- # Fail loudly (and fail CI pipelines) if any fatal errors are found,
- # unless they are astroid-errors, in which case just warn.
- # This is to avoid introducing a dependency on bleeding-edge astroid
- # for pylint CI pipelines generally, even though we want to use astroid main
- # for the purpose of diffing emitted messages and generating PR comments.
- if astroid_errors:
- warnings.warn(f"Fatal errors traced to astroid: {astroid_errors}")
- assert not other_fatal_msgs, other_fatal_msgs
-
- def _handle_compare_command(self) -> None:
- with open(self.config.base_file, encoding="utf-8") as f:
- main_dict: PackageMessages = json.load(f)
- with open(self.config.new_file, encoding="utf-8") as f:
- new_dict: PackageMessages = json.load(f)
+ command_class = CompareCommand
+ self.command = command_class(self.primer_directory, self.packages, self.config)
- final_main_dict: PackageMessages = {}
- for package, messages in main_dict.items():
- final_main_dict[package] = []
- for message in messages:
- try:
- new_dict[package].remove(message)
- except ValueError:
- final_main_dict[package].append(message)
-
- self._create_comment(final_main_dict, new_dict)
-
- def _create_comment(
- self, all_missing_messages: PackageMessages, all_new_messages: PackageMessages
- ) -> None:
- comment = ""
- for package, missing_messages in all_missing_messages.items():
- if len(comment) >= MAX_GITHUB_COMMENT_LENGTH:
- break
-
- new_messages = all_new_messages[package]
- package_data = self.packages[package]
-
- if not missing_messages and not new_messages:
- continue
-
- comment += f"\n\n**Effect on [{package}]({self.packages[package].url}):**\n"
-
- # Create comment for new messages
- count = 1
- astroid_errors = 0
- new_non_astroid_messages = ""
- if new_messages:
- print("Now emitted:")
- for message in new_messages:
- filepath = str(message["path"]).replace(
- str(package_data.clone_directory), ""
- )
- # Existing astroid errors may still show up as "new" because the timestamp
- # in the message is slightly different.
- if message["symbol"] == "astroid-error":
- astroid_errors += 1
- else:
- new_non_astroid_messages += (
- f"{count}) {message['symbol']}:\n*{message['message']}*\n"
- f"{package_data.url}/blob/{package_data.branch}{filepath}#L{message['line']}\n"
- )
- print(message)
- count += 1
-
- if astroid_errors:
- comment += (
- f"{astroid_errors} error(s) were found stemming from the `astroid` library. "
- "This is unlikely to have been caused by your changes. "
- "A GitHub Actions warning links directly to the crash report template. "
- "Please open an issue against `astroid` if one does not exist already. \n\n"
- )
- if new_non_astroid_messages:
- comment += (
- "The following messages are now emitted:\n\n\n\n"
- + new_non_astroid_messages
- + "\n \n\n"
- )
-
- # Create comment for missing messages
- count = 1
- if missing_messages:
- comment += (
- "The following messages are no longer emitted:\n\n\n\n"
- )
- print("No longer emitted:")
- for message in missing_messages:
- comment += f"{count}) {message['symbol']}:\n*{message['message']}*\n"
- filepath = str(message["path"]).replace(
- str(package_data.clone_directory), ""
- )
- assert not package_data.url.endswith(
- ".git"
- ), "You don't need the .git at the end of the github url."
- comment += f"{package_data.url}/blob/{package_data.branch}{filepath}#L{message['line']}\n"
- count += 1
- print(message)
- if missing_messages:
- comment += "\n \n\n"
-
- if comment == "":
- comment = (
- "🤖 According to the primer, this change has **no effect** on the"
- " checked open source code. 🤖🎉\n\n"
- )
- else:
- comment = (
- f"🤖 **Effect of this PR on checked open source code:** 🤖\n\n{comment}"
- )
- hash_information = (
- f"*This comment was generated for commit {self.config.commit}*"
- )
- if len(comment) + len(hash_information) >= MAX_GITHUB_COMMENT_LENGTH:
- truncation_information = (
- f"*This comment was truncated because GitHub allows only"
- f" {MAX_GITHUB_COMMENT_LENGTH} characters in a comment.*"
- )
- max_len = (
- MAX_GITHUB_COMMENT_LENGTH
- - len(hash_information)
- - len(truncation_information)
- )
- comment = f"{comment[:max_len - 10]}...\n\n{truncation_information}\n\n"
- comment += hash_information
- with open(self.primer_directory / "comment.txt", "w", encoding="utf-8") as f:
- f.write(comment)
-
- def _lint_package(self, data: PackageToLint) -> list[dict[str, str | int]]:
- # We want to test all the code we can
- enables = ["--enable-all-extensions", "--enable=all"]
- # Duplicate code takes too long and is relatively safe
- # TODO: Find a way to allow cyclic-import and compare output correctly
- disables = ["--disable=duplicate-code,cyclic-import"]
- arguments = data.pylint_args + enables + disables
- output = StringIO()
- reporter = JSONReporter(output)
- Run(arguments, reporter=reporter, exit=False)
- return json.loads(output.getvalue())
+ def run(self) -> None:
+ self.command.run()
@staticmethod
def _get_packages_to_lint_from_json(json_path: Path) -> dict[str, PackageToLint]:
diff --git a/pylint/testutils/_primer/primer_command.py b/pylint/testutils/_primer/primer_command.py
new file mode 100644
index 0000000000..b3a39445f9
--- /dev/null
+++ b/pylint/testutils/_primer/primer_command.py
@@ -0,0 +1,32 @@
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+
+from __future__ import annotations
+
+import abc
+import argparse
+from pathlib import Path
+from typing import Dict, List, Union
+
+from pylint.testutils._primer import PackageToLint
+
+PackageMessages = Dict[str, List[Dict[str, Union[str, int]]]]
+
+
+class PrimerCommand:
+ """Generic primer action with required arguments."""
+
+ def __init__(
+ self,
+ primer_directory: Path,
+ packages: dict[str, PackageToLint],
+ config: argparse.Namespace,
+ ) -> None:
+ self.primer_directory = primer_directory
+ self.packages = packages
+ self.config = config
+
+ @abc.abstractmethod
+ def run(self) -> None:
+ pass
diff --git a/pylint/testutils/_primer/primer_compare_command.py b/pylint/testutils/_primer/primer_compare_command.py
new file mode 100644
index 0000000000..48953b7f1f
--- /dev/null
+++ b/pylint/testutils/_primer/primer_compare_command.py
@@ -0,0 +1,146 @@
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+from __future__ import annotations
+
+import json
+from pathlib import Path
+
+from pylint.testutils._primer.primer_command import PackageMessages, PrimerCommand
+
+MAX_GITHUB_COMMENT_LENGTH = 65536
+
+
+class CompareCommand(PrimerCommand):
+ def run(self) -> None:
+ main_messages = self._load_json(self.config.base_file)
+ pr_messages = self._load_json(self.config.new_file)
+ missing_messages, new_messages = self._cross_reference(
+ main_messages, pr_messages
+ )
+ comment = self._create_comment(missing_messages, new_messages)
+ with open(self.primer_directory / "comment.txt", "w", encoding="utf-8") as f:
+ f.write(comment)
+
+ @staticmethod
+ def _cross_reference(
+ main_dict: PackageMessages, pr_messages: PackageMessages
+ ) -> tuple[PackageMessages, PackageMessages]:
+ missing_messages: PackageMessages = {}
+ for package, messages in main_dict.items():
+ missing_messages[package] = []
+ for message in messages:
+ try:
+ pr_messages[package].remove(message)
+ except ValueError:
+ missing_messages[package].append(message)
+ return missing_messages, pr_messages
+
+ @staticmethod
+ def _load_json(file_path: Path | str) -> PackageMessages:
+ with open(file_path, encoding="utf-8") as f:
+ result: PackageMessages = json.load(f)
+ return result
+
+ def _create_comment(
+ self, all_missing_messages: PackageMessages, all_new_messages: PackageMessages
+ ) -> str:
+ comment = ""
+ for package, missing_messages in all_missing_messages.items():
+ if len(comment) >= MAX_GITHUB_COMMENT_LENGTH:
+ break
+ new_messages = all_new_messages[package]
+ if not missing_messages and not new_messages:
+ continue
+ comment += self._create_comment_for_package(
+ package, new_messages, missing_messages
+ )
+ if comment == "":
+ comment = (
+ "🤖 According to the primer, this change has **no effect** on the"
+ " checked open source code. 🤖🎉\n\n"
+ )
+ else:
+ comment = (
+ f"🤖 **Effect of this PR on checked open source code:** 🤖\n\n{comment}"
+ )
+ return self._truncate_comment(comment)
+
+ def _create_comment_for_package(
+ self, package: str, new_messages, missing_messages
+ ) -> str:
+ comment = f"\n\n**Effect on [{package}]({self.packages[package].url}):**\n"
+ # Create comment for new messages
+ count = 1
+ astroid_errors = 0
+ new_non_astroid_messages = ""
+ if new_messages:
+ print("Now emitted:")
+ for message in new_messages:
+ filepath = str(message["path"]).replace(
+ str(self.packages[package].clone_directory), ""
+ )
+ # Existing astroid errors may still show up as "new" because the timestamp
+ # in the message is slightly different.
+ if message["symbol"] == "astroid-error":
+ astroid_errors += 1
+ else:
+ new_non_astroid_messages += (
+ f"{count}) {message['symbol']}:\n*{message['message']}*\n"
+ f"{self.packages[package].url}/blob/{self.packages[package].branch}{filepath}#L{message['line']}\n"
+ )
+ print(message)
+ count += 1
+
+ if astroid_errors:
+ comment += (
+ f"{astroid_errors} error(s) were found stemming from the `astroid` library. "
+ "This is unlikely to have been caused by your changes. "
+ "A GitHub Actions warning links directly to the crash report template. "
+ "Please open an issue against `astroid` if one does not exist already. \n\n"
+ )
+ if new_non_astroid_messages:
+ comment += (
+ "The following messages are now emitted:\n\n\n\n"
+ + new_non_astroid_messages
+ + "\n \n\n"
+ )
+
+ # Create comment for missing messages
+ count = 1
+ if missing_messages:
+ comment += "The following messages are no longer emitted:\n\n\n\n"
+ print("No longer emitted:")
+ for message in missing_messages:
+ comment += f"{count}) {message['symbol']}:\n*{message['message']}*\n"
+ filepath = str(message["path"]).replace(
+ str(self.packages[package].clone_directory), ""
+ )
+ assert not self.packages[package].url.endswith(
+ ".git"
+ ), "You don't need the .git at the end of the github url."
+ comment += f"{self.packages[package].url}/blob/{self.packages[package].branch}{filepath}#L{message['line']}\n"
+ count += 1
+ print(message)
+ if missing_messages:
+ comment += "\n \n\n"
+ return comment
+
+ def _truncate_comment(self, comment: str) -> str:
+ """GitHub allows only a set number of characters in a comment."""
+ hash_information = (
+ f"*This comment was generated for commit {self.config.commit}*"
+ )
+ if len(comment) + len(hash_information) >= MAX_GITHUB_COMMENT_LENGTH:
+ truncation_information = (
+ f"*This comment was truncated because GitHub allows only"
+ f" {MAX_GITHUB_COMMENT_LENGTH} characters in a comment.*"
+ )
+ max_len = (
+ MAX_GITHUB_COMMENT_LENGTH
+ - len(hash_information)
+ - len(truncation_information)
+ )
+ comment = f"{comment[:max_len - 10]}...\n\n{truncation_information}\n\n"
+ comment += hash_information
+ return comment
diff --git a/pylint/testutils/_primer/primer_prepare_command.py b/pylint/testutils/_primer/primer_prepare_command.py
new file mode 100644
index 0000000000..9fec829bc6
--- /dev/null
+++ b/pylint/testutils/_primer/primer_prepare_command.py
@@ -0,0 +1,41 @@
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+from __future__ import annotations
+
+import git
+
+from pylint.testutils._primer.primer_command import PrimerCommand
+
+
+class PrepareCommand(PrimerCommand):
+ def run(self) -> None:
+ commit_string = ""
+ if self.config.clone:
+ for package, data in self.packages.items():
+ local_commit = data.lazy_clone()
+ print(f"Cloned '{package}' at commit '{local_commit}'.")
+ commit_string += local_commit + "_"
+ elif self.config.check:
+ for package, data in self.packages.items():
+ local_commit = git.Repo(data.clone_directory).head.object.hexsha
+ print(f"Found '{package}' at commit '{local_commit}'.")
+ commit_string += local_commit + "_"
+ elif self.config.make_commit_string:
+ for package, data in self.packages.items():
+ remote_sha1_commit = (
+ git.cmd.Git().ls_remote(data.url, data.branch).split("\t")[0]
+ )
+ print(f"'{package}' remote is at commit '{remote_sha1_commit}'.")
+ commit_string += remote_sha1_commit + "_"
+ elif self.config.read_commit_string:
+ with open(
+ self.primer_directory / "commit_string.txt", encoding="utf-8"
+ ) as f:
+ print(f.read())
+
+ if commit_string:
+ with open(
+ self.primer_directory / "commit_string.txt", "w", encoding="utf-8"
+ ) as f:
+ f.write(commit_string)
diff --git a/pylint/testutils/_primer/primer_run_command.py b/pylint/testutils/_primer/primer_run_command.py
new file mode 100644
index 0000000000..aba2ed115e
--- /dev/null
+++ b/pylint/testutils/_primer/primer_run_command.py
@@ -0,0 +1,72 @@
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+
+from __future__ import annotations
+
+import json
+import sys
+import warnings
+from io import StringIO
+from itertools import chain
+
+from pylint.lint import Run
+from pylint.reporters import JSONReporter
+from pylint.testutils._primer.package_to_lint import PackageToLint
+from pylint.testutils._primer.primer_command import PackageMessages, PrimerCommand
+
+GITHUB_CRASH_TEMPLATE_LOCATION = "/home/runner/.cache"
+CRASH_TEMPLATE_INTRO = "There is a pre-filled template"
+
+
+class RunCommand(PrimerCommand):
+ def run(self) -> None:
+ packages: PackageMessages = {}
+
+ for package, data in self.packages.items():
+ output = self._lint_package(data)
+ packages[package] = output
+ print(f"Successfully primed {package}.")
+
+ astroid_errors = []
+ other_fatal_msgs = []
+ for msg in chain.from_iterable(packages.values()):
+ if msg["type"] == "fatal":
+ # Remove the crash template location if we're running on GitHub.
+ # We were falsely getting "new" errors when the timestamp changed.
+ assert isinstance(msg["message"], str)
+ if GITHUB_CRASH_TEMPLATE_LOCATION in msg["message"]:
+ msg["message"] = msg["message"].rsplit(CRASH_TEMPLATE_INTRO)[0]
+ if msg["symbol"] == "astroid-error":
+ astroid_errors.append(msg)
+ else:
+ other_fatal_msgs.append(msg)
+
+ with open(
+ self.primer_directory
+ / f"output_{'.'.join(str(i) for i in sys.version_info[:3])}_{self.config.type}.txt",
+ "w",
+ encoding="utf-8",
+ ) as f:
+ json.dump(packages, f)
+
+ # Fail loudly (and fail CI pipelines) if any fatal errors are found,
+ # unless they are astroid-errors, in which case just warn.
+ # This is to avoid introducing a dependency on bleeding-edge astroid
+ # for pylint CI pipelines generally, even though we want to use astroid main
+ # for the purpose of diffing emitted messages and generating PR comments.
+ if astroid_errors:
+ warnings.warn(f"Fatal errors traced to astroid: {astroid_errors}")
+ assert not other_fatal_msgs, other_fatal_msgs
+
+ def _lint_package(self, data: PackageToLint) -> list[dict[str, str | int]]:
+ # We want to test all the code we can
+ enables = ["--enable-all-extensions", "--enable=all"]
+ # Duplicate code takes too long and is relatively safe
+ # TODO: Find a way to allow cyclic-import and compare output correctly
+ disables = ["--disable=duplicate-code,cyclic-import"]
+ arguments = data.pylint_args + enables + disables
+ output = StringIO()
+ reporter = JSONReporter(output)
+ Run(arguments, reporter=reporter, exit=False)
+ return json.loads(output.getvalue())
diff --git a/tests/testutils/_primer/fixtures/message_changed/expected_truncated.txt b/tests/testutils/_primer/fixtures/message_changed/expected_truncated.txt
new file mode 100644
index 0000000000..e8db96a3ed
--- /dev/null
+++ b/tests/testutils/_primer/fixtures/message_changed/expected_truncated.txt
@@ -0,0 +1,20 @@
+🤖 **Effect of this PR on checked open source code:** 🤖
+
+
+
+**Effect on [astroid](https://github.com/PyCQA/astroid):**
+The following messages are now emitted:
+
+
+
+1) locally-disabled:
+*Locally disabling redefined-builtin [we added some text in the message] (W0622)*
+https://github.com/PyCQA/astroid/blob/main/astroid/__init__.py#L91
+
+
+
+The fol...
+
+*This comment was truncated because GitHub allows only 500 characters in a comment.*
+
+*This comment was generated for commit v2.14.2*
diff --git a/tests/testutils/_primer/test_primer.py b/tests/testutils/_primer/test_primer.py
index 99ef4bcbdd..3093fa98ee 100644
--- a/tests/testutils/_primer/test_primer.py
+++ b/tests/testutils/_primer/test_primer.py
@@ -3,6 +3,8 @@
# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
"""Test the primer commands. """
+from __future__ import annotations
+
import sys
from pathlib import Path
from unittest.mock import patch
@@ -20,39 +22,70 @@
PRIMER_CURRENT_INTERPRETER = (3, 10)
+DEFAULT_ARGS = ["python tests/primer/__main__.py", "compare", "--commit=v2.14.2"]
+
@pytest.mark.skipif(
sys.platform in {"win32", "darwin"},
- reason="Primers are internal will never be run on costly github action (mac or windows)",
+ reason=(
+ "Primers are internal and will never be run on costly github action (mac or windows)"
+ ),
)
@pytest.mark.skipif(
sys.version_info[:2] != PRIMER_CURRENT_INTERPRETER or IS_PYPY,
- reason=f"Primers are internal will always be run for only one interpreter (currently {PRIMER_CURRENT_INTERPRETER})",
-)
-@pytest.mark.parametrize(
- "directory",
- [
- pytest.param(p, id=str(p.relative_to(FIXTURES_PATH)))
- for p in FIXTURES_PATH.iterdir()
- if p.is_dir()
- ],
+ reason=(
+ "Primers are internal and will always be run for only one interpreter (currently"
+ f" {PRIMER_CURRENT_INTERPRETER})"
+ ),
)
-def test_compare(directory: Path) -> None:
- main = directory / "main.json"
- pr = directory / "pr.json"
- expected_file = directory / "expected.txt"
- new_argv = [
- "python tests/primer/__main__.py",
- "compare",
- "--commit=v2.14.2",
- f"--base-file={main}",
- f"--new-file={pr}",
- ]
- with patch("sys.argv", new_argv):
- Primer(PRIMER_DIRECTORY, PACKAGES_TO_PRIME_PATH).run()
- with open(PRIMER_DIRECTORY / "comment.txt", encoding="utf8") as f:
- content = f.read()
- with open(expected_file, encoding="utf8") as f:
- expected = f.read()
- # rstrip so the expected.txt can end with a newline
- assert content == expected.rstrip("\n")
+class TestPrimer:
+ @pytest.mark.parametrize(
+ "directory",
+ [
+ pytest.param(p, id=str(p.relative_to(FIXTURES_PATH)))
+ for p in FIXTURES_PATH.iterdir()
+ if p.is_dir()
+ ],
+ )
+ def test_compare(self, directory: Path) -> None:
+ """Test for the standard case.
+
+ Directory in 'fixtures/' with 'main.json', 'pr.json' and 'expected.txt'."""
+ self.__assert_expected(directory)
+
+ def test_truncated_compare(self) -> None:
+ """Test for the truncation of comments that are too long."""
+ max_comment_length = 500
+ directory = FIXTURES_PATH / "message_changed"
+ with patch(
+ "pylint.testutils._primer.primer_compare_command.MAX_GITHUB_COMMENT_LENGTH",
+ max_comment_length,
+ ):
+ content = self.__assert_expected(
+ directory, expected_file=directory / "expected_truncated.txt"
+ )
+ assert len(content) < max_comment_length
+
+ @staticmethod
+ def __assert_expected(
+ directory: Path,
+ main: Path | None = None,
+ pr: Path | None = None,
+ expected_file: Path | None = None,
+ ) -> str:
+ if main is None:
+ main = directory / "main.json"
+ if pr is None:
+ pr = directory / "pr.json"
+ if expected_file is None:
+ expected_file = directory / "expected.txt"
+ new_argv = DEFAULT_ARGS + [f"--base-file={main}", f"--new-file={pr}"]
+ with patch("sys.argv", new_argv):
+ Primer(PRIMER_DIRECTORY, PACKAGES_TO_PRIME_PATH).run()
+ with open(PRIMER_DIRECTORY / "comment.txt", encoding="utf8") as f:
+ content = f.read()
+ with open(expected_file, encoding="utf8") as f:
+ expected = f.read()
+ # rstrip so the expected.txt can end with a newline
+ assert content == expected.rstrip("\n")
+ return content