euv-engine 0.29.0

A high-performance 2D game engine built on the euv framework, featuring ECS, fixed-timestep game loop, canvas rendering, physics, collision detection, sprite animation, and audio.
Documentation
use super::*;

fn all_buffer_usages() -> [BufferUsage; 10] {
    [
        BufferUsage::MapRead,
        BufferUsage::MapWrite,
        BufferUsage::CopySource,
        BufferUsage::CopyDestination,
        BufferUsage::Index,
        BufferUsage::Vertex,
        BufferUsage::Uniform,
        BufferUsage::Storage,
        BufferUsage::Indirect,
        BufferUsage::QueryResolve,
    ]
}

fn all_texture_usages() -> [TextureUsage; 4] {
    [
        TextureUsage::CopySource,
        TextureUsage::CopyDestination,
        TextureUsage::TextureBinding,
        TextureUsage::RenderAttachment,
    ]
}

fn all_shader_stages() -> [ShaderStage; 3] {
    [
        ShaderStage::Vertex,
        ShaderStage::Fragment,
        ShaderStage::Compute,
    ]
}

#[test]
fn every_buffer_usage_occupies_exactly_one_bit() {
    for usage in all_buffer_usages() {
        let bit: u32 = usage.usage_bit();
        assert_eq!(
            bit.count_ones(),
            1,
            "{usage:?} must map to a single power-of-two bit so uses combine with |, got {bit:#x}"
        );
        assert!(
            bit.is_power_of_two(),
            "{usage:?} must be a power of two, got {bit:#x}"
        );
    }
}

#[test]
fn the_buffer_usage_bits_are_all_distinct() {
    let mut seen: Vec<u32> = Vec::new();
    for usage in all_buffer_usages() {
        let bit: u32 = usage.usage_bit();
        assert!(
            !seen.contains(&bit),
            "{usage:?} collides with an earlier buffer use at {bit:#x}"
        );
        seen.push(bit);
    }
    assert_eq!(seen.len(), 10);
}

#[test]
fn the_buffer_and_texture_families_are_not_interchangeable() {
    assert_ne!(
        BufferUsage::CopySource.usage_bit(),
        TextureUsage::CopySource.usage_bit(),
        "the two families share variant NAMES but not values — CopySource is 4 \
         for a buffer and 1 for a texture, so sharing a table would silently \
         misdeclare one of them"
    );
    assert_ne!(
        BufferUsage::CopyDestination.usage_bit(),
        TextureUsage::CopyDestination.usage_bit()
    );
}

#[test]
fn the_texture_usage_bits_are_all_distinct_single_bits() {
    let mut seen: Vec<u32> = Vec::new();
    for usage in all_texture_usages() {
        let bit: u32 = usage.usage_bit();
        assert_eq!(
            bit.count_ones(),
            1,
            "{usage:?} must be a single bit, got {bit:#x}"
        );
        assert!(!seen.contains(&bit), "{usage:?} collides at {bit:#x}");
        seen.push(bit);
    }
}

#[test]
fn combining_buffer_uses_yields_the_union_of_their_bits() {
    let a: u32 = BufferUsage::Vertex.usage_bit();
    let b: u32 = BufferUsage::CopyDestination.usage_bit();
    let combined: u32 = a | b;
    assert_eq!(
        combined.count_ones(),
        2,
        "two distinct uses occupy two bits"
    );
    assert!(combined & a != 0 && combined & b != 0);
}

#[test]
fn a_repeated_use_does_not_add_a_bit() {
    let once: u32 = BufferUsage::Storage.usage_bit();
    let twice: u32 = once | once;
    assert_eq!(
        once, twice,
        "`|` must be idempotent, or a caller listing a use twice gets a \
         different descriptor for the same intent"
    );
}

#[test]
fn every_shader_stage_occupies_exactly_one_bit() {
    for stage in all_shader_stages() {
        let bit: u32 = stage.stage_bit();
        assert_eq!(
            bit.count_ones(),
            1,
            "{stage:?} must be a single bit so visibility combines with |, got {bit:#x}"
        );
    }
}

#[test]
fn the_shader_stage_bits_are_all_distinct() {
    let mut seen: Vec<u32> = Vec::new();
    for stage in all_shader_stages() {
        let bit: u32 = stage.stage_bit();
        assert!(!seen.contains(&bit), "{stage:?} collides at {bit:#x}");
        seen.push(bit);
    }
}

#[test]
fn combining_two_stages_exposes_exactly_them() {
    let vertex: u32 = ShaderStage::Vertex.stage_bit();
    let fragment: u32 = ShaderStage::Fragment.stage_bit();
    let both: u32 = vertex | fragment;
    assert_eq!(both.count_ones(), 2);
    assert_eq!(
        both & ShaderStage::Compute.stage_bit(),
        0,
        "Compute was not asked for, so a layout built this way must not          expose it — over-declaring visibility is a validation error in WebGPU"
    );
}

#[test]
fn the_vertex_step_mode_strings_are_the_wgsl_spellings() {
    assert_eq!(VertexStepMode::Vertex.as_str(), "vertex");
    assert_eq!(
        VertexStepMode::Instance.as_str(),
        "instance",
        "the string is written into the WebGPU layout, so a typo is rejected \
         only at pipeline creation"
    );
    assert_ne!(
        VertexStepMode::Vertex.as_str(),
        VertexStepMode::Instance.as_str()
    );
}

#[test]
fn the_render_quality_ladder_is_distinct_and_defaults_to_high() {
    let all: [RenderQuality; 3] = [
        RenderQuality::Low,
        RenderQuality::Medium,
        RenderQuality::High,
    ];
    for i in 0..all.len() {
        for j in (i + 1)..all.len() {
            assert_ne!(all[i], all[j], "two quality levels share a value");
        }
    }
    assert_eq!(
        RenderQuality::default(),
        RenderQuality::High,
        "the documented default errs toward visual fidelity over a few \
         milliseconds of GPU time, because users notice aliasing first"
    );
}

#[test]
fn the_render_quality_names_are_distinct_and_ordered() {
    let names: Vec<String> = [
        RenderQuality::Low,
        RenderQuality::Medium,
        RenderQuality::High,
    ]
    .iter()
    .map(|quality: &RenderQuality| format!("{quality:?}"))
    .collect();
    assert_eq!(
        names,
        vec!["Low", "Medium", "High"],
        "the ladder must read low → medium → high in that order"
    );
}