Files
Unlucky-Launcher/crates/core/tests/game/layout.rs
T
AskaEth bc41dfcf7d
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
Unlucky Launcher v0.0.1-alpha
Rust 原生单文件 Minecraft 启动器:CLI 完整可用(install/launch/accounts/java/diagnose/agent),
MCP server 接入外部 Agent 工具,GUI 开发中。
2026-10-31 21:54:10 +08:00

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()]
);
}