liminal-server 0.25.4

Standalone server for the liminal messaging bus
Documentation
use std::io::{self, ErrorKind};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{channel, sync_channel};

use super::Reader;
use crate::test_log::CapturedLog;

type TestResult = Result<(), Box<dyn std::error::Error>>;

fn error_count(log: &CapturedLog) -> usize {
    log.contents()
        .lines()
        .filter(|line| line.contains("ERROR"))
        .count()
}

fn eof_reader() -> Result<(Reader, Arc<AtomicBool>), Box<dyn std::error::Error>> {
    let (sender, inbox) = sync_channel(1);
    let completed = Arc::new(AtomicBool::new(false));
    let worker_completed = Arc::clone(&completed);
    let worker = std::thread::Builder::new().spawn(move || {
        assert!(sender.send(Ok(None)).is_ok());
        worker_completed.store(true, Ordering::Release);
    })?;
    let reader = Reader {
        inbox: Some(inbox),
        worker: Some(worker),
    };
    assert!(reader.receive()?.is_none());
    Ok((reader, completed))
}

#[test]
fn closing_a_reader_whose_peer_already_hung_up_logs_no_error() -> TestResult {
    let (reader, completed) = eof_reader()?;
    let log = CapturedLog::default();
    log.capture(|| {
        reader.close_with(|| {
            Err(io::Error::new(
                ErrorKind::NotConnected,
                "peer socket is already gone",
            ))
        });
    });
    assert!(
        completed.load(Ordering::Acquire),
        "close joined the EOF reader"
    );
    assert_eq!(error_count(&log), 0, "{}", log.contents());
    Ok(())
}

#[test]
fn a_reader_shutdown_failure_other_than_not_connected_logs_its_error() -> TestResult {
    let (reader, completed) = eof_reader()?;
    let log = CapturedLog::default();
    log.capture(|| {
        reader.close_with(|| {
            Err(io::Error::new(
                ErrorKind::PermissionDenied,
                "reader shutdown refused",
            ))
        });
    });
    assert!(completed.load(Ordering::Acquire));
    assert_eq!(error_count(&log), 1, "{}", log.contents());
    assert!(
        log.contents()
            .contains("wire link reader interruption failed")
    );
    assert!(log.contents().contains("reader shutdown refused"));
    Ok(())
}

#[test]
fn closing_a_reader_releases_its_full_inbox_before_joining() -> TestResult {
    let (sender, inbox) = sync_channel(1);
    let (filled, ready) = channel();
    let completed = Arc::new(AtomicBool::new(false));
    let worker_completed = Arc::clone(&completed);
    let worker = std::thread::Builder::new().spawn(move || {
        assert!(sender.send(Ok(None)).is_ok());
        assert!(filled.send(()).is_ok());
        assert!(sender.send(Ok(None)).is_err());
        worker_completed.store(true, Ordering::Release);
    })?;
    ready.recv()?;
    let reader = Reader {
        inbox: Some(inbox),
        worker: Some(worker),
    };
    let log = CapturedLog::default();
    log.capture(|| reader.close_with(|| Ok(())));
    assert!(completed.load(Ordering::Acquire));
    assert_eq!(error_count(&log), 0, "{}", log.contents());
    Ok(())
}

#[test]
fn an_already_interrupted_reader_still_reports_its_worker_panic() -> TestResult {
    let worker = std::thread::Builder::new().spawn(|| {
        std::panic::resume_unwind(Box::new("reader worker panic fixture"));
    })?;
    let reader = Reader {
        inbox: None,
        worker: Some(worker),
    };
    let log = CapturedLog::default();
    log.capture(|| {
        reader.close_with(|| {
            Err(io::Error::new(
                ErrorKind::NotConnected,
                "peer socket is already gone",
            ))
        });
    });
    assert_eq!(error_count(&log), 1, "{}", log.contents());
    assert!(log.contents().contains("wire link reader panicked"));
    assert!(
        !log.contents()
            .contains("wire link reader interruption failed")
    );
    Ok(())
}