brlapi 0.4.1

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

//! Tests for cooperative braille display sharing

use brlapi::cooperative::{AppType, ContentQuality, CooperationConfig, CooperativeDisplay};
use brlapi::{BrlApiError, Connection, Result};
use std::time::Duration;

/// Common test helper to try connecting
fn try_connect() -> Result<Connection> {
    Connection::open().or_else(|_| {
        // If connection fails, create a mock or skip test
        Err(BrlApiError::custom(
            "No BrlAPI daemon available for testing",
        ))
    })
}

#[test]
fn test_app_type_priorities() {
    // Test that priority values match our research-based expectations
    assert_eq!(AppType::ScreenReader.base_priority(), 50); // Orca default
    assert_eq!(AppType::ScreenReaderReview.base_priority(), 70); // Orca flat review mode
    assert_eq!(AppType::SystemAlert.base_priority(), 65);
    assert_eq!(AppType::UserApp.base_priority(), 45);
    assert_eq!(AppType::BackgroundTask.base_priority(), 45);
    assert_eq!(AppType::Debug.base_priority(), 45);
    assert_eq!(AppType::LowPriority.base_priority(), 15);

    // Test that review mode has higher priority than regular screen reader
    assert!(AppType::ScreenReaderReview.base_priority() > AppType::ScreenReader.base_priority());

    // Test that system alerts have higher priority than screen readers
    assert!(AppType::SystemAlert.base_priority() > AppType::ScreenReader.base_priority());

    // Test that user apps have lower priority than screen readers
    assert!(AppType::UserApp.base_priority() < AppType::ScreenReader.base_priority());

    // Test that background tasks have same priority as user apps (both 45)
    assert_eq!(
        AppType::BackgroundTask.base_priority(),
        AppType::UserApp.base_priority()
    );
}

#[test]
fn test_content_quality_priority_offsets() {
    // Test priority offsets based on BRLTTY 6.1+ behavior
    assert_eq!(ContentQuality::Good.priority_offset(), 20);
    assert_eq!(ContentQuality::Fair.priority_offset(), 10);
    assert_eq!(ContentQuality::Poor.priority_offset(), 0);
    assert_eq!(ContentQuality::None.priority_offset(), -10);

    // Test that good content gets highest priority boost
    assert!(ContentQuality::Good.priority_offset() > ContentQuality::Fair.priority_offset());
    assert!(ContentQuality::Fair.priority_offset() > ContentQuality::Poor.priority_offset());
    assert!(ContentQuality::Poor.priority_offset() > ContentQuality::None.priority_offset());
}

#[test]
fn test_content_quality_durations() {
    // Test that different content qualities have appropriate durations
    assert!(ContentQuality::Good.default_duration() > ContentQuality::Fair.default_duration());
    assert!(ContentQuality::Fair.default_duration() > ContentQuality::Poor.default_duration());
    assert!(ContentQuality::Poor.default_duration() > ContentQuality::None.default_duration());

    // Test specific duration expectations
    assert_eq!(
        ContentQuality::Good.default_duration(),
        Duration::from_secs(5)
    );
    assert_eq!(
        ContentQuality::Fair.default_duration(),
        Duration::from_secs(3)
    );
    assert_eq!(
        ContentQuality::Poor.default_duration(),
        Duration::from_secs(2)
    );
    assert_eq!(
        ContentQuality::None.default_duration(),
        Duration::from_secs(1)
    );
}

#[test]
fn test_cooperation_config_defaults() {
    let config = CooperationConfig::default();

    // Test that defaults follow our research-based recommendations
    assert!(config.respect_screen_reader_focus);
    assert!(config.auto_adjust_priority);
    assert!(config.retry_when_busy);

    // Test that brief notifications are shorter than Orca's 3s default
    assert!(config.brief_notification_time < Duration::from_secs(3));
    assert_eq!(config.brief_notification_time, Duration::from_secs(2));

    // Test reasonable retry parameters
    assert_eq!(config.retry_delay, Duration::from_secs(1));
    assert_eq!(config.max_retries, 3);
}

#[test]
fn test_cooperative_display_creation() {
    // Test that CooperativeDisplay can be created for different app types
    let app_types = [
        AppType::ScreenReader,
        AppType::ScreenReaderReview,
        AppType::SystemAlert,
        AppType::UserApp,
        AppType::BackgroundTask,
        AppType::Debug,
        AppType::LowPriority,
    ];

    for app_type in app_types.iter() {
        if let Ok(_connection) = try_connect() {
            // If we can connect, test actual creation
            let display_result = CooperativeDisplay::open(*app_type);
            match display_result {
                Ok(_display) => {
                    println!("Successfully created CooperativeDisplay for {:?}", app_type);
                }
                Err(e) => {
                    println!(
                        "CooperativeDisplay creation failed for {:?}: {}",
                        app_type, e
                    );
                    // This might be expected if no daemon is running
                }
            }
        } else {
            println!("Skipping CooperativeDisplay test - no BrlAPI daemon available");
        }
    }
}

#[test]
fn test_background_app_creation() {
    if let Ok(_connection) = try_connect() {
        match CooperativeDisplay::background_app() {
            Ok(_display) => {
                println!("Successfully created background app display");
                // Background apps should use BackgroundTask priority
                // (We can't directly test this without exposing internals)
            }
            Err(e) => {
                println!("Background app creation failed: {}", e);
            }
        }
    } else {
        println!("Skipping background app test - no BrlAPI daemon available");
    }
}

#[test]
fn test_interactive_app_creation() {
    if let Ok(_connection) = try_connect() {
        match CooperativeDisplay::interactive_app() {
            Ok(_display) => {
                println!("Successfully created interactive app display");
                // Interactive apps should use UserApp priority
            }
            Err(e) => {
                println!("Interactive app creation failed: {}", e);
            }
        }
    } else {
        println!("Skipping interactive app test - no BrlAPI daemon available");
    }
}

#[test]
fn test_message_queueing() {
    if let Ok(_connection) = try_connect() {
        match CooperativeDisplay::open(AppType::UserApp) {
            Ok(mut display) => {
                // Test message queueing
                assert_eq!(display.pending_messages(), 0);

                display
                    .queue_message("Test message 1", ContentQuality::Fair)
                    .expect("Should queue message");
                assert_eq!(display.pending_messages(), 1);

                display
                    .queue_message("Test message 2", ContentQuality::Good)
                    .expect("Should queue message");
                assert_eq!(display.pending_messages(), 2);

                // Test queue processing (may not actually display if daemon unavailable)
                match display.process_queue() {
                    Ok(processed) => {
                        println!("Processed {} queued messages", processed);
                    }
                    Err(e) => {
                        println!("Queue processing failed: {}", e);
                        // This might be expected if display is busy
                    }
                }
            }
            Err(e) => {
                println!("CooperativeDisplay creation failed: {}", e);
            }
        }
    } else {
        println!("Skipping queueing test - no BrlAPI daemon available");
    }
}

#[test]
fn test_simple_notification_functions() {
    // Test that the simple functions exist and can be called
    // These functions handle their own connection management

    match brlapi::cooperative::notify("Test notification") {
        Ok(()) => {
            println!("Simple notify() succeeded");
        }
        Err(e) => {
            println!("Simple notify() failed: {} (expected if no daemon)", e);
        }
    }

    match brlapi::cooperative::alert("Test alert") {
        Ok(()) => {
            println!("Simple alert() succeeded");
        }
        Err(e) => {
            println!("Simple alert() failed: {} (expected if no daemon)", e);
        }
    }

    match brlapi::cooperative::debug("Test debug message") {
        Ok(()) => {
            println!("Simple debug() succeeded");
        }
        Err(e) => {
            println!("Simple debug() failed: {} (expected if no daemon)", e);
        }
    }
}

#[test]
fn test_cooperative_behavior_patterns() {
    if let Ok(_connection) = try_connect() {
        match CooperativeDisplay::open(AppType::UserApp) {
            Ok(mut display) => {
                // Test different content display methods
                let test_cases = [
                    ("Regular message", ContentQuality::Fair),
                    ("Interactive prompt", ContentQuality::Good),
                    ("Status update", ContentQuality::Poor),
                    ("Background info", ContentQuality::None),
                ];

                for (text, quality) in test_cases.iter() {
                    match display.show_content(text, *quality) {
                        Ok(()) => {
                            println!("Successfully displayed: {} ({:?})", text, quality);
                        }
                        Err(e) => {
                            println!("Display failed for '{}': {}", text, e);
                            // This might be expected if screen reader is active
                        }
                    }
                }

                // Test status messages
                match display.show_status("Processing file 1 of 10") {
                    Ok(()) => {
                        println!("Status message displayed successfully");
                    }
                    Err(e) => {
                        println!("Status message failed: {}", e);
                    }
                }

                // Test regular messages
                match display.show_message("Operation completed") {
                    Ok(()) => {
                        println!("Regular message displayed successfully");
                    }
                    Err(e) => {
                        println!("Regular message failed: {}", e);
                    }
                }
            }
            Err(e) => {
                println!("CooperativeDisplay creation failed: {}", e);
            }
        }
    } else {
        println!("Skipping behavior patterns test - no BrlAPI daemon available");
    }
}

#[test]
fn test_priority_adjustment_calculation() {
    // Test priority calculation logic (doesn't require BrlAPI connection)
    let base_priority = AppType::UserApp.base_priority(); // 45

    // Test content quality adjustments
    let good_adjustment = base_priority as i32 + ContentQuality::Good.priority_offset();
    let fair_adjustment = base_priority as i32 + ContentQuality::Fair.priority_offset();
    let poor_adjustment = base_priority as i32 + ContentQuality::Poor.priority_offset();
    let none_adjustment = base_priority as i32 + ContentQuality::None.priority_offset();

    assert_eq!(good_adjustment, 65); // 45 + 20
    assert_eq!(fair_adjustment, 55); // 45 + 10
    assert_eq!(poor_adjustment, 45); // 45 + 0
    assert_eq!(none_adjustment, 35); // 45 + (-10)

    // Test that adjustments maintain proper ordering
    assert!(good_adjustment > fair_adjustment);
    assert!(fair_adjustment > poor_adjustment);
    assert!(poor_adjustment > none_adjustment);

    // Test that good quality UserApp exceeds ScreenReader base priority
    assert!(good_adjustment > AppType::ScreenReader.base_priority() as i32);
}

#[test]
fn test_duration_calculations() {
    // Test that app types have reasonable default durations
    assert!(AppType::ScreenReader.default_duration() >= Duration::from_secs(3));
    assert!(AppType::ScreenReaderReview.default_duration() >= Duration::from_secs(3));
    assert!(AppType::SystemAlert.default_duration() >= Duration::from_secs(3));
    assert!(AppType::UserApp.default_duration() >= Duration::from_secs(3));
    assert!(AppType::BackgroundTask.default_duration() >= Duration::from_secs(4));
    assert!(AppType::Debug.default_duration() >= Duration::from_secs(3));
    assert!(AppType::LowPriority.default_duration() <= Duration::from_secs(2));
}

#[test]
fn test_evidence_based_design() {
    // Test that our design matches the research findings

    // Screen reader base priority should be 50 (Orca default)
    assert_eq!(AppType::ScreenReader.base_priority(), 50);

    // System alerts should be higher than screen reader base
    assert!(AppType::SystemAlert.base_priority() > 50);

    // User apps should be slightly below screen reader base
    assert!(AppType::UserApp.base_priority() < 50);
    assert!(AppType::UserApp.base_priority() >= 40);

    // Good content should boost UserApp above ScreenReader base
    let user_app_good =
        AppType::UserApp.base_priority() as i32 + ContentQuality::Good.priority_offset();
    assert!(user_app_good > AppType::ScreenReader.base_priority() as i32);

    // Brief notifications should be less than Orca's 3s default
    let config = CooperationConfig::default();
    assert!(config.brief_notification_time < Duration::from_secs(3));
}