release: v0.2.4 里程碑快照(孤儿提交,无父;树 = dev 68362e5)
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

本批内容见 CHANGELOG.md 的 0.2.4 段。按 D41 用孤儿快照推 main,保留「公开史只有里程碑」的模型。
This commit is contained in:
AskaEth committed 2026-11-11 21:41:38 +08:00
commit a5d1dec495
1580 files changed
+320938

No files matched your search

+166
View File
@@ -0,0 +1,166 @@
//! game 快照 → `LaunchRequest` 适配层测试(缺失库显式记录、features 派生)。
use crate::prelude::*;
fn write_file(path: &std::path::Path, content: &str) {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent).expect("建目录");
}
std::fs::write(path, content).expect("写文件");
}
fn jvm_tokens() -> Vec<String> {
vec![
"-Djava.library.path=${natives_directory}".to_string(),
"-cp".to_string(),
"${classpath}".to_string(),
]
}
fn game_tokens() -> Vec<String> {
vec![
"--username".to_string(),
"${auth_player_name}".to_string(),
"--version".to_string(),
"${version_name}".to_string(),
]
}
#[test]
fn adapter_records_missing_libraries_and_fails_before_start() {
let dir = temp_dir("adapter-strict");
let lib_ok = dir.join("libraries/a.jar");
let lib_missing = dir.join("libraries/b.jar");
let primary = dir.join("versions/1.20.1/1.20.1.jar");
write_file(&lib_ok, "x");
write_file(&primary, "x");
let libraries = vec![
LibraryInput::new(lib_ok.clone(), "org.example:a:1"),
LibraryInput::new(lib_missing.clone(), "org.example:b:2"),
];
let jvm = jvm_tokens();
let game = game_tokens();
let snapshot = SnapshotInput {
version_name: Some("1.20.1 Fabric"),
version_type: Some("release"),
game_version: Some("1.20.1"),
main_class: Some("net.fabricmc.loader.impl.launch.knot.KnotClient"),
jvm_arguments: &jvm,
game_arguments: &game,
libraries: &libraries,
primary_jar: primary.clone(),
game_dir: dir.clone(),
assets_index: Some("17"),
natives_dir: dir.join("versions/1.20.1/natives"),
library_dir: dir.join("libraries"),
assets_root: dir.join("assets"),
game_assets: dir.join("assets/virtual/legacy"),
..SnapshotInput::minimal("1.20.1", "")
};
let mut options = AdapterOptions::strict();
options.java = JavaSelection::new("C:/java/bin/java.exe", 21, 64);
options.memory = MemoryOptions {
min_mb: Some(512),
max_mb: Some(2048),
};
options.resolution = Some(Resolution {
width: 1280,
height: 720,
});
let output = build_request(&snapshot, options);
assert_eq!(output.classpath_entries, 2, "a.jar + 主 jar");
assert_eq!(output.missing.len(), 1);
assert_eq!(output.missing[0].name, "org.example:b:2");
assert!(output.warnings.iter().any(|warning| matches!(
warning,
AdapterWarning::MissingLibrary { name, .. } if name == "org.example:b:2"
)));
// features 派生:给了分辨率就打开 has_custom_resolution;1.20.1 支持 QuickPlay。
assert!(output.request.features.has_custom_resolution);
assert!(output.request.features.has_quick_plays_support);
assert_eq!(output.request.instance.name, "1.20.1 Fabric");
assert_eq!(
output.request.manifest.main_class.as_deref(),
Some("net.fabricmc.loader.impl.launch.knot.KnotClient")
);
// 严格模式下缺失库 → 组装前显式失败(不再交给运行时 NoClassDefFoundError)。
let ctx = PlatformCtx::new(OsFamily::Windows, "x86_64");
match assemble(&output.request, &ctx) {
Err(LaunchError::Assembly(AssemblyError::MissingComponents(items))) => {
assert_eq!(items.len(), 1);
}
other => panic!("应因缺组件失败,实际:{other:?}"),
}
}
#[test]
fn adapter_tolerant_mode_skips_missing_libraries() {
let dir = temp_dir("adapter-tolerant");
let lib_missing = dir.join("libraries/b.jar");
let libraries = vec![LibraryInput::new(lib_missing.clone(), "org.example:b:2")];
let jvm = jvm_tokens();
let snapshot = SnapshotInput {
main_class: Some("net.minecraft.client.main.Main"),
jvm_arguments: &jvm,
libraries: &libraries,
primary_jar: dir.join("client.jar"),
game_dir: dir.clone(),
assets_index: Some("5"),
..SnapshotInput::minimal("1.12.2", "")
};
let mut options = AdapterOptions::strict();
options.check_classpath = false;
options.java = JavaSelection::new("java", 8, 64);
let output = build_request(&snapshot, options);
assert!(
!output.request.classpath.is_empty(),
"宽松模式仍保留库(交给启动期报错)"
);
assert!(output.request.missing_components.is_empty());
// 宽松模式不报缺失库(其它告警——如缺游戏参数——与本用例无关)。
assert!(!output
.warnings
.iter()
.any(|warning| matches!(warning, AdapterWarning::MissingLibrary { .. })));
}
#[test]
fn adapter_warns_on_incomplete_manifest() {
let dir = temp_dir("adapter-incomplete");
let snapshot = SnapshotInput {
main_class: None,
..SnapshotInput::minimal("1.7.10", "")
};
let output = build_request(&snapshot, AdapterOptions::strict());
let warnings = &output.warnings;
assert!(warnings.contains(&AdapterWarning::MissingMainClass));
assert!(warnings.contains(&AdapterWarning::MissingAssetsIndex));
assert!(warnings.contains(&AdapterWarning::MissingGameArguments));
// 1.7.10 < 1.20:不支持 QuickPlay。
assert!(!output.request.features.has_quick_plays_support);
assert_eq!(
output.request.dirs.assets_index, "legacy",
"缺索引时兜底 legacy"
);
let _ = dir;
}
#[test]
fn adapter_legacy_arguments_are_passed_through() {
let dir = temp_dir("adapter-legacy");
let snapshot = SnapshotInput {
main_class: Some("net.minecraft.launchwrapper.Launch"),
legacy_arguments: Some("--username ${auth_player_name} --gameDir ${game_directory}"),
game_dir: dir.clone(),
..SnapshotInput::minimal("1.12.2", "")
};
let output = build_request(&snapshot, AdapterOptions::strict());
assert!(output.request.manifest.is_legacy());
assert!(output.request.manifest.game.is_empty());
assert!(!output
.warnings
.contains(&AdapterWarning::MissingGameArguments));
}