feat: detect cpu arch in runtime

This commit is contained in:
Louis 2025-08-20 21:37:34 +07:00
parent f7df8d2a38
commit ebae86f3e6
No known key found for this signature in database
GPG Key ID: 44FA9F4D33C37DE2
2 changed files with 151 additions and 1 deletions

View File

@ -24,11 +24,38 @@ impl CpuStaticInfo {
CpuStaticInfo { CpuStaticInfo {
name, name,
core_count: System::physical_core_count().unwrap_or(0), core_count: System::physical_core_count().unwrap_or(0),
arch: std::env::consts::ARCH.to_string(), arch: CpuStaticInfo::get_runtime_arch(),
extensions: CpuStaticInfo::get_extensions(), extensions: CpuStaticInfo::get_extensions(),
} }
} }
fn get_runtime_arch() -> String {
// Use sysinfo to get the actual CPU architecture at runtime
let mut system = System::new();
system.refresh_cpu_all();
if let Some(cpu) = system.cpus().first() {
let brand = cpu.brand().to_lowercase();
// Detect architecture based on CPU brand/model
if brand.contains("aarch64") || brand.contains("arm64") || brand.contains("apple m") {
"aarch64".to_string()
} else if brand.contains("x86")
|| brand.contains("amd64")
|| brand.contains("i386")
|| brand.contains("i686")
{
"x86_64".to_string()
} else {
// Fallback to compile-time architecture if we can't detect
std::env::consts::ARCH.to_string()
}
} else {
// Fallback to compile-time architecture if no CPU info available
std::env::consts::ARCH.to_string()
}
}
// TODO: see if we need to check for all CPU extensions // TODO: see if we need to check for all CPU extensions
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn get_extensions() -> Vec<String> { fn get_extensions() -> Vec<String> {

View File

@ -1,4 +1,5 @@
use crate::commands::*; use crate::commands::*;
use crate::types::CpuStaticInfo;
use tauri::test::mock_app; use tauri::test::mock_app;
#[test] #[test]
@ -14,3 +15,125 @@ fn test_system_usage() {
let usage = get_system_usage(app.handle().clone()); let usage = get_system_usage(app.handle().clone());
println!("System Usage Info: {:?}", usage); println!("System Usage Info: {:?}", usage);
} }
#[cfg(test)]
mod cpu_tests {
use super::*;
#[test]
fn test_cpu_static_info_new() {
let cpu_info = CpuStaticInfo::new();
// Test that all fields are populated
assert!(!cpu_info.name.is_empty());
assert_ne!(cpu_info.name, "unknown"); // Should have detected a CPU name
assert!(cpu_info.core_count > 0);
assert!(!cpu_info.arch.is_empty());
// Architecture should be one of the expected values
assert!(
cpu_info.arch == "aarch64" ||
cpu_info.arch == "x86_64" ||
cpu_info.arch == std::env::consts::ARCH
);
// Extensions should be a valid list (can be empty on non-x86)
assert!(cpu_info.extensions.is_empty() || !cpu_info.extensions.is_empty());
println!("CPU Info: {:?}", cpu_info);
}
#[test]
fn test_cpu_info_consistency() {
// Test that multiple calls return consistent information
let info1 = CpuStaticInfo::new();
let info2 = CpuStaticInfo::new();
assert_eq!(info1.name, info2.name);
assert_eq!(info1.core_count, info2.core_count);
assert_eq!(info1.arch, info2.arch);
assert_eq!(info1.extensions, info2.extensions);
}
#[test]
fn test_cpu_name_not_empty() {
let cpu_info = CpuStaticInfo::new();
assert!(!cpu_info.name.is_empty());
assert!(cpu_info.name.len() > 0);
}
#[test]
fn test_core_count_positive() {
let cpu_info = CpuStaticInfo::new();
assert!(cpu_info.core_count > 0);
}
#[test]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn test_x86_extensions() {
let cpu_info = CpuStaticInfo::new();
// On x86/x86_64, we should always have at least FPU
assert!(cpu_info.extensions.contains(&"fpu".to_string()));
// Check that all extensions are valid x86 feature names
let valid_extensions = [
"fpu", "mmx", "sse", "sse2", "sse3", "ssse3", "sse4_1", "sse4_2",
"pclmulqdq", "avx", "avx2", "avx512_f", "avx512_dq", "avx512_ifma",
"avx512_pf", "avx512_er", "avx512_cd", "avx512_bw", "avx512_vl",
"avx512_vbmi", "avx512_vbmi2", "avx512_vnni", "avx512_bitalg",
"avx512_vpopcntdq", "avx512_vp2intersect", "aes", "f16c"
];
for ext in &cpu_info.extensions {
assert!(
valid_extensions.contains(&ext.as_str()),
"Unknown extension: {}",
ext
);
}
}
#[test]
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
fn test_non_x86_extensions() {
let cpu_info = CpuStaticInfo::new();
// On non-x86 architectures, extensions should be empty
assert!(cpu_info.extensions.is_empty());
}
#[test]
fn test_arch_detection() {
let cpu_info = CpuStaticInfo::new();
// Architecture should be a valid string
assert!(!cpu_info.arch.is_empty());
// Should be one of the common architectures
let common_archs = ["x86_64", "aarch64", "arm", "x86"];
let is_common_arch = common_archs.iter().any(|&arch| cpu_info.arch == arch);
let is_compile_time_arch = cpu_info.arch == std::env::consts::ARCH;
assert!(
is_common_arch || is_compile_time_arch,
"Unexpected architecture: {}",
cpu_info.arch
);
}
#[test]
fn test_cpu_info_serialization() {
let cpu_info = CpuStaticInfo::new();
// Test that the struct can be serialized (since it derives Serialize)
let serialized = serde_json::to_string(&cpu_info);
assert!(serialized.is_ok());
let json_str = serialized.unwrap();
assert!(json_str.contains("name"));
assert!(json_str.contains("core_count"));
assert!(json_str.contains("arch"));
assert!(json_str.contains("extensions"));
}
}