A game framework written with osu! in mind.
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.Threading;
5using System.Threading.Tasks;
6using NUnit.Framework;
7using osu.Framework.Allocation;
8using osu.Framework.Graphics.Containers;
9using osu.Framework.Platform;
10using osu.Framework.Testing;
11
12namespace osu.Framework.Tests.Platform
13{
14 [TestFixture]
15 public class ComponentAsyncDisposalTest
16 {
17 private TestTestGame game;
18 private HeadlessGameHost host;
19
20 private const int timeout = 10000;
21
22 /// <summary>
23 /// Ensure that if a component is on the async disposal queue, the game will wait for the queue to empty before disposing itself.
24 /// While we generally don't dispose games in normal execution, this may be used in tests, and can cause unwanted exceptions in (cancelled) load
25 /// methods if not ordered correctly.
26 /// </summary>
27 [Test]
28 public void TestChildDisposedBeforeGame()
29 {
30 var gameCreated = new ManualResetEventSlim();
31
32 var task = Task.Run(() =>
33 {
34 using (host = new TestRunHeadlessGameHost(@"host", false))
35 {
36 game = new TestTestGame();
37 gameCreated.Set();
38 host.Run(game);
39 }
40 });
41
42 Assert.IsTrue(gameCreated.Wait(timeout));
43 Assert.IsTrue(game.BecameAlive.Wait(timeout));
44
45 var container = new DisposableContainer();
46
47 game.Schedule(() => game.Add(container));
48
49 Assert.IsTrue(container.BecameAlive.Wait(timeout));
50
51 game.Schedule(() =>
52 {
53 game.ClearInternal(false);
54 game.DisposeChildAsync(container);
55 });
56
57 game.Exit();
58
59 Assert.IsTrue(task.Wait(timeout));
60
61 game.Dispose();
62
63 Assert.IsTrue(container.DisposedSuccessfully.Wait(timeout));
64 }
65
66 private class DisposableContainer : Container
67 {
68 [Resolved]
69 private TestTestGame game { get; set; }
70
71 public readonly ManualResetEventSlim BecameAlive = new ManualResetEventSlim();
72
73 public readonly ManualResetEventSlim DisposedSuccessfully = new ManualResetEventSlim();
74
75 protected override void LoadComplete()
76 {
77 BecameAlive.Set();
78 }
79
80 protected override void Dispose(bool isDisposing)
81 {
82 game.DisposalStarted.Wait(timeout);
83
84 // make sure we take some time to dispose (forcing Game to wait on the async queue).
85 Thread.Sleep(100);
86
87 // we want to ensure that game is still alive pending our disposal.
88 if (!game.IsDisposed)
89 DisposedSuccessfully.Set();
90
91 base.Dispose(isDisposing);
92 }
93 }
94
95 [Cached]
96 private class TestTestGame : TestGame
97 {
98 public readonly ManualResetEventSlim BecameAlive = new ManualResetEventSlim();
99
100 public readonly ManualResetEventSlim DisposalStarted = new ManualResetEventSlim();
101
102 public readonly ManualResetEventSlim DisposalCompleted = new ManualResetEventSlim();
103
104 protected override void LoadComplete()
105 {
106 BecameAlive.Set();
107 }
108
109 protected override void Dispose(bool isDisposing)
110 {
111 DisposalStarted.Set();
112 base.Dispose(isDisposing);
113 DisposalCompleted.Set();
114 }
115 }
116 }
117}