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.Linq;
5using NUnit.Framework;
6using osu.Framework.Development;
7using osu.Framework.Testing;
8using osu.Framework.Testing.Drawables.Steps;
9
10namespace osu.Framework.Tests.Visual.Testing
11{
12 public class TestSceneTest : FrameworkTestScene
13 {
14 private int setupRun;
15 private int setupStepsRun;
16 private int setupStepsDummyRun;
17 private int teardownStepsRun;
18 private int teardownStepsDummyRun;
19 private int testRunCount;
20 private int testRunCountDummyRun;
21
22 [SetUp]
23 public virtual void SetUp()
24 {
25 // Under nUnit, [SetUp] is run once for the base TestScene.TestConstructor() method. Our own test method has not run yet by this point, so this is ignored.
26 // The test browser does _not_ invoke [SetUp] for the constructor, and the TestScene.TestConstructor() method is skipped.
27 if (DebugUtils.IsNUnitRunning && TestContext.CurrentContext.Test.MethodName == nameof(TestConstructor))
28 return;
29
30 setupRun++;
31 }
32
33 [SetUpSteps]
34 public virtual void SetUpSteps()
35 {
36 // Under nUnit, [SetUpSteps] is run once for the base TestScene.TestConstructor() method. Our own test method has not run yet by this point, so this is ignored.
37 // The test browser does _not_ invoke [SetUpSteps] for the constructor, and the TestScene.TestConstructor() method is skipped.
38 if (DebugUtils.IsNUnitRunning && TestContext.CurrentContext.Test.MethodName == nameof(TestConstructor))
39 return;
40
41 AddStep(new SingleStepButton(true)
42 {
43 Name = "set up dummy",
44 Action = () => setupStepsDummyRun++
45 });
46
47 AddStep("set up second step", () => { });
48 setupStepsRun++;
49 }
50
51 [TearDownSteps]
52 public virtual void TearDownSteps()
53 {
54 // Under nUnit, [TearDownSteps] is run once for the base TestScene.TestConstructor() method. Our own test method has not run yet by this point, so this is ignored.
55 // The test browser does _not_ invoke [TearDownSteps] for the constructor, and the TestScene.TestConstructor() method is skipped.
56 if (DebugUtils.IsNUnitRunning && TestContext.CurrentContext.Test.MethodName == nameof(TestConstructor))
57 return;
58
59 AddStep("tear down dummy", () => teardownStepsDummyRun++);
60 teardownStepsRun++;
61 }
62
63 public TestSceneTest()
64 {
65 // A dummy test used to pad the setup steps for visual testing purposes.
66 AddStep("dummy test", () => { });
67 }
68
69 [Test, Repeat(2)]
70 public void TestTest()
71 {
72 AddStep("increment run count", () => testRunCountDummyRun++);
73
74 // Under nUnit, the test steps for each method are run during [TearDown], meaning setup count == test run count.
75 // The test browser immediately invokes all test methods _before_ any test steps are run, meaning test run count == total number of tests,
76 // so the dummy value is used instead to track the true test run count inside the test browser.
77 //
78 // nUnit:
79 // [SetUp] -> [Test] -> [TearDown] -> {{ Run steps }} -> [SetUp] -> [Test] -> [TearDown] -> {{ Run steps }} ...
80 // Test browser:
81 // [SetUp] -> [Test] -> [TearDown] -> [SetUp] -> [Test] -> [TearDown] -> ... -> {{ Run steps }}
82 //
83 AddAssert("correct [SetUp] run count", () => setupRun == (DebugUtils.IsNUnitRunning ? testRunCount : testRunCountDummyRun));
84
85 // Under both nUnit and the test browser, this should be invoked once for all test methods _before_ any test steps are run.
86 AddAssert("correct [SetUpSteps] run count", () => setupStepsRun == testRunCount);
87
88 // Under both nUnit and the test browser, this should be invoked once before each test method.
89 AddAssert("correct setup step run", () => setupStepsDummyRun == testRunCountDummyRun);
90
91 // Under both nUnit and the test browser, this should be invoked once for all test methods _before_ any test steps are run.
92 AddAssert("correct [TearDownSteps] run count", () => teardownStepsRun == testRunCount);
93
94 // Under both nUnit and the test browser, this should be invoked once _after_ each test method.
95 AddAssert("correct teardown step run", () => teardownStepsDummyRun == testRunCountDummyRun - 1);
96
97 AddAssert("setup step marked as such", () => StepsContainer.OfType<StepButton>().First(s => s.Text.ToString() == "set up second step").IsSetupStep);
98
99 testRunCount++;
100 }
101
102 [TestCase(1)]
103 [TestCase(2)]
104 [Repeat(2)]
105 public void TestTestCase(int _) => TestTest();
106
107 protected override ITestSceneTestRunner CreateRunner() => new TestRunner();
108
109 private class TestRunner : TestSceneTestRunner
110 {
111 public override void RunTestBlocking(TestScene test)
112 {
113 base.RunTestBlocking(test);
114
115 // This will only ever trigger via NUnit
116 Assert.That(test.StepsContainer, Has.Count.GreaterThan(0));
117 }
118 }
119 }
120}