142 lines
6.9 KiB
Python
142 lines
6.9 KiB
Python
import os
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
TOOL = Path(__file__).resolve().parents[1] / "debt_split.py"
|
|
INDEX = "docs/dev/TechnicalDebt.md"
|
|
|
|
A_STUB = "- [libSceA](../../core/libs/prx/libSceA/Export.cpp) - `sceAOne` returns 0"
|
|
A_FUNCTIONAL = "- `sceATwo` in [libSceA](../../core/libs/prx/libSceA/src/Two.cpp) ignores its flags\n - flag 1 is unknown"
|
|
B_STUB = "- [libSceB](../../core/libs/prx/libSceB/Export.cpp) - `sceBOne` returns 0"
|
|
C_STUB = "- [libSceC](../../core/libs/prx/libSceC/Export.cpp) - `sceCOne` returns 0"
|
|
SHADER = "- [Recompiler](../../core/shader/recompiler/Lower.cpp) rounds once, pinned in [a test](../../core/libs/prx/libSceA/tests/Round.cpp)"
|
|
SHARED = "- A limitation of every library, see [architecture](ARCHITECTURE.md)"
|
|
|
|
|
|
def index(stubs, functional):
|
|
return ("# Project technical debt\n\n### Build\n\n- A toolchain limitation\n\n### Silent stubs\n\n"
|
|
"Everything throws, except:\n\n" + "\n".join(stubs) + "\n\n### Functional\n\n" + "\n".join(functional) + "\n")
|
|
|
|
|
|
ORIGINAL = index([A_STUB, B_STUB, C_STUB], [A_FUNCTIONAL, SHADER, SHARED])
|
|
|
|
|
|
class DebtSplitTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.directory = tempfile.TemporaryDirectory()
|
|
self.root = Path(self.directory.name)
|
|
self.git("init", "-q", "-b", "main")
|
|
self.write(INDEX, ORIGINAL)
|
|
self.git("add", ".")
|
|
self.git("commit", "-qm", "docs: record technical debt")
|
|
|
|
def tearDown(self):
|
|
self.directory.cleanup()
|
|
|
|
def git(self, *args, check=True):
|
|
return subprocess.run(
|
|
["git", "-c", "user.name=Debt Test", "-c", "user.email=test@example.com", "-c", "commit.gpgsign=false", *args],
|
|
cwd=self.root, capture_output=True, text=True, check=check,
|
|
env={**os.environ, "GIT_EDITOR": "true"},
|
|
)
|
|
|
|
def write(self, path, text):
|
|
(self.root / path).parent.mkdir(parents=True, exist_ok=True)
|
|
(self.root / path).write_text(text, encoding="utf-8")
|
|
|
|
def read(self, path):
|
|
return (self.root / path).read_text(encoding="utf-8")
|
|
|
|
def tool(self, command):
|
|
return subprocess.run([sys.executable, str(TOOL), command], cwd=self.root, capture_output=True, text=True, timeout=20)
|
|
|
|
def split(self):
|
|
result = self.tool("split")
|
|
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
|
self.git("add", "-A")
|
|
self.git("commit", "-qm", "docs: split technical debt")
|
|
|
|
def test_split_moves_entries_by_their_first_core_link(self):
|
|
self.split()
|
|
self.assertEqual(self.read("docs/dev/debt/libSceA.md"),
|
|
"# libSceA technical debt\n\n### Silent stubs\n\n" + A_STUB.replace("../../", "../../../") +
|
|
"\n\n### Functional\n\n" + A_FUNCTIONAL.replace("../../", "../../../") + "\n")
|
|
self.assertIn(SHADER.replace("../../", "../../../"), self.read("docs/dev/debt/shader.md"))
|
|
text = self.read(INDEX)
|
|
for entry in (A_STUB, B_STUB, C_STUB, SHADER):
|
|
self.assertNotIn(entry.split(" - ")[0], text)
|
|
for kept in ("- A toolchain limitation", "Everything throws, except:", SHARED, "### Silent stubs", "[debt](debt/)"):
|
|
self.assertIn(kept, text)
|
|
|
|
def test_split_again_appends_to_existing_files(self):
|
|
self.split()
|
|
moved = self.read("docs/dev/debt/libSceA.md")
|
|
extra = "- [libSceA](../../core/libs/prx/libSceA/Three.cpp) - `sceAThree` returns 0"
|
|
self.write(INDEX, self.read(INDEX).replace("Everything throws, except:\n", "Everything throws, except:\n\n" + extra))
|
|
result = self.tool("split")
|
|
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
|
self.assertEqual(result.stdout.split(), ["docs/dev/debt/libSceA.md"])
|
|
stub = A_STUB.replace("../../", "../../../")
|
|
self.assertEqual(self.read("docs/dev/debt/libSceA.md"), moved.replace(stub, stub + "\n" + extra.replace("../../", "../../../")))
|
|
self.assertEqual(self.read(INDEX).count("[debt](debt/)"), 1)
|
|
|
|
def rebase(self, stubs, functional):
|
|
self.git("checkout", "-qb", "change")
|
|
self.write(INDEX, index(stubs, functional))
|
|
self.git("commit", "-qam", "feat: change debt")
|
|
self.git("checkout", "-q", "main")
|
|
self.split()
|
|
self.git("checkout", "-q", "change")
|
|
rebase = self.git("rebase", "main", check=False)
|
|
self.assertNotEqual(rebase.returncode, 0, rebase.stdout + rebase.stderr)
|
|
return self.tool("resolve")
|
|
|
|
def test_resolve_moves_added_edited_and_removed_entries(self):
|
|
added = "- [libSceA](../../core/libs/prx/libSceA/Export.cpp) - `sceAFour` returns 0"
|
|
edited = B_STUB + " and logs once"
|
|
result = self.rebase([A_STUB, added, edited], [A_FUNCTIONAL, SHADER, SHARED])
|
|
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
|
self.assertEqual(sorted(result.stdout.split()),
|
|
["docs/dev/debt/libSceA.md", "docs/dev/debt/libSceB.md", "docs/dev/debt/libSceC.md"])
|
|
self.assertEqual(self.git("diff", "--name-only", "--diff-filter=U").stdout, "")
|
|
self.git("rebase", "--continue")
|
|
a = self.read("docs/dev/debt/libSceA.md")
|
|
self.assertLess(a.index("`sceAOne`"), a.index("`sceAFour`"))
|
|
self.assertLess(a.index("`sceAFour`"), a.index("### Functional"))
|
|
self.assertIn(edited.replace("../../", "../../../"), self.read("docs/dev/debt/libSceB.md"))
|
|
self.assertFalse((self.root / "docs/dev/debt/libSceC.md").exists())
|
|
self.assertEqual(self.read(INDEX), self.git("show", "main:" + INDEX).stdout)
|
|
|
|
def test_resolve_refuses_changes_to_lines_that_stay_in_the_index(self):
|
|
result = self.rebase([A_STUB, B_STUB, C_STUB], [A_FUNCTIONAL, SHADER, SHARED + " too"])
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertIn("resolve those by hand", result.stderr)
|
|
self.assertIn("<<<<<<<", self.read(INDEX))
|
|
|
|
def test_resolve_refuses_an_entry_main_changed_too(self):
|
|
self.git("checkout", "-qb", "change")
|
|
self.write(INDEX, index([A_STUB, B_STUB + " here", C_STUB], [A_FUNCTIONAL, SHADER, SHARED]))
|
|
self.git("commit", "-qam", "feat: change b")
|
|
self.git("checkout", "-q", "main")
|
|
self.split()
|
|
path = "docs/dev/debt/libSceB.md"
|
|
self.write(path, self.read(path).replace("returns 0", "returns 1"))
|
|
self.git("commit", "-qam", "docs: change b")
|
|
self.git("checkout", "-q", "change")
|
|
self.assertNotEqual(self.git("rebase", "main", check=False).returncode, 0)
|
|
result = self.tool("resolve")
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertIn("entry not found", result.stderr)
|
|
|
|
def test_resolve_needs_a_conflict(self):
|
|
result = self.tool("resolve")
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertIn("not in a three-way conflict", result.stderr)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|