import os import sys import tempfile import unittest sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import qr_bridge as qb class TestTrisToTargetQuads(unittest.TestCase): def test_half(self): self.assertEqual(qb.tris_to_target_quads(10000), 5000) def test_odd_floors(self): self.assertEqual(qb.tris_to_target_quads(3), 1) def test_at_least_one(self): self.assertEqual(qb.tris_to_target_quads(1), 1) self.assertEqual(qb.tris_to_target_quads(0), 1) class TestBuildSettings(unittest.TestCase): def _s(self, **kw): return qb.build_settings("/i.fbx", "/o.fbx", "/p.txt", 500, **kw) def test_core_lines_present(self): s = self._s() self.assertIn('FileIn="/i.fbx"', s) self.assertIn('FileOut="/o.fbx"', s) self.assertIn('ProgressFile="/p.txt"', s) self.assertIn("TargetQuadCount=500", s) self.assertIn("HostApp=Blender", s) self.assertTrue(s.endswith("\n")) def test_hard_edges_toggle(self): self.assertIn("AutoDetectHardEdges=1", self._s(hard_edges=True)) self.assertIn("AutoDetectHardEdges=0", self._s(hard_edges=False)) def test_exact_toggle(self): self.assertIn("ExactQuadCount=0", self._s(exact=False)) self.assertIn("ExactQuadCount=1", self._s(exact=True)) class TestParseProgress(unittest.TestCase): def test_success(self): self.assertEqual(qb.parse_progress("2\n"), ("success", "")) def test_error_with_text(self): self.assertEqual(qb.parse_progress("-3\n重拓扑失败"), ("error", "重拓扑失败")) def test_error_no_text(self): self.assertEqual(qb.parse_progress("-3"), ("error", "")) def test_running_fraction(self): self.assertEqual(qb.parse_progress("0.5"), ("running", "")) def test_running_positive_nonfinal(self): # 正整数但非终态标记 2 -> running(引擎进度信号,非成功) self.assertEqual(qb.parse_progress("1"), ("running", "")) def test_success_float_form(self): self.assertEqual(qb.parse_progress("2.0"), ("success", "")) def test_running_empty(self): self.assertEqual(qb.parse_progress(""), ("running", "")) def test_running_garbage(self): self.assertEqual(qb.parse_progress("abc"), ("running", "")) class TestFindQrEngine(unittest.TestCase): def setUp(self): self._env = dict(os.environ) def tearDown(self): os.environ.clear() os.environ.update(self._env) def test_env_var_priority(self): with tempfile.NamedTemporaryFile(suffix=".exe", delete=False) as f: path = f.name try: os.environ["QR_ENGINE"] = path self.assertEqual(qb.find_qr_engine(), path) finally: os.remove(path) def test_missing_returns_none(self): os.environ["QR_ENGINE"] = os.path.join(tempfile.gettempdir(), "nope_xremesh.exe") os.environ["APPDATA"] = tempfile.mkdtemp() self.assertIsNone(qb.find_qr_engine()) def test_invalid_override_does_not_fall_through(self): # QR_ENGINE 权威:显式设了但无效,即便 APPDATA 下装了真引擎也返回 None root = tempfile.mkdtemp() os.environ["QR_ENGINE"] = os.path.join(root, "nope.exe") os.environ["APPDATA"] = root eng_dir = os.path.join(root, "Blender Foundation", "Blender", "5.0", "scripts", "addons", "QuadRemesher", "Engine") os.makedirs(eng_dir) open(os.path.join(eng_dir, "xremesh.exe"), "w").close() self.assertIsNone(qb.find_qr_engine()) def test_globs_addon_path_picks_last(self): root = tempfile.mkdtemp() os.environ.pop("QR_ENGINE", None) os.environ["APPDATA"] = root base = os.path.join(root, "Blender Foundation", "Blender") for ver in ("4.2", "5.0"): eng_dir = os.path.join(base, ver, "scripts", "addons", "QuadRemesher", "Engine") os.makedirs(eng_dir) open(os.path.join(eng_dir, "xremesh.exe"), "w").close() chosen = qb.find_qr_engine() self.assertIsNotNone(chosen) self.assertIn(os.path.join("5.0", "scripts"), chosen) if __name__ == "__main__": unittest.main()