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
Guards / core 零 UI 守门 (push) Waiting to run
Guards / 文件行数 ≤400 守门 (push) Waiting to run
Guards / 依赖许可与来源(cargo-deny) (push) Waiting to run
单提交快照:main 只保留已发布版本的代码树,开发史在 dev。
198 lines
6.8 KiB
Rust
198 lines
6.8 KiB
Rust
//! `ull java list` / `ull java install` / `ull launch` 的 Java 选择(D13)CLI 端。
|
||
//!
|
||
//! 不联网:`install` 只测参数校验与稳定错误码;`launch --dry-run` 断言 `java` 块的结构
|
||
//! 与「实例覆盖优先」——真正的下载/解压由 `core::game::java` 的测试与本机验收覆盖。
|
||
|
||
#[path = "support/mod.rs"]
|
||
mod support;
|
||
|
||
use support::{parse_json, run_cli, write_version, TempDir};
|
||
|
||
/// 一个可组装的版本 id。
|
||
const VERSION: &str = "1.20.1";
|
||
|
||
#[test]
|
||
fn java_list_reports_the_managed_root() {
|
||
let dir = TempDir::new("java-list");
|
||
let (code, stdout, stderr) = run_cli(&["--json", "--data-dir", &dir.arg(), "java", "list"]);
|
||
assert_eq!(code, 0, "stderr: {stderr}");
|
||
let envelope = parse_json(&stdout);
|
||
let json = &envelope["data"];
|
||
assert!(
|
||
json["managed_root"]
|
||
.as_str()
|
||
.is_some_and(|root| root.ends_with("java-managed")),
|
||
"managed_root 应是 data/java-managed:{json}"
|
||
);
|
||
// D23:多根扫描 + 落点模式要说清(`--data-dir` 是 custom 数据模式 → portable 落点)。
|
||
assert!(
|
||
json["managed_roots"]
|
||
.as_array()
|
||
.is_some_and(|roots| !roots.is_empty()),
|
||
"{json}"
|
||
);
|
||
assert_eq!(json["install"]["mode"], "portable", "{json}");
|
||
assert!(
|
||
json["install"]["root"]
|
||
.as_str()
|
||
.is_some_and(|root| root.ends_with("java-managed")),
|
||
"落点根应是数据根里的 java-managed:{json}"
|
||
);
|
||
assert!(json["candidates"].is_array(), "{json}");
|
||
}
|
||
|
||
#[test]
|
||
fn java_vendors_lists_the_four_vendors_with_the_current_order() {
|
||
let dir = TempDir::new("java-vendors");
|
||
let (code, stdout, stderr) = run_cli(&["--json", "--data-dir", &dir.arg(), "java", "vendors"]);
|
||
assert_eq!(code, 0, "stderr: {stderr}");
|
||
let envelope = parse_json(&stdout);
|
||
let json = &envelope["data"];
|
||
let order: Vec<&str> = json["order"]
|
||
.as_array()
|
||
.expect("order 数组")
|
||
.iter()
|
||
.filter_map(|item| item.as_str())
|
||
.collect();
|
||
assert_eq!(order.len(), 4, "{json}");
|
||
assert!(order.contains(&"temurin"), "{json}");
|
||
assert!(order.contains(&"oracle"), "{json}");
|
||
assert!(order.contains(&"microsoft"), "{json}");
|
||
assert!(order.contains(&"zulu"), "{json}");
|
||
// 本机时区/语言决定 domestic/global,两种默认序都合法,但来源必须明示。
|
||
let source = json["priority_source"].as_str().unwrap_or("?");
|
||
assert_eq!(
|
||
source, "region_default",
|
||
"没设 java.vendorPriority 时:{json}"
|
||
);
|
||
let vendors = json["vendors"].as_array().expect("vendors 数组");
|
||
assert_eq!(vendors.len(), 4);
|
||
let microsoft = vendors
|
||
.iter()
|
||
.find(|row| row["vendor"] == "microsoft")
|
||
.expect("应有 microsoft 行");
|
||
assert_eq!(
|
||
microsoft["supported"].as_array().map(Vec::len),
|
||
Some(4),
|
||
"MS 只发 25/21/17/11:{microsoft}"
|
||
);
|
||
assert!(
|
||
microsoft["sources"]
|
||
.as_array()
|
||
.is_some_and(|sources| !sources.is_empty()),
|
||
"MS 的源(aka.ms → VS CDN)要列出来:{microsoft}"
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn java_install_rejects_an_unknown_vendor_without_network() {
|
||
let dir = TempDir::new("java-vendor-bad");
|
||
let (code, stdout, _stderr) = run_cli(&[
|
||
"--json",
|
||
"--data-dir",
|
||
&dir.arg(),
|
||
"java",
|
||
"install",
|
||
"21",
|
||
"--vendor",
|
||
"graalvm",
|
||
]);
|
||
assert_eq!(code, 1);
|
||
let envelope = parse_json(&stdout);
|
||
assert_eq!(envelope["error"]["code"], "usage", "{envelope}");
|
||
}
|
||
|
||
#[test]
|
||
fn java_install_rejects_an_absurd_major_without_network() {
|
||
let dir = TempDir::new("java-install");
|
||
let (code, stdout, _stderr) =
|
||
run_cli(&["--json", "--data-dir", &dir.arg(), "java", "install", "4"]);
|
||
assert_eq!(code, 1);
|
||
let envelope = parse_json(&stdout);
|
||
assert_eq!(envelope["error"]["code"], "usage", "{envelope}");
|
||
}
|
||
|
||
#[test]
|
||
fn launch_dry_run_reports_the_java_requirement_of_the_version() {
|
||
let dir = TempDir::new("java-launch");
|
||
write_version(dir.path(), VERSION);
|
||
let (code, stdout, stderr) = run_cli(&[
|
||
"--json",
|
||
"--data-dir",
|
||
&dir.arg(),
|
||
"launch",
|
||
VERSION,
|
||
"--dry-run",
|
||
]);
|
||
assert_eq!(code, 0, "stderr: {stderr}");
|
||
let envelope = parse_json(&stdout);
|
||
let json = &envelope["data"];
|
||
let java = &json["java"];
|
||
// 1.20.1 的清单没写 javaVersion → 静态表兜底 17。
|
||
assert_eq!(java["required_major"], 17, "{json}");
|
||
assert_eq!(java["requirement_source"], "table", "{json}");
|
||
assert_eq!(json["version_id"], VERSION);
|
||
// `source` 只可能是探测到的来源之一(本机未必装了 Java)。
|
||
let source = java["source"].as_str().unwrap_or("none");
|
||
assert!(
|
||
["instance", "managed", "JAVA_HOME", "PATH", "none"].contains(&source),
|
||
"意外的来源:{source}"
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn instance_java_override_wins_over_the_system_java() {
|
||
let dir = TempDir::new("java-override");
|
||
write_version(dir.path(), VERSION);
|
||
// 故意给一个不存在的路径:只要能进 `source=instance` 就说明覆盖优先级生效
|
||
// (探测失败只影响 major,不该把用户指定的路径换成系统 Java)。
|
||
let fake = dir.path().join("some-jdk").join("bin").join("java");
|
||
let (code, stdout, stderr) = run_cli(&[
|
||
"--json",
|
||
"--data-dir",
|
||
&dir.arg(),
|
||
"launch",
|
||
VERSION,
|
||
"--dry-run",
|
||
"--java",
|
||
&fake.display().to_string(),
|
||
]);
|
||
assert_eq!(code, 0, "stderr: {stderr}");
|
||
let envelope = parse_json(&stdout);
|
||
let json = &envelope["data"];
|
||
assert_eq!(json["java"]["source"], "instance", "{json}");
|
||
assert_eq!(
|
||
json["java"]["binary"].as_str(),
|
||
Some(fake.display().to_string().as_str())
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn legacy_version_requires_java_8_and_flags_a_newer_runtime() {
|
||
let dir = TempDir::new("java-legacy");
|
||
write_version(dir.path(), "1.12.2");
|
||
let (code, stdout, stderr) = run_cli(&[
|
||
"--json",
|
||
"--data-dir",
|
||
&dir.arg(),
|
||
"launch",
|
||
"1.12.2",
|
||
"--dry-run",
|
||
]);
|
||
assert_eq!(code, 0, "stderr: {stderr}");
|
||
let envelope = parse_json(&stdout);
|
||
let json = &envelope["data"];
|
||
assert_eq!(json["java"]["required_major"], 8, "{json}");
|
||
assert_eq!(json["java"]["required"], "Java 8", "{json}");
|
||
// 本机若有比 8 新的 Java,必须给出 java_too_new 告警(不能只靠「最低版本」)。
|
||
if let Some(code) = json["java"]["mismatch"]["code"].as_str() {
|
||
assert!(code == "java_too_new" || code == "java_too_old", "{json}");
|
||
assert!(
|
||
json["warnings"]
|
||
.as_array()
|
||
.is_some_and(|warnings| !warnings.is_empty()),
|
||
"不符时必须进 warnings:{json}"
|
||
);
|
||
}
|
||
}
|