2026-06-18 10:39:52 +08:00
|
|
|
"""Tests for services.file_service path resolution — no GUI dependency."""
|
|
|
|
|
import os
|
|
|
|
|
import sys
|
|
|
|
|
import unittest
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
|
|
|
|
|
|
|
|
|
import services.file_service as fs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestGetDataDir(unittest.TestCase):
|
|
|
|
|
"""get_data_dir() three-tier fallback (docs/10-lan-update.md §5)."""
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
self._env = os.environ.get("CMBOT_DATA_DIR")
|
|
|
|
|
self._frozen = getattr(sys, "frozen", None)
|
|
|
|
|
os.environ.pop("CMBOT_DATA_DIR", None)
|
|
|
|
|
if hasattr(sys, "frozen"):
|
|
|
|
|
del sys.frozen
|
|
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
|
if self._env is None:
|
|
|
|
|
os.environ.pop("CMBOT_DATA_DIR", None)
|
|
|
|
|
else:
|
|
|
|
|
os.environ["CMBOT_DATA_DIR"] = self._env
|
|
|
|
|
if self._frozen is None:
|
|
|
|
|
if hasattr(sys, "frozen"):
|
|
|
|
|
del sys.frozen
|
|
|
|
|
else:
|
|
|
|
|
sys.frozen = self._frozen
|
|
|
|
|
|
|
|
|
|
def test_env_override_wins(self):
|
|
|
|
|
os.environ["CMBOT_DATA_DIR"] = r"X:\custom\data"
|
|
|
|
|
self.assertEqual(fs.get_data_dir(), Path(r"X:\custom\data"))
|
|
|
|
|
|
|
|
|
|
def test_env_override_wins_even_when_frozen(self):
|
|
|
|
|
sys.frozen = True
|
|
|
|
|
os.environ["CMBOT_DATA_DIR"] = r"X:\custom\data"
|
|
|
|
|
self.assertEqual(fs.get_data_dir(), Path(r"X:\custom\data"))
|
|
|
|
|
|
|
|
|
|
def test_packaged_uses_home_dotcmbot(self):
|
|
|
|
|
sys.frozen = True
|
|
|
|
|
self.assertEqual(fs.get_data_dir(), Path.home() / ".cmbot")
|
|
|
|
|
|
|
|
|
|
def test_dev_uses_project_root(self):
|
|
|
|
|
# not frozen, no env -> same as program/app dir (project root in dev)
|
|
|
|
|
self.assertEqual(fs.get_data_dir(), fs.get_app_dir())
|
|
|
|
|
|
|
|
|
|
def test_blank_env_is_ignored(self):
|
|
|
|
|
os.environ["CMBOT_DATA_DIR"] = " "
|
|
|
|
|
self.assertEqual(fs.get_data_dir(), fs.get_app_dir())
|
|
|
|
|
|
|
|
|
|
|
2026-06-18 14:29:09 +08:00
|
|
|
class TestGetOutputDir(unittest.TestCase):
|
|
|
|
|
"""get_output_dir() default location (docs/10-lan-update.md §5)."""
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
import tempfile
|
|
|
|
|
self.tmp = Path(tempfile.mkdtemp())
|
|
|
|
|
self._env = os.environ.get("CMBOT_DATA_DIR")
|
|
|
|
|
self._frozen = getattr(sys, "frozen", None)
|
|
|
|
|
os.environ["CMBOT_DATA_DIR"] = str(self.tmp / "data")
|
|
|
|
|
if hasattr(sys, "frozen"):
|
|
|
|
|
del sys.frozen
|
|
|
|
|
self._orig_app_dir = fs.get_app_dir
|
|
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
|
import shutil
|
|
|
|
|
fs.get_app_dir = self._orig_app_dir
|
|
|
|
|
if self._env is None:
|
|
|
|
|
os.environ.pop("CMBOT_DATA_DIR", None)
|
|
|
|
|
else:
|
|
|
|
|
os.environ["CMBOT_DATA_DIR"] = self._env
|
|
|
|
|
if self._frozen is None:
|
|
|
|
|
if hasattr(sys, "frozen"):
|
|
|
|
|
del sys.frozen
|
|
|
|
|
else:
|
|
|
|
|
sys.frozen = self._frozen
|
|
|
|
|
shutil.rmtree(str(self.tmp), ignore_errors=True)
|
|
|
|
|
|
|
|
|
|
def test_dev_uses_data_output(self):
|
|
|
|
|
# not frozen -> <data_dir>/output
|
|
|
|
|
self.assertEqual(fs.get_output_dir(), Path(os.environ["CMBOT_DATA_DIR"]) / "output")
|
|
|
|
|
|
|
|
|
|
def test_frozen_uses_install_root(self):
|
|
|
|
|
app = self.tmp / "install" / "app"
|
|
|
|
|
app.mkdir(parents=True)
|
|
|
|
|
fs.get_app_dir = lambda: app
|
|
|
|
|
sys.frozen = True
|
|
|
|
|
self.assertEqual(fs.get_output_dir(), self.tmp / "install" / "合并后的图片")
|
|
|
|
|
|
|
|
|
|
def test_frozen_falls_back_when_not_writable(self):
|
|
|
|
|
app = self.tmp / "install" / "app"
|
|
|
|
|
app.mkdir(parents=True)
|
|
|
|
|
fs.get_app_dir = lambda: app
|
|
|
|
|
sys.frozen = True
|
|
|
|
|
orig = fs._is_dir_writable
|
|
|
|
|
fs._is_dir_writable = lambda p: False
|
|
|
|
|
try:
|
|
|
|
|
self.assertEqual(fs.get_output_dir(), Path(os.environ["CMBOT_DATA_DIR"]) / "output")
|
|
|
|
|
finally:
|
|
|
|
|
fs._is_dir_writable = orig
|
|
|
|
|
|
|
|
|
|
|
2026-06-22 17:49:59 +08:00
|
|
|
class TestGetOutfitOutputDir(unittest.TestCase):
|
|
|
|
|
"""get_outfit_output_dir() default location (docs/11 §9)."""
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
import tempfile
|
|
|
|
|
self.tmp = Path(tempfile.mkdtemp())
|
|
|
|
|
self._env = os.environ.get("CMBOT_DATA_DIR")
|
|
|
|
|
self._frozen = getattr(sys, "frozen", None)
|
|
|
|
|
os.environ["CMBOT_DATA_DIR"] = str(self.tmp / "data")
|
|
|
|
|
if hasattr(sys, "frozen"):
|
|
|
|
|
del sys.frozen
|
|
|
|
|
self._orig_app_dir = fs.get_app_dir
|
|
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
|
import shutil
|
|
|
|
|
fs.get_app_dir = self._orig_app_dir
|
|
|
|
|
if self._env is None:
|
|
|
|
|
os.environ.pop("CMBOT_DATA_DIR", None)
|
|
|
|
|
else:
|
|
|
|
|
os.environ["CMBOT_DATA_DIR"] = self._env
|
|
|
|
|
if self._frozen is None:
|
|
|
|
|
if hasattr(sys, "frozen"):
|
|
|
|
|
del sys.frozen
|
|
|
|
|
else:
|
|
|
|
|
sys.frozen = self._frozen
|
|
|
|
|
shutil.rmtree(str(self.tmp), ignore_errors=True)
|
|
|
|
|
|
|
|
|
|
def test_dev_uses_project_outfit_folder(self):
|
|
|
|
|
app = self.tmp / "project"
|
|
|
|
|
app.mkdir()
|
|
|
|
|
fs.get_app_dir = lambda: app
|
|
|
|
|
|
|
|
|
|
self.assertEqual(fs.get_outfit_output_dir(), app / "穿搭图片")
|
|
|
|
|
|
|
|
|
|
def test_frozen_uses_install_root_outfit_folder(self):
|
|
|
|
|
app = self.tmp / "install" / "app"
|
|
|
|
|
app.mkdir(parents=True)
|
|
|
|
|
fs.get_app_dir = lambda: app
|
|
|
|
|
sys.frozen = True
|
|
|
|
|
|
|
|
|
|
self.assertEqual(fs.get_outfit_output_dir(), self.tmp / "install" / "穿搭图片")
|
|
|
|
|
|
|
|
|
|
def test_frozen_falls_back_when_not_writable(self):
|
|
|
|
|
app = self.tmp / "install" / "app"
|
|
|
|
|
app.mkdir(parents=True)
|
|
|
|
|
fs.get_app_dir = lambda: app
|
|
|
|
|
sys.frozen = True
|
|
|
|
|
orig = fs._is_dir_writable
|
|
|
|
|
fs._is_dir_writable = lambda p: False
|
|
|
|
|
try:
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
fs.get_outfit_output_dir(),
|
|
|
|
|
Path(os.environ["CMBOT_DATA_DIR"]) / "output" / "穿搭图片",
|
|
|
|
|
)
|
|
|
|
|
finally:
|
|
|
|
|
fs._is_dir_writable = orig
|
|
|
|
|
|
|
|
|
|
|
2026-06-18 10:39:52 +08:00
|
|
|
if __name__ == "__main__":
|
|
|
|
|
unittest.main()
|