修:draw-ui / oil-motion 原被当子模块指针收录 ⇒ 改为正常文件入库(两份内容原先对别人是空的)
一、问题(本轮实测)
`draw-ui` 与 `oil-motion` 目录里**各自带一个内嵌 `.git`** ⇒ 上一次提交把它们记成了 **gitlink(子模块指针)**
⇒ 仓库里只存了一个不属于任何远端的 commit id,**别人克隆下来这两份是空的** ✗(`git status` 显示 ` m draw-ui` / ` m oil-motion` = 子模块内容有改动)。
二、处置(可回退)
· 把两处的 `.git` **挪走**(⛔ 不是删除)⇒ `归档/内嵌git-20261008/{draw-ui,oil-motion}.git`;
· `git rm --cached` 掉那两个 gitlink,再 `git add` 两个目录 ⇒ **按正常文件入库**(内容才真的进仓库)。
三、副作用(如实记)
挪走 `.git` 后,这两个技能**不能再原地 `git pull` 取上游更新**(要更新得重新拉一份覆盖);
如需恢复其本地仓库,把 `归档/内嵌git-20261008/` 里的 `.git` 挪回原处即可。
This commit is contained in:
1 parent
e03465c398
commit
237a09a5b0
161 files changed
+19429
-2
No files matched your search
@@ -0,0 +1,232 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import subprocess
|
||||
import sys
|
||||
import unittest
|
||||
from argparse import Namespace
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
MODULE_PATH = Path(__file__).resolve().parents[1] / "scripts" / "motion_budget.py"
|
||||
SPEC = importlib.util.spec_from_file_location("motion_budget", MODULE_PATH)
|
||||
assert SPEC and SPEC.loader
|
||||
MOTION_BUDGET = importlib.util.module_from_spec(SPEC)
|
||||
sys.modules[SPEC.name] = MOTION_BUDGET
|
||||
SPEC.loader.exec_module(MOTION_BUDGET)
|
||||
|
||||
|
||||
def arguments(**overrides: object) -> Namespace:
|
||||
values: dict[str, object] = {
|
||||
"frames": 96,
|
||||
"display": (240, 240),
|
||||
"dpr": 1.0,
|
||||
"max_texture": 4096,
|
||||
"driver": "pointer",
|
||||
"time_control": "scrub",
|
||||
"parameter_space": "circular",
|
||||
"access": "auto",
|
||||
"background_owner": "page",
|
||||
"atlas_max_memory_mib": 192.0,
|
||||
"linear_video_min_frames": 180,
|
||||
"source": None,
|
||||
"cell": None,
|
||||
"scroll_pages": None,
|
||||
"frames_per_page": 24.0,
|
||||
}
|
||||
values.update(overrides)
|
||||
return Namespace(**values)
|
||||
|
||||
|
||||
class MotionBudgetSelectionTests(unittest.TestCase):
|
||||
def test_cli_requires_explicit_background_owner(self) -> None:
|
||||
completed = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(MODULE_PATH),
|
||||
"--frames",
|
||||
"240",
|
||||
"--display",
|
||||
"1280x720",
|
||||
"--strict",
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
self.assertNotEqual(completed.returncode, 0)
|
||||
self.assertIn("--background-owner", completed.stderr)
|
||||
|
||||
def test_small_random_sequence_selects_alpha_atlas(self) -> None:
|
||||
report = MOTION_BUDGET.build_report(arguments())
|
||||
|
||||
self.assertEqual(report["delivery"]["selected"], "alpha-atlas")
|
||||
self.assertEqual(report["runtime"]["renderer"], "css-alpha-atlas")
|
||||
self.assertEqual(report["runtime"]["controller"], "frame-scrub")
|
||||
self.assertTrue(report["passes"])
|
||||
|
||||
def test_large_circular_sequence_falls_back_to_chroma_video(self) -> None:
|
||||
report = MOTION_BUDGET.build_report(
|
||||
arguments(
|
||||
frames=48,
|
||||
display=(640, 640),
|
||||
parameter_space="circular",
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(report["texture"]["columnsPerSheet"], 6)
|
||||
self.assertEqual(report["texture"]["rowsPerSheet"], 6)
|
||||
self.assertEqual(report["texture"]["framesPerSheet"], 36)
|
||||
self.assertEqual(report["texture"]["sheetCount"], 2)
|
||||
self.assertEqual(report["delivery"]["selected"], "chroma-video")
|
||||
self.assertIn(
|
||||
"atlas-budget-exceeded",
|
||||
report["delivery"]["reasonCodes"],
|
||||
)
|
||||
self.assertTrue(report["passes"])
|
||||
|
||||
def test_large_linear_scroll_selects_chroma_video(self) -> None:
|
||||
report = MOTION_BUDGET.build_report(
|
||||
arguments(
|
||||
frames=551,
|
||||
display=(1536, 864),
|
||||
driver="scroll",
|
||||
parameter_space="linear",
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(report["delivery"]["selected"], "chroma-video")
|
||||
self.assertEqual(report["runtime"]["renderer"], "webgl-chroma-video")
|
||||
self.assertEqual(report["runtime"]["controller"], "frame-scrub")
|
||||
self.assertIn("long-linear-sequence", report["delivery"]["reasonCodes"])
|
||||
self.assertTrue(report["passes"])
|
||||
|
||||
def test_two_dimensional_input_is_never_flattened_to_video(self) -> None:
|
||||
report = MOTION_BUDGET.build_report(
|
||||
arguments(
|
||||
frames=400,
|
||||
display=(900, 900),
|
||||
parameter_space="2d",
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(report["delivery"]["selected"], "alpha-atlas")
|
||||
self.assertIn(
|
||||
"two-dimensional-parameter-needs-discrete-frames",
|
||||
report["delivery"]["reasonCodes"],
|
||||
)
|
||||
self.assertTrue(
|
||||
any(
|
||||
"拆分参数轴" in failure and "用户确认" in failure
|
||||
for failure in report["failures"]
|
||||
)
|
||||
)
|
||||
self.assertFalse(report["passes"])
|
||||
|
||||
def test_discrete_states_are_split_instead_of_flattened(self) -> None:
|
||||
report = MOTION_BUDGET.build_report(
|
||||
arguments(
|
||||
frames=400,
|
||||
display=(900, 900),
|
||||
driver="state",
|
||||
parameter_space="discrete",
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(report["delivery"]["selected"], "alpha-atlas")
|
||||
self.assertIn(
|
||||
"discrete-states-need-independent-assets",
|
||||
report["delivery"]["reasonCodes"],
|
||||
)
|
||||
self.assertFalse(report["passes"])
|
||||
|
||||
def test_baked_scene_video_when_background_belongs_to_video(self) -> None:
|
||||
report = MOTION_BUDGET.build_report(
|
||||
arguments(
|
||||
frames=551,
|
||||
display=(1536, 864),
|
||||
driver="scroll",
|
||||
parameter_space="linear",
|
||||
background_owner="video",
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(report["delivery"]["selected"], "baked-video")
|
||||
self.assertEqual(report["runtime"]["renderer"], "baked-video")
|
||||
self.assertEqual(report["runtime"]["controller"], "frame-scrub")
|
||||
self.assertIn(
|
||||
"background-baked-into-video",
|
||||
report["delivery"]["reasonCodes"],
|
||||
)
|
||||
self.assertIn("long-linear-sequence", report["delivery"]["reasonCodes"])
|
||||
self.assertEqual(report["backgroundOwner"], "video")
|
||||
self.assertTrue(report["passes"])
|
||||
|
||||
def test_scroll_segment_play_is_not_misclassified_as_scrub(self) -> None:
|
||||
report = MOTION_BUDGET.build_report(
|
||||
arguments(
|
||||
frames=240,
|
||||
display=(1280, 720),
|
||||
driver="scroll",
|
||||
time_control="segment-play",
|
||||
parameter_space="linear",
|
||||
background_owner="video",
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(report["delivery"]["selected"], "baked-video")
|
||||
self.assertEqual(report["runtime"]["controller"], "segment-playback")
|
||||
self.assertEqual(report["access"], "sequential")
|
||||
|
||||
def test_cli_requires_explicit_time_control(self) -> None:
|
||||
completed = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(MODULE_PATH),
|
||||
"--frames",
|
||||
"240",
|
||||
"--display",
|
||||
"1280x720",
|
||||
"--background-owner",
|
||||
"video",
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
self.assertNotEqual(completed.returncode, 0)
|
||||
self.assertIn("--time-control", completed.stderr)
|
||||
|
||||
def test_baked_video_is_not_failed_by_atlas_budget(self) -> None:
|
||||
report = MOTION_BUDGET.build_report(
|
||||
arguments(
|
||||
frames=900,
|
||||
display=(900, 900),
|
||||
parameter_space="circular",
|
||||
background_owner="video",
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(report["delivery"]["selected"], "baked-video")
|
||||
self.assertTrue(report["passes"])
|
||||
|
||||
def test_baked_video_rejects_flattened_two_dimensional_input(self) -> None:
|
||||
report = MOTION_BUDGET.build_report(
|
||||
arguments(
|
||||
frames=400,
|
||||
display=(900, 900),
|
||||
parameter_space="2d",
|
||||
background_owner="video",
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(report["delivery"]["selected"], "baked-video")
|
||||
self.assertIn(
|
||||
"baked-video-needs-independent-clips",
|
||||
report["delivery"]["reasonCodes"],
|
||||
)
|
||||
self.assertFalse(report["passes"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user