brlapi 0.4.1

Safe Rust bindings for the BrlAPI library
// SPDX-License-Identifier: LGPL-2.1

//! Connection management and basic API tests

// Connection type is accessed via common::try_connect() helpers

mod common;

#[test]
fn test_connection_open_default() {
    // Test basic connection with default settings
    match common::try_connect() {
        Ok(conn) => {
            // Connection succeeded - verify basics
            let fd = conn.file_descriptor();
            assert!(fd >= 0, "File descriptor should be valid");
            println!("Connection opened successfully, fd: {fd}");
        }
        Err(e) => {
            // Connection failed - acceptable if no daemon running
            println!("Connection failed (expected if no brltty daemon): {e}");
            // Don't fail the test - this is environment dependent
        }
    }
}

#[test]
fn test_connection_with_localhost_settings() {
    let settings = common::localhost_settings();

    match common::try_connect_with_settings(&settings) {
        Ok(conn) => {
            let fd = conn.file_descriptor();
            assert!(fd >= 0, "File descriptor should be valid");
            println!("Localhost connection opened successfully, fd: {fd}");
        }
        Err(e) => {
            println!("Localhost connection failed: {e}");
            // Environment dependent - don't fail test
        }
    }
}

#[test]
fn test_connection_with_ipv4_settings() {
    let settings = common::ipv4_settings();

    match common::try_connect_with_settings(&settings) {
        Ok(conn) => {
            let fd = conn.file_descriptor();
            assert!(fd >= 0, "File descriptor should be valid");
            println!("IPv4 connection opened successfully, fd: {fd}");
        }
        Err(e) => {
            println!("IPv4 connection failed: {e}");
        }
    }
}

#[test]
fn test_connection_cycling() {
    // Test sequential connection establishment and cleanup
    // While BrlAPI theoretically supports multiple simultaneous connections via handles,
    // empirical testing shows connection establishment may be serialized by the daemon

    // Use fewer connections in CI environments to avoid resource exhaustion
    let connection_count = std::env::var("BRLAPI_TEST_CONNECTIONS")
        .ok()
        .and_then(|s| s.parse().ok())
        .unwrap_or(if std::env::var("CI").is_ok() { 2 } else { 3 });

    println!(
        "Testing {} sequential BrlAPI connections...",
        connection_count
    );

    let mut successful_connections = 0;

    for i in 0..connection_count {
        match common::try_connect() {
            Ok(conn) => {
                let fd = conn.file_descriptor();
                println!("Connection {i} opened, fd: {fd}");
                successful_connections += 1;

                // Test basic operations
                if let Ok((width, height)) = conn.display_size() {
                    println!("Connection {i}: Display size {width}x{height}");
                }
                if let Ok(driver) = conn.display_driver() {
                    println!("Connection {i}: Driver '{driver}'");
                }

                // Connection is automatically closed when conn goes out of scope
                drop(conn);
                println!("Connection {i} closed");
            }
            Err(e) => {
                println!("Connection {i} failed: {e}");
                // Continue trying - some failures are expected if no daemon is running
            }
        }
    }

    // Test passes if we got at least one connection or all failed gracefully
    println!("Successfully cycled through {successful_connections} connections");
}

#[test]
fn test_connection_drop_cleanup() {
    // Test that connections are properly cleaned up when dropped
    let conn_result = common::try_connect();

    if let Ok(conn) = conn_result {
        let fd = conn.file_descriptor();
        println!("Connection opened with fd: {fd}");

        // Drop explicitly to test cleanup
        drop(conn);
        println!("Connection dropped - cleanup should have occurred");

        // Note: We can't easily test that cleanup actually worked
        // without inspecting BrlAPI internal state, but the test
        // verifies the drop doesn't panic
    }
}

#[test]
fn test_connection_file_descriptor_validity() {
    // Test that file descriptors returned are valid
    if let Ok(conn) = common::try_connect() {
        let fd = conn.file_descriptor();

        // File descriptor should be non-negative
        assert!(fd >= 0, "File descriptor should be non-negative, got {fd}");

        // File descriptor should not be one of the standard descriptors
        // (unless explicitly redirected, but that would be unusual for BrlAPI)
        if fd <= 2 {
            println!("Warning: File descriptor {fd} is a standard descriptor");
        }

        // Test that the same connection returns the same FD
        let fd2 = conn.file_descriptor();
        assert_eq!(
            fd, fd2,
            "Connection should return consistent file descriptor"
        );

        println!("Connection file descriptor: {fd}");
    } else {
        println!("No connection available for file descriptor testing");
    }
}

#[test]
fn test_connection_methods_without_connection() {
    // Test that connection methods handle disconnected state gracefully
    let conn_result = common::try_connect();

    match conn_result {
        Ok(conn) => {
            // Test driver information retrieval
            let driver_result = conn.display_driver();
            let driver_name = if let Some(name) =
                common::assert_reasonable_result(driver_result, "Driver name query")
            {
                assert!(!name.is_empty(), "Driver name should not be empty");
                assert!(name.len() <= brlapi_sys::BRLAPI_MAXNAMELENGTH as usize);
                Some(name)
            } else {
                None
            };

            // Test model identifier retrieval
            let model_result = conn.display_model();
            if let Some(model) =
                common::assert_reasonable_result(model_result, "Model identifier query")
            {
                // Some drivers (like NoBraille) may return empty model identifiers
                // This is valid behavior, so we only check length constraints
                assert!(model.len() <= brlapi_sys::BRLAPI_MAXNAMELENGTH as usize);
                if !model.is_empty() {
                    println!("Model identifier: {model}");
                } else {
                    println!("Model identifier: (empty - valid for some drivers like NoBraille)");
                }
            }

            // Test display size retrieval
            let display_result = conn.display_size();
            if let Some((width, height)) =
                common::assert_reasonable_result(display_result, "Display size query")
            {
                // Skip size assertions for NoBraille driver which typically has 0x0 dimensions
                if let Some(ref name) = driver_name {
                    if name == "NoBraille" {
                        println!(
                            "Skipping size assertions for NoBraille driver (width={width}, height={height})"
                        );
                    } else {
                        assert!(
                            width > 0 && width <= 1000,
                            "Width should be reasonable for {name}"
                        );
                        assert!(
                            height > 0 && height <= 100,
                            "Height should be reasonable for {name}"
                        );
                    }
                } else {
                    // If we don't know the driver name, be more lenient
                    assert!(width <= 1000, "Width should be within reasonable bounds");
                    assert!(height <= 100, "Height should be within reasonable bounds");
                }
            }
        }
        Err(e) => {
            println!("Connection failed, testing with failed connection: {e}");
            // Test passes - we're testing graceful handling of no connection
        }
    }
}