use brlapi::{BrlApiError, Connection};
#[test]
fn test_connection_error_handling() {
use brlapi::ConnectionSettings;
let bad_settings = ConnectionSettings::for_host("invalid-host-that-does-not-exist");
match Connection::open_with_settings(Some(&bad_settings)) {
Ok(_) => {
println!("Connection to invalid host succeeded (unexpected)");
}
Err(e) => {
println!("Connection to invalid host failed as expected: {e}");
let suggestions = e.suggestions();
println!(" Error message: {e}");
println!(" Suggestions: {:?}", suggestions);
assert!(e.is_connection_error() || e.is_operation_error());
}
}
}
#[test]
fn test_error_types() {
let errors_and_categories = vec![
(BrlApiError::ConnectionRefused, "connection"),
(BrlApiError::AuthenticationFailed, "connection"),
(BrlApiError::TTYBusy, "resource"),
(BrlApiError::DeviceBusy, "resource"),
(BrlApiError::NoMem, "resource"),
(BrlApiError::OperationNotSupported, "operation"),
(BrlApiError::UnknownInstruction, "operation"),
(BrlApiError::InvalidParameter, "operation"),
];
for (error, category) in errors_and_categories {
match category {
"connection" => {
assert!(error.is_connection_error());
assert!(!error.is_resource_busy());
assert!(!error.is_operation_error());
}
"resource" => {
assert!(!error.is_connection_error());
assert!(error.is_resource_busy());
assert!(!error.is_operation_error());
}
"operation" => {
assert!(!error.is_connection_error());
assert!(!error.is_resource_busy());
assert!(error.is_operation_error());
}
_ => panic!("Unknown category: {category}"),
}
assert!(!error.to_string().is_empty());
assert!(!error.to_string().is_empty());
println!("Error {error:?} correctly classified as {category}");
}
}
#[test]
fn test_error_helpfulness() {
let test_cases = vec![
(
BrlApiError::ConnectionRefused,
vec!["BRLTTY", "systemctl", "daemon"],
),
(
BrlApiError::TTYBusy,
vec!["TTY", "connection", "applications"],
),
(BrlApiError::UnknownTTY, vec!["TTY", "console", "Ctrl+Alt"]),
(
BrlApiError::DriverError,
vec!["display", "connection", "brltty"],
),
];
for (error, keywords) in test_cases {
let error_msg = error.to_string();
let suggestions = error.suggestions();
let msg_lower = error_msg.to_lowercase();
for keyword in &keywords {
if !msg_lower.contains(&keyword.to_lowercase()) {
println!("Error message for {error:?} doesn't contain '{keyword}'");
println!(" Message: {error_msg}");
}
}
if suggestions.is_empty() {
println!("No suggestions provided for {:?}", error);
} else {
println!("Error {error:?} has {} suggestions", suggestions.len());
for suggestion in suggestions {
assert!(!suggestion.is_empty());
if !(suggestion.contains(':')
|| suggestion.contains("sudo")
|| suggestion.contains("systemctl")
|| suggestion.contains("Check"))
{
println!("Suggestion may not be actionable: {suggestion}");
}
}
}
}
}
#[test]
fn test_error_from_c_codes() {
let test_cases = vec![
(
brlapi_sys::brlapi_error::BRLAPI_ERROR_SUCCESS,
BrlApiError::Success,
),
(
brlapi_sys::brlapi_error::BRLAPI_ERROR_NOMEM,
BrlApiError::NoMem,
),
(
brlapi_sys::brlapi_error::BRLAPI_ERROR_TTYBUSY,
BrlApiError::TTYBusy,
),
(
brlapi_sys::brlapi_error::BRLAPI_ERROR_UNKNOWNTTY,
BrlApiError::UnknownTTY,
),
];
for (c_code, expected_error) in test_cases {
let converted = BrlApiError::from_c_error(c_code as i32);
assert_eq!(
std::mem::discriminant(&converted),
std::mem::discriminant(&expected_error)
);
println!("C error code {c_code} correctly converts to {converted:?}");
}
let unknown_error = BrlApiError::from_c_error(99999);
assert!(matches!(unknown_error, BrlApiError::OperationNotSupported));
println!("Unknown error codes fall back to OperationNotSupported");
}