flense 0.4.1

Purpose-oriented lensing
Documentation
//! Tests for [`LensSliceMut`].

#![allow(clippy::float_cmp)]

use std::array;

use assert2::assert;
use flense::prelude::*;

use super::*;

#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
#[test]
fn slice_mut_subrange_writes() {
    let mut verts = [Vertex::default(); 4];
    {
        let mut lens: LensSliceMut<'_, (Normal, Other, Color, Position)> =
            verts.as_mut_slice().lens_slice_mut();
        let mut sub = lens.slice_mut(1..3).unwrap();
        assert!(sub.len() == 2);
        sub.get_mut::<Position, _>(0).unwrap()[0] = 7.0;
        sub.get_mut::<Position, _>(1).unwrap()[0] = 8.0;
        assert!(sub.get_mut::<Position, _>(2).is_none());
    }
    assert!(verts[0].position == [0.0, 0.0, 0.0]);
    assert!(verts[1].position == [7.0, 0.0, 0.0]);
    assert!(verts[2].position == [8.0, 0.0, 0.0]);
}

#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
#[test]
fn mut_iter_interleaved_front_back() {
    let mut verts: [Vertex; 6] = array::from_fn(|i| Vertex {
        other: i as u64,
        ..Vertex::default()
    });
    {
        let lens: LensSliceMut<'_, (Normal, Other, Color, Position)> =
            verts.as_mut_slice().lens_slice_mut();
        let mut it = lens.into_iter();
        let mut seen = Vec::new();
        // A front and back lens held at once must not alias.
        while let (Some(mut front), Some(mut back)) = (it.next(), it.next_back()) {
            seen.push(*front.as_ref::<Other, _>());
            seen.push(*back.as_ref::<Other, _>());
            front.as_mut::<Position, _>()[0] = 1.0;
            back.as_mut::<Position, _>()[0] = 2.0;
        }
        seen.sort_unstable();
        assert!(seen == [0, 1, 2, 3, 4, 5]);
    }
    assert!(verts[0].position[0] == 1.0);
    assert!(verts[5].position[0] == 2.0);
}

#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
#[test]
fn mut_iter_collect_then_write() {
    let mut verts = [Vertex::default(); 4];
    {
        let lens: LensSliceMut<'_, (Normal, Other, Color, Position)> =
            verts.as_mut_slice().lens_slice_mut();
        // All per-element lenses alive at once must be disjoint.
        let mut all: Vec<_> = lens.into_iter().collect();
        for (i, l) in all.iter_mut().enumerate() {
            l.as_mut::<Position, _>()[0] = i as f32;
            *l.as_mut::<Other, _>() = i as u64;
        }
    }
    for (i, v) in verts.iter().enumerate() {
        assert!(v.position[0] == i as f32);
        assert!(v.other == i as u64);
    }
}

#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
#[test]
fn zip_mut_truncates_and_writes() {
    // Two slices joined by `zip`, which truncates to the shorter length.
    let mut positions = vec![[0.0f32, 0.0, 0.0]; 5];
    let mut colors = vec![[1.0f32, 1.0, 1.0]; 3];
    {
        let zipped = zip_lens_mut::<(Position,), (Color,), 1>(
            positions.as_mut_slice(),
            colors.as_mut_slice(),
        );
        assert!(zipped.len() == 3);
        for mut l in zipped {
            let c = l.as_mut::<Color, _>()[0];
            l.as_mut::<Position, _>()[0] += c;
            l.as_mut::<Color, _>()[1] = 9.0;
        }
    }
    // Zipped prefix was modified; the truncated tail of `positions` was not.
    assert!(positions[2] == [1.0, 0.0, 0.0]);
    assert!(positions[3] == [0.0, 0.0, 0.0]);
    assert!(colors[2] == [1.0, 9.0, 1.0]);
}

#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
#[test]
fn split_lens_slice_mut() {
    let mut verts = [
        Vertex {
            position: [1.0, 2.0, 3.0],
            color: [4.0, 5.0, 6.0],
            normal: [7, 8],
            other: 0xAA,
        },
        Vertex {
            position: [10.0, 20.0, 30.0],
            color: [40.0, 50.0, 60.0],
            normal: [70, 80],
            other: 0xBB,
        },
    ];
    {
        let lens: LensSliceMut<'_, (Normal, Other, Color, Position)> =
            verts.as_mut_slice().lens_slice_mut();
        let (mut lhs, mut rhs) = lens.split::<(Color, Other), _>();
        assert!(lhs.len() == 2);
        assert!(rhs.len() == 2);

        for i in 0..2 {
            *lhs.get_mut::<Color, _>(i).unwrap() = [100.0 + i as f32, 0.0, 0.0];
            *lhs.get_mut::<Other, _>(i).unwrap() = 0x1000 + i as u64;
            *rhs.get_mut::<Position, _>(i).unwrap() = [200.0 + i as f32, 0.0, 0.0];
            *rhs.get_mut::<Normal, _>(i).unwrap() = [i as u8, (i + 1) as u8];
        }
    }

    assert!(verts[0].color == [100.0, 0.0, 0.0]);
    assert!(verts[1].color == [101.0, 0.0, 0.0]);
    assert!(verts[0].other == 0x1000);
    assert!(verts[1].other == 0x1001);
    assert!(verts[0].position == [200.0, 0.0, 0.0]);
    assert!(verts[1].position == [201.0, 0.0, 0.0]);
    assert!(verts[0].normal == [0, 1]);
    assert!(verts[1].normal == [1, 2]);
}

#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
#[test]
fn lens_slice_mut_get_mut() {
    let mut verts = [
        Vertex {
            position: [1.0, 2.0, 3.0],
            color: [4.0, 5.0, 6.0],
            normal: [7, 8],
            other: 0xAA,
        },
        Vertex {
            position: [10.0, 20.0, 30.0],
            color: [40.0, 50.0, 60.0],
            normal: [70, 80],
            other: 0xBB,
        },
    ];
    {
        let mut lens: LensSliceMut<'_, (Normal, Other, Color, Position)> =
            verts.as_mut_slice().lens_slice_mut();
        for i in 0..2 {
            *lens.get_mut::<Position, _>(i).unwrap() = [200.0 + i as f32, 0.0, 0.0];
            *lens.get_mut::<Other, _>(i).unwrap() = 0x1000 + i as u64;
            *lens.get_mut::<Color, _>(i).unwrap() = [100.0 + i as f32, 0.0, 0.0];
            *lens.get_mut::<Normal, _>(i).unwrap() = [i as u8, (i + 1) as u8];
            // Shared accessors observe the just-written values.
            assert!(*lens.get::<Position, _>(i).unwrap() == [200.0 + i as f32, 0.0, 0.0]);
            assert!(*lens.get::<Color, _>(i).unwrap() == [100.0 + i as f32, 0.0, 0.0]);
        }
        assert!(lens.get_mut::<Position, _>(2).is_none());
        assert!(lens.get::<Position, _>(2).is_none());
    }
    assert!(verts[0].position == [200.0, 0.0, 0.0]);
    assert!(verts[1].position == [201.0, 0.0, 0.0]);
    assert!(verts[0].color == [100.0, 0.0, 0.0]);
    assert!(verts[1].color == [101.0, 0.0, 0.0]);
    assert!(verts[0].other == 0x1000);
    assert!(verts[1].other == 0x1001);
    assert!(verts[0].normal == [0, 1]);
    assert!(verts[1].normal == [1, 2]);
}

#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
#[test]
fn lens_slice_mut_get_all() {
    let mut verts = [
        Vertex {
            position: [1.0, 2.0, 3.0],
            color: [4.0, 5.0, 6.0],
            normal: [7, 8],
            other: 0xAA,
        },
        Vertex {
            position: [10.0, 20.0, 30.0],
            color: [40.0, 50.0, 60.0],
            normal: [70, 80],
            other: 0xBB,
        },
    ];
    let expected = verts;
    let lens: LensSliceMut<'_, (Normal, Other, Color, Position)> =
        verts.as_mut_slice().lens_slice_mut();

    for (i, v) in expected.iter().enumerate() {
        let l = lens.get_all(i).unwrap();
        assert!(*l.as_ref::<Position, _>() == v.position);
        assert!(*l.as_ref::<Color, _>() == v.color);
        assert!(*l.as_ref::<Normal, _>() == v.normal);
        assert!(*l.as_ref::<Other, _>() == v.other);
    }
    assert!(lens.get_all(2).is_none());
}

#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
#[test]
fn lens_slice_mut_get_all_mut() {
    let mut verts = [
        Vertex {
            position: [1.0, 2.0, 3.0],
            color: [4.0, 5.0, 6.0],
            normal: [7, 8],
            other: 0xAA,
        },
        Vertex {
            position: [10.0, 20.0, 30.0],
            color: [40.0, 50.0, 60.0],
            normal: [70, 80],
            other: 0xBB,
        },
    ];
    {
        let mut lens: LensSliceMut<'_, (Normal, Other, Color, Position)> =
            verts.as_mut_slice().lens_slice_mut();

        for i in 0..2 {
            let mut l = lens.get_all_mut(i).unwrap();
            *l.as_mut::<Position, _>() = [200.0 + i as f32, 0.0, 0.0];
            *l.as_mut::<Color, _>() = [100.0 + i as f32, 0.0, 0.0];
            *l.as_mut::<Normal, _>() = [i as u8, (i + 1) as u8];
            *l.as_mut::<Other, _>() = 0x1000 + i as u64;
        }
        assert!(lens.get_all_mut(2).is_none());
    }

    assert!(verts[0].position == [200.0, 0.0, 0.0]);
    assert!(verts[1].position == [201.0, 0.0, 0.0]);
    assert!(verts[0].color == [100.0, 0.0, 0.0]);
    assert!(verts[1].color == [101.0, 0.0, 0.0]);
    assert!(verts[0].normal == [0, 1]);
    assert!(verts[1].normal == [1, 2]);
    assert!(verts[0].other == 0x1000);
    assert!(verts[1].other == 0x1001);
}

#[test]
fn lens_slice_mut_get_all_mut_out_of_bounds() {
    let mut verts: [Vertex; 0] = [];
    let mut lens: LensSliceMut<'_, (Normal, Other, Color, Position)> =
        verts.as_mut_slice().lens_slice_mut();
    assert!(lens.is_empty());
    assert!(lens.get_all_mut(0).is_none());
}

#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
#[test]
fn lens_slice_mut_get_all_mut_after_split() {
    let mut verts = [
        Vertex {
            position: [1.0, 2.0, 3.0],
            color: [4.0, 5.0, 6.0],
            normal: [7, 8],
            other: 0xAA,
        },
        Vertex {
            position: [10.0, 20.0, 30.0],
            color: [40.0, 50.0, 60.0],
            normal: [70, 80],
            other: 0xBB,
        },
    ];
    {
        let lens: LensSliceMut<'_, (Normal, Other, Color, Position)> =
            verts.as_mut_slice().lens_slice_mut();
        let (mut lhs, mut rhs) = lens.split::<(Position,), _>();

        let mut l = lhs.get_all_mut(0).unwrap();
        *l.as_mut::<Position, _>() = [999.0, 0.0, 0.0];

        let mut r = rhs.get_all_mut(1).unwrap();
        *r.as_mut::<Color, _>() = [888.0, 0.0, 0.0];
        *r.as_mut::<Normal, _>() = [42, 43];
        *r.as_mut::<Other, _>() = 0xFEED;
    }

    assert!(verts[0].position == [999.0, 0.0, 0.0]);
    assert!(verts[1].color == [888.0, 0.0, 0.0]);
    assert!(verts[1].normal == [42, 43]);
    assert!(verts[1].other == 0xFEED);
}