genswap 0.1.0

Generation-tracked ArcSwap wrapper for 5-1600x faster cached reads in read-heavy workloads
Documentation
use super::*;
use std::sync::Arc;
use std::thread;

#[test]
fn test_basic_create_and_read() {
    let swap = Arc::new(GenSwap::new(42u64));
    assert_eq!(swap.generation(), 0);
    assert_eq!(*swap.load_full(), 42);
}

#[test]
fn test_update_increments_generation() {
    let swap = Arc::new(GenSwap::new(1));
    assert_eq!(swap.generation(), 0);

    swap.update(2);
    assert_eq!(swap.generation(), 1);
    assert_eq!(*swap.load_full(), 2);

    swap.update(3);
    assert_eq!(swap.generation(), 2);
    assert_eq!(*swap.load_full(), 3);
}

#[test]
fn test_update_arc() {
    let swap = Arc::new(GenSwap::new(1));
    assert_eq!(swap.generation(), 0);

    let arc = Arc::new(100);
    swap.update_arc(Arc::clone(&arc));
    assert_eq!(swap.generation(), 1);
    assert_eq!(*swap.load_full(), 100);
}

#[test]
fn test_new_from_arc() {
    let arc = Arc::new(42);
    let swap = GenSwap::new_from_arc(arc);
    assert_eq!(swap.generation(), 0);
    assert_eq!(*swap.load_full(), 42);
}

#[test]
fn test_rcu() {
    let swap = Arc::new(GenSwap::new(5));
    assert_eq!(swap.generation(), 0);

    swap.rcu(|&n| n * 2);
    assert_eq!(*swap.load_full(), 10);
    assert_eq!(swap.generation(), 1);

    swap.rcu(|&n| n + 1);
    assert_eq!(*swap.load_full(), 11);
    assert_eq!(swap.generation(), 2);
}

#[test]
fn test_cached_reader_basic() {
    let swap = Arc::new(GenSwap::new(42));
    let mut reader = swap.reader();

    assert_eq!(**reader.get(), 42);
    assert_eq!(reader.cached_generation(), 0);
}

#[test]
fn test_cached_reader_cache_hit() {
    let swap = Arc::new(GenSwap::new(42));
    let mut reader = swap.reader();

    let first = Arc::clone(reader.get());
    let second = Arc::clone(reader.get());

    // Should return the same Arc (pointer equality)
    assert!(Arc::ptr_eq(&first, &second));
}

#[test]
fn test_cached_reader_cache_miss() {
    let swap = Arc::new(GenSwap::new(42));
    let mut reader = swap.reader();

    let first = Arc::clone(reader.get());
    assert_eq!(*first, 42);
    assert_eq!(reader.cached_generation(), 0);

    // Update the swap
    swap.update(100);

    let second = Arc::clone(reader.get());
    assert_eq!(*second, 100);
    assert_eq!(reader.cached_generation(), 1);

    // Should NOT be the same Arc
    assert!(!Arc::ptr_eq(&first, &second));
}

#[test]
fn test_cached_reader_get_clone() {
    let swap = Arc::new(GenSwap::new(vec![1, 2, 3]));
    let mut reader = swap.reader();

    let arc1 = reader.get_clone();
    let arc2 = reader.get_clone();

    assert_eq!(*arc1, vec![1, 2, 3]);
    assert!(Arc::ptr_eq(&arc1, &arc2));
}

#[test]
fn test_is_stale() {
    let swap = Arc::new(GenSwap::new(42));
    let mut reader = swap.reader();

    assert!(!reader.is_stale());

    swap.update(100);
    assert!(reader.is_stale());

    reader.get(); // Refresh
    assert!(!reader.is_stale());
}

#[test]
fn test_force_refresh() {
    let swap = Arc::new(GenSwap::new(42));
    let mut reader = swap.reader();

    let first_gen = reader.cached_generation();
    assert_eq!(first_gen, 0);

    // force_refresh reloads even without generation change
    reader.force_refresh();
    let second_gen = reader.cached_generation();
    assert_eq!(second_gen, 0); // Generation unchanged
    assert_eq!(**reader.get(), 42); // Data unchanged

    // Now update and verify force_refresh works
    swap.update(100);
    assert!(reader.is_stale());

    reader.force_refresh();
    assert!(!reader.is_stale());
    assert_eq!(**reader.get(), 100);
}

#[test]
fn test_cached() {
    let swap = Arc::new(GenSwap::new(42));
    let mut reader = swap.reader();

    let _ = reader.get();
    swap.update(100);

    // cached() returns the old value
    assert_eq!(**reader.cached(), 42);

    // get() returns the new value
    assert_eq!(**reader.get(), 100);
}

#[test]
fn test_multiple_readers() {
    let swap = Arc::new(GenSwap::new(1));
    let mut reader1 = swap.reader();
    let mut reader2 = swap.reader();

    assert_eq!(**reader1.get(), 1);
    assert_eq!(**reader2.get(), 1);

    swap.update(2);

    // Both readers should see the update
    assert_eq!(**reader1.get(), 2);
    assert_eq!(**reader2.get(), 2);
}

#[test]
fn test_multiple_updates() {
    let swap = Arc::new(GenSwap::new(0));
    let mut reader = swap.reader();

    for i in 1..=100 {
        swap.update(i);
        assert_eq!(**reader.get(), i);
        assert_eq!(swap.generation(), i as u64);
    }
}

#[test]
fn test_default() {
    let swap: GenSwap<i32> = GenSwap::default();
    assert_eq!(*swap.load_full(), 0);
    assert_eq!(swap.generation(), 0);
}

#[test]
fn test_from() {
    let swap = GenSwap::from(42);
    assert_eq!(*swap.load_full(), 42);
    assert_eq!(swap.generation(), 0);
}

#[test]
fn test_send_sync() {
    fn assert_send<T: Send>() {}
    fn assert_sync<T: Sync>() {}
    fn assert_not_sync<T: Send>() {}

    assert_send::<GenSwap<i32>>();
    assert_sync::<GenSwap<i32>>();

    // CachedReader is Send but not Sync
    assert_send::<CachedReader<i32>>();
    assert_not_sync::<CachedReader<i32>>();
}

#[test]
fn test_threaded_single_reader_per_thread() {
    let swap = Arc::new(GenSwap::new(0));

    let handles: Vec<_> = (0..4)
        .map(|_| {
            let swap = Arc::clone(&swap);
            thread::spawn(move || {
                let mut reader = swap.reader();
                for _ in 0..1000 {
                    let _value = reader.get();
                }
            })
        })
        .collect();

    // Update from main thread
    for i in 1..=10 {
        thread::sleep(std::time::Duration::from_micros(100));
        swap.update(i);
    }

    for handle in handles {
        handle.join().unwrap();
    }
}

#[test]
fn test_threaded_writer_and_readers() {
    let swap = Arc::new(GenSwap::new(0));

    // Spawn readers
    let reader_handles: Vec<_> = (0..4)
        .map(|_| {
            let swap = Arc::clone(&swap);
            thread::spawn(move || {
                let mut reader = swap.reader();
                let mut last_seen = 0;
                for _ in 0..10000 {
                    let value = **reader.get();
                    // Values should be monotonically increasing
                    assert!(value >= last_seen);
                    last_seen = value;
                }
            })
        })
        .collect();

    // Spawn writer
    let writer_swap = Arc::clone(&swap);
    let writer_handle = thread::spawn(move || {
        for i in 1..=100 {
            writer_swap.update(i);
            thread::sleep(std::time::Duration::from_micros(10));
        }
    });

    writer_handle.join().unwrap();
    for handle in reader_handles {
        handle.join().unwrap();
    }

    // Verify final state
    assert_eq!(swap.generation(), 100);
    assert_eq!(*swap.load_full(), 100);
}

#[test]
fn test_rcu_with_contention() {
    let swap = Arc::new(GenSwap::new(0));
    let handles: Vec<_> = (0..4)
        .map(|_| {
            let swap = Arc::clone(&swap);
            thread::spawn(move || {
                for _ in 0..250 {
                    swap.rcu(|&n| n + 1);
                }
            })
        })
        .collect();

    for handle in handles {
        handle.join().unwrap();
    }

    // All 1000 increments should have been applied
    assert_eq!(*swap.load_full(), 1000);
    assert_eq!(swap.generation(), 1000);
}

#[test]
fn test_large_data() {
    // Test with larger data structures
    let data = vec![1u64; 10000];
    let swap = Arc::new(GenSwap::new(data.clone()));
    let mut reader = swap.reader();

    assert_eq!(**reader.get(), data);

    let new_data = vec![2u64; 10000];
    swap.update(new_data.clone());

    assert_eq!(**reader.get(), new_data);
}

#[test]
fn test_string_data() {
    let swap = Arc::new(GenSwap::new(String::from("hello")));
    let mut reader = swap.reader();

    assert_eq!(**reader.get(), "hello");

    swap.update(String::from("world"));
    assert_eq!(**reader.get(), "world");
}

#[test]
fn test_complex_type() {
    #[derive(Debug, Clone, PartialEq)]
    struct Config {
        name: String,
        count: usize,
        flags: Vec<bool>,
    }

    let config1 = Config {
        name: "test".to_string(),
        count: 42,
        flags: vec![true, false, true],
    };

    let swap = Arc::new(GenSwap::new(config1.clone()));
    let mut reader = swap.reader();

    assert_eq!(**reader.get(), config1);

    let config2 = Config {
        name: "updated".to_string(),
        count: 100,
        flags: vec![false, false],
    };

    swap.update(config2.clone());
    assert_eq!(**reader.get(), config2);
}

#[test]
fn test_generation_monotonic() {
    let swap = Arc::new(GenSwap::new(0));
    let mut last_gen = 0;

    for i in 1..=1000 {
        swap.update(i);
        let gen = swap.generation();
        assert!(gen > last_gen, "Generation should be monotonically increasing");
        assert_eq!(gen, i as u64);
        last_gen = gen;
    }
}

#[test]
fn test_reader_observes_all_updates_eventually() {
    let swap = Arc::new(GenSwap::new(0));
    let mut reader = swap.reader();

    for i in 1..=100 {
        swap.update(i);
        // Reader should eventually see the update
        let value = **reader.get();
        assert!(value >= i);
    }
}