Files
workinf_Blender_Wasm/my-tools/test_wbv2_validation.py
mes123456 f3e21c6b98
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
chore: checkpoint web Blender migration source, tools and docs
Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer,
web workflow engine and protocol, tooling, docs and task records.
Large generated evidence, user-testing archives, build snapshots and
imported test assets remain untracked.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-11-10 11:18:01 +08:00

131 lines
7.1 KiB
Python

"""Validation normalization tests; fixtures do not claim Blender coverage."""
from pathlib import Path
import tempfile
import unittest
from wbv2_validation import prepare_validation_command
class ValidationCommandTests(unittest.TestCase):
def setUp(self):
temporary = tempfile.TemporaryDirectory()
self.addCleanup(temporary.cleanup)
self.root = Path(temporary.name)
for relative in [".emscripten-web", ".emsdk/node/24.19.0_64bit/bin/node",
"build_blender_native/bin/blender", "web/node_modules/@playwright/test/cli.js",
"check.py", "check.mjs"]:
path = self.root / relative
path.parent.mkdir(parents=True, exist_ok=True)
path.touch()
(self.root / ".emsdk/upstream/bin").mkdir(parents=True)
def prepare(self, argv, cwd=".", **kwargs):
return prepare_validation_command(self.root, argv, cwd, **kwargs)
def test_actual_source_build_wrapper_normalizes(self):
entries = {
"EM_CONFIG": str(self.root / ".emscripten-web"),
"EM_LLVM_ROOT": str(self.root / ".emsdk/upstream/bin"),
"EM_BINARYEN_ROOT": str(self.root / ".emsdk/upstream"),
"EM_NODE_JS": str(self.root / ".emsdk/node/24.19.0_64bit/bin/node"),
"EM_LLVM_ADD_VERSION": "", "EM_CLANG_ADD_VERSION": "",
}
argv = ["env", *[f"{name}={value}" for name, value in entries.items()],
"cmake", "--build", "build_web_blender6", "--target", "web_engine", "--parallel", "4"]
command, directory, env, unset = self.prepare(argv)
self.assertEqual(["cmake", "--build", "build_web_blender6", "--target", "web_engine", "-j", "4"], command)
self.assertEqual(entries, env)
self.assertEqual(self.root, directory)
self.assertEqual([], unset)
def test_actual_chromium_wrapper_uses_supervisor_receipt_directory(self):
argv = ["env", "-u", "SERVER_OPERATOR_NATIVE_REPORT",
f"BLENDER_BIN={self.root}/build_blender_native/bin/blender", "WEB_TEST_PORT=5197",
f"WBV2_RECEIPT_DIR={self.root}/tests/golden/old-attempt/cases", "node",
"node_modules/@playwright/test/cli.js", "test", "--config", "playwright.config.ts",
"tests/e2e/server-operator-native.spec.ts", "--workers=1"]
command, directory, env, unset = self.prepare(argv, "web")
self.assertEqual("node", command[0])
self.assertEqual(self.root / "web", directory)
self.assertEqual({"BLENDER_BIN": str(self.root / "build_blender_native/bin/blender"), "WEB_TEST_PORT": "5197"}, env)
self.assertEqual(["SERVER_OPERATOR_NATIVE_REPORT"], unset)
self.assertNotIn("WBV2_RECEIPT_DIR", env)
def test_worker_cannot_override_supervisor_environment(self):
_, _, env, unset = self.prepare([
"env", "WBV2_RECEIPT_DIR=/tmp/stale", "WBV2_DEPLOYED_ORIGIN=http://wrong",
"WEB_BUILD_JOBS=1000", "CMAKE_BUILD_PARALLEL_LEVEL=1000", "node", "check.mjs",
])
self.assertEqual({}, env)
self.assertEqual([], unset)
def test_parallel_flags_respect_resource_budget(self):
for program in ["cmake", "ninja", "ctest"]:
for flags in [["--parallel", "999"], ["--parallel=999"], ["-j999"], ["-j=999"], ["-j", "0"], ["-j"]]:
with self.subTest(program=program, flags=flags):
command, _, _, _ = self.prepare([program, *flags], max_build_jobs=3)
self.assertEqual([program, "-j", "3"], command)
self.assertEqual(["ninja", "-j", "8"], self.prepare(["ninja", "-j100"], max_build_jobs=20)[0])
self.assertEqual(["ninja", "-j", "2"], self.prepare(["ninja", "-j2"])[0])
def test_default_build_jobs_are_explicitly_bounded(self):
self.assertEqual(["ninja", "-j", "3"], self.prepare(["ninja"], max_build_jobs=3)[0])
self.assertEqual(["ctest", "-j", "3"], self.prepare(["ctest"], max_build_jobs=3)[0])
self.assertEqual(["cmake", "--build", "build", "-j", "3", "--", "target"],
self.prepare(["cmake", "--build", "build", "--", "target"], max_build_jobs=3)[0])
self.assertEqual(["cmake", "--version"], self.prepare(["cmake", "--version"])[0])
def test_rejects_env_injection_options_and_unsets(self):
wrappers = [
["PATH=/tmp"], ["NODE_OPTIONS=--require=/tmp/evil.js"], ["PYTHONPATH=/tmp"],
["LD_PRELOAD=/tmp/evil.so"], ["-i"], ["-S", "node check.mjs"],
["--chdir=/tmp"], ["-u", "WBV2_RECEIPT_DIR"], ["-u", "PATH"],
["WEB_TEST_PORT=5197", "WEB_TEST_PORT=5198"], ["EM_CLANG_ADD_VERSION=evil"],
]
for wrapper in wrappers:
with self.subTest(wrapper=wrapper), self.assertRaises(ValueError):
self.prepare(["env", *wrapper, "node", "check.mjs"])
def test_invalid_ports_and_missing_paths(self):
for assignment in ["WEB_TEST_PORT=0", "WEB_TEST_PORT=65536", "WEB_TEST_PORT=abc",
"WEB_TEST_PORT=80", "EM_CONFIG=", "EM_CONFIG=check.py",
"BLENDER_BIN=/tmp/blender", "EM_NODE_JS=check.py"]:
with self.subTest(assignment=assignment), self.assertRaises(ValueError):
self.prepare(["env", assignment, "node", "check.mjs"])
def test_toolchain_symlink_escape_is_rejected(self):
(self.root / ".emscripten-web").unlink()
(self.root / ".emscripten-web").symlink_to("/etc/passwd")
with self.assertRaisesRegex(ValueError, "escapes"):
self.prepare(["env", f"EM_CONFIG={self.root}/.emscripten-web", "node", "check.mjs"])
def test_preserves_mandatory_validation_guards(self):
commands = [
["bash", "check.py"], ["env", "node", "-e", "check.mjs"],
["node", "--eval=1", "check.mjs"], ["node", "-eprocess.exit(0)", "check.mjs"],
["python3", "-cprint(0)", "check.py"], ["python3", "missing.py"],
["node", "check.mjs", "--project=firefox"], ["npm", "run", "test:webkit"],
["npm", "install"], ["git", "reset", "--hard"],
["env", "env", "node", "check.mjs"], ["env"],
]
for command in commands:
with self.subTest(command=command), self.assertRaises(ValueError):
self.prepare(command)
def test_accepts_plain_validation_and_env_option_terminator(self):
for command in [["git", "diff", "--check"], ["npm", "run", "test"], ["node", "check.mjs"], ["python3", "check.py"]]:
self.assertEqual((command, self.root, {}, []), self.prepare(command))
self.assertEqual((["node", "check.mjs"], self.root, {}, []), self.prepare(["env", "--", "node", "check.mjs"]))
def test_rejects_non_argv_input_and_repository_escape(self):
for command in ["node check.mjs", [], ["node", ""], ["node", "bad\0arg"]]:
with self.subTest(command=command), self.assertRaises(ValueError):
self.prepare(command)
for cwd in ["../outside", "/tmp", "missing"]:
with self.subTest(cwd=cwd), self.assertRaises(ValueError):
self.prepare(["npm", "run", "test"], cwd)
if __name__ == "__main__":
unittest.main()