Giter Site home page Giter Site logo

amethyst_test_support's Introduction

Amethyst Test Support

Test harness to support testing of Amethyst types, including:

  • Bundle
  • State
  • System
  • Resource loading.
  • Arbitrary types that Systems use during processing.

The test harness minimizes boilerplate code to set up an Amethyst Application with common bundles, and can take in logic that is normally masked behind a number of layers through a thin interface.

Usage

The following shows an example of testing a State. More examples are in the Examples section.

extern crate amethyst;
extern crate amethyst_test_support;

use std::marker::PhantomData;

use amethyst_test_support::prelude::*;
use amethyst::{
    core::bundle::{self, SystemBundle},
    ecs::prelude::*,
    prelude::*,
};

#[derive(Debug)]
struct LoadResource;

struct LoadingState<'a, 'b, S>
where
    S: State<GameData<'a, 'b>> + 'static,
{
    next_state: Option<S>,
    state_data: PhantomData<State<GameData<'a, 'b>>>,
}
impl<'a, 'b, S> LoadingState<'a, 'b, S>
where
    S: State<GameData<'a, 'b>> + 'static,
{
    fn new(next_state: S) -> Self {
        LoadingState {
            next_state: Some(next_state),
            state_data: PhantomData,
        }
    }
}
impl<'a, 'b, S> State<GameData<'a, 'b>> for LoadingState<'a, 'b, S>
where
    S: State<GameData<'a, 'b>> + 'static,
{
    fn update(&mut self, data: StateData<GameData>) -> Trans<GameData<'a, 'b>> {
        data.data.update(&data.world);
        data.world.add_resource(LoadResource);
        Trans::Switch(Box::new(self.next_state.take().unwrap()))
    }
}

#[test]
fn assertion_push_with_loading_state_with_add_resource_succeeds() {
    let assertion = |world: &mut World| {
        world.read_resource::<LoadResource>();
    };

    assert!(
        AmethystApplication::blank()
            .with_state(|| LoadingState::new(EmptyState))
            .with_assertion(assertion)
            .run()
            .is_ok()
    );
}

The Amethyst application is initialized with one of the following functions, each providing a different level of default bundles:

extern crate amethyst_test_support;

use amethyst_test_support::prelude::*;

#[test]
fn test_name() {
    // Start with no bundles
    AmethystApplication::blank();

    // Start with the Transform, Input, and UI bundles
    AmethystApplication::base();

    // Start with the Animation, Transform, Input, UI, and Render bundles.
    let visibility = false; // Whether the window should be shown
    AmethystApplication::render_base("test_name", visibility);
}

Next, attach the logic you wish to test using the various .with_*(..) methods:

#[test]
fn test_name() {
    let effect = |world: &mut World| {
         let entity = world.create_entity().with(MyComponent(0)).build();

        // `EffectReturn` is a convenience wrapper struct to pass values between this effect
        // function and the assertion function.
        world.add_resource(EffectReturn(entity));
    };

    let assertion = |world: &mut World| {
        let entity = world.read_resource::<EffectReturn<Entity>>().0.clone();

        let my_component_storage = world.read_storage::<MyComponent>();
        let my_component = component_zero_storage
            .get(entity)
            .expect("Entity should have a `MyComponent` component.");

        // If "my_system" ran, the value in the `MyComponent` should be 1.
        assert_eq!(1, my_component.0);
    };

    let visibility = false; // Whether the window should be shown
    AmethystApplication::render_base("test_name", visibility)
        .with_bundle(MyBundle::new()) // Can be invoked multiple times.
        .with_bundle_fn(|| MyNonSendBundle::new()) // Can be invoked multiple times.
        .with_state(|| MyState::new()) // Must be before any calls to `.with_resource(R)`.
                                       // Can only be invoked once.
        .with_system(MySystem::new(), "my_system", &[]) // Can be invoked multiple times.
        .with_effect(effect) // Can only be invoked once.
        .with_assertion(assertion) // Can only be invoked once.
        .with_resource(MyResource::new()) // Adds a resource to the world.
                                          // Must be after any calls to `.with_state(S)`.
                                          // Can be invoked multiple times.
         // ...
}

Finally, call .run() to run the application. This returns amethyst::Result<()>, so you can wrap it in an assert!(..);:

#[test]
fn test_name() {
    let effect = // ...
    let assertion = // ...

    let visibility = false; // Whether the window should be shown
    assert!(
        AmethystApplication::render_base("test_name", visibility)
            // ...
            .with_effect(effect)
            .with_assertion(assertion)
            .run()
            .is_ok()
    );
}

Examples

Testing a bundle:

extern crate amethyst;
extern crate amethyst_test_support;

use amethyst_test_support::prelude::*;
use amethyst::{
    core::bundle::{self, SystemBundle},
    ecs::prelude::*,
    prelude::*,
};

#[derive(Debug)]
struct ApplicationResource;

#[derive(Debug)]
struct MySystem;
type MySystemData<'s> = ReadExpect<'s, ApplicationResource>;
impl<'s> System<'s> for MySystem {
    type SystemData = MySystemData<'s>;
    fn run(&mut self, _: Self::SystemData) {}
    fn setup(&mut self, res: &mut Resources) {
        MySystemData::setup(res);
        res.insert(ApplicationResource);
    }
}

#[derive(Debug)]
struct MyBundle;
impl<'a, 'b> SystemBundle<'a, 'b> for MyBundle {
    fn build(self, builder: &mut DispatcherBuilder<'a, 'b>) -> bundle::Result<()> {
        builder.add(MySystem, "my_system", &[]);
        Ok(())
    }
}

#[test]
fn bundle_registers_system_with_resource() {
    assert!(
        AmethystApplication::blank()
            .with_bundle(MyBundle)
            .with_assertion(|world| { world.read_resource::<ApplicationResource>(); })
            .run()
            .is_ok()
    );
}

Testing a system:

extern crate amethyst;
extern crate amethyst_test_support;

use amethyst_test_support::prelude::*;
use amethyst::{
    ecs::prelude::*,
    prelude::*,
};

struct MyComponent(pub i32);
impl Component for MyComponent {
    type Storage = DenseVecStorage<Self>;
}

#[derive(Debug)]
struct MySystem;
type MySystemData<'s> = WriteStorage<'s, MyComponent>;
impl<'s> System<'s> for MySystem {
    type SystemData = MySystemData<'s>;
    fn run(&mut self, mut my_component_storage: Self::SystemData) {
        for mut my_component in (&mut my_component_storage).join() {
            my_component.0 += 1
        }
    }
}

#[test]
fn system_increases_component_value_by_one() {
    let effect_fn = |world: &mut World| {
        let entity = world.create_entity().with(MyComponent(0)).build();

        world.add_resource(EffectReturn(entity));
    };
    let assertion = |world: &mut World| {
        let entity = world.read_resource::<EffectReturn<Entity>>().0.clone();

        let my_component_storage = world.read_storage::<MyComponent>();
        let my_component = my_component_storage
            .get(entity)
            .expect("Entity should have a `MyComponent` component.");

        // If the system ran, the value in the `MyComponent` should be 1.
        assert_eq!(1, my_component.0);
    };

    assert!(
        AmethystApplication::blank()
            .with_system(MySystem, "my_system", &[])
            .with_effect(effect_fn)
            .with_assertion(assertion)
            .run()
            .is_ok()
    );
}

amethyst_test_support's People

Contributors

azriel91 avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.