fdc48aa573
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 17s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 18s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 18s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 18s
Project CI / Backend tests (push) Failing after 17s
Project CI / AI game creator shell Rust smoke (push) Failing after 19s
Project CI / Native shell tests (push) Failing after 18s
Project CI / AI game creator shell Rust crates (push) Failing after 18s
Project CI / Frontend tests (push) Failing after 6s
Project CI / AI game creator shell web tests (push) Failing after 11s
Project CI / Repository checks (push) Failing after 11s
使用固定版本 godot-cpp 替换手写 C ABI 引导并保留执行协议 采用 MSVC 与 CMake 构建,校验依赖归档和源码缓存 修复动态卸载时的实例绑定与单例包装生命周期 补充构建缓存测试、实机验收结果及分发文档
74 lines
3.2 KiB
Python
74 lines
3.2 KiB
Python
import hashlib
|
|
import importlib.util
|
|
import json
|
|
from pathlib import Path
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
import zipfile
|
|
from unittest.mock import patch
|
|
|
|
sys.dont_write_bytecode = True
|
|
spec = importlib.util.spec_from_file_location("dependencies", Path(__file__).parents[1] / "prepare_dependencies.py")
|
|
dependencies = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(dependencies)
|
|
|
|
|
|
class DependencyTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.temp = tempfile.TemporaryDirectory()
|
|
self.addCleanup(self.temp.cleanup)
|
|
self.root = Path(self.temp.name)
|
|
self.commit = "a" * 40
|
|
self.name = f"godot-cpp-{self.commit}"
|
|
self.archive = self.root / ".build/dependencies" / f"{self.commit}.zip"
|
|
self.archive.parent.mkdir(parents=True)
|
|
(self.root / "vendor").mkdir()
|
|
(self.root / "vendor/LICENSE.txt").write_bytes(b"MIT\r\n")
|
|
self.write_archive({"LICENSE.md": b"MIT\n", "src/core.cpp": b"verified"})
|
|
|
|
def write_archive(self, entries):
|
|
with zipfile.ZipFile(self.archive, "w") as output:
|
|
for name, content in entries.items():
|
|
output.writestr(f"{self.name}/{name}", content)
|
|
provenance = {"commit": self.commit,
|
|
"archiveUrl": f"https://codeload.github.com/godotengine/godot-cpp/zip/{self.commit}",
|
|
"archiveSha256": hashlib.sha256(self.archive.read_bytes()).hexdigest()}
|
|
(self.root / "vendor/provenance.json").write_text(json.dumps(provenance), encoding="utf-8")
|
|
|
|
def test_verified_archive_is_reusable_offline(self):
|
|
with patch("urllib.request.urlopen", side_effect=AssertionError("Unexpected network")):
|
|
source = dependencies.prepare(self.root)
|
|
self.assertEqual(source, dependencies.prepare(self.root))
|
|
self.assertEqual((source / "src/core.cpp").read_bytes(), b"verified")
|
|
|
|
def test_corrupt_archive_is_rejected_and_preserved(self):
|
|
self.archive.write_bytes(b"corrupt")
|
|
with self.assertRaisesRegex(ValueError, "SHA256 mismatch"):
|
|
dependencies.prepare(self.root)
|
|
self.assertEqual(self.archive.read_bytes(), b"corrupt")
|
|
|
|
def test_modified_source_is_rejected_even_with_valid_archive(self):
|
|
source = dependencies.prepare(self.root)
|
|
(source / "src/core.cpp").write_bytes(b"modified")
|
|
with self.assertRaisesRegex(ValueError, "Modified godot-cpp"):
|
|
dependencies.prepare(self.root)
|
|
self.assertEqual((source / "src/core.cpp").read_bytes(), b"modified")
|
|
|
|
def test_extra_source_is_rejected(self):
|
|
source = dependencies.prepare(self.root)
|
|
(source / "extra.cpp").write_text("unexpected", encoding="utf-8")
|
|
with self.assertRaisesRegex(ValueError, "Unexpected dependency"):
|
|
dependencies.prepare(self.root)
|
|
|
|
def test_archive_paths_cannot_escape_on_windows_or_posix(self):
|
|
for name in ["../../escaped", "C:\\escaped", "..\\escaped"]:
|
|
with self.subTest(name=name):
|
|
self.write_archive({name: b"unexpected"})
|
|
with self.assertRaisesRegex(ValueError, "Unsafe dependency"):
|
|
dependencies.prepare(self.root)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|