A game framework written with osu! in mind.
at master 190 lines 6.0 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; 5using System.Collections.Generic; 6using System.Linq; 7using System.Threading; 8using System.Threading.Tasks; 9using NUnit.Framework; 10using osu.Framework.Allocation; 11using osu.Framework.Graphics; 12using osu.Framework.Graphics.Containers; 13using osu.Framework.Graphics.Shapes; 14using osu.Framework.Graphics.Sprites; 15using osu.Framework.Logging; 16using osuTK; 17using osuTK.Graphics; 18 19namespace osu.Framework.Tests.Visual.Drawables 20{ 21 public class TestSceneDrawableLoadCancellation : FrameworkTestScene 22 { 23 private readonly List<SlowLoader> loaders = new List<SlowLoader>(); 24 25 [SetUp] 26 public void SetUp() => Schedule(() => 27 { 28 loaders.Clear(); 29 Child = createLoader(); 30 }); 31 32 [Test] 33 public void TestConcurrentLoad() 34 { 35 AddStep("replace slow loader", () => { Child = createLoader(); }); 36 AddStep("replace slow loader", () => { Child = createLoader(); }); 37 AddStep("replace slow loader", () => { Child = createLoader(); }); 38 39 AddUntilStep("all but last loader cancelled", () => loaders.AsEnumerable().Reverse().Skip(1).All(l => l.WasCancelled)); 40 41 AddUntilStep("last loader began loading", () => !loaders.Last().WasCancelled); 42 43 AddStep("allow load to complete", () => loaders.Last().AllowLoadCompletion()); 44 45 AddUntilStep("last loader loaded", () => loaders.Last().HasLoaded); 46 } 47 48 [Test] 49 public void TestLoadAsyncCancel() 50 { 51 bool loaded = false; 52 53 PausableLoadDrawable loader = null; 54 CancellationTokenSource cancellationSource = null; 55 56 AddStep("start async load", () => LoadComponentAsync(loader = new PausableLoadDrawable(0), _ => loaded = true, (cancellationSource = new CancellationTokenSource()).Token)); 57 58 AddUntilStep("load started", () => loader.IsLoading); 59 60 AddStep("cancel", () => cancellationSource.Cancel()); 61 62 AddUntilStep("load cancelled", () => !loader.IsLoading); 63 AddAssert("didn't callback", () => !loaded); 64 } 65 66 private int id; 67 68 private SlowLoader createLoader() 69 { 70 var loader = new SlowLoader(id++); 71 loaders.Add(loader); 72 return loader; 73 } 74 75 public class SlowLoader : CompositeDrawable 76 { 77 private readonly int id; 78 private PausableLoadDrawable loadable; 79 80 public bool WasCancelled => loadable?.IsLoading == false; 81 public bool HasLoaded => loadable?.IsLoaded ?? false; 82 83 public void AllowLoadCompletion() => loadable?.AllowLoadCompletion(); 84 85 public SlowLoader(int id) 86 { 87 this.id = id; 88 RelativeSizeAxes = Axes.Both; 89 90 Anchor = Anchor.Centre; 91 Origin = Anchor.Centre; 92 93 Size = new Vector2(0.9f); 94 95 InternalChildren = new Drawable[] 96 { 97 new Box 98 { 99 Colour = Color4.Navy, 100 RelativeSizeAxes = Axes.Both, 101 }, 102 }; 103 } 104 105 protected override void LoadComplete() 106 { 107 base.LoadComplete(); 108 109 this.FadeInFromZero(200); 110 LoadComponentAsync(loadable = new PausableLoadDrawable(id), AddInternal); 111 } 112 } 113 114 public class PausableLoadDrawable : CompositeDrawable 115 { 116 private readonly int id; 117 118 public bool IsLoading; 119 120 public PausableLoadDrawable(int id) 121 { 122 this.id = id; 123 124 RelativeSizeAxes = Axes.Both; 125 126 Anchor = Anchor.Centre; 127 Origin = Anchor.Centre; 128 129 Size = new Vector2(0.9f); 130 131 InternalChildren = new Drawable[] 132 { 133 new Box 134 { 135 Colour = Color4.NavajoWhite, 136 RelativeSizeAxes = Axes.Both 137 }, 138 new SpriteText 139 { 140 Text = id.ToString(), 141 Colour = Color4.Black, 142 Font = new FontUsage(size: 50), 143 Anchor = Anchor.Centre, 144 Origin = Anchor.Centre 145 } 146 }; 147 } 148 149 private readonly CancellationTokenSource ourSource = new CancellationTokenSource(); 150 151 [BackgroundDependencyLoader] 152 private void load(CancellationToken? cancellation) 153 { 154 IsLoading = true; 155 156 using (var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(ourSource.Token, cancellation ?? CancellationToken.None)) 157 { 158 try 159 { 160 Task.Delay(99999, linkedSource.Token).Wait(linkedSource.Token); 161 } 162 catch (OperationCanceledException) 163 { 164 if (!ourSource.IsCancellationRequested) 165 { 166 IsLoading = false; 167 throw; 168 } 169 } 170 } 171 172 Logger.Log($"Load {id} complete!"); 173 } 174 175 public void AllowLoadCompletion() => ourSource.Cancel(); 176 177 protected override void LoadComplete() 178 { 179 base.LoadComplete(); 180 this.FadeInFromZero(200); 181 } 182 183 protected override void Dispose(bool isDisposing) 184 { 185 base.Dispose(isDisposing); 186 ourSource.Dispose(); 187 } 188 } 189 } 190}