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;
5using System.Diagnostics;
6using NUnit.Framework;
7using osu.Framework.Allocation;
8using osu.Framework.Graphics;
9using osu.Framework.Graphics.Containers;
10using osu.Framework.Testing;
11using osu.Framework.Tests.Visual;
12
13namespace osu.Framework.Tests.Exceptions
14{
15 [HeadlessTest]
16 public class TestSceneDependencyInjectionExceptions : FrameworkTestScene
17 {
18 [Test]
19 public void TestImmediateException()
20 {
21 Exception thrownException = null;
22
23 AddStep("add thrower", () =>
24 {
25 try
26 {
27 Child = new Thrower(typeof(Exception));
28 }
29 catch (Exception ex)
30 {
31 thrownException = ex;
32 }
33 });
34
35 assertCorrectStack(() => thrownException);
36 }
37
38 [Test]
39 public void TestImmediateAggregateException()
40 {
41 Exception thrownException = null;
42
43 AddStep("add thrower", () =>
44 {
45 try
46 {
47 Child = new Thrower(typeof(Exception), true);
48 }
49 catch (Exception ex)
50 {
51 thrownException = ex;
52 }
53 });
54
55 assertCorrectStack(() => thrownException);
56 }
57
58 [Test]
59 public void TestAsyncException()
60 {
61 AsyncThrower thrower = null;
62
63 AddStep("add thrower", () => Child = thrower = new AsyncThrower(typeof(Exception)));
64 AddUntilStep("wait for exception", () => thrower.ThrownException != null);
65
66 assertCorrectStack(() => thrower.ThrownException);
67 }
68
69 private void assertCorrectStack(Func<Exception> exception) => AddAssert("exception has correct callstack", () =>
70 {
71 var stackTrace = exception().StackTrace;
72 Debug.Assert(stackTrace != null);
73
74 return stackTrace.Contains($"{nameof(TestSceneDependencyInjectionExceptions)}.{nameof(Thrower)}");
75 });
76
77 private class AsyncThrower : CompositeDrawable
78 {
79 public Exception ThrownException { get; private set; }
80
81 private readonly Type exceptionType;
82
83 public AsyncThrower(Type exceptionType)
84 {
85 this.exceptionType = exceptionType;
86 }
87
88 protected override void LoadComplete()
89 {
90 base.LoadComplete();
91 LoadComponentAsync(new Thrower(exceptionType), AddInternal);
92 }
93
94 public override bool UpdateSubTree()
95 {
96 try
97 {
98 return base.UpdateSubTree();
99 }
100 catch (Exception ex)
101 {
102 ThrownException = ex;
103 }
104
105 return true;
106 }
107 }
108
109 private class Thrower : Drawable
110 {
111 private readonly Type exceptionType;
112 private readonly bool aggregate;
113
114 public Thrower(Type exceptionType, bool aggregate = false)
115 {
116 this.exceptionType = exceptionType;
117 this.aggregate = aggregate;
118 }
119
120 [BackgroundDependencyLoader]
121 private void load()
122 {
123 if (aggregate)
124 {
125 try
126 {
127 // ReSharper disable once PossibleNullReferenceException
128 throw (Exception)Activator.CreateInstance(exceptionType);
129 }
130 catch (Exception ex)
131 {
132 throw new AggregateException(ex);
133 }
134 }
135
136 // ReSharper disable once PossibleNullReferenceException
137 throw (Exception)Activator.CreateInstance(exceptionType);
138 }
139 }
140 }
141}