shine-stdext 0.2.0

SHINE. General utilities for the general purpose multi-threaded rendering engine.
use rand;

use log::{debug, trace};
use permutohedron::Heap;
use rand::seq::SliceRandom;
use std::cell::Cell;
use std::mem;

use shine_stdext::arena::IndexedArena;
use shine_testutils::init_test;

struct DropTracker<'a>(&'a Cell<usize>);

impl<'a> Drop for DropTracker<'a> {
    fn drop(&mut self) {
        trace!("drop");
        self.0.set(self.0.get() + 1);
    }
}

struct Node<'a>(i32, DropTracker<'a>);

#[test]
fn simple() {
    init_test(module_path!());

    let drop_counter = Cell::new(0);
    {
        let mut arena = IndexedArena::new();

        debug!("store");
        assert_eq!(arena.len(), 0);

        let (id1, _) = arena.allocate(Node(1, DropTracker(&drop_counter)));
        assert_eq!(arena.len(), 1);

        let (id2, _) = arena.allocate(Node(2, DropTracker(&drop_counter)));
        assert_eq!(arena.len(), 2);

        let (id3, _) = arena.allocate(Node(3, DropTracker(&drop_counter)));
        assert_eq!(arena.len(), 3);

        let (id4, _) = arena.allocate(Node(4, DropTracker(&drop_counter)));
        assert_eq!(arena.len(), 4);

        assert_eq!(arena[id1].0, 1);
        assert_eq!(arena[id2].0, 2);
        assert_eq!(arena[id3].0, 3);
        assert_eq!(arena[id4].0, 4);
        assert_eq!(drop_counter.get(), 0);

        debug!("remove");
        let node3 = arena.deallocate(id3);
        assert_eq!(arena.len(), 3);
        assert_eq!(drop_counter.get(), 0);
        mem::drop(node3);
        assert_eq!(drop_counter.get(), 1);

        debug!("add");
        let (id3, _) = arena.allocate(Node(103, DropTracker(&drop_counter)));
        assert_eq!(arena.len(), 4);

        assert_eq!(arena[id1].0, 1);
        assert_eq!(arena[id2].0, 2);
        assert_eq!(arena[id3].0, 103);
        assert_eq!(arena[id4].0, 4);
    }
    assert_eq!(drop_counter.get(), 5);
}

#[test]
fn stress() {
    init_test(module_path!());

    let mut data = [1usize, 2, 5, 7, 100, 4000];

    let mut heap = Heap::new(&mut data);
    while let Some(sizes) = heap.next_permutation() {
        trace!("permutation {:?}", sizes);

        let drop_counter = Cell::new(0);
        let mut drop_count = 0;
        {
            let mut arena = IndexedArena::new();

            for &mut cnt in sizes.into_iter() {
                let rem = cnt / 2;
                let mut ids = Vec::new();

                trace!("store {}", cnt);
                for i in 0..cnt {
                    assert_eq!(arena.len(), i);
                    let (id, _) = arena.allocate(Node(i as i32, DropTracker(&drop_counter)));
                    ids.push((i as i32, id));
                }
                assert_eq!(arena.len(), cnt);
                assert_eq!(drop_counter.get(), drop_count);

                ids.shuffle(&mut rand::thread_rng());

                trace!("check");
                for v in ids.iter() {
                    assert_eq!(arena[v.1].0, v.0);
                }

                trace!("remove half");
                for i in 0..rem {
                    assert_eq!(drop_counter.get(), drop_count + i);
                    assert_eq!(arena.len(), cnt - i);
                    let d = arena.deallocate(ids[i].1);
                    mem::drop(d);
                    ids[i].1 = usize::max_value();
                }
                assert_eq!(arena.len(), cnt - rem);
                assert_eq!(drop_counter.get(), drop_count + rem);

                trace!("check");
                for v in ids.iter() {
                    if v.1 != usize::max_value() {
                        assert_eq!(arena[v.1].0, v.0);
                    }
                }

                trace!("add back");
                for v in ids.iter_mut() {
                    if v.1 == usize::max_value() {
                        let (id, _) = arena.allocate(Node(-v.0, DropTracker(&drop_counter)));
                        v.1 = id;
                    }
                }
                assert_eq!(arena.len(), ids.len());
                assert_eq!(drop_counter.get(), drop_count + rem);

                trace!("check");
                for v in ids.iter() {
                    assert!(arena[v.1].0 == v.0 || arena[v.1].0 == -v.0);
                }

                arena.clear();
                assert_eq!(arena.len(), 0);
                assert_eq!(drop_counter.get(), drop_count + rem + cnt);
                drop_count += rem + cnt;
            }
        }
        assert_eq!(drop_counter.get(), drop_count);
    }
}