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
Guards / core 零 UI 守门 (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Rust 原生单文件 Minecraft 启动器:CLI 完整可用(install/launch/accounts/java/diagnose/agent), MCP server 接入外部 Agent 工具,GUI 开发中。
144 lines
4.5 KiB
Rust
144 lines
4.5 KiB
Rust
//! `.minecraft` 目录布局:路径规则、Maven 坐标、已安装版本枚举。
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use unlucky_core::game::error::GameError;
|
|
use unlucky_core::game::layout::GameLayout;
|
|
use unlucky_core::game::library::MavenCoordinate;
|
|
|
|
use crate::helpers::TempDir;
|
|
|
|
fn layout() -> (TempDir, GameLayout) {
|
|
let temp = TempDir::new("layout");
|
|
let layout = GameLayout::new(temp.path().to_path_buf());
|
|
(temp, layout)
|
|
}
|
|
|
|
#[test]
|
|
fn version_paths_follow_mojang_layout() {
|
|
let (_temp, layout) = layout();
|
|
let root = layout.root();
|
|
|
|
assert_eq!(layout.versions_dir(), root.join("versions"));
|
|
assert_eq!(layout.version_dir("1.20.1"), root.join("versions/1.20.1"));
|
|
assert_eq!(
|
|
layout.version_json("1.20.1"),
|
|
root.join("versions/1.20.1/1.20.1.json")
|
|
);
|
|
assert_eq!(
|
|
layout.version_jar("1.20.1"),
|
|
root.join("versions/1.20.1/1.20.1.jar")
|
|
);
|
|
// ULL 扩展:实例私有状态与 natives 解压目录。
|
|
assert_eq!(
|
|
layout.version_state_dir("1.20.1"),
|
|
root.join("versions/1.20.1/.ull")
|
|
);
|
|
assert_eq!(
|
|
layout.natives_dir("1.20.1"),
|
|
root.join("versions/1.20.1/natives")
|
|
);
|
|
assert_eq!(
|
|
layout.version_libraries_dir("1.20.1"),
|
|
root.join("versions/1.20.1/libraries")
|
|
);
|
|
assert_eq!(layout.ull_root(), root.join(".ull"));
|
|
}
|
|
|
|
#[test]
|
|
fn asset_paths_use_two_level_sharding() {
|
|
let (_temp, layout) = layout();
|
|
let root = layout.root();
|
|
|
|
assert_eq!(layout.asset_index("5"), root.join("assets/indexes/5.json"));
|
|
let hash = "a3b3b1b0e0d8f2a8f0fbe1a4b0a8f6a1c6c6a7a2";
|
|
assert_eq!(
|
|
layout.asset_object(hash).expect("哈希合法"),
|
|
root.join("assets/objects/a3").join(hash)
|
|
);
|
|
assert_eq!(
|
|
layout.log_config("client-1.12.xml"),
|
|
root.join("assets/log_configs/client-1.12.xml")
|
|
);
|
|
|
|
let error = layout.asset_object("a").expect_err("短哈希应报错");
|
|
assert!(matches!(error, GameError::InvalidAssetHash { .. }));
|
|
}
|
|
|
|
#[test]
|
|
fn maven_coordinates_parse_and_lay_out() {
|
|
let coordinate = MavenCoordinate::parse("org.lwjgl:lwjgl:3.3.2").expect("坐标应可解析");
|
|
assert_eq!(coordinate.group, "org.lwjgl");
|
|
assert_eq!(coordinate.group_path(), "org/lwjgl");
|
|
assert_eq!(coordinate.file_name(), "lwjgl-3.3.2.jar");
|
|
assert_eq!(
|
|
coordinate.relative_path(),
|
|
PathBuf::from("org/lwjgl/lwjgl/3.3.2/lwjgl-3.3.2.jar")
|
|
);
|
|
|
|
let natives =
|
|
MavenCoordinate::parse("org.lwjgl:lwjgl:3.3.2:natives-windows-64").expect("带分类器");
|
|
assert_eq!(
|
|
natives.relative_path(),
|
|
PathBuf::from("org/lwjgl/lwjgl/3.3.2/lwjgl-3.3.2-natives-windows-64.jar")
|
|
);
|
|
|
|
let non_jar = MavenCoordinate::parse("net.java:jinput:1.0.0@zip").expect("带扩展名");
|
|
assert_eq!(non_jar.file_name(), "jinput-1.0.0.zip");
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_coordinates_are_rejected() {
|
|
for raw in ["a:b", "a::1", "a:b:c:d:e"] {
|
|
let error = MavenCoordinate::parse(raw).expect_err("不合法坐标应报错");
|
|
assert!(
|
|
matches!(error, GameError::InvalidCoordinate { .. }),
|
|
"{raw}"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn library_paths_land_under_libraries() {
|
|
let (_temp, layout) = layout();
|
|
let root = layout.root();
|
|
let coordinate = MavenCoordinate::parse("com.mojang:logging:1.1.1").expect("坐标应可解析");
|
|
|
|
assert_eq!(
|
|
layout.library(&coordinate),
|
|
root.join("libraries/com/mojang/logging/1.1.1/logging-1.1.1.jar")
|
|
);
|
|
assert_eq!(
|
|
layout.version_library("1.20.1", &coordinate),
|
|
root.join("versions/1.20.1/libraries/com/mojang/logging/1.1.1/logging-1.1.1.jar")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn installed_versions_requires_manifest_file() {
|
|
let (_temp, layout) = layout();
|
|
|
|
assert!(
|
|
layout
|
|
.installed_versions()
|
|
.expect("缺目录视为空")
|
|
.is_empty(),
|
|
"目录不存在时不能报错"
|
|
);
|
|
|
|
let version_dir = layout.version_dir("1.20.1");
|
|
std::fs::create_dir_all(&version_dir).expect("建目录");
|
|
std::fs::write(layout.version_json("1.20.1"), "{}").expect("写清单");
|
|
// 只有 jar 没有 json 的目录不算已安装。
|
|
let broken = layout.version_dir("1.19.4");
|
|
std::fs::create_dir_all(&broken).expect("建目录");
|
|
std::fs::write(layout.version_jar("1.19.4"), b"jar").expect("写 jar");
|
|
|
|
assert!(layout.is_version_installed("1.20.1"));
|
|
assert!(!layout.is_version_installed("1.19.4"));
|
|
assert_eq!(
|
|
layout.installed_versions().expect("应可枚举"),
|
|
vec!["1.20.1".to_owned()]
|
|
);
|
|
}
|