brlapi 0.4.1

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

//! Concurrency and thread safety tests

use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;

mod common;

#[test]
fn test_concurrent_connections() {
    // Test actual concurrent connections - BrlAPI supports this when daemon is running
    let success_count = Arc::new(AtomicUsize::new(0));
    let failure_count = Arc::new(AtomicUsize::new(0));

    let connection_count = 3;
    let mut handles = Vec::new();

    println!(
        "Testing concurrent connections (BrlAPI supports multiple simultaneous connections)..."
    );

    // Start multiple threads that try to connect simultaneously
    for i in 0..connection_count {
        let success_count = Arc::clone(&success_count);
        let failure_count = Arc::clone(&failure_count);

        let handle = thread::spawn(move || {
            match common::try_connect() {
                Ok(conn) => {
                    success_count.fetch_add(1, Ordering::SeqCst);

                    // Test some operations on this connection
                    let fd = conn.file_descriptor();
                    println!("Concurrent connection {i}: fd={fd}");

                    // Test driver info retrieval
                    if let Ok(driver) = conn.display_driver() {
                        println!("Concurrent connection {i}: Driver: {driver}");
                    }

                    // Test display size
                    if let Ok((w, h)) = conn.display_size() {
                        println!("Concurrent connection {i}: Display: {w}x{h}");
                    }

                    // Hold connection briefly to test true concurrency
                    thread::sleep(std::time::Duration::from_millis(100));

                    println!("Concurrent connection {i}: completed operations");
                }
                Err(e) => {
                    failure_count.fetch_add(1, Ordering::SeqCst);
                    println!("Concurrent connection {i}: Connection failed: {e}");
                }
            }
        });

        handles.push(handle);
    }

    // Wait for all threads to complete
    for handle in handles {
        handle
            .join()
            .expect("Concurrent connection thread should not panic");
    }

    let successes = success_count.load(Ordering::SeqCst);
    let failures = failure_count.load(Ordering::SeqCst);

    println!("Concurrent connection test: {successes} successes, {failures} failures");

    if successes > 0 {
        println!(
            "[SUCCESS] Concurrent connections work properly (BrlAPI supports multiple simultaneous connections)"
        );
    } else {
        println!("[INFO] No BrlAPI daemon available - concurrent test skipped");
    }

    // Test passes regardless - we're testing the concurrent capability
    assert!(successes + failures <= connection_count);
}

#[test]
fn test_shared_connection_operations() {
    // Test thread-safe operations on a single shared connection
    // This demonstrates the correct pattern for concurrent BrlAPI usage
    use std::sync::{Arc, Mutex};

    if let Ok(connection) = common::try_connect() {
        // Wrap connection in Arc<Mutex<>> for safe sharing
        let shared_conn = Arc::new(Mutex::new(connection));

        let handle1 = {
            let conn_clone = Arc::clone(&shared_conn);
            thread::spawn(move || {
                // Thread 1: Test operations with exclusive access
                let conn = conn_clone
                    .lock()
                    .expect("Shared connection mutex should not be poisoned");
                if let Ok((w, h)) = conn.display_size() {
                    println!("Thread 1: Display size {w}x{h}");
                }
                if let Ok(driver) = conn.display_driver() {
                    println!("Thread 1: Driver {driver}");
                }
                println!("Thread 1 completed operations");
            })
        };

        let handle2 = {
            let conn_clone = Arc::clone(&shared_conn);
            thread::spawn(move || {
                // Thread 2: Test operations with exclusive access
                let conn = conn_clone
                    .lock()
                    .expect("Shared connection mutex should not be poisoned in thread 2");
                if let Ok((w, h)) = conn.display_size() {
                    println!("Thread 2: Display size {w}x{h}");
                }
                if let Ok(driver) = conn.display_driver() {
                    println!("Thread 2: Driver {driver}");
                }
                println!("Thread 2 completed operations");
            })
        };

        handle1
            .join()
            .expect("Shared connection thread 1 should not panic");
        handle2
            .join()
            .expect("Shared connection thread 2 should not panic");

        println!("[SUCCESS] Shared connection operations test completed");
    } else {
        println!("[INFO] No BrlAPI daemon available - shared connection test skipped");
    }
}

#[test]
fn test_sequential_connection_behavior() {
    // Test BrlAPI's sequential connection behavior for comparison with concurrent
    const NUM_ATTEMPTS: usize = 3;
    let results = Arc::new(std::sync::Mutex::new(Vec::new()));

    println!("Testing sequential connection behavior (one after another)...");

    // Test sequential attempts (open, use, close, repeat)
    for i in 0..NUM_ATTEMPTS {
        let thread_result = match common::try_connect() {
            Ok(conn) => {
                let fd = conn.file_descriptor();
                let result = format!("Attempt {i}: FD {fd}");
                println!("  {}", result);

                // Do a quick operation to verify connection works
                if let Ok((w, h)) = conn.display_size() {
                    println!("    Display: {w}x{h}");
                }

                // Connection drops here, allowing next attempt
                result
            }
            Err(_) => {
                let result = format!("Attempt {i}: No daemon available");
                println!("  {}", result);
                result
            }
        };

        results
            .lock()
            .expect("Sequential test results mutex should not be poisoned")
            .push(thread_result);
    }

    let results = results
        .lock()
        .expect("Sequential test results mutex should not be poisoned for reading");
    println!("Sequential connection behavior results:");
    for result in results.iter() {
        println!("  {result}");
    }

    println!("[NOTE] Note: BrlAPI supports both patterns:");
    println!("   - Multiple simultaneous connections work with running daemon");
    println!("   - Sequential connections always work (open/close/open)");
    println!("   - Both patterns are valid and supported");

    assert_eq!(results.len(), NUM_ATTEMPTS);
}