Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions Libraries/PyKotor/tests/cli/test_json_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from pykotor.resource.type import ResourceType
from pykotor.tools.resource_json import (
_serialize_mdl_face,
_supports_live_progress,
export_installation_to_json_tree,
iter_installation_resource_documents,
serialize_file_resource_document,
Expand Down Expand Up @@ -922,3 +923,43 @@ def fake_main(argv: list[str]) -> int:
assert "--merge-module" in captured_argv
assert captured_argv.count("--merge-path") == 2
assert "--merge-conflict-policy" in captured_argv


class _FakeTtyStream:
def __init__(self, tty: bool) -> None:
self._tty = tty

def isatty(self) -> bool:
return self._tty


@pytest.mark.parametrize(
("env", "expected"),
[
({"CI": "true"}, False),
({"GITHUB_ACTIONS": "1"}, False),
({}, True),
],
)
def test_supports_live_progress_disables_in_ci(
env: dict[str, str],
expected: bool,
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.delenv("CI", raising=False)
monkeypatch.delenv("GITHUB_ACTIONS", raising=False)
for key, value in env.items():
monkeypatch.setenv(key, value)

stream = _FakeTtyStream(tty=True)
assert _supports_live_progress(stream) is expected


def test_supports_live_progress_requires_tty_when_not_in_ci(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.delenv("CI", raising=False)
monkeypatch.delenv("GITHUB_ACTIONS", raising=False)

assert _supports_live_progress(_FakeTtyStream(tty=False)) is False
assert _supports_live_progress(_FakeTtyStream(tty=True)) is True
99 changes: 99 additions & 0 deletions Libraries/PyKotor/tests/diff_tool/test_cli_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""Regression tests for diff_tool CLI path normalization and install detection."""

from __future__ import annotations

import pathlib
import sys
import tempfile
import unittest

THIS_SCRIPT_PATH = pathlib.Path(__file__).resolve()
PYKOTOR_PATH = THIS_SCRIPT_PATH.parents[2].joinpath("src")
UTILITY_PATH = THIS_SCRIPT_PATH.parents[4].joinpath("Libraries", "Utility", "src")


def add_sys_path(p: pathlib.Path) -> None:
working_dir = str(p)
if working_dir not in sys.path:
sys.path.append(working_dir)


if PYKOTOR_PATH.joinpath("pykotor").exists():
add_sys_path(PYKOTOR_PATH)
if UTILITY_PATH.joinpath("utility").exists():
add_sys_path(UTILITY_PATH)

from pykotor.diff_tool.cli_utils import ( # noqa: E402
is_kotor_install_dir,
normalize_path_arg,
)


class TestNormalizePathArg(unittest.TestCase):
def test_none_and_empty(self) -> None:
self.assertIsNone(normalize_path_arg(None))
self.assertIsNone(normalize_path_arg(""))
self.assertIsNone(normalize_path_arg(" "))

def test_strips_surrounding_quotes(self) -> None:
self.assertEqual(
normalize_path_arg('"C:\\Program Files\\KOTOR"'),
"C:\\Program Files\\KOTOR",
)
self.assertEqual(
normalize_path_arg("'C:/Games/KOTOR'"),
"C:/Games/KOTOR",
)

def test_strips_trailing_slashes_after_quote_removal(self) -> None:
self.assertEqual(
normalize_path_arg('"C:\\Program Files\\KOTOR\\"'),
"C:\\Program Files\\KOTOR",
)

def test_mangled_powershell_quote_space_path(self) -> None:
mangled = 'C:\\Steam\\steamapps\\common\\swkotor" C:\\Other\\path'
self.assertEqual(
normalize_path_arg(mangled),
"C:\\Steam\\steamapps\\common\\swkotor",
)

def test_removes_embedded_quotes(self) -> None:
self.assertEqual(
normalize_path_arg('C:\\"broken"\\path'),
"C:\\broken\\path",
)


class TestIsKotorInstallDir(unittest.TestCase):
def test_valid_installation(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = pathlib.Path(tmp)
(root / "chitin.key").write_bytes(b"key")
self.assertTrue(is_kotor_install_dir(root))

def test_missing_chitin_key(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = pathlib.Path(tmp)
self.assertFalse(is_kotor_install_dir(root))

def test_file_path_is_not_install(self) -> None:
with tempfile.NamedTemporaryFile() as tmp_file:
self.assertFalse(is_kotor_install_dir(pathlib.Path(tmp_file.name)))

@unittest.skipIf(
sys.platform == "win32",
"Case-mismatch path semantics differ on Windows filesystems.",
)
def test_case_mismatched_chitin_key_path(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = pathlib.Path(tmp) / "KotorInstall"
root.mkdir()
(root / "chitin.key").write_bytes(b"key")

mismatched = pathlib.Path(tmp) / "kotorinstall"
self.assertTrue(is_kotor_install_dir(mismatched))


if __name__ == "__main__":
unittest.main()
Loading