#![allow(unused_imports)]
use std::os::raw::c_char;
use objc2::{msg_send, rc::autoreleasepool};
use crate::{
error::{Error, Result},
hardware::iokit::{FanInfo, GpuStats, IOKit, IOKitImpl, MockIOKit, ThermalInfo},
utils::{
bindings::{
smc_key_from_chars,
SMC_KEY_AMBIENT_TEMP,
SMC_KEY_BATTERY_TEMP,
SMC_KEY_CPU_TEMP,
SMC_KEY_FAN_NUM,
SMC_KEY_GPU_TEMP,
},
test_utils::{create_test_dictionary, create_test_object},
},
};
#[test]
fn test_smc_key_from_chars() {
let key = [b'T' as c_char, b'C' as c_char, b'0' as c_char, b'P' as c_char];
let result = smc_key_from_chars(key);
let expected = (b'T' as u32) << 24 | (b'C' as u32) << 16 | (b'0' as u32) << 8 | (b'P' as u32);
assert_eq!(result, expected);
}
#[test]
fn test_get_cpu_temperature() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_get_cpu_temperature().returning(|| Ok(45.5));
let result = mock_iokit.get_cpu_temperature().unwrap();
assert_eq!(result, 45.5);
}
#[test]
fn test_get_gpu_temperature() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_get_gpu_temperature().returning(|| Ok(55.0));
let result = mock_iokit.get_gpu_temperature().unwrap();
assert_eq!(result, 55.0);
}
#[test]
fn test_get_gpu_stats() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_get_gpu_stats().returning(|| {
Ok(GpuStats {
utilization: 50.0,
perf_cap: 50.0,
perf_threshold: 100.0,
memory_used: 1024 * 1024 * 1024, memory_total: 4 * 1024 * 1024 * 1024, name: "Test GPU".to_string(),
})
});
let result = mock_iokit.get_gpu_stats().unwrap();
assert_eq!(result.utilization, 50.0);
assert_eq!(result.memory_total, 4 * 1024 * 1024 * 1024);
assert_eq!(result.name, "Test GPU");
}
#[test]
fn test_io_service_matching() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_io_service_matching().returning(|_| create_test_dictionary());
let _result = mock_iokit.io_service_matching("TestService");
}
#[test]
fn test_io_service_get_matching_service() {
let mut mock_iokit = MockIOKit::new();
let dict = create_test_dictionary();
mock_iokit.expect_io_service_get_matching_service().returning(|_| None);
let result = mock_iokit.io_service_get_matching_service(&dict);
assert!(result.is_none());
}
#[test]
fn test_io_registry_entry_create_cf_properties() {
let mut mock_iokit = MockIOKit::new();
let obj = create_test_object();
mock_iokit
.expect_io_registry_entry_create_cf_properties()
.returning(|_| Ok(create_test_dictionary()));
let result = mock_iokit.io_registry_entry_create_cf_properties(&obj);
assert!(result.is_ok());
}
#[test]
fn test_get_string_property() {
let mut mock_iokit = MockIOKit::new();
let dict = create_test_dictionary();
mock_iokit.expect_get_string_property().returning(|_, key| {
if key == "TestKey" {
Some("TestValue".to_string())
} else {
None
}
});
let result = mock_iokit.get_string_property(&dict, "TestKey");
assert_eq!(result, Some("TestValue".to_string()));
let result = mock_iokit.get_string_property(&dict, "NonExistentKey");
assert_eq!(result, None);
}
#[test]
fn test_get_number_property() {
let mut mock_iokit = MockIOKit::new();
let dict = create_test_dictionary();
mock_iokit.expect_get_number_property().returning(|_, key| {
if key == "TestKey" {
Some(42)
} else {
None
}
});
let result = mock_iokit.get_number_property(&dict, "TestKey");
assert_eq!(result, Some(42));
let result = mock_iokit.get_number_property(&dict, "NonExistentKey");
assert_eq!(result, None);
}
#[test]
fn test_get_bool_property() {
let mut mock_iokit = MockIOKit::new();
let dict = create_test_dictionary();
mock_iokit.expect_get_bool_property().returning(|_, key| {
if key == "TestKey" {
Some(true)
} else {
None
}
});
let result = mock_iokit.get_bool_property(&dict, "TestKey");
assert_eq!(result, Some(true));
let result = mock_iokit.get_bool_property(&dict, "NonExistentKey");
assert_eq!(result, None);
}
#[test]
fn test_get_dict_property() {
let mut mock_iokit = MockIOKit::new();
let dict = create_test_dictionary();
mock_iokit.expect_get_dict_property().returning(|_, key| {
if key == "TestKey" {
Some(create_test_dictionary())
} else {
None
}
});
let result = mock_iokit.get_dict_property(&dict, "TestKey");
assert!(result.is_some());
let result = mock_iokit.get_dict_property(&dict, "NonExistentKey");
assert!(result.is_none());
}
#[test]
fn test_get_thermal_info() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_get_thermal_info().returning(|| {
Ok(ThermalInfo {
cpu_temp: 45.0,
gpu_temp: 55.0,
heatsink_temp: Some(40.0),
ambient_temp: Some(25.0),
battery_temp: Some(35.0),
is_throttling: false,
cpu_power: Some(15.0),
})
});
let result = mock_iokit.get_thermal_info().unwrap();
assert_eq!(result.cpu_temp, 45.0);
assert_eq!(result.gpu_temp, 55.0);
assert_eq!(result.heatsink_temp, Some(40.0));
assert_eq!(result.ambient_temp, Some(25.0));
assert_eq!(result.battery_temp, Some(35.0));
assert!(!result.is_throttling);
assert_eq!(result.cpu_power, Some(15.0));
}
#[test]
fn test_get_thermal_info_with_failures() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_get_thermal_info().returning(|| {
Ok(ThermalInfo {
cpu_temp: 45.0,
gpu_temp: 55.0,
heatsink_temp: None,
ambient_temp: None,
battery_temp: None,
is_throttling: false,
cpu_power: None,
})
});
let result = mock_iokit.get_thermal_info();
assert!(result.is_ok());
let info = result.unwrap();
assert_eq!(info.cpu_temp, 45.0);
assert_eq!(info.gpu_temp, 55.0);
assert_eq!(info.heatsink_temp, None);
assert_eq!(info.ambient_temp, None);
assert_eq!(info.battery_temp, None);
assert_eq!(info.cpu_power, None);
assert!(!info.is_throttling);
}
#[test]
fn test_get_fan_info() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_get_fan_info().with(mockall::predicate::eq(0)).returning(|_| {
Ok(FanInfo { speed_rpm: 2000, min_speed: 500, max_speed: 5000, percentage: 40.0 })
});
let result = mock_iokit.get_fan_info(0).unwrap();
assert_eq!(result.speed_rpm, 2000);
assert_eq!(result.min_speed, 500);
assert_eq!(result.max_speed, 5000);
assert_eq!(result.percentage, 40.0);
}
#[test]
fn test_get_fan_info_min_max_equal() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_get_fan_info().with(mockall::predicate::eq(0)).returning(|_| {
Ok(FanInfo {
speed_rpm: 2000,
min_speed: 2000, max_speed: 2000, percentage: 0.0, })
});
let result = mock_iokit.get_fan_info(0).unwrap();
assert_eq!(result.speed_rpm, 2000);
assert_eq!(result.min_speed, 2000);
assert_eq!(result.max_speed, 2000);
assert_eq!(result.percentage, 0.0);
}
#[test]
fn test_io_registry_entry_get_parent() {
let mut mock_iokit = MockIOKit::new();
let obj = create_test_object();
mock_iokit.expect_io_registry_entry_get_parent().returning(|_| None);
let result = mock_iokit.io_registry_entry_get_parent(&obj);
assert!(result.is_none());
}
#[test]
fn test_get_service() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_get_service().returning(|_| Ok(create_test_object()));
let result = mock_iokit.get_service("TestService");
assert!(result.is_ok());
}
#[test]
#[cfg_attr(not(feature = "skip-ffi-crashes"), ignore)]
fn test_impl_get_service_safety() {
let iokit = IOKitImpl;
let result = iokit.get_service("TestService");
assert!(result.is_err());
match result {
Err(e) => {
let error_msg = format!("{}", e);
assert!(error_msg.contains("Service access disabled for stability"));
},
_ => panic!("Expected an error from the disabled service access"),
}
}
#[test]
fn test_get_heatsink_temperature() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_get_heatsink_temperature().returning(|| Ok(40.0));
let result = mock_iokit.get_heatsink_temperature().unwrap();
assert_eq!(result, 40.0);
}
#[test]
fn test_get_ambient_temperature() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_get_ambient_temperature().returning(|| Ok(25.0));
let result = mock_iokit.get_ambient_temperature().unwrap();
assert_eq!(result, 25.0);
}
#[test]
fn test_get_battery_temperature() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_get_battery_temperature().returning(|| Ok(35.0));
let result = mock_iokit.get_battery_temperature().unwrap();
assert_eq!(result, 35.0);
}
#[test]
fn test_get_cpu_power() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_get_cpu_power().returning(|| Ok(15.0));
let result = mock_iokit.get_cpu_power().unwrap();
assert_eq!(result, 15.0);
}
#[test]
fn test_check_thermal_throttling() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_check_thermal_throttling().returning(|| Ok(true));
let result = mock_iokit.check_thermal_throttling().unwrap();
assert!(result);
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_check_thermal_throttling().returning(|| Ok(false));
let result = mock_iokit.check_thermal_throttling().unwrap();
assert!(!result);
}
#[test]
fn test_read_smc_key() {
let mut mock_iokit = MockIOKit::new();
let key = [b'T' as c_char, b'C' as c_char, b'0' as c_char, b'P' as c_char];
mock_iokit.expect_read_smc_key().with(mockall::predicate::eq(key)).returning(|_| Ok(42.0));
let result = mock_iokit.read_smc_key(key).unwrap();
assert_eq!(result, 42.0);
}
#[test]
fn test_get_fan_count() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_get_fan_count().returning(|| Ok(2));
let result = mock_iokit.get_fan_count().unwrap();
assert_eq!(result, 2);
}
#[test]
fn test_get_fan_speed() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_get_fan_speed().returning(|| Ok(2000));
let result = mock_iokit.get_fan_speed().unwrap();
assert_eq!(result, 2000);
}
#[test]
fn test_get_all_fans() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_get_all_fans().returning(|| {
Ok(vec![
FanInfo { speed_rpm: 2000, min_speed: 500, max_speed: 5000, percentage: 40.0 },
FanInfo { speed_rpm: 1800, min_speed: 400, max_speed: 4500, percentage: 35.0 },
])
});
let result = mock_iokit.get_all_fans().unwrap();
assert_eq!(result.len(), 2);
assert_eq!(result[0].speed_rpm, 2000);
assert_eq!(result[1].speed_rpm, 1800);
}
#[test]
fn test_get_all_fans_empty() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_get_fan_count().returning(|| Ok(0));
mock_iokit.expect_get_all_fans().returning(|| Ok(vec![]));
let result = mock_iokit.get_all_fans().unwrap();
assert!(result.is_empty());
}
#[test]
fn test_get_all_fans_partial_failure() {
let mut mock_iokit = MockIOKit::new();
mock_iokit.expect_get_fan_count().returning(|| Ok(2));
mock_iokit.expect_get_fan_info().with(mockall::predicate::eq(0)).returning(|_| {
Ok(FanInfo { speed_rpm: 2000, min_speed: 500, max_speed: 5000, percentage: 40.0 })
});
mock_iokit
.expect_get_fan_info()
.with(mockall::predicate::eq(1))
.returning(|_| Err(Error::io_kit("Failed to get fan info")));
let all_fans_result: Result<Vec<FanInfo>> = {
let fan_count = mock_iokit.get_fan_count().unwrap();
let fan_count = fan_count.min(4);
let mut fans = Vec::with_capacity(fan_count as usize);
for i in 0..fan_count {
if let Ok(fan_info) = mock_iokit.get_fan_info(i) {
fans.push(fan_info);
}
}
Ok(fans)
};
let result = all_fans_result.unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].speed_rpm, 2000);
}
#[test]
fn test_create_fan_keys() {
let actual_fan0_key = [b'F' as c_char, b'0' as c_char, b'A' as c_char, b'c' as c_char];
let expected_fan0_key = [b'F' as c_char, b'0' as c_char, b'A' as c_char, b'c' as c_char];
assert_eq!(actual_fan0_key, expected_fan0_key);
let actual_fan1_key = [b'F' as c_char, b'1' as c_char, b'A' as c_char, b'c' as c_char];
let expected_fan1_key = [b'F' as c_char, b'1' as c_char, b'A' as c_char, b'c' as c_char];
assert_eq!(actual_fan1_key, expected_fan1_key);
}
#[test]
fn test_fan_percentage_calculation() {
let speed = 2500;
let min = 1000;
let max = 5000;
let expected_percentage = ((speed - min) as f64 / (max - min) as f64) * 100.0;
let fan_info = FanInfo {
speed_rpm: speed,
min_speed: min,
max_speed: max,
percentage: expected_percentage,
};
assert_eq!(fan_info.percentage, expected_percentage);
assert!(fan_info.percentage > 0.0 && fan_info.percentage < 100.0);
}
#[test]
fn test_fan_percentage_limits() {
let min_fan = FanInfo { speed_rpm: 1000, min_speed: 1000, max_speed: 5000, percentage: 0.0 };
assert_eq!(min_fan.percentage, 0.0);
let max_fan = FanInfo { speed_rpm: 5000, min_speed: 1000, max_speed: 5000, percentage: 100.0 };
assert_eq!(max_fan.percentage, 100.0);
let zero_max_fan = FanInfo {
speed_rpm: 2000,
min_speed: 1000,
max_speed: 0, percentage: 0.0,
};
assert_eq!(zero_max_fan.percentage, 0.0);
}
#[test]
fn test_parse_smc_data() {
#[cfg(feature = "skip-ffi-crashes")]
{
let iokit = IOKitImpl;
let float_type = *b"flt\0";
let result = iokit.parse_smc_data(float_type, [0; 32]);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 42.5);
let uint_type = *b"uint";
let result = iokit.parse_smc_data(uint_type, [0; 32]);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 100.0);
let sint16_type = *b"si16";
let result = iokit.parse_smc_data(sint16_type, [0; 32]);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 50.0);
let sp78_type = *b"SP78";
let result = iokit.parse_smc_data(sp78_type, [0; 32]);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 35.5);
let unknown_type = *b"xxxx";
let result = iokit.parse_smc_data(unknown_type, [0; 32]);
assert!(result.is_err());
match result {
Err(e) => {
let error_msg = format!("{}", e);
assert!(error_msg.contains("Unsupported SMC data type"));
},
_ => panic!("Expected an error for unsupported data type"),
}
}
}
#[test]
fn test_gpu_stats_default() {
let stats = GpuStats {
utilization: 0.0,
perf_cap: 0.0,
perf_threshold: 0.0,
memory_used: 0,
memory_total: 0,
name: "".to_string(),
};
assert_eq!(stats.utilization, 0.0);
assert_eq!(stats.perf_cap, 0.0);
assert_eq!(stats.perf_threshold, 0.0);
assert_eq!(stats.memory_used, 0);
assert_eq!(stats.memory_total, 0);
assert_eq!(stats.name, "");
}
#[test]
fn test_gpu_stats_clone() {
let original = GpuStats {
utilization: 50.0,
perf_cap: 60.0,
perf_threshold: 70.0,
memory_used: 1024 * 1024 * 1024,
memory_total: 4 * 1024 * 1024 * 1024,
name: "Test GPU".to_string(),
};
let cloned = original.clone();
assert_eq!(cloned.utilization, original.utilization);
assert_eq!(cloned.perf_cap, original.perf_cap);
assert_eq!(cloned.perf_threshold, original.perf_threshold);
assert_eq!(cloned.memory_used, original.memory_used);
assert_eq!(cloned.memory_total, original.memory_total);
assert_eq!(cloned.name, original.name);
}
#[test]
fn test_fan_info_clone() {
let fan = FanInfo { speed_rpm: 2000, min_speed: 500, max_speed: 5000, percentage: 40.0 };
let fan_clone = fan.clone();
assert_eq!(fan.speed_rpm, fan_clone.speed_rpm);
assert_eq!(fan.min_speed, fan_clone.min_speed);
assert_eq!(fan.max_speed, fan_clone.max_speed);
assert_eq!(fan.percentage, fan_clone.percentage);
}
#[test]
fn test_thermal_info_clone() {
let info = ThermalInfo {
cpu_temp: 45.0,
gpu_temp: 55.0,
heatsink_temp: Some(40.0),
ambient_temp: Some(25.0),
battery_temp: Some(35.0),
is_throttling: false,
cpu_power: Some(15.0),
};
let info_clone = info.clone();
assert_eq!(info.cpu_temp, info_clone.cpu_temp);
assert_eq!(info.gpu_temp, info_clone.gpu_temp);
assert_eq!(info.heatsink_temp, info_clone.heatsink_temp);
assert_eq!(info.ambient_temp, info_clone.ambient_temp);
assert_eq!(info.battery_temp, info_clone.battery_temp);
assert_eq!(info.is_throttling, info_clone.is_throttling);
assert_eq!(info.cpu_power, info_clone.cpu_power);
}
#[test]
fn test_thermal_info_debug() {
let info = ThermalInfo {
cpu_temp: 45.0,
gpu_temp: 55.0,
heatsink_temp: Some(40.0),
ambient_temp: Some(25.0),
battery_temp: Some(35.0),
is_throttling: false,
cpu_power: Some(15.0),
};
let debug_str = format!("{:?}", info);
assert!(debug_str.contains("cpu_temp: 45.0"));
assert!(debug_str.contains("gpu_temp: 55.0"));
assert!(debug_str.contains("heatsink_temp: Some(40.0)"));
assert!(debug_str.contains("ambient_temp: Some(25.0)"));
assert!(debug_str.contains("battery_temp: Some(35.0)"));
assert!(debug_str.contains("is_throttling: false"));
assert!(debug_str.contains("cpu_power: Some(15.0)"));
}
#[test]
fn test_fan_info_debug() {
let fan = FanInfo { speed_rpm: 2000, min_speed: 500, max_speed: 5000, percentage: 40.0 };
let debug_str = format!("{:?}", fan);
assert!(debug_str.contains("speed_rpm: 2000"));
assert!(debug_str.contains("min_speed: 500"));
assert!(debug_str.contains("max_speed: 5000"));
assert!(debug_str.contains("percentage: 40.0"));
}
#[test]
fn test_iokit_impl_default() {
let iokit = IOKitImpl;
assert!(matches!(iokit, IOKitImpl));
}
#[test]
fn test_iokit_impl_debug() {
let iokit = IOKitImpl;
let debug_str = format!("{:?}", iokit);
assert!(debug_str.contains("IOKitImpl"));
}
#[cfg(feature = "unstable-tests")]
#[test]
fn test_real_gpu_stats() {
autoreleasepool(|_| {
let iokit = IOKitImpl;
println!("Created IOKitImpl instance");
println!("Testing IOAccelerator service directly");
let matching = iokit.io_service_matching("IOAccelerator");
println!("Got matching dictionary for IOAccelerator");
let service_opt = iokit.io_service_get_matching_service(&matching);
match service_opt {
Some(service) => {
println!("Found IOAccelerator service, trying to get properties");
match iokit.io_registry_entry_create_cf_properties(&service) {
Ok(props) => {
println!("Successfully got properties from IOAccelerator");
println!("============== Testing Key Access ==============");
let vram_keys = [
"VRAM,totalMB",
"VRAM,usedMB",
"totalVRAM",
"usedVRAM",
"vramUsage", "vramFree", ];
for key in vram_keys.iter() {
if let Some(value) = iokit.get_number_property(&props, key) {
println!("{}: {}", key, value);
} else {
println!("{}: Not found", key);
}
}
let name_keys = [
"model",
"name",
"IOGLBundleName",
"IOAccelRevision",
"device-id", "vendor-id", "IOAccelIndex", "IOAccelTypes",
"gpuType", "gpu_product", ];
for key in name_keys.iter() {
if let Some(value) = iokit.get_string_property(&props, key) {
println!("{}: {}", key, value);
} else {
println!("{}: Not found", key);
}
}
let perf_keys = [
"IOGPUCurrentPowerState",
"IOGPUMaximumPowerState",
"deviceUtilization", "powerState", "GPUPerfCap",
"GPUPerfThreshold",
];
for key in perf_keys.iter() {
if let Some(value) = iokit.get_number_property(&props, key) {
println!("{}: {}", key, value);
} else {
println!("{}: Not found", key);
}
}
println!("\n============== Testing Metal API ==============");
use crate::utils::bindings::MTLCreateSystemDefaultDevice;
println!("Creating Metal device...");
autoreleasepool(|_pool| {
unsafe {
let device = MTLCreateSystemDefaultDevice();
if device.is_null() {
println!("Failed to create Metal device");
return;
}
println!("Metal device created successfully");
let device_obj: *mut objc2::runtime::AnyObject = device.cast();
println!("Fetching device name...");
let name_obj: *mut objc2::runtime::AnyObject =
msg_send![device_obj, name];
if name_obj.is_null() {
println!("Failed to get device name");
} else {
autoreleasepool(|_| {
let _: () = msg_send![name_obj, retain];
let utf8_string: *const u8 = msg_send![name_obj, UTF8String];
if utf8_string.is_null() {
println!("Failed to get UTF8 string");
} else {
let c_str =
std::ffi::CStr::from_ptr(utf8_string as *const i8);
let name = c_str.to_string_lossy();
println!("GPU name from Metal API: {}", name);
}
let _: () = msg_send![name_obj, release];
});
}
println!("Releasing Metal device...");
let _: () = msg_send![device_obj, release];
println!("Metal device released");
}
});
println!("Memory management handled properly, test continuing...");
},
Err(e) => {
println!("Error getting properties: {:?}", e);
},
}
},
None => {
println!("IOAccelerator service not found");
},
}
});
}
#[test]
#[cfg(feature = "skip-ffi-crashes")]
fn test_smc_read_key_mocks() {
let iokit = IOKitImpl;
if cfg!(feature = "skip-ffi-crashes") {
let cpu_temp = iokit.smc_read_key(SMC_KEY_CPU_TEMP);
assert!(cpu_temp.is_ok());
assert_eq!(cpu_temp.as_ref().unwrap(), &42.5);
let gpu_temp = iokit.smc_read_key(SMC_KEY_GPU_TEMP);
assert!(cpu_temp.is_ok());
assert_eq!(gpu_temp.as_ref().unwrap(), &42.5);
let ambient_temp = iokit.smc_read_key(SMC_KEY_AMBIENT_TEMP);
assert!(ambient_temp.is_ok());
assert_eq!(ambient_temp.as_ref().unwrap(), &26.0);
let battery_temp = iokit.smc_read_key(SMC_KEY_BATTERY_TEMP);
assert!(battery_temp.is_ok());
assert_eq!(battery_temp.as_ref().unwrap(), &35.0);
let fan_count = iokit.smc_read_key(SMC_KEY_FAN_NUM);
assert!(fan_count.is_ok());
assert_eq!(fan_count.as_ref().unwrap(), &2.0);
let fan_key = [b'F' as c_char, b'0' as c_char, b'A' as c_char, b'c' as c_char];
let fan_speed = iokit.smc_read_key(fan_key);
assert!(fan_speed.is_ok());
assert_eq!(fan_speed.as_ref().unwrap(), &2000.0);
let unknown_key = [b'X' as c_char, b'X' as c_char, b'X' as c_char, b'X' as c_char];
let unknown = iokit.smc_read_key(unknown_key);
assert!(unknown.is_ok());
assert_eq!(unknown.as_ref().unwrap(), &0.0);
}
}