ufotofu 0.10.1

Abstractions for lazily consuming and producing sequences
Documentation
//! Development helpers for Queues, specifically for property-testing them.

use core::num::NonZeroUsize;
use core::{cmp::min, fmt::Debug};

use alloc::vec::Vec;
use std::vec;

use arbitrary::Arbitrary;

use crate::queues::{Queue, QueueExt};

/// The possible operations that can be performed on a queue.
#[derive(Debug, Clone, Copy, Arbitrary)]
pub enum QueueOp {
    /// Call `enqueue`.
    Enqueue,
    /// Call `dequeue`.
    Dequeue,
    /// Call `bulk_enqueue` with a slice of some number of items.
    BulkEnqueue(NonZeroUsize),
    /// Call `bulk_dequeue` with a slice of some number of items.
    BulkDequeue(NonZeroUsize),
}

/// Check whether the given `Q: Queue` does indeed correctly implement a FIFO queue. Panics if it does not.
pub async fn assert_fifo_queue<Q>(q: &mut Q, data: &[Q::Item], mut ops: Vec<QueueOp>)
where
    Q: Queue + Debug,
    Q::Item: Clone + PartialEq + Debug + Default,
{
    let mut has_deq_op = false;
    let mut has_enq_op = false;
    for op in ops.iter() {
        match op {
            QueueOp::Enqueue | QueueOp::BulkEnqueue(_) => has_enq_op = true,
            QueueOp::Dequeue | QueueOp::BulkDequeue(_) => has_deq_op = true,
        }
    }
    if !has_deq_op {
        ops.push(QueueOp::Dequeue);
    }
    if !has_enq_op {
        ops.push(QueueOp::Enqueue);
    }

    let mut enqueued_items_count = 0;
    let mut dequeued_items_count = 0;
    let mut expected_current_len = 0;

    let mut dequeued_items = vec![Q::Item::default(); data.len()];

    let mut i = 0;
    while enqueued_items_count < data.len() || dequeued_items_count < data.len() {
        match ops[i] {
            QueueOp::Enqueue => {
                if enqueued_items_count < data.len()
                    && q.enqueue(data[enqueued_items_count].clone()).is_none() {
                        enqueued_items_count += 1;
                        expected_current_len += 1;
                        assert_eq!(q.len(), expected_current_len);
                    }
            }

            QueueOp::Dequeue => {
                if dequeued_items_count < data.len() {
                    match q.dequeue() {
                        None => assert!(q.is_empty()),
                        Some(dequeued) => {
                            dequeued_items[dequeued_items_count] = dequeued;
                            dequeued_items_count += 1;
                            expected_current_len -= 1;
                            assert_eq!(q.len(), expected_current_len);
                        }
                    }
                }
            }

            QueueOp::BulkEnqueue(amount) => {
                if enqueued_items_count < data.len() {
                    let slice_len = min(amount.into(), data.len() - enqueued_items_count);

                    let amount_enqueued = q
                        .bulk_enqueue(&data[enqueued_items_count..enqueued_items_count + slice_len])
                        .await;

                    enqueued_items_count += amount_enqueued;
                    expected_current_len += amount_enqueued;
                    assert_eq!(q.len(), expected_current_len);
                }
            }

            QueueOp::BulkDequeue(amount) => {
                if dequeued_items_count < data.len() {
                    let slice_len = min(amount.into(), data.len() - dequeued_items_count);

                    let amount_dequeued = q
                        .bulk_dequeue(
                            &mut dequeued_items
                                [dequeued_items_count..dequeued_items_count + slice_len],
                        )
                        .await;

                    dequeued_items_count += amount_dequeued;
                    expected_current_len -= amount_dequeued;
                    assert_eq!(q.len(), expected_current_len);
                }
            }
        }

        i += 1;
        if i == ops.len() {
            i = 0;
        }
    }

    assert!(q.is_empty());
    assert_eq!(&dequeued_items[..], data);
}