euv-engine 0.30.2

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 shape_of<T: Collider>(value: T) -> ColliderShape {
    value.shape()
}

fn center_of<T: Collider>(value: T) -> Vector2D {
    value.center()
}

fn shape3d_of<T: Collider3D>(value: T) -> ColliderShape3D {
    value.shape()
}

#[test]
fn the_three_asset_types_are_mutually_distinct() {
    let all: [AssetType; 3] = [AssetType::Image, AssetType::Audio, AssetType::Data];
    for i in 0..all.len() {
        for j in (i + 1)..all.len() {
            assert_ne!(all[i], all[j]);
        }
    }
    assert_eq!(AssetType::default(), AssetType::Image);
}

#[test]
fn the_three_asset_states_are_mutually_distinct() {
    let all: [AssetState; 3] = [AssetState::Loading, AssetState::Loaded, AssetState::Error];
    for i in 0..all.len() {
        for j in (i + 1)..all.len() {
            assert_ne!(all[i], all[j]);
        }
    }
    assert_eq!(
        AssetState::default(),
        AssetState::Loading,
        "an asset starts life as Loading, never as a finished state"
    );
}

#[test]
fn an_asset_type_is_hashable_so_it_can_key_the_cache() {
    let kinds: HashSet<AssetType> = [
        AssetType::Image,
        AssetType::Audio,
        AssetType::Data,
        AssetType::Image,
    ]
    .into_iter()
    .collect();
    assert_eq!(kinds.len(), 3, "the repeated Image must collapse");
}

#[test]
fn the_three_audio_play_states_are_mutually_distinct() {
    let all: [AudioPlayState; 3] = [
        AudioPlayState::Playing,
        AudioPlayState::Paused,
        AudioPlayState::Stopped,
    ];
    for i in 0..all.len() {
        for j in (i + 1)..all.len() {
            assert_ne!(all[i], all[j]);
        }
    }
    assert_eq!(AudioPlayState::default(), AudioPlayState::Playing);
}

#[test]
fn a_fresh_asset_cache_has_nothing_loaded() {
    let cache: AssetCache = AssetCache::default();
    assert_eq!(
        cache.loaded_count(),
        0,
        "a cache that has loaded nothing must report zero"
    );
}

#[test]
fn a_fresh_asset_cache_reports_an_unknown_key_as_absent() {
    let cache: AssetCache = AssetCache::default();
    assert!(
        cache.get_state("missing.png").is_none(),
        "an unknown key must read as None rather than panic"
    );
    assert!(cache.get_image("missing.png").is_none());
}

#[test]
fn the_closure_store_alias_is_a_shared_cell() {
    let store: AssetClosures = Rc::new(EngineCell::new(AssetClosureStore::default()));
    let cloned: AssetClosures = Rc::clone(&store);
    assert!(
        Rc::ptr_eq(&store, &cloned),
        "the alias is an std::rc::Rc, so a clone must share one allocation"
    );
}

#[test]
fn the_pending_counter_alias_starts_at_zero_and_is_writable() {
    let pending: AssetPending = Rc::new(EngineCell::new(0));
    assert_eq!(*pending.get(), 0);
    *pending.get_mut() = 3;
    assert_eq!(
        *pending.get(),
        3,
        "the counter is a plain u32 behind a cell"
    );
}

#[test]
fn an_aabb_collider_reports_the_aabb_shape() {
    let collider: AabbCollider = AabbCollider::new(Rect::new(0.0, 0.0, 4.0, 2.0));
    assert_eq!(collider.shape(), ColliderShape::Aabb);
    assert_eq!(shape_of(collider), ColliderShape::Aabb);
}

#[test]
fn a_circle_collider_reports_the_circle_shape() {
    let collider: CircleCollider = CircleCollider::from_center(Vector2D::new(1.0, 2.0), 3.0);
    assert_eq!(collider.shape(), ColliderShape::Circle);
    assert_eq!(shape_of(collider), ColliderShape::Circle);
}

#[test]
fn a_circle_collider_centre_is_its_own_circle_centre() {
    let collider: CircleCollider = CircleCollider::from_center(Vector2D::new(1.0, 2.0), 3.0);
    let observed: Vector2D = center_of(collider);
    assert!(
        (observed.get_x() - 1.0).abs() < f64::EPSILON,
        "a circle's collider centre is the circle's centre, got {}",
        observed.get_x()
    );
    assert!((observed.get_y() - 2.0).abs() < f64::EPSILON);
}

#[test]
fn a_2d_collider_contains_a_point_inside_and_rejects_one_outside() {
    let rect: AabbCollider = AabbCollider::new(Rect::new(0.0, 0.0, 4.0, 2.0));
    assert!(rect.contains_point(Vector2D::new(1.0, 1.0)));
    assert!(!rect.contains_point(Vector2D::new(9.0, 1.0)));

    let circle: CircleCollider = CircleCollider::from_center(Vector2D::new(0.0, 0.0), 2.0);
    assert!(circle.contains_point(Vector2D::new(1.0, 0.0)));
    assert!(
        !circle.contains_point(Vector2D::new(5.0, 0.0)),
        "a point well outside the radius must be rejected"
    );
}

#[test]
fn a_3d_box_collider_reports_the_aabb_shape() {
    let collider: AabbCollider3D =
        AabbCollider3D::new(AABB3D::new(Vector3D::zero(), Vector3D::new(1.0, 1.0, 1.0)));
    assert_eq!(collider.shape(), ColliderShape3D::Aabb);
    assert_eq!(shape3d_of(collider), ColliderShape3D::Aabb);
}

#[test]
fn a_3d_sphere_collider_reports_the_sphere_shape() {
    let collider: SphereCollider3D = SphereCollider3D::new(Sphere::new(Vector3D::zero(), 1.0));
    assert_eq!(collider.shape(), ColliderShape3D::Sphere);
    assert_eq!(shape3d_of(collider), ColliderShape3D::Sphere);
}

#[test]
fn the_two_collider_traits_report_different_shape_enums() {
    let two: ColliderShape = shape_of(AabbCollider::default());
    let three: ColliderShape3D = shape3d_of(AabbCollider3D::default());
    assert_eq!(format!("{two:?}"), "Aabb");
    assert_eq!(format!("{three:?}"), "Aabb");
    assert_eq!(ColliderShape::default(), ColliderShape::Aabb);
    assert_eq!(ColliderShape3D::default(), ColliderShape3D::Aabb);
}