CI / runtime smoke (macos arm64, probe) (push) Skipped
CI / fmt (rustfmt --check) (push) Canceled after 0s
CI / clippy (ubuntu-latest) (push) Canceled after 0s
CI / clippy (windows-latest) (push) Canceled after 0s
CI / test (ubuntu-latest) (push) Canceled after 0s
CI / test (windows-latest) (push) Canceled after 0s
CI / build release (ubuntu-latest) (push) Canceled after 0s
CI / build release (windows-latest) (push) Canceled after 0s
CI / runtime smoke (ubuntu x64, xvfb) (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好一个文件) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Release / 选槽位(默认两个 Windows) (push) Canceled after 0s
Release / build (${{ matrix.slot }}) (push) Canceled after 0s
Release / GitHub Release (push) Canceled after 0s
214 lines
7.8 KiB
Rust
214 lines
7.8 KiB
Rust
//! host/install_processors.rs 的单测(拆文件守 400 行上限)。
|
||
//!
|
||
//! 夹具在 [`super::fixture`]:这里只断言「计划怎么组」「找不到 Java 怎么说」
|
||
//! 「哪一步挂了怎么报」「进度文案长什么样」。
|
||
|
||
use std::path::PathBuf;
|
||
|
||
use unlucky_core::addon::loader::forge::InstallProfile;
|
||
use unlucky_core::addon::loader::processors::{ProcessorError, StepStatus};
|
||
use unlucky_core::addon::loader::LoaderKind as CoreLoaderKind;
|
||
use unlucky_core::download::DownloadErrorKind;
|
||
use unlucky_core::game::install::{FileFailure, FileOutcome, FileRole, FileStatus, InstallReport};
|
||
use unlucky_core::game::java::JavaRequirement;
|
||
|
||
use super::fixture::{input, installer, outcome, profile, Fake, Harness};
|
||
use super::{diagnostic, java_missing, libraries_failed, stage, step_line, StageInput};
|
||
|
||
#[test]
|
||
fn step_line_names_the_step_and_marks_the_outcome() {
|
||
let executed = step_line(&outcome(StepStatus::Executed), 9);
|
||
assert_eq!(
|
||
executed,
|
||
"installer 处理器 3/9:net.minecraftforge:installertools:1.3.0(1234 ms)"
|
||
);
|
||
let skipped = step_line(&outcome(StepStatus::Skipped), 9);
|
||
assert!(skipped.contains("产物已就绪"), "{skipped}");
|
||
}
|
||
|
||
#[test]
|
||
fn diagnostic_keeps_the_stable_code_and_the_hint() {
|
||
let failed = ProcessorError::failed("net.minecraftforge:installertools:1.3.0", "1", "boom")
|
||
.with_hint("确认 Java 版本与磁盘权限");
|
||
let text = diagnostic(&failed);
|
||
assert!(text.contains("processor_failed"), "{text}");
|
||
assert!(text.contains("退出码 1"), "{text}");
|
||
assert!(text.contains("boom"), "stderr 尾部要留:{text}");
|
||
assert!(text.contains("建议:确认 Java 版本"), "{text}");
|
||
|
||
let plan = diagnostic(&ProcessorError::plan("install profile 缺 `jar`"));
|
||
assert!(plan.contains("processor_plan"), "{plan}");
|
||
}
|
||
|
||
#[test]
|
||
fn java_missing_points_at_the_java_page() {
|
||
let managed = PathBuf::from("/tmp/.ULL/java");
|
||
let text = java_missing(&JavaRequirement::table(21, None), &managed);
|
||
assert!(text.contains("Java 21+"), "{text}");
|
||
assert!(text.contains("Java」页"), "{text}");
|
||
assert!(text.contains("JAVA_HOME"), "{text}");
|
||
assert!(text.contains(&managed.display().to_string()), "{text}");
|
||
}
|
||
|
||
#[test]
|
||
fn libraries_failed_lists_at_most_three() {
|
||
let files: Vec<FileOutcome> = (0..4)
|
||
.map(|index| FileOutcome {
|
||
role: FileRole::Library,
|
||
name: format!("lib{index}.jar"),
|
||
dest: PathBuf::from(format!("lib{index}.jar")),
|
||
status: FileStatus::Failed,
|
||
bytes: 0,
|
||
source: None,
|
||
failure: Some(FileFailure {
|
||
kind: DownloadErrorKind::Network,
|
||
message: "连接超时".to_string(),
|
||
}),
|
||
})
|
||
.collect();
|
||
let report = InstallReport {
|
||
files,
|
||
version_json: None,
|
||
warnings: Vec::new(),
|
||
};
|
||
let text = libraries_failed(&report);
|
||
assert!(text.contains("4 个失败,共 4 个"), "{text}");
|
||
assert!(text.contains("lib0.jar"), "{text}");
|
||
assert!(!text.contains("lib3.jar"), "最多列三条:{text}");
|
||
}
|
||
|
||
#[test]
|
||
fn stage_skips_when_the_version_json_is_already_there() {
|
||
let h = Harness::new();
|
||
let installer = installer();
|
||
let profile = profile();
|
||
let mut input = input(&profile, &installer);
|
||
input.installed = true;
|
||
let fake = Fake::new();
|
||
let result = stage(&h.env, &h.engine, &input, &fake, &h.reporter(), &h.cancel);
|
||
assert!(matches!(result, Ok(None)));
|
||
assert!(fake.calls().is_empty(), "已装完就不该再动网络/Java");
|
||
assert!(h.log().contains("跳过 installer processors"), "{}", h.log());
|
||
}
|
||
|
||
#[test]
|
||
fn stage_skips_an_empty_processor_list() {
|
||
let h = Harness::new();
|
||
let installer = installer();
|
||
let profile = InstallProfile::default();
|
||
let input = input(&profile, &installer);
|
||
let fake = Fake::new();
|
||
let result = stage(&h.env, &h.engine, &input, &fake, &h.reporter(), &h.cancel);
|
||
assert!(matches!(result, Ok(None)), "没有 processor 就没这段");
|
||
assert!(fake.calls().is_empty());
|
||
}
|
||
|
||
#[test]
|
||
fn stage_reports_missing_installer_metadata() {
|
||
let h = Harness::new();
|
||
let input = StageInput {
|
||
kind: CoreLoaderKind::Forge,
|
||
mc_version: "1.20.1",
|
||
version_id: "1.20.1-forge-47.2.0",
|
||
profile: None,
|
||
installer: None,
|
||
installed: false,
|
||
};
|
||
let fake = Fake::new();
|
||
let err = stage(&h.env, &h.engine, &input, &fake, &h.reporter(), &h.cancel).unwrap_err();
|
||
assert!(err.contains("install profile"), "{err}");
|
||
assert!(h.log().contains("失败:"), "{}", h.log());
|
||
}
|
||
|
||
#[test]
|
||
fn stage_builds_the_command_line_from_the_install_profile() {
|
||
let h = Harness::new();
|
||
let installer = installer();
|
||
let profile = profile();
|
||
let input = input(&profile, &installer);
|
||
let fake = Fake::new();
|
||
let run = stage(&h.env, &h.engine, &input, &fake, &h.reporter(), &h.cancel)
|
||
.expect("stage 成功")
|
||
.expect("应当真的跑了 processors");
|
||
assert_eq!(run.executed(), 1);
|
||
let steps = fake.steps();
|
||
assert_eq!(steps.len(), 1);
|
||
let (index, label, args, classpath) = &steps[0];
|
||
assert_eq!(*index, 1);
|
||
assert_eq!(label, "net.minecraftforge:installertools:1.3.0");
|
||
assert_eq!(args, &vec!["--task".to_string(), "MCP_DATA".to_string()]);
|
||
assert_eq!(*classpath, 1, "jar 自己不在 -cp 里(拼命令行时才补)");
|
||
assert_eq!(fake.calls(), vec!["libraries", "java", "run"]);
|
||
let messages = h.messages();
|
||
assert!(
|
||
messages.iter().any(|m| m.contains("installer 处理器 1/1")),
|
||
"面板要看到当前处理器:{messages:?}"
|
||
);
|
||
assert!(
|
||
messages
|
||
.last()
|
||
.is_some_and(|m| m.contains("正在写入版本清单")),
|
||
"{messages:?}"
|
||
);
|
||
assert!(
|
||
h.log().contains("processors 完成:执行 1 步"),
|
||
"{}",
|
||
h.log()
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn stage_fails_loudly_without_a_usable_java() {
|
||
let h = Harness::new();
|
||
let installer = installer();
|
||
let profile = profile();
|
||
let input = input(&profile, &installer);
|
||
let mut fake = Fake::new();
|
||
fake.java = None;
|
||
let err = stage(&h.env, &h.engine, &input, &fake, &h.reporter(), &h.cancel).unwrap_err();
|
||
assert!(err.contains("Java"), "{err}");
|
||
assert!(err.contains("托管目录"), "{err}");
|
||
assert_eq!(
|
||
fake.calls(),
|
||
vec!["libraries", "java"],
|
||
"没 Java 就不该起进程"
|
||
);
|
||
assert!(h.log().contains("失败:"), "{}", h.log());
|
||
}
|
||
|
||
#[test]
|
||
fn stage_fails_loudly_when_a_processor_exits_nonzero() {
|
||
let h = Harness::new();
|
||
let installer = installer();
|
||
let profile = profile();
|
||
let input = input(&profile, &installer);
|
||
let mut fake = Fake::new();
|
||
fake.run_error = Some(ProcessorError::failed(
|
||
"net.minecraftforge:installertools:1.3.0",
|
||
"1",
|
||
"java.lang.NoSuchMethodError",
|
||
));
|
||
let err = stage(&h.env, &h.engine, &input, &fake, &h.reporter(), &h.cancel).unwrap_err();
|
||
assert!(err.contains("processor_failed"), "{err}");
|
||
assert!(err.contains("NoSuchMethodError"), "{err}");
|
||
let log = h.log();
|
||
assert!(log.contains("失败:"), "{log}");
|
||
assert!(log.contains("NoSuchMethodError"), "日志要能回溯:{log}");
|
||
}
|
||
|
||
#[test]
|
||
fn stage_reports_incomplete_library_downloads() {
|
||
let h = Harness::new();
|
||
let installer = installer();
|
||
let profile = profile();
|
||
let input = input(&profile, &installer);
|
||
let mut fake = Fake::new();
|
||
fake.libraries_ok = false;
|
||
let err = stage(&h.env, &h.engine, &input, &fake, &h.reporter(), &h.cancel).unwrap_err();
|
||
assert!(err.contains("依赖库没下全"), "{err}");
|
||
assert!(err.contains("连接超时"), "{err}");
|
||
assert_eq!(fake.calls(), vec!["libraries"], "库没下全就别去选 Java");
|
||
let progress = h.meter.lock().expect("进度锁").progress.clone();
|
||
assert!(progress.files_total >= 2, "依赖库要计入文件总数");
|
||
}
|