#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