qubit-atomic 0.10.3

User-friendly atomic operations wrapper providing JDK-like atomic API
Documentation
/*******************************************************************************
 *
 *    Copyright (c) 2025 - 2026 Haixing Hu.
 *
 *    SPDX-License-Identifier: Apache-2.0
 *
 *    Licensed under the Apache License, Version 2.0.
 *
 ******************************************************************************/

use std::sync::Arc;
use std::thread;

use qubit_atomic::AtomicCount;

#[test]
fn test_new_get() {
    let counter = AtomicCount::new(42);
    assert_eq!(counter.get(), 42);
}

#[test]
fn test_zero() {
    let counter = AtomicCount::zero();
    assert_eq!(counter.get(), 0);
    assert!(counter.is_zero());
}

#[test]
fn test_default() {
    let counter = AtomicCount::default();
    assert_eq!(counter.get(), 0);
}

#[test]
fn test_from() {
    let counter = AtomicCount::from(9);
    assert_eq!(counter.get(), 9);
}

#[test]
fn test_is_zero() {
    let counter = AtomicCount::new(0);
    assert!(counter.is_zero());

    counter.inc();
    assert!(!counter.is_zero());
}

#[test]
fn test_is_positive() {
    let counter = AtomicCount::new(0);
    assert!(!counter.is_positive());

    counter.inc();
    assert!(counter.is_positive());
}

#[test]
fn test_inc_returns_new_value() {
    let counter = AtomicCount::zero();

    assert_eq!(counter.inc(), 1);
    assert_eq!(counter.inc(), 2);
    assert_eq!(counter.get(), 2);
}

#[test]
fn test_add_returns_new_value() {
    let counter = AtomicCount::new(2);

    assert_eq!(counter.add(3), 5);
    assert_eq!(counter.get(), 5);
}

#[test]
fn test_add_zero_returns_current_value() {
    let counter = AtomicCount::new(2);

    assert_eq!(counter.add(0), 2);
    assert_eq!(counter.get(), 2);
}

#[test]
fn test_try_add_success() {
    let counter = AtomicCount::new(2);

    assert_eq!(counter.try_add(3), Some(5));
    assert_eq!(counter.get(), 5);
}

#[test]
fn test_try_add_overflow_keeps_value() {
    let counter = AtomicCount::new(1);

    assert_eq!(counter.try_add(usize::MAX), None);
    assert_eq!(counter.get(), 1);
}

#[test]
#[should_panic(expected = "atomic counter overflow")]
fn test_add_overflow_panics() {
    let counter = AtomicCount::new(usize::MAX);
    counter.inc();
}

#[test]
fn test_dec_returns_new_value() {
    let counter = AtomicCount::new(2);

    assert_eq!(counter.dec(), 1);
    assert_eq!(counter.dec(), 0);
    assert!(counter.is_zero());
}

#[test]
#[should_panic(expected = "atomic counter underflow")]
fn test_dec_underflow_panics() {
    let counter = AtomicCount::zero();
    counter.dec();
}

#[test]
fn test_try_dec_success() {
    let counter = AtomicCount::new(1);

    assert_eq!(counter.try_dec(), Some(0));
    assert_eq!(counter.get(), 0);
}

#[test]
fn test_try_dec_underflow_keeps_value() {
    let counter = AtomicCount::zero();

    assert_eq!(counter.try_dec(), None);
    assert_eq!(counter.get(), 0);
}

#[test]
fn test_sub_returns_new_value() {
    let counter = AtomicCount::new(5);

    assert_eq!(counter.sub(3), 2);
    assert_eq!(counter.get(), 2);
}

#[test]
fn test_sub_zero_returns_current_value() {
    let counter = AtomicCount::new(5);

    assert_eq!(counter.sub(0), 5);
    assert_eq!(counter.get(), 5);
}

#[test]
#[should_panic(expected = "atomic counter underflow")]
fn test_sub_underflow_panics() {
    let counter = AtomicCount::new(2);
    counter.sub(3);
}

#[test]
fn test_try_sub_success() {
    let counter = AtomicCount::new(5);

    assert_eq!(counter.try_sub(3), Some(2));
    assert_eq!(counter.get(), 2);
}

#[test]
fn test_try_sub_underflow_keeps_value() {
    let counter = AtomicCount::new(2);

    assert_eq!(counter.try_sub(3), None);
    assert_eq!(counter.get(), 2);
}

#[test]
fn test_debug() {
    let counter = AtomicCount::new(42);

    assert_eq!(format!("{counter:?}"), "AtomicCount { value: 42 }");
}

#[test]
fn test_display() {
    let counter = AtomicCount::new(42);

    assert_eq!(format!("{counter}"), "42");
}

#[test]
fn test_concurrent_inc() {
    const THREAD_COUNT: usize = 8;
    const ITERATIONS: usize = 1_000;

    let counter = Arc::new(AtomicCount::zero());
    let mut handles = Vec::with_capacity(THREAD_COUNT);

    for _ in 0..THREAD_COUNT {
        let counter = Arc::clone(&counter);
        handles.push(thread::spawn(move || {
            for _ in 0..ITERATIONS {
                counter.inc();
            }
        }));
    }

    for handle in handles {
        handle
            .join()
            .expect("counter increment thread should not panic");
    }

    assert_eq!(counter.get(), THREAD_COUNT * ITERATIONS);
}

#[test]
fn test_concurrent_dec_to_zero() {
    const THREAD_COUNT: usize = 8;
    const ITERATIONS: usize = 1_000;

    let counter = Arc::new(AtomicCount::new(THREAD_COUNT * ITERATIONS));
    let mut handles = Vec::with_capacity(THREAD_COUNT);

    for _ in 0..THREAD_COUNT {
        let counter = Arc::clone(&counter);
        handles.push(thread::spawn(move || {
            for _ in 0..ITERATIONS {
                counter.dec();
            }
        }));
    }

    for handle in handles {
        handle
            .join()
            .expect("counter decrement thread should not panic");
    }

    assert_eq!(counter.get(), 0);
    assert!(counter.is_zero());
}