Files
Unlucky-Launcher/crates/core/tests/liteloader/support.rs
T
AskaEth a5d1dec495
CI / fmt (rustfmt --check) (push) Waiting to run
CI / clippy (ubuntu-latest) (push) Waiting to run
CI / clippy (windows-latest) (push) Waiting to run
CI / test (ubuntu-latest) (push) Waiting to run
CI / test (windows-latest) (push) Waiting to run
CI / build release (ubuntu-latest) (push) Waiting to run
CI / build release (windows-latest) (push) Waiting to run
CI / runtime smoke (ubuntu x64, xvfb) (push) Waiting to run
CI / runtime smoke (macos arm64, probe) (push) Skipped
CI / update-e2e (windows x64) (push) Waiting to run
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Waiting to run
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Waiting to run
Guards / core 零 UI 守门 (push) Waiting to run
Guards / presenter 零 UI 守门 (push) Waiting to run
Guards / design 零第三方依赖守门 (push) Waiting to run
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Waiting to run
Guards / 文件行数 ≤400 守门 (push) Waiting to run
Guards / 依赖许可与来源(cargo-deny) (push) Waiting to run
Release guard / 发布标记语义守门(push tag → 稳定版) (push) Waiting to run
Signing status(播报,不是门禁) / 判据自测(三行/退出码/变异重放/接线) (push) Waiting to run
Signing status(播报,不是门禁) / 签名就绪度(播报) (push) Waiting to run
Release / 选槽位(默认四个:Windows 两档 + Linux 两档) (push) Waiting to run
release: v0.2.4 里程碑快照(孤儿提交,无父;树 = dev 68362e5)
本批内容见 CHANGELOG.md 的 0.2.4 段。按 D41 用孤儿快照推 main,保留「公开史只有里程碑」的模型。
2026-11-11 21:41:38 +08:00

158 lines
5.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 集成测试公用件:临时游戏目录、离线下载引擎、元数据夹具、父版本清单夹具。
#![allow(dead_code)]
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;
use unlucky_core::addon::loader::liteloader::{self, LiteLoaderEntry};
use unlucky_core::addon::loader::LoaderRelease;
use unlucky_core::download::backoff::BackoffPolicy;
use unlucky_core::download::cache::ContentCache;
use unlucky_core::download::chunked::ChunkedConfig;
use unlucky_core::download::config::EngineConfig;
use unlucky_core::download::engine::DownloadEngine;
use unlucky_core::download::health::HealthRegistry;
use unlucky_core::download::mirrors::MirrorTable;
use unlucky_core::download::region::RegionProfile;
use unlucky_core::game::layout::GameLayout;
use unlucky_core::game::manifest::VersionJson;
/// 真实响应的裁剪版(见 `tests/liteloader.rs` 文件头的来源说明)。
pub const VERSION_TABLE: &str = include_str!("fixtures/liteloader-versions.json");
/// 1.12.2-SNAPSHOT 的 maven-metadata(真实响应原样)。
pub const METADATA_XML: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>com.mumfrey</groupId>
<artifactId>liteloader</artifactId>
<version>1.12.2-SNAPSHOT</version>
<versioning>
<snapshot>
<timestamp>20171128.144431</timestamp>
<buildNumber>4</buildNumber>
</snapshot>
<lastUpdated>20171128144431</lastUpdated>
</versioning>
</metadata>"#;
/// 原版 1.12.2(老式 `minecraftArguments`,与官方清单同形)。
pub const VANILLA_1122: &str = r#"{
"id": "1.12.2",
"type": "release",
"mainClass": "net.minecraft.client.main.Main",
"assets": "1.12",
"assetIndex": {"id": "1.12"},
"minecraftArguments": "--username ${auth_player_name} --version ${version_name}",
"libraries": [
{"name": "com.mojang:patchy:1.1", "url": "https://libraries.minecraft.net/"}
]
}"#;
/// Forge 1.12.2-14.23.5.2860(spec-0 的 version.json:主类 launchwrapper + FMLTweaker)。
pub const FORGE_1122: &str = r#"{
"id": "1.12.2-forge-14.23.5.2860",
"inheritsFrom": "1.12.2",
"mainClass": "net.minecraft.launchwrapper.Launch",
"minecraftArguments": "--tweakClass net.minecraftforge.fml.common.launcher.FMLTweaker",
"libraries": [
{"name": "net.minecraftforge:forge:1.12.2-14.23.5.2860", "url": "https://maven.minecraftforge.net/"}
]
}"#;
/// 唯一临时目录守卫(`drop` 时递归删除)。
pub struct TempDir {
path: PathBuf,
}
impl TempDir {
pub fn new(tag: &str) -> Self {
static COUNTER: AtomicUsize = AtomicUsize::new(0);
let unique = COUNTER.fetch_add(1, Ordering::Relaxed);
let stamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_or(0, |since| since.as_nanos());
let path = std::env::temp_dir().join(format!(
"ull-liteloader-{tag}-{}-{stamp}-{unique}",
std::process::id()
));
let _ = std::fs::remove_dir_all(&path);
std::fs::create_dir_all(&path).expect("临时目录应可创建");
Self { path }
}
pub fn path(&self) -> &Path {
&self.path
}
}
impl Drop for TempDir {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.path);
}
}
/// 离线引擎:单次尝试、短超时、分块关闭(测试里只用来跑「全部跳过」的执行段)。
pub fn engine(cache_root: &Path) -> DownloadEngine {
let config = EngineConfig {
max_attempts_per_source: 1,
backoff: BackoffPolicy {
base_ms: 1,
max_ms: 2,
jitter_ratio: 0.0,
max_attempts: 1,
},
connect_timeout: Duration::from_secs(3),
read_timeout: Duration::from_secs(3),
chunked: ChunkedConfig::disabled(),
..EngineConfig::default()
};
DownloadEngine::new(
config,
Arc::new(MirrorTable::builtin()),
Arc::new(RegionProfile::direct(0)),
Arc::new(HealthRegistry::in_memory()),
Arc::new(ContentCache::open(cache_root.join("cache"))),
)
}
/// 解析夹具表里某个原版版本的条目。
pub fn entries(mc_version: &str) -> Vec<LiteLoaderEntry> {
let table = liteloader::parse_versions(VERSION_TABLE).expect("夹具版本表应可解析");
table.get(mc_version).cloned().unwrap_or_default()
}
/// 取 1.12.2 的 SNAPSHOT 条目并合成 release(父版本由调用方给)。
pub fn liteloader_release(parent_version_id: &str) -> LoaderRelease {
let entries = entries("1.12.2");
let entry = liteloader::default_entry(&entries).expect("1.12.2 应有一条版本");
let url = liteloader::snapshot_artifact_url(entry, METADATA_XML).expect("快照直链");
liteloader::build_release("1.12.2", parent_version_id, entry, &url).expect("合成清单")
}
/// 把一份版本 JSON 写进 `versions/<id>/<id>.json`(模拟「父版本已装好」)。
pub fn put_version(layout: &GameLayout, id: &str, json: &str) {
let path = layout.version_json(id);
std::fs::create_dir_all(path.parent().expect("版本目录")).expect("建版本目录");
std::fs::write(&path, json).expect("写版本 JSON");
}
/// 读盘上的版本 JSON。
pub fn read_version(layout: &GameLayout, id: &str) -> VersionJson {
let path = layout.version_json(id);
let text = std::fs::read_to_string(&path)
.unwrap_or_else(|error| panic!("读不到 {}:{error}", path.display()));
VersionJson::from_json_str(&text).expect("版本 JSON 应可解析")
}
/// 按计划把每个待下文件写成占位字节(`execute` 的跳过判据 = 无 sha1 且存在即信)。
pub fn prefill(plan: &unlucky_core::game::install::InstallPlan) -> usize {
for file in &plan.files {
std::fs::create_dir_all(file.dest.parent().expect("文件父目录")).expect("建库目录");
std::fs::write(&file.dest, b"placeholder").expect("写占位文件");
}
plan.files.len()
}