rnp 0.1.146

A simple layer 4 ping tool for cloud.
Documentation
mod test_common;
mod test_mocks;

use futures_intrusive::sync::ManualResetEvent;
use pretty_assertions::assert_eq;
use rnp::*;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use test_mocks::*;
use tokio::runtime::Runtime;

#[test]
fn ping_with_rnp_core_should_work() {
    test_common::initialize();

    let actual_ping_results = Arc::new(Mutex::new(Vec::<MockPingClientResult>::new()));
    let config = create_mock_rnp_config(actual_ping_results.clone(), 6, 3, 1);
    let rt = Runtime::new().unwrap();
    rt.block_on(async {
        let stop_event = Arc::new(ManualResetEvent::new(false));
        let mut rp = PingRunnerCore::new(config, stop_event);
        rp.run_warmup_pings().await;
        rp.start_running_normal_pings();
        rp.join().await;
    });

    let results = actual_ping_results.lock().unwrap();
    assert_eq!(
        vec![
            MockPingClientResult::Success(Duration::from_micros(12345)),
            MockPingClientResult::Timeout,
            MockPingClientResult::PreparationFailed,
            MockPingClientResult::Success(Duration::from_micros(12345)),
            MockPingClientResult::Timeout,
            MockPingClientResult::PreparationFailed,
            MockPingClientResult::PingFailed,
            MockPingClientResult::AppHandshakeFailed(Duration::from_micros(23456)),
            MockPingClientResult::DisconnectFailed(Duration::from_micros(34567)),
        ],
        *results
    );
}

#[test]
fn ping_with_rnp_core_stress_should_work() {
    test_common::initialize();

    let actual_ping_results = Arc::new(Mutex::new(Vec::<MockPingClientResult>::new()));
    let config = create_mock_rnp_config(actual_ping_results.clone(), 1000, 0, 10);
    let rt = Runtime::new().unwrap();
    rt.block_on(async move {
        let stop_event = Arc::new(ManualResetEvent::new(false));
        let mut rp = PingRunnerCore::new(config, stop_event);
        rp.run_warmup_pings().await;
        rp.start_running_normal_pings();
        rp.join().await;
    });

    let results = actual_ping_results.lock().unwrap();
    assert_eq!(1000, results.len());
}

#[test]
fn ping_with_rnp_core_stop_event_should_work() {
    test_common::initialize();

    let actual_ping_results = Arc::new(Mutex::new(Vec::<MockPingClientResult>::new()));
    let config = create_mock_rnp_config(actual_ping_results.clone(), 1000, 0, 10);
    let rt = Runtime::new().unwrap();
    rt.block_on(async {
        let stop_event = Arc::new(ManualResetEvent::new(false));
        let stop_event_clone = stop_event.clone();

        let mut rp = PingRunnerCore::new(config, stop_event);
        rp.run_warmup_pings().await;

        rp.start_running_normal_pings();
        tokio::spawn(async move { stop_event_clone.set() });

        rp.join().await;
    });
}

#[test]
fn ping_with_rnp_core_exit_on_fail_should_work() {
    test_common::initialize();

    let actual_ping_results = Arc::new(Mutex::new(Vec::<MockPingClientResult>::new()));
    let exit_reason = Arc::new(Mutex::new(None));

    let mut config = create_mock_rnp_config(actual_ping_results.clone(), 10, 0, 1);
    config.result_processor_config.exit_on_fail = true;
    config.result_processor_config.exit_failure_reason = Some(exit_reason.clone());

    let rt = Runtime::new().unwrap();
    rt.block_on(async {
        let stop_event = Arc::new(ManualResetEvent::new(false));
        let mut rp = PingRunnerCore::new(config, stop_event);
        rp.run_warmup_pings().await;
        rp.start_running_normal_pings();
        rp.join().await;
    });

    assert!(exit_reason.lock().unwrap().is_some());

    let failed_ping_result = exit_reason.lock().unwrap();
    assert!(!failed_ping_result.as_ref().unwrap().is_succeeded);
    assert!(failed_ping_result.as_ref().unwrap().is_timed_out || !failed_ping_result.as_ref().unwrap().ping_error.is_empty());
}

fn create_mock_rnp_config(
    actual_ping_results: Arc<Mutex<Vec<MockPingClientResult>>>,
    ping_count: u32,
    warmup_count: u32,
    parallel_ping_count: u32,
) -> RnpPingRunnerConfig {
    RnpPingRunnerConfig {
        worker_config: PingWorkerConfig {
            protocol: RnpSupportedProtocol::TCP,
            target: "10.0.0.1:443".parse().unwrap(),
            source_ip: "10.0.0.2".parse().unwrap(),
            ping_interval: Duration::from_millis(0),
            ping_client_config: PingClientConfig {
                wait_timeout: Duration::from_millis(1000),
                time_to_live: Some(128),
                check_disconnect: false,
                wait_before_disconnect: Duration::ZERO,
                disconnect_timeout: Duration::ZERO,
                server_name: None,
                log_tls_key: false,
                alpn_protocol: None,
                use_timer_rtt: false,
            },
        },
        worker_scheduler_config: PingWorkerSchedulerConfig {
            source_ports: PortRangeList { ranges: vec![(1024..=2048)] },
            ping_count: Some(ping_count),
            warmup_count,
            parallel_ping_count,
        },
        result_processor_config: PingResultProcessorConfig {
            common_config: PingResultProcessorCommonConfig { quiet_level: RNP_QUIET_LEVEL_NONE },
            exit_on_fail: false,
            exit_failure_reason: None,
            csv_log_path: None,
            json_log_path: None,
            text_log_path: None,
            show_result_scatter: false,
            show_latency_scatter: false,
            latency_buckets: None,
        },
        external_ping_client_factory: Some(|_, config| {
            Some(Box::new(MockPingClient::new(
                config,
                vec![
                    MockPingClientResult::Success(Duration::from_micros(12345)),
                    MockPingClientResult::Timeout,
                    MockPingClientResult::PreparationFailed,
                    MockPingClientResult::PingFailed,
                    MockPingClientResult::AppHandshakeFailed(Duration::from_micros(23456)),
                    MockPingClientResult::DisconnectFailed(Duration::from_micros(34567)),
                ],
            )))
        }),
        extra_ping_result_processors: vec![Box::new(MockPingResultProcessor::new(actual_ping_results))],
    }
}