namespace NitroxModel.DataStructures.Util; [TestClass] public class OptionalTest { /// /// These optional additions should be in test method but MSTest /// reuses instances which causes Optional{T}.HasValue to be called before the new conditions are added. /// [ClassInitialize] public static void Init(TestContext _) { Optional.ApplyHasValueCondition(v => v.GetType() == typeof(A) || v.Threshold > 200); // Cheat: allow check if type A to do more complex tests on Optional.HasValue Optional.ApplyHasValueCondition(v => v.Threshold <= 200); } [TestMethod] public void OptionalGet() { Optional op = Optional.Of("test"); op.Value.Should().Be("test"); } [TestMethod] public void OptionalIsPresent() { Optional op = Optional.Of("test"); op.HasValue.Should().BeTrue(); } [TestMethod] public void OptionalIsNotPresent() { Optional op = Optional.Empty; op.HasValue.Should().BeFalse(); } [TestMethod] public void OptionalOrElseValidValue() { Optional op = Optional.Of("test"); op.OrElse("test2").Should().Be("test"); } [TestMethod] public void OptionalOrElseNoValue() { Optional op = Optional.Empty; op.OrElse("test").Should().Be("test"); } [TestMethod] public void OptionalEmpty() { Optional op = Optional.Empty; op.HasValue.Should().BeFalse(); } [TestMethod] public void OptionalSetValueNull() { Optional op = Optional.Of(new Random()); Assert.IsTrue(op.HasValue); Assert.ThrowsException(() => { op = null; }, "Setting optional to null should not be allowed."); op = Optional.Empty; Assert.IsFalse(op.HasValue); } [TestMethod] public void OptionalHasValueDynamicChecks() { Optional opBase = Optional.Of(new Base()); opBase.HasValue.Should().BeTrue(); opBase.Value.Threshold.Should().Be(202); Optional a = Optional.Of(new A()); a.HasValue.Should().BeTrue(); Optional actuallyB = Optional.Of(new B()); actuallyB.HasValue.Should().BeFalse(); Optional b = Optional.Of(new B()); b.HasValue.Should().BeFalse(); // A check should still happen on Base because Optional includes more-specific-than-itself checks. Optional aAsBase = Optional.Of((Base)a); aAsBase.HasValue.Should().BeTrue(); aAsBase.Value.Threshold.Should().Be(200); // Optional should always do all checks because anything can be in it. // Note: This test can fail if Optional.ApplyHasValueCondition isn't called early enough. Run this test method directly and it should work. Optional bAsObj = Optional.Of(new B()); bAsObj.HasValue.Should().BeFalse(); // Type C inheritance doesn't allow for type A. But Optional has the check on A. It should skip the A check on C because inheritance doesn't match up. Optional cAsObj = Optional.Of(new C()); cAsObj.HasValue.Should().BeTrue(); ((C)cAsObj.Value).Threshold.Should().Be(203); } [TestMethod] public void OptionalEqualsCheck() { Optional op = Optional.OfNullable(null); Optional op1 = Optional.OfNullable(null); Optional op2 = Optional.Of("Test"); Optional op3 = Optional.Of("Test2"); Optional op4 = Optional.Of("Test"); Assert.IsFalse(op.Equals(op2)); Assert.IsFalse(op.Equals(op3)); Assert.IsFalse(op2.Equals(op3)); Assert.IsTrue(op.Equals(op1)); Assert.IsTrue(op2.Equals(op4)); Assert.IsTrue(op != op2); Assert.IsTrue(op2 == op4); } private class Base { public virtual int Threshold => 202; } private class A : Base { public override int Threshold => 200; } private class B : A { public override int Threshold => 201; } private class C : Base { public override int Threshold => 203; } }