package rabbitescape.render;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*;
import java.io.PrintStream;
import org.junit.*;
import rabbitescape.engine.*;
import rabbitescape.engine.config.Config;
import rabbitescape.engine.config.MemoryConfigStorage;
import rabbitescape.engine.textworld.Comment;
import rabbitescape.engine.textworld.TextWorldManip;
import rabbitescape.render.gameloop.*;
public class TestGameLoop
{
@Test
public void If_rendering_is_quick_we_render_all_frames()
{
TestObjects obj = prepareGameLoop( 0 ); // 0ms to draw a frame
// Progress through the frames
obj.gameLoop.step();
// At each step, we should draw
obj.calls.assertCalls( "waitMs(0)", "draw(0)", "waitMs(70)" );
// 10 more frames
for ( int i = 0; i < 10; ++i )
{
obj.gameLoop.step();
}
obj.calls.assertCalls(
"waitMs(0)", "draw(0)", "waitMs(70)",
"waitMs(0)", "draw(1)", "waitMs(70)",
"waitMs(0)", "draw(2)", "waitMs(70)",
"waitMs(0)", "draw(3)", "waitMs(70)",
"waitMs(0)", "draw(4)", "waitMs(70)",
"waitMs(0)", "draw(5)", "waitMs(70)",
"waitMs(0)", "draw(6)", "waitMs(70)",
"waitMs(0)", "draw(7)", "waitMs(70)",
"waitMs(0)", "draw(8)", "waitMs(70)",
"waitMs(0)", "draw(9)", "waitMs(70)",
"waitMs(0)", "draw(0)", "waitMs(70)" // Start again after 10
);
}
@Test
public void If_rendering_is_fairly_quick_we_render_all_frames()
{
TestObjects obj = prepareGameLoop( 55 ); // 55ms per frame
// Do 5 frames
for ( int i = 0; i < 5; ++i )
{
obj.gameLoop.step();
}
obj.calls.assertCalls(
"waitMs(0)", "draw(0)", "waitMs(15)",
"waitMs(0)", "draw(1)", "waitMs(15)",
"waitMs(0)", "draw(2)", "waitMs(15)",
"waitMs(0)", "draw(3)", "waitMs(15)",
"waitMs(0)", "draw(4)", "waitMs(15)"
);
}
@Test
public void Long_pause_in_middle_does_not_cause_us_to_skip()
{
TestObjects obj = prepareGameLoop( 45 );
// More frames
for ( int i = 0; i < 5; ++i )
{
obj.gameLoop.step();
}
// A long pause happens...
obj.input.time += 3000;
// More frames
for ( int i = 0; i < 6; ++i )
{
obj.gameLoop.step();
}
obj.calls.assertCalls(
"waitMs(0)", "draw(0)", "waitMs(25)",
"waitMs(0)", "draw(1)", "waitMs(25)",
"waitMs(0)", "draw(2)", "waitMs(25)",
"waitMs(0)", "draw(3)", "waitMs(25)",
"waitMs(0)", "draw(4)", "waitMs(25)",
"waitMs(0)", "draw(5)", "waitMs(-2975)", // We'd like to go back
"waitMs(0)", "draw(6)", "waitMs(25)", // in time, but once we've
"waitMs(0)", "draw(7)", "waitMs(25)", // expressed that, we
"waitMs(0)", "draw(8)", "waitMs(25)", // carry on regardless.
"waitMs(0)", "draw(9)", "waitMs(25)",
"waitMs(0)", "draw(0)", "waitMs(25)"
);
}
// ---
class TestObjects
{
public CallTracker calls;
public GameLoop gameLoop;
public SimpleClockInput input;
public DrawTracker graphics;
}
private TestObjects prepareGameLoop( int msPerFrame )
{
TestObjects ret = new TestObjects();
ret.calls = new CallTracker();
ret.input = new SimpleClockInput( ret.calls );
// 0 ms to draw each per frame
ret.graphics = new DrawTracker( ret.input, ret.calls, msPerFrame );
World neverWonWorld = new NeverWonWorld( TextWorldManip.createEmptyWorld( 5, 5 ) );
GeneralPhysics physics = new GeneralPhysics(
neverWonWorld,
null,
false
);
Config config = new Config( null, new MemoryConfigStorage() );
PrintStream debugout = null;
ret.gameLoop = new GameLoop(
ret.input, physics, new WaterAnimation( neverWonWorld ), ret.graphics, config, debugout );
ret.gameLoop.resetClock();
return ret;
}
private static class NeverWonWorld extends World
{
public NeverWonWorld( World w )
{
super(
w.size,
w.blockTable.getListCopy(),
w.rabbits,
w.things,
w.getWaterContents(),
w.abilities,
w.name,
w.description,
w.author_name,
w.author_url,
w.hints,
w.solutions,
w.num_rabbits,
w.num_to_save,
w.rabbit_delay,
null,
w.num_saved,
w.num_killed,
1,
w.getRabbitIndexCount(),
w.paused,
new Comment[] {},
w.changes.statsListener,
VoidMarkerStyle.Style.HIGHLIGHTER
);
assertThat( completionState(), equalTo( CompletionState.RUNNING ) );
}
}
private static class SimpleClockInput implements Input
{
private final CallTracker calls;
private long time = 0;
public SimpleClockInput( CallTracker calls )
{
this.calls = calls;
}
@Override
public void waitMs( long wait_time )
{
calls.track( "waitMs", wait_time );
time += wait_time;
}
@Override
public long timeNow()
{
return time;
}
@Override
public void dispose()
{
}
}
private static class DrawTracker extends DoNothingGraphics
{
private final SimpleClockInput input;
private final CallTracker calls;
private final int renderTimeMs;
public DrawTracker(
SimpleClockInput input, CallTracker calls, int renderTimeMs )
{
super();
this.input = input;
this.calls = calls;
this.renderTimeMs = renderTimeMs;
}
@Override
public void draw( int frame )
{
calls.track( "draw", frame );
input.time += renderTimeMs;
}
}
private static class DoNothingGraphics implements Graphics
{
@Override
public void draw( int frame )
{
}
@Override
public void rememberScrollPos()
{
}
@Override
public void drawIfScrolled( int frame )
{
}
@Override
public void dispose()
{
}
}
}