blob: 8ccdb69b506edbcb57757abd0079fbbf51a9c40d [file] [log] [blame] [edit]
#!/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()