aboutsummaryrefslogtreecommitdiff
path: root/llvm_tools/get_patch_unittest.py
diff options
context:
space:
mode:
Diffstat (limited to 'llvm_tools/get_patch_unittest.py')
-rwxr-xr-xllvm_tools/get_patch_unittest.py233
1 files changed, 233 insertions, 0 deletions
diff --git a/llvm_tools/get_patch_unittest.py b/llvm_tools/get_patch_unittest.py
new file mode 100755
index 00000000..8ccdb69b
--- /dev/null
+++ b/llvm_tools/get_patch_unittest.py
@@ -0,0 +1,233 @@
+#!/usr/bin/env python3
+# Copyright 2024 The ChromiumOS Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Tests for get_patch."""
+
+import json
+from pathlib import Path
+import tempfile
+from typing import Any, Dict, Generator, List, Set
+import unittest
+from unittest import mock
+
+import get_patch
+import git_llvm_rev
+
+
+COMMIT_FIXTURES: List[Dict[str, Any]] = [
+ {"subject": "A commit subject", "sha": "abcdef1234567890", "rev": 5},
+ {"subject": "Another commit subject", "sha": "feed9999", "rev": 9},
+]
+
+JSON_FIXTURE: List[Dict[str, Any]] = [
+ {
+ "metadata": {"title": "An existing patch"},
+ "platforms": ["another platform"],
+ "rel_patch_path": "cherry/nowhere.patch",
+ "version_range": {"from": 1, "until": 256},
+ },
+]
+
+
+def _mock_get_commit_subj(_, sha: str) -> str:
+ gen: Generator[Dict[str, Any], None, None] = (
+ fixture for fixture in COMMIT_FIXTURES if fixture["sha"] == sha
+ )
+ return next(gen)["subject"]
+
+
+def _mock_to_rev(sha: get_patch.LLVMGitRef, _) -> git_llvm_rev.Rev:
+ gen: Generator[Dict[str, Any], None, None] = (
+ fixture for fixture in COMMIT_FIXTURES if fixture["sha"] == sha.git_ref
+ )
+ return git_llvm_rev.Rev("main", next(gen)["rev"])
+
+
+def _mock_from_rev(_, rev: git_llvm_rev.Rev) -> get_patch.LLVMGitRef:
+ gen: Generator[Dict[str, Any], None, None] = (
+ fixture for fixture in COMMIT_FIXTURES if fixture["rev"] == rev.number
+ )
+ return get_patch.LLVMGitRef(next(gen)["sha"])
+
+
+def _mock_git_format_patch(*_) -> str:
+ return "[category] This is a fake commit fixture"
+
+
+def _mock_write_patch(*_) -> None:
+ return
+
+
+def _mock_get_changed_packages(*_) -> Set[Path]:
+ return {get_patch.LLVM_PKG_PATH}
+
+
+class TestGetPatch(unittest.TestCase):
+ """Test case harness for get_patch."""
+
+ def setUp(self) -> None:
+ """Set up the mocks and directory structure."""
+
+ self.module_patcher = mock.patch.multiple(
+ "get_patch",
+ get_commit_subj=_mock_get_commit_subj,
+ git_format_patch=_mock_git_format_patch,
+ get_changed_packages=_mock_get_changed_packages,
+ _write_patch=_mock_write_patch,
+ )
+ self.module_patcher.start()
+ self.addCleanup(self.module_patcher.stop)
+ self.llvm_gitsha_patcher = mock.patch.multiple(
+ "get_patch.LLVMGitRef",
+ to_rev=_mock_to_rev,
+ from_rev=_mock_from_rev,
+ )
+ self.llvm_gitsha_patcher.start()
+ self.addCleanup(self.llvm_gitsha_patcher.stop)
+
+ self.llvm_project_dir = Path(tempfile.mkdtemp())
+ self.addCleanup(self.llvm_project_dir.rmdir)
+ self.chromiumos_root = Path(tempfile.mkdtemp())
+ self.addCleanup(self.chromiumos_root.rmdir)
+ self.workdir = self.chromiumos_root / get_patch.LLVM_PKG_PATH / "files"
+ self.workdir.mkdir(parents=True, exist_ok=True)
+
+ def _cleanup_workdir():
+ # We individually clean up these directories as a guarantee
+ # we aren't creating any extraneous files. We don't want to
+ # use shm.rmtree here because we don't want clean up any
+ # files unaccounted for.
+ workdir_recurse = self.workdir
+ while workdir_recurse not in (self.chromiumos_root, Path.root):
+ workdir_recurse.rmdir()
+ workdir_recurse = workdir_recurse.parent
+
+ self.addCleanup(_cleanup_workdir)
+
+ self.patches_json_file = (
+ self.workdir / get_patch.PATCH_METADATA_FILENAME
+ )
+ start_ref = get_patch.LLVMGitRef("abcdef1234567890")
+ self.ctx = get_patch.PatchContext(
+ self.llvm_project_dir,
+ self.chromiumos_root,
+ start_ref,
+ platforms=["some platform"],
+ )
+
+ def write_json_fixture(self) -> None:
+ with self.patches_json_file.open("w", encoding="utf-8") as f:
+ json.dump(JSON_FIXTURE, f)
+ f.write("\n")
+
+ def test_bad_cherrypick_version(self) -> None:
+ """Test that bad cherrypick versions raises."""
+ start_sha_fixture = COMMIT_FIXTURES[0]
+
+ def _try_make_patches():
+ # This fixture is the same as the start_sha.
+ self.ctx.make_patches(
+ get_patch.LLVMGitRef(start_sha_fixture["sha"])
+ )
+
+ self.assertRaises(get_patch.CherrypickVersionError, _try_make_patches)
+
+ def test_make_patches(self) -> None:
+ """Test we can make patch entries from a git commit."""
+
+ fixture = COMMIT_FIXTURES[1]
+ # We manually write and delete this file because it must have the name
+ # as specified by get_patch. tempfile cannot guarantee us this name.
+ self.write_json_fixture()
+ try:
+ entries = self.ctx.make_patches(
+ get_patch.LLVMGitRef(fixture["sha"])
+ )
+ self.assertEqual(len(entries), 1)
+ if entries[0].metadata:
+ self.assertEqual(
+ entries[0].metadata["title"], fixture["subject"]
+ )
+ else:
+ self.fail("metadata was None")
+ finally:
+ self.patches_json_file.unlink()
+
+ def test_apply_patch_to_json(self) -> None:
+ """Test we can apply patches to the JSON file."""
+
+ fixture = COMMIT_FIXTURES[1]
+ fixture_sha = fixture["sha"]
+ expected_json_entry = {
+ "metadata": {"title": fixture["subject"], "info": []},
+ "platforms": ["some platform"],
+ "rel_patch_path": f"cherry/{fixture_sha}.patch",
+ "version_range": {
+ "from": self.ctx.start_ref.to_rev(self.llvm_project_dir).number,
+ "until": fixture["rev"],
+ },
+ }
+ cherrydir = self.workdir / "cherry"
+ cherrydir.mkdir()
+ self._apply_patch_to_json_helper(fixture, expected_json_entry)
+ cherrydir.rmdir()
+
+ def test_apply_patch_to_json_no_cherry(self) -> None:
+ """Test we can apply patches to the JSON file, without a cherry dir."""
+
+ fixture = COMMIT_FIXTURES[1]
+ fixture_sha = fixture["sha"]
+ expected_json_entry = {
+ "metadata": {"title": fixture["subject"], "info": []},
+ "platforms": ["some platform"],
+ "rel_patch_path": f"{fixture_sha}.patch",
+ "version_range": {
+ "from": self.ctx.start_ref.to_rev(self.llvm_project_dir).number,
+ "until": fixture["rev"],
+ },
+ }
+ self._apply_patch_to_json_helper(fixture, expected_json_entry)
+
+ def _apply_patch_to_json_helper(self, fixture, expected_json_entry) -> None:
+ # We manually write and delete this file because it must have the name
+ # as specified by get_patch. tempfile cannot guarantee us this name.
+ self.write_json_fixture()
+ patch_source = get_patch.LLVMGitRef.from_rev(
+ self.llvm_project_dir,
+ git_llvm_rev.Rev("origin", fixture["rev"]),
+ )
+ try:
+ self.ctx.apply_patches(patch_source)
+ with self.patches_json_file.open(encoding="utf-8") as f:
+ edited = json.load(f)
+ self.assertEqual(edited, JSON_FIXTURE + [expected_json_entry])
+ finally:
+ self.patches_json_file.unlink()
+
+ def test_apply_patch_dry_run(self) -> None:
+ """Test dry running patches does nothing."""
+
+ fixture = COMMIT_FIXTURES[1]
+ old_dry_run = self.ctx.dry_run
+ self.ctx.dry_run = True
+ # We manually write and delete this file because it must have the name
+ # as specified by get_patch. tempfile cannot guarantee us this name.
+ self.write_json_fixture()
+ patch_source = get_patch.LLVMGitRef.from_rev(
+ self.llvm_project_dir,
+ git_llvm_rev.Rev("origin", fixture["rev"]),
+ )
+ try:
+ self.ctx.apply_patches(patch_source)
+ with self.patches_json_file.open(encoding="utf-8") as f:
+ maybe_edited = json.load(f)
+ self.assertEqual(maybe_edited, JSON_FIXTURE)
+ finally:
+ self.ctx.dry_run = old_dry_run
+ self.patches_json_file.unlink()
+
+
+if __name__ == "__main__":
+ unittest.main()