#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2011 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
#if UNITTEST
using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using NUnit.Framework;
using Nuclex.Graphics;
using Nuclex.Testing.Xna;
namespace Nuclex.Game.States {
#if false
/// Unit test for the loading screen game state
[TestFixture]
internal class LoadingScreenStateTest {
#region class TestGameState
/// Game state used for unit testing
private class TestGameState : GameState, ILoadableGameState {
#region class SynchronousAsyncResult
/// Dummy async result for a synchronously completed process
private class SynchronousAsyncResult : IAsyncResult {
/// Initializes a new dummy async result
/// State that will be provided by the async result
public SynchronousAsyncResult(object state) {
this.state = state;
}
/// User defined state passed to the async result
public object AsyncState {
get { return this.state; }
}
///
/// Wait handle that can be used to wait for the asynchronous process
///
public WaitHandle AsyncWaitHandle {
get {
if (this.waitHandle == null) {
this.waitHandle = new ManualResetEvent(true);
}
return this.waitHandle;
}
}
/// Whether the process has completed synchronously
public bool CompletedSynchronously {
get { return true; }
}
/// True of the asynchronous process has already completed
public bool IsCompleted {
get { return true; }
}
/// State being provided by the async result
private object state;
/// Event that can be used to wait for the process to complete
private ManualResetEvent waitHandle;
}
#endregion // class SynchronousAsyncResult
#region class AsynchronousAsyncResult
/// Dummy async result for an asynchronously completed process
public class AsynchronousAsyncResult : IAsyncResult {
/// Initializes a new dummy async result
///
/// Callback that will be invoked when the asynchronous process completes
///
/// State that will be provided by the async result
public AsynchronousAsyncResult(AsyncCallback callback, object state) {
this.callback = callback;
this.state = state;
}
/// User defined state passed to the async result
public object AsyncState {
get { return this.state; }
}
///
/// Wait handle that can be used to wait for the asynchronous process
///
public WaitHandle AsyncWaitHandle {
get {
if (this.waitHandle == null) {
SetCompleted(); // To finish in Dispose()
this.waitHandle = new ManualResetEvent(this.completed);
}
return this.waitHandle;
}
}
/// Whether the process has completed synchronously
public bool CompletedSynchronously {
get { return false; }
}
/// True of the asynchronous process has already completed
public bool IsCompleted {
get { return this.completed; }
}
/// Moves the game state into the completed state
public void SetCompleted() {
if (!this.completed) {
if (this.waitHandle != null) {
this.waitHandle.Set();
}
this.completed = true;
this.callback(this);
}
}
/// State being provided by the async result
private object state;
/// Event that can be used to wait for the process to complete
private ManualResetEvent waitHandle;
///
/// Callback that will be invoked when the asynchronous process completes
///
private AsyncCallback callback;
/// Whether the asynchronous process has completed
private bool completed;
}
#endregion // class SynchronousAsyncResult
/// Can be fired when the loading progress has changed
public event EventHandler ProgressChanged;
/// Initializes a new test game state
///
/// Game state manager the test game state belongs to
///
public TestGameState(GameStateManager manager) :
base(manager) { }
/// Begins loading the game state
///
/// Callback to be called when the game state has been loaded
///
/// User defined object to pass on to the callback
/// A result handle that can be used to wait for the loading process
public IAsyncResult BeginLoad(AsyncCallback callback, object state) {
if (this.Asynchronous) {
this.AsyncResult = new AsynchronousAsyncResult(callback, state);
return this.AsyncResult;
} else {
this.AsyncResult = new SynchronousAsyncResult(state);
callback(this.AsyncResult);
return this.AsyncResult;
}
}
/// Waits for the loading operation to finish
/// Pending operation to wait for
public void EndLoad(IAsyncResult asyncResult) { }
/// Gives the game component a chance to initialize itself
public override void Initialize() {
base.Initialize();
++this.InitializeCallCount;
}
/// Called when the component needs to update its state.
/// Provides a snapshot of the Game's timing values
public override void Update(GameTime gameTime) { }
/// Changes the reported loading progress
/// New progress to report in a range from 0.0 to 1.0
public void SetProgress(float progress) {
EventHandler copy = ProgressChanged;
if (copy != null) {
copy(this, new LoadProgressEventArgs(progress));
}
}
/// Number of times the Initialize() method has been called
public int InitializeCallCount;
/// Whether the loading process should complete asynchronously
public bool Asynchronous;
/// Asynchronous result that is tracking the loading progress
public IAsyncResult AsyncResult;
}
#endregion // class TestGameState
#region class TestLoadingScreenState
/// Loading screen game state used for testing
private class TestLoadingScreenState : LoadingScreenState {
/// Initializes a new loading screen game state
///
/// Game state manager the loading screen state belongs to
///
///
/// Game state that will be loaded by the loading screen
///
public TestLoadingScreenState(
GameStateManager gameStateManager, TestGameState gameStateToLoad
) :
base(gameStateManager, gameStateToLoad) { }
/// Game state being loaded by the loading screen
public new TestGameState GameStateToLoad {
get { return base.GameStateToLoad; }
}
/// Called when the load progress has changed
/// New load progress
protected override void OnLoadProgressChanged(float progress) {
this.LoadProgress = progress;
base.OnLoadProgressChanged(progress);
}
/// Most recently received loading progress
public float LoadProgress;
}
#endregion // class TestLoadingScreenState
/// Called before each test is run
[SetUp]
public void Setup() {
this.mockedGraphicsDeviceService = new MockedGraphicsDeviceService();
GameServiceContainer services = new GameServiceContainer();
services.AddService(
typeof(IGraphicsDeviceService), this.mockedGraphicsDeviceService
);
this.stateManager = new GameStateManager(services);
this.testState = new TestGameState(this.stateManager);
}
/// Called after each test has run
[TearDown]
public void Teardown() {
if (this.testState != null) {
this.testState.Dispose();
this.testState = null;
}
if (this.mockedGraphicsDeviceService != null) {
this.mockedGraphicsDeviceService.DestroyDevice();
this.mockedGraphicsDeviceService = null;
}
}
/// Verifies that the constructor is working
[Test]
public void TestConstructor() {
LoadingScreenState loadingScreen = new LoadingScreenState(
this.stateManager, this.testState
);
}
///
/// Ensures that the Initialize() call is passed on to the game state being loaded
///
[Test]
public void TestInitialize() {
LoadingScreenState loadingScreen =
new LoadingScreenState(this.stateManager, this.testState);
Assert.AreEqual(0, testState.InitializeCallCount);
loadingScreen.Initialize();
Assert.AreEqual(1, testState.InitializeCallCount);
}
///
/// Tests whether the loading screen can be disposed before loading has begun
///
[Test]
public void TestDisposeUnloaded() {
using (
LoadingScreenState loadingScreen =
new LoadingScreenState(this.stateManager, this.testState)
) {
loadingScreen.Initialize();
}
}
///
/// Tests whether the loading screen is able to begin the background loading process
///
[Test]
public void TestStartLoadingSynchronously() {
using (
LoadingScreenState loadingScreen =
new LoadingScreenState(this.stateManager, this.testState)
) {
loadingScreen.Initialize();
loadingScreen.StartLoading();
}
}
///
/// Tests whether the loading screen is able to begin the background loading process
/// for an asynchronously loading game state.
///
[Test]
public void TestStartLoadingAsynchronously() {
using (
LoadingScreenState loadingScreen =
new LoadingScreenState(this.stateManager, this.testState)
) {
loadingScreen.Initialize();
this.testState.Asynchronous = true;
loadingScreen.StartLoading();
}
}
///
/// Tests whether the loading screen can cope with the StartLoading() method
/// being called multiple times.
///
[Test]
public void TestStartLoadingMultipleTimes() {
using (
LoadingScreenState loadingScreen =
new LoadingScreenState(this.stateManager, this.testState)
) {
loadingScreen.Initialize();
loadingScreen.StartLoading();
loadingScreen.StartLoading();
loadingScreen.StartLoading();
}
}
///
/// Verifies that the GameStateToLoad property is returning the right value
///
[Test]
public void TestGameStateToLoad() {
using (
TestLoadingScreenState loadingScreen =
new TestLoadingScreenState(this.stateManager, this.testState)
) {
Assert.AreSame(this.testState, loadingScreen.GameStateToLoad);
}
}
///
/// Verifies that the GameStateToLoad property is returning the right value
///
[Test]
public void TestProgressReporting() {
using (
TestLoadingScreenState loadingScreen =
new TestLoadingScreenState(this.stateManager, this.testState)
) {
loadingScreen.Initialize();
// Before StartLoading(), progress reports are ignored
this.testState.SetProgress(0.123f);
Assert.AreNotEqual(0.123f, loadingScreen.LoadProgress);
// Begin the asynchronous loading process
this.testState.Asynchronous = true;
loadingScreen.StartLoading();
// This progress report should be accepted
this.testState.SetProgress(0.1234f);
Assert.AreEqual(0.1234f, loadingScreen.LoadProgress);
// Move the game state into the completed state
TestGameState.AsynchronousAsyncResult asyncResult =
(this.testState.AsyncResult as TestGameState.AsynchronousAsyncResult);
asyncResult.SetCompleted();
// This progress report should not be processed anymore
this.testState.SetProgress(0.12345f);
Assert.AreNotEqual(0.12345f, loadingScreen.LoadProgress);
}
}
///
/// Verifies that the loading screen switched to the actual game state when
/// loading has finished
///
[Test]
public void TestGameStateSwitching() {
using (
LoadingScreenState loadingScreen =
new LoadingScreenState(this.stateManager, this.testState)
) {
loadingScreen.Initialize();
this.stateManager.Push(loadingScreen);
Assert.AreSame(loadingScreen, this.stateManager.ActiveState);
// Synchronous mode - loading finishes right here
loadingScreen.StartLoading();
// The loading screen may decide to do the switch in Update() or Draw() to
// avoid thread synchronization issues.
loadingScreen.Update(new GameTime());
loadingScreen.Draw(new GameTime());
// By now, the loading screen should have established the game state it was
// loading as the new active state
Assert.AreSame(this.testState, this.stateManager.ActiveState);
}
}
/// Game state used to test the loading screen state
private TestGameState testState;
/// Mock of the graphics device service
private MockedGraphicsDeviceService mockedGraphicsDeviceService;
/// State manager used for testing the loading screen state
private GameStateManager stateManager;
}
#endif
} // namespace Nuclex.Game.States
#endif // UNITTEST