A game framework written with osu! in mind.
at master 112 lines 3.9 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2// See the LICENCE file in the repository root for full licence text. 3 4using System.Collections.Generic; 5using System.Threading; 6using System.Threading.Tasks; 7using NUnit.Framework; 8using osu.Framework.Bindables; 9using osu.Framework.Configuration; 10using osu.Framework.Development; 11using osu.Framework.Platform; 12using osu.Framework.Testing; 13using osu.Framework.Threading; 14 15namespace osu.Framework.Tests.Platform 16{ 17 [TestFixture] 18 public class GameHostSuspendTest 19 { 20 private TestTestGame game; 21 private HeadlessGameHost host; 22 23 private const int timeout = 10000; 24 25 [TestCase(ExecutionMode.SingleThread)] 26 [TestCase(ExecutionMode.MultiThreaded)] 27 public void TestPauseResume(ExecutionMode threadMode) 28 { 29 var gameCreated = new ManualResetEventSlim(); 30 31 IBindable<GameThreadState> updateThreadState = null; 32 33 var task = Task.Run(() => 34 { 35 using (host = new ExecutionModeGameHost(@"host", threadMode)) 36 { 37 game = new TestTestGame(); 38 gameCreated.Set(); 39 host.Run(game); 40 } 41 }); 42 43 Assert.IsTrue(gameCreated.Wait(timeout)); 44 Assert.IsTrue(game.BecameAlive.Wait(timeout)); 45 46 // check scheduling is working before suspend 47 var completed = new ManualResetEventSlim(); 48 game.Schedule(() => 49 { 50 updateThreadState = host.UpdateThread.State.GetBoundCopy(); 51 updateThreadState.BindValueChanged(state => 52 { 53 if (state.NewValue != GameThreadState.Starting) 54 Assert.IsTrue(ThreadSafety.IsUpdateThread); 55 }); 56 completed.Set(); 57 }); 58 59 Assert.IsTrue(completed.Wait(timeout / 10)); 60 Assert.AreEqual(GameThreadState.Running, updateThreadState.Value); 61 62 host.Suspend(); 63 64 // in single-threaded execution, the main thread may already be in the process of updating one last time. 65 int gameUpdates = 0; 66 game.Scheduler.AddDelayed(() => ++gameUpdates, 0, true); 67 Assert.That(() => gameUpdates, Is.LessThan(2).After(timeout / 10)); 68 Assert.AreEqual(GameThreadState.Paused, updateThreadState.Value); 69 70 // check that scheduler doesn't process while suspended.. 71 completed.Reset(); 72 game.Schedule(() => completed.Set()); 73 Assert.IsFalse(completed.Wait(timeout / 10)); 74 75 // ..and does after resume. 76 host.Resume(); 77 Assert.IsTrue(completed.Wait(timeout / 10)); 78 Assert.AreEqual(GameThreadState.Running, updateThreadState.Value); 79 80 game.Exit(); 81 Assert.IsTrue(task.Wait(timeout)); 82 Assert.AreEqual(GameThreadState.Exited, updateThreadState.Value); 83 } 84 85 private class ExecutionModeGameHost : TestRunHeadlessGameHost 86 { 87 private readonly ExecutionMode threadMode; 88 89 public ExecutionModeGameHost(string name, ExecutionMode threadMode) 90 : base(name) 91 { 92 this.threadMode = threadMode; 93 } 94 95 protected override void SetupConfig(IDictionary<FrameworkSetting, object> defaultOverrides) 96 { 97 base.SetupConfig(defaultOverrides); 98 Config.SetValue(FrameworkSetting.ExecutionMode, threadMode); 99 } 100 } 101 102 private class TestTestGame : TestGame 103 { 104 public readonly ManualResetEventSlim BecameAlive = new ManualResetEventSlim(); 105 106 protected override void LoadComplete() 107 { 108 BecameAlive.Set(); 109 } 110 } 111 } 112}