first commit
This commit is contained in:
148
Nitrox.Test/Model/Core/DependencyInjectionTests.cs
Normal file
148
Nitrox.Test/Model/Core/DependencyInjectionTests.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using Autofac;
|
||||
|
||||
namespace NitroxModel.Core;
|
||||
|
||||
[TestClass]
|
||||
public class DependencyInjectionTests
|
||||
{
|
||||
[TestInitialize]
|
||||
public void Init()
|
||||
{
|
||||
NitroxServiceLocator.InitializeDependencyContainer(new DependencyInjectionTestsAutoFacRegistrar());
|
||||
NitroxServiceLocator.BeginNewLifetimeScope();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldResolveDependencyPolymorphically()
|
||||
{
|
||||
// Arrange
|
||||
IRootDependency polymorphicallyResolvedDependency = NitroxServiceLocator.LocateService<IRootDependency>();
|
||||
|
||||
// Assert
|
||||
polymorphicallyResolvedDependency.Should().NotBeNull();
|
||||
polymorphicallyResolvedDependency.Should().BeOfType<RootDependency>();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldResolveConcreteType()
|
||||
{
|
||||
// Arrange
|
||||
DependencyWithRootDependency directConcreteTypeDependency = NitroxServiceLocator.LocateService<DependencyWithRootDependency>();
|
||||
|
||||
// Assert
|
||||
directConcreteTypeDependency.Should().NotBeNull();
|
||||
directConcreteTypeDependency.RootDependency.Should().NotBeNull();
|
||||
directConcreteTypeDependency.RootDependency.Should().BeOfType<RootDependency>();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldResolveGenericDependencies()
|
||||
{
|
||||
// Arrange
|
||||
IServicer<ServiceRecipientA> servicerA = NitroxServiceLocator.LocateService<IServicer<ServiceRecipientA>>();
|
||||
IServicer<ServiceRecipientB> servicerB = NitroxServiceLocator.LocateService<IServicer<ServiceRecipientB>>();
|
||||
|
||||
// Assert
|
||||
servicerA.Should().NotBeNull();
|
||||
servicerA.Should().BeOfType<ServiceAProvider>();
|
||||
Invoking(() => servicerA.PerformService(null)).Should().Throw<NotImplementedException>();
|
||||
|
||||
servicerB.Should().NotBeNull();
|
||||
servicerB.Should().BeOfType<ServiceBProvider>();
|
||||
Invoking(() => servicerB.PerformService(null)).Should().Throw<NotImplementedException>();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldResolveGenericDependenciesFromManuallyConstructedTypeInstances()
|
||||
{
|
||||
// Arrange
|
||||
Type servicerInstanceType = typeof(IServicer<>);
|
||||
Type recipientAType = typeof(ServiceRecipientA);
|
||||
Type recipientBType = typeof(ServiceRecipientB);
|
||||
Type servicerAType = servicerInstanceType.MakeGenericType(recipientAType);
|
||||
Type servicerBType = servicerInstanceType.MakeGenericType(recipientBType);
|
||||
|
||||
// Act
|
||||
IServicer<ServiceRecipientA> servicerA = (BaseServiceProvider<ServiceRecipientA>)NitroxServiceLocator.LocateService(servicerAType);
|
||||
IServicer<ServiceRecipientB> servicerB = (BaseServiceProvider<ServiceRecipientB>)NitroxServiceLocator.LocateService(servicerBType);
|
||||
|
||||
// Assert
|
||||
servicerA.Should().NotBeNull();
|
||||
servicerA.Should().BeOfType<ServiceAProvider>();
|
||||
Invoking(() => servicerA.PerformService(null)).Should().Throw<NotImplementedException>();
|
||||
|
||||
servicerB.Should().NotBeNull();
|
||||
servicerB.Should().BeOfType<ServiceBProvider>();
|
||||
Invoking(() => servicerB.PerformService(null)).Should().Throw<NotImplementedException>();
|
||||
}
|
||||
|
||||
private class DependencyInjectionTestsAutoFacRegistrar : IAutoFacRegistrar
|
||||
{
|
||||
public void RegisterDependencies(ContainerBuilder containerBuilder)
|
||||
{
|
||||
containerBuilder.RegisterType<RootDependency>().As<IRootDependency>();
|
||||
containerBuilder.RegisterType<DependencyWithRootDependency>();
|
||||
|
||||
containerBuilder.RegisterAssemblyTypes(Assembly.GetAssembly(GetType()))
|
||||
.AsClosedTypesOf(typeof(IServicer<>));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface IRootDependency
|
||||
{
|
||||
}
|
||||
|
||||
public class RootDependency : IRootDependency
|
||||
{
|
||||
}
|
||||
|
||||
public class DependencyWithRootDependency
|
||||
{
|
||||
public IRootDependency RootDependency { get; }
|
||||
|
||||
public DependencyWithRootDependency(IRootDependency rootDependency)
|
||||
{
|
||||
RootDependency = rootDependency;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IServiced
|
||||
{
|
||||
}
|
||||
|
||||
public interface IServicer<T>
|
||||
where T : IServiced
|
||||
{
|
||||
void PerformService(T serviced);
|
||||
}
|
||||
|
||||
public class ServiceRecipientA : IServiced
|
||||
{
|
||||
}
|
||||
|
||||
public class ServiceRecipientB : IServiced
|
||||
{
|
||||
}
|
||||
|
||||
public abstract class BaseServiceProvider<TServiced> : IServicer<TServiced>
|
||||
where TServiced : IServiced
|
||||
{
|
||||
public abstract void PerformService(TServiced serviced);
|
||||
}
|
||||
|
||||
public class ServiceAProvider : BaseServiceProvider<ServiceRecipientA>
|
||||
{
|
||||
public override void PerformService(ServiceRecipientA serviced)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class ServiceBProvider : BaseServiceProvider<ServiceRecipientB>
|
||||
{
|
||||
public override void PerformService(ServiceRecipientB serviced)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
117
Nitrox.Test/Model/DataStructures/CircularBufferTest.cs
Normal file
117
Nitrox.Test/Model/DataStructures/CircularBufferTest.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace NitroxModel.DataStructures;
|
||||
|
||||
[TestClass]
|
||||
public class CircularBufferTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void ShouldLimitSizeToMaxSize()
|
||||
{
|
||||
CircularBuffer<string> buffer = new(1);
|
||||
buffer.Count.Should().Be(0);
|
||||
buffer.Add("1");
|
||||
buffer.Count.Should().Be(1);
|
||||
buffer.Add("2");
|
||||
buffer.Count.Should().Be(1);
|
||||
|
||||
buffer = new CircularBuffer<string>(5);
|
||||
buffer.Count.Should().Be(0);
|
||||
buffer.Add("1");
|
||||
buffer.Count.Should().Be(1);
|
||||
buffer.Add("2");
|
||||
buffer.Count.Should().Be(2);
|
||||
buffer.Add("3");
|
||||
buffer.Count.Should().Be(3);
|
||||
buffer.Add("4");
|
||||
buffer.Count.Should().Be(4);
|
||||
buffer.Add("5");
|
||||
buffer.Count.Should().Be(5);
|
||||
buffer.Add("6");
|
||||
buffer.Count.Should().Be(5);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldOverwriteOldestItemInBufferWhenCapped()
|
||||
{
|
||||
CircularBuffer<string> buffer = new(3);
|
||||
buffer.Add("1");
|
||||
buffer[0].Should().Be("1");
|
||||
buffer.Add("2");
|
||||
buffer[1].Should().Be("2");
|
||||
buffer.Add("3");
|
||||
buffer[2].Should().Be("3");
|
||||
buffer.Add("4");
|
||||
buffer[0].Should().Be("4");
|
||||
buffer.Add("5");
|
||||
buffer[1].Should().Be("5");
|
||||
buffer[2].Should().Be("3");
|
||||
buffer.Add("6");
|
||||
buffer[2].Should().Be("6");
|
||||
buffer.Add("7");
|
||||
buffer.Should().ContainInOrder("7", "5", "6");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldDiscardAddIfCapacityReached()
|
||||
{
|
||||
CircularBuffer<string> buffer = new(0);
|
||||
buffer.Count.Should().Be(0);
|
||||
buffer.Add("1");
|
||||
buffer.Count.Should().Be(0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldBeEmptyWhenCleared()
|
||||
{
|
||||
CircularBuffer<string> buffer = new(10);
|
||||
buffer.Count.Should().Be(0);
|
||||
buffer.Add("1");
|
||||
buffer.Add("1");
|
||||
buffer.Add("1");
|
||||
buffer.Count.Should().Be(3);
|
||||
buffer.Clear();
|
||||
buffer.Count.Should().Be(0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldGiveLastChanged()
|
||||
{
|
||||
CircularBuffer<int> buffer = new(3);
|
||||
buffer.LastChangedIndex.Should().Be(-1);
|
||||
buffer.Add(1);
|
||||
buffer.LastChangedIndex.Should().Be(0);
|
||||
buffer.Add(2);
|
||||
buffer.LastChangedIndex.Should().Be(1);
|
||||
buffer.Add(3);
|
||||
buffer.LastChangedIndex.Should().Be(2);
|
||||
buffer.Add(4);
|
||||
buffer.LastChangedIndex.Should().Be(0);
|
||||
buffer.Add(5);
|
||||
buffer.LastChangedIndex.Should().Be(1);
|
||||
buffer.Add(6);
|
||||
buffer.LastChangedIndex.Should().Be(2);
|
||||
buffer.Add(7);
|
||||
buffer.LastChangedIndex.Should().Be(0);
|
||||
buffer.Add(8);
|
||||
buffer.LastChangedIndex.Should().Be(1);
|
||||
buffer.Clear();
|
||||
buffer.LastChangedIndex.Should().Be(-1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReverseOrderWithNegativeIndex()
|
||||
{
|
||||
CircularBuffer<int> buffer = new(6);
|
||||
buffer.AddRange(1, 2, 3, 4, 5, 6);
|
||||
buffer[-1].Should().Be(6);
|
||||
buffer[-2].Should().Be(5);
|
||||
buffer[-3].Should().Be(4);
|
||||
buffer[-4].Should().Be(3);
|
||||
buffer[-5].Should().Be(2);
|
||||
buffer[-6].Should().Be(1);
|
||||
buffer[-7].Should().Be(6);
|
||||
buffer[-8].Should().Be(5);
|
||||
}
|
||||
}
|
45
Nitrox.Test/Model/DataStructures/NitroxIdTest.cs
Normal file
45
Nitrox.Test/Model/DataStructures/NitroxIdTest.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace NitroxModel.DataStructures;
|
||||
|
||||
[TestClass]
|
||||
public class NitroxIdTest
|
||||
{
|
||||
private NitroxId id1;
|
||||
private NitroxId id2;
|
||||
|
||||
[TestMethod]
|
||||
public void SameGuidEquality()
|
||||
{
|
||||
Guid guid = Guid.NewGuid();
|
||||
id1 = new(guid);
|
||||
id2 = new(guid);
|
||||
|
||||
(id1 == id2).Should().BeTrue();
|
||||
id1.Equals(id2).Should().BeTrue();
|
||||
(id1 != id2).Should().BeFalse();
|
||||
(!id1.Equals(id2)).Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void NullGuidEquality()
|
||||
{
|
||||
id1 = new();
|
||||
id2 = null;
|
||||
|
||||
(id1 == id2).Should().BeFalse();
|
||||
id1.Equals(id2).Should().BeFalse();
|
||||
(id1 != id2).Should().BeTrue();
|
||||
(!id1.Equals(id2)).Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BothNullEquality()
|
||||
{
|
||||
id1 = id2 = null;
|
||||
(id1 != id2).Should().BeFalse();
|
||||
(id1 == id2).Should().BeTrue();
|
||||
}
|
||||
}
|
39
Nitrox.Test/Model/DataStructures/NitroxInt3Test.cs
Normal file
39
Nitrox.Test/Model/DataStructures/NitroxInt3Test.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace NitroxModel.DataStructures
|
||||
{
|
||||
[TestClass]
|
||||
public class NitroxInt3Test
|
||||
{
|
||||
private NitroxInt3 int3;
|
||||
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
{
|
||||
int3 = new NitroxInt3(5, 10, 15);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Equals()
|
||||
{
|
||||
NitroxInt3 other1 = new NitroxInt3(5, 10, 15);
|
||||
NitroxInt3 other2 = new NitroxInt3(15, 10, 5);
|
||||
|
||||
int3.Equals(other1).Should().BeTrue();
|
||||
int3.Equals(other2).Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Floor()
|
||||
{
|
||||
NitroxInt3.Floor(5.1f, 10.4f, 15.5f).Should().Be(int3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Ceil()
|
||||
{
|
||||
NitroxInt3.Ceil(4.1f, 9.4f, 14.5f).Should().Be(int3);
|
||||
}
|
||||
}
|
||||
}
|
29
Nitrox.Test/Model/DataStructures/NitroxVersionTest.cs
Normal file
29
Nitrox.Test/Model/DataStructures/NitroxVersionTest.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace NitroxModel.DataStructures;
|
||||
|
||||
[TestClass]
|
||||
public class NitroxVersionTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void Equals()
|
||||
{
|
||||
NitroxVersion a = new(2, 1);
|
||||
NitroxVersion b = new(1, 15);
|
||||
|
||||
NitroxVersion source = new(2, 1);
|
||||
source.Equals(a).Should().BeTrue();
|
||||
source.Equals(b).Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Compare()
|
||||
{
|
||||
NitroxVersion source = new(2, 1);
|
||||
source.CompareTo(new(2, 1)).Should().Be(0);
|
||||
source.CompareTo(new(1, 15)).Should().Be(1);
|
||||
source.CompareTo(new (2, 2)).Should().Be(-1);
|
||||
source.CompareTo(new (3, 1)).Should().Be(-1);
|
||||
}
|
||||
}
|
78
Nitrox.Test/Model/DataStructures/PriorityQueueTest.cs
Normal file
78
Nitrox.Test/Model/DataStructures/PriorityQueueTest.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace NitroxModel.DataStructures
|
||||
{
|
||||
using StringPriorityQueue = PriorityQueue<string>;
|
||||
|
||||
[TestClass]
|
||||
public class PriorityQueueTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void SameOrder()
|
||||
{
|
||||
StringPriorityQueue queue = new StringPriorityQueue();
|
||||
queue.Enqueue(0, "First");
|
||||
queue.Enqueue(0, "Second");
|
||||
queue.Enqueue(0, "Third");
|
||||
|
||||
Assert.AreEqual("First", queue.Dequeue());
|
||||
Assert.AreEqual("Second", queue.Dequeue());
|
||||
Assert.AreEqual("Third", queue.Dequeue());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DifferentOrder()
|
||||
{
|
||||
StringPriorityQueue queue = new StringPriorityQueue();
|
||||
queue.Enqueue(3, "First");
|
||||
queue.Enqueue(2, "Second");
|
||||
queue.Enqueue(1, "Third");
|
||||
|
||||
Assert.AreEqual("First", queue.Dequeue());
|
||||
Assert.AreEqual("Second", queue.Dequeue());
|
||||
Assert.AreEqual("Third", queue.Dequeue());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SomeAreSameOrder()
|
||||
{
|
||||
StringPriorityQueue queue = new StringPriorityQueue();
|
||||
queue.Enqueue(2, "First");
|
||||
queue.Enqueue(2, "Second");
|
||||
queue.Enqueue(0, "Third");
|
||||
|
||||
Assert.AreEqual("First", queue.Dequeue());
|
||||
Assert.AreEqual("Second", queue.Dequeue());
|
||||
Assert.AreEqual("Third", queue.Dequeue());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PrioritySanity()
|
||||
{
|
||||
StringPriorityQueue queue = new StringPriorityQueue();
|
||||
queue.Enqueue(2, "Second");
|
||||
queue.Enqueue(3, "First");
|
||||
queue.Enqueue(1, "Third");
|
||||
|
||||
Assert.AreEqual("First", queue.Dequeue());
|
||||
Assert.AreEqual("Second", queue.Dequeue());
|
||||
Assert.AreEqual("Third", queue.Dequeue());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CountSanity()
|
||||
{
|
||||
StringPriorityQueue queue = new StringPriorityQueue();
|
||||
queue.Enqueue(2, "Second");
|
||||
queue.Enqueue(3, "First");
|
||||
queue.Enqueue(1, "Third");
|
||||
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
|
||||
queue.Dequeue();
|
||||
queue.Dequeue();
|
||||
|
||||
Assert.AreEqual(1, queue.Count);
|
||||
}
|
||||
}
|
||||
}
|
162
Nitrox.Test/Model/DataStructures/ThreadSafeListTest.cs
Normal file
162
Nitrox.Test/Model/DataStructures/ThreadSafeListTest.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace NitroxModel.DataStructures
|
||||
{
|
||||
[TestClass]
|
||||
public class ThreadSafeListTest
|
||||
{
|
||||
private ThreadSafeList<string> list;
|
||||
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
{
|
||||
list = new ThreadSafeList<string>();
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
list.Add($"test {i}");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Insert()
|
||||
{
|
||||
list.Insert(5, "derp");
|
||||
list[5].Should().Be("derp");
|
||||
list[0] = "Hello world!";
|
||||
list[0].Should().Be("Hello world!");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RemoveAt()
|
||||
{
|
||||
list.RemoveAt(5);
|
||||
foreach (string item in list)
|
||||
{
|
||||
item.Should().NotBe("test 5");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Remove()
|
||||
{
|
||||
list.Remove("test 0");
|
||||
list[0].Should().Be("test 1");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Find()
|
||||
{
|
||||
list.Find(s => s == "test 1").Should().Be("test 1");
|
||||
list.Find(s => s == "tesT 1").Should().BeNull();
|
||||
list.Find(s => s == "test 1361").Should().BeNull();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadAndWriteSimultaneous()
|
||||
{
|
||||
int iterations = 500000;
|
||||
|
||||
ThreadSafeList<int> comeGetMe = new(iterations);
|
||||
List<long> countsRead = new();
|
||||
long addCount = 0;
|
||||
|
||||
Random r = new Random();
|
||||
DoReaderWriter(() =>
|
||||
{
|
||||
countsRead.Add(Interlocked.Read(ref addCount));
|
||||
},
|
||||
i =>
|
||||
{
|
||||
comeGetMe.Add(r.Next());
|
||||
Interlocked.Increment(ref addCount);
|
||||
},
|
||||
iterations);
|
||||
|
||||
addCount.Should().Be(iterations);
|
||||
countsRead.Count.Should().BeGreaterThan(0);
|
||||
countsRead.Last().Should().Be(iterations);
|
||||
comeGetMe.Count.Should().Be(iterations);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IterateAndAddSimultaneous()
|
||||
{
|
||||
int iterations = 500000;
|
||||
|
||||
ThreadSafeList<int> comeGetMe = new(iterations);
|
||||
long addCount = 0;
|
||||
long iterationsReadMany = 0;
|
||||
|
||||
Random r = new Random();
|
||||
DoReaderWriter(() =>
|
||||
{
|
||||
foreach (int unused in comeGetMe)
|
||||
{
|
||||
Interlocked.Increment(ref iterationsReadMany);
|
||||
}
|
||||
},
|
||||
i =>
|
||||
{
|
||||
comeGetMe.Add(r.Next());
|
||||
Interlocked.Increment(ref addCount);
|
||||
},
|
||||
iterations);
|
||||
|
||||
addCount.Should().Be(iterations);
|
||||
iterationsReadMany.Should().BePositive();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IterateAndAdd()
|
||||
{
|
||||
ThreadSafeList<int> nums = new()
|
||||
{
|
||||
1,2,3,4,5
|
||||
};
|
||||
|
||||
foreach (int num in nums)
|
||||
{
|
||||
if (num == 3)
|
||||
{
|
||||
nums.Add(10);
|
||||
}
|
||||
}
|
||||
|
||||
nums.Count.Should().Be(6);
|
||||
nums.Last().Should().Be(10);
|
||||
}
|
||||
|
||||
private void DoReaderWriter(Action reader, Action<int> writer, int iterators)
|
||||
{
|
||||
ManualResetEventSlim barrier = new(false);
|
||||
Thread readerThread = new(() =>
|
||||
{
|
||||
while (!barrier.IsSet)
|
||||
{
|
||||
reader();
|
||||
Thread.Yield();
|
||||
}
|
||||
|
||||
// Read one last time after writer finishes
|
||||
reader();
|
||||
});
|
||||
Thread writerThread = new(() =>
|
||||
{
|
||||
for (int i = 0; i < iterators; i++)
|
||||
{
|
||||
writer(i);
|
||||
}
|
||||
barrier.Set();
|
||||
});
|
||||
|
||||
readerThread.Start();
|
||||
writerThread.Start();
|
||||
barrier.Wait();
|
||||
}
|
||||
}
|
||||
}
|
56
Nitrox.Test/Model/DataStructures/ThreadSafeQueueTest.cs
Normal file
56
Nitrox.Test/Model/DataStructures/ThreadSafeQueueTest.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace NitroxModel.DataStructures
|
||||
{
|
||||
[TestClass]
|
||||
public class ThreadSafeQueueTest
|
||||
{
|
||||
private ThreadSafeQueue<string> queue;
|
||||
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
{
|
||||
queue = new ThreadSafeQueue<string>();
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
queue.Enqueue($"test {i}");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Peek()
|
||||
{
|
||||
queue.Peek().Should().Be("test 0");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Enqueue()
|
||||
{
|
||||
queue.Enqueue("derp");
|
||||
queue.Count.Should().Be(11);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Dequeue()
|
||||
{
|
||||
queue.Dequeue().Should().Be("test 0");
|
||||
queue.Count.Should().Be(9);
|
||||
queue.Should().NotContain("test 0");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Clear()
|
||||
{
|
||||
queue.Clear();
|
||||
queue.Count.Should().Be(0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Contains()
|
||||
{
|
||||
queue.Contains("test 5").Should().BeTrue();
|
||||
queue.Contains("test 11").Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
151
Nitrox.Test/Model/DataStructures/ThreadSafeSetTest.cs
Normal file
151
Nitrox.Test/Model/DataStructures/ThreadSafeSetTest.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace NitroxModel.DataStructures
|
||||
{
|
||||
[TestClass]
|
||||
public class ThreadSafeSetTest
|
||||
{
|
||||
private ThreadSafeSet<string> set;
|
||||
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
{
|
||||
set = new ThreadSafeSet<string>();
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
set.Add($"test {i}");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Remove()
|
||||
{
|
||||
set.Should().Contain("test 0");
|
||||
set.Remove("test 0");
|
||||
set.Should().NotContain("test 0");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Contains()
|
||||
{
|
||||
set.Contains("test 1").Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Except()
|
||||
{
|
||||
string[] exclude = { "test 0", "test 5", "test 9" };
|
||||
set.Should().Contain(exclude);
|
||||
set.ExceptWith(exclude);
|
||||
set.Should().NotContain(exclude);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadAndWriteSimultaneous()
|
||||
{
|
||||
int iterations = 500000;
|
||||
|
||||
ThreadSafeSet<string> comeGetMe = new();
|
||||
List<long> countsRead = new();
|
||||
long addCount = 0;
|
||||
|
||||
Random r = new Random();
|
||||
DoReaderWriter(() =>
|
||||
{
|
||||
countsRead.Add(Interlocked.Read(ref addCount));
|
||||
},
|
||||
i =>
|
||||
{
|
||||
comeGetMe.Add(new string(Enumerable.Repeat(' ', 10).Select(c => (char)r.Next('A', 'Z')).ToArray()));
|
||||
Interlocked.Increment(ref addCount);
|
||||
},
|
||||
iterations);
|
||||
|
||||
addCount.Should().Be(iterations);
|
||||
countsRead.Count.Should().BeGreaterThan(0);
|
||||
countsRead.Last().Should().Be(iterations);
|
||||
comeGetMe.Count.Should().Be(iterations);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IterateAndAddSimultaneous()
|
||||
{
|
||||
int iterations = 500000;
|
||||
|
||||
ThreadSafeSet<int> comeGetMe = new();
|
||||
long addCount = 0;
|
||||
long iterationsReadMany = 0;
|
||||
|
||||
Random r = new();
|
||||
DoReaderWriter(() =>
|
||||
{
|
||||
foreach (int item in comeGetMe)
|
||||
{
|
||||
Interlocked.Increment(ref iterationsReadMany);
|
||||
}
|
||||
},
|
||||
i =>
|
||||
{
|
||||
comeGetMe.Add(r.Next());
|
||||
Interlocked.Increment(ref addCount);
|
||||
},
|
||||
iterations);
|
||||
|
||||
addCount.Should().Be(iterations);
|
||||
iterationsReadMany.Should().BePositive();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IterateAndAdd()
|
||||
{
|
||||
ThreadSafeSet<int> nums = new()
|
||||
{
|
||||
1,2,3,4,5
|
||||
};
|
||||
|
||||
foreach (int num in nums)
|
||||
{
|
||||
if (num == 3)
|
||||
{
|
||||
nums.Add(10);
|
||||
}
|
||||
}
|
||||
|
||||
nums.Count.Should().Be(6);
|
||||
nums.Last().Should().Be(10);
|
||||
}
|
||||
|
||||
private void DoReaderWriter(Action reader, Action<int> writer, int iterators)
|
||||
{
|
||||
ManualResetEventSlim barrier = new(false);
|
||||
Thread readerThread = new(() =>
|
||||
{
|
||||
while (!barrier.IsSet)
|
||||
{
|
||||
reader();
|
||||
Thread.Yield();
|
||||
}
|
||||
|
||||
// Read one last time after writer finishes
|
||||
reader();
|
||||
});
|
||||
Thread writerThread = new(() =>
|
||||
{
|
||||
for (int i = 0; i < iterators; i++)
|
||||
{
|
||||
writer(i);
|
||||
}
|
||||
barrier.Set();
|
||||
});
|
||||
|
||||
readerThread.Start();
|
||||
writerThread.Start();
|
||||
barrier.Wait();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace NitroxModel.DataStructures.Unity
|
||||
{
|
||||
[TestClass]
|
||||
public class NitroxQuaternionTest
|
||||
{
|
||||
private NitroxQuaternion defaultVal;
|
||||
private const float TOLERANCE = 0.0001f;
|
||||
|
||||
[TestInitialize]
|
||||
public void Init()
|
||||
{
|
||||
defaultVal = new NitroxQuaternion(0.5682333f, -0.01828304f, -0.5182831f, 0.6388735f);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestEquality()
|
||||
{
|
||||
NitroxQuaternion other1 = new NitroxQuaternion(0.5682333f, -0.01828304f, -0.5182831f, 0.6388735f);
|
||||
NitroxQuaternion other2 = new NitroxQuaternion(-0.5682333f, 0.01828304f, 0.5182831f, -0.6388735f);
|
||||
NitroxQuaternion other3 = new NitroxQuaternion(0.5682343f, -0.01828314f, -0.5182841f, 0.6388745f);
|
||||
|
||||
defaultVal.Equals(other1, TOLERANCE).Should().BeTrue();
|
||||
defaultVal.Equals(other2, TOLERANCE).Should().BeTrue();
|
||||
defaultVal.Equals(other3, TOLERANCE).Should().BeTrue(); //Tolerance to low to detect the difference
|
||||
|
||||
(defaultVal == other1).Should().BeTrue();
|
||||
(defaultVal == other2).Should().BeTrue();
|
||||
(defaultVal != other3).Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestMultiplication()
|
||||
{
|
||||
NitroxQuaternion result1 = defaultVal * new NitroxQuaternion(-10f, 0.5f, 0.004f, 256.1111f);
|
||||
NitroxQuaternion result2 = new NitroxQuaternion(-10f, 0.5f, 0.004f, 256.1111f) * defaultVal;
|
||||
NitroxQuaternion expectedResult1 = new NitroxQuaternion(139.4012f, 0.8175055f, -132.6342f, 169.3162f);
|
||||
NitroxQuaternion expectedResult2 = new NitroxQuaternion(138.8831f, -9.543612f, -132.8368f, 169.3162f);
|
||||
|
||||
result1.Equals(expectedResult1, TOLERANCE).Should().BeTrue($"Expected: {expectedResult1} - Found: {result1}");
|
||||
result2.Equals(expectedResult2, TOLERANCE).Should().BeTrue($"Expected: {expectedResult2} - Found: {result2}");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestToEuler()
|
||||
{
|
||||
NitroxVector3 euler1 = defaultVal.ToEuler();
|
||||
NitroxVector3 euler2 = new NitroxQuaternion(0.5f, 0.5f, -0.5f, 0.5f).ToEuler();
|
||||
NitroxVector3 euler3 = new NitroxQuaternion(-0.5f, 0.5f, -0.5f, -0.5f).ToEuler();
|
||||
NitroxVector3 expectedResult1 = new NitroxVector3(45f, 300f, 255f);
|
||||
NitroxVector3 expectedResult1Other = new NitroxVector3(104.5108f, 50.75358f, 316.9205f); //defaultVal can be interpreted as both euler :shrug:
|
||||
NitroxVector3 expectedResult2 = new NitroxVector3(90f, 90f, 0f);
|
||||
NitroxVector3 expectedResult3 = new NitroxVector3(90f, 270f, 0f);
|
||||
|
||||
(euler1.Equals(expectedResult1, TOLERANCE) || euler1.Equals(expectedResult1Other, TOLERANCE)).Should().BeTrue($"Expected: {expectedResult1} or {expectedResult1Other}- Found: {euler1}");
|
||||
euler2.Equals(expectedResult2, TOLERANCE).Should().BeTrue($"Expected: {expectedResult2} - Found: {euler2}");
|
||||
euler3.Equals(expectedResult3, TOLERANCE).Should().BeTrue($"Expected: {expectedResult3} - Found: {euler3}");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestFromEuler()
|
||||
{
|
||||
NitroxQuaternion result1 = NitroxQuaternion.FromEuler(new NitroxVector3(45f, 300f, 255f));
|
||||
NitroxQuaternion result2 = NitroxQuaternion.FromEuler(new NitroxVector3(45f, -60f, 615f));
|
||||
NitroxQuaternion result3 = NitroxQuaternion.FromEuler(new NitroxVector3(400f, 10f, -0.07f));
|
||||
NitroxQuaternion result4 = NitroxQuaternion.FromEuler(new NitroxVector3(360f, 0f, -720));
|
||||
NitroxQuaternion expectedResult3 = new NitroxQuaternion(-0.3406684f, -0.08210776f, 0.03038081f, -0.9360985f);
|
||||
|
||||
result1.Equals(defaultVal, TOLERANCE).Should().BeTrue($"Expected: {defaultVal} - Found: {result1}");
|
||||
result2.Equals(defaultVal, TOLERANCE).Should().BeTrue($"Expected: {defaultVal} - Found: {result2}");
|
||||
result3.Equals(expectedResult3, TOLERANCE).Should().BeTrue($"Expected: {expectedResult3} - Found: {result3}");
|
||||
result4.Equals(NitroxQuaternion.Identity, TOLERANCE).Should().BeTrue($"Expected: {NitroxQuaternion.Identity} - Found: {result4}");
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace NitroxModel.DataStructures.Unity
|
||||
{
|
||||
[TestClass]
|
||||
public class NitroxTransformTest
|
||||
{
|
||||
private const float TOLERANCE = 0.005f;
|
||||
|
||||
private static readonly NitroxTransform root = new NitroxTransform(new NitroxVector3(1, 1, 1), NitroxQuaternion.FromEuler(0, 0, 0), new NitroxVector3(1, 1, 1));
|
||||
private static readonly NitroxTransform child1 = new NitroxTransform(new NitroxVector3(5, 3, -6), NitroxQuaternion.FromEuler(30, 0, 10), new NitroxVector3(2, 2, 2));
|
||||
private static readonly NitroxTransform child2 = new NitroxTransform(new NitroxVector3(11, 0, -0.5f), NitroxQuaternion.FromEuler(180, 30, 80), new NitroxVector3(0.5f, 0.5f, 0.5f));
|
||||
private static readonly NitroxTransform grandchild1 = new NitroxTransform(new NitroxVector3(13, 8, 15), NitroxQuaternion.FromEuler(-50, 5, 10), new NitroxVector3(1, 1, 1));
|
||||
private static readonly NitroxTransform grandchild2 = new NitroxTransform(new NitroxVector3(3, 18, -5), NitroxQuaternion.FromEuler(-5, 15, 1), new NitroxVector3(10, 10, 10));
|
||||
|
||||
[ClassInitialize]
|
||||
public static void Setup(TestContext testContext)
|
||||
{
|
||||
child1.SetParent(root, false);
|
||||
child2.SetParent(root, false);
|
||||
grandchild1.SetParent(child1, false);
|
||||
grandchild2.SetParent(child2, false);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PositionTest()
|
||||
{
|
||||
NitroxVector3 rootResult = new NitroxVector3(1, 1, 1);
|
||||
NitroxVector3 child1Result = new NitroxVector3(6, 4, -5);
|
||||
NitroxVector3 child2Result = new NitroxVector3(12, 1, 0.5f);
|
||||
NitroxVector3 grandchild1Result = new NitroxVector3(28.82663f, 6.55587f, 31.11665f);
|
||||
NitroxVector3 grandchild2Result = new NitroxVector3(5.79976f, -2.040047f, 6.966463f);
|
||||
|
||||
root.Position.Equals(rootResult, TOLERANCE).Should().BeTrue($"Expected: {rootResult} Found: {root.Position}");
|
||||
child1.Position.Equals(child1Result, TOLERANCE).Should().BeTrue($"Expected: {child1Result} Found: {child1.Position}");
|
||||
child2.Position.Equals(child2Result, TOLERANCE).Should().BeTrue($"Expected: {child2Result} Found: {child2.Position}");
|
||||
grandchild1.Position.Equals(grandchild1Result, TOLERANCE).Should().BeTrue($"Expected: {grandchild1Result} Found: {grandchild1.Position}");
|
||||
grandchild2.Position.Equals(grandchild2Result, TOLERANCE).Should().BeTrue($"Expected: {grandchild2Result} Found: {grandchild2.Position}");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RotationTest()
|
||||
{
|
||||
NitroxQuaternion rootResult = NitroxQuaternion.FromEuler(0, 0, 0);
|
||||
NitroxQuaternion child1Result = NitroxQuaternion.FromEuler(30, 0, 10);
|
||||
NitroxQuaternion child2Result = NitroxQuaternion.FromEuler(8.537737e-07f, 210, 260);
|
||||
NitroxQuaternion grandchild1Result = NitroxQuaternion.FromEuler(340.0263f, 355.2486f, 21.87437f);
|
||||
NitroxQuaternion grandchild2Result = NitroxQuaternion.FromEuler(15.60783f, 212.4433f, 261.9936f);
|
||||
|
||||
root.Rotation.Equals(rootResult, TOLERANCE).Should().BeTrue($"Expected: {rootResult} Found: {root.Rotation}");
|
||||
child1.Rotation.Equals(child1Result, TOLERANCE).Should().BeTrue($"Expected: {child1Result} Found: {child1.Rotation}");
|
||||
child2.Rotation.Equals(child2Result, TOLERANCE).Should().BeTrue($"Expected: {child2Result} Found: {child2.Rotation}");
|
||||
grandchild1.Rotation.Equals(grandchild1Result, TOLERANCE).Should().BeTrue($"Expected: {grandchild1Result} Found: {grandchild1.Rotation}");
|
||||
grandchild2.Rotation.Equals(grandchild2Result, TOLERANCE).Should().BeTrue($"Expected: {grandchild2Result} Found: {grandchild2.Rotation}");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ChangingTransformTest()
|
||||
{
|
||||
NitroxVector3 beforeLocalPosition = new NitroxVector3(15, 2, -7);
|
||||
NitroxQuaternion beforeRotation = NitroxQuaternion.FromEuler(45, 15, 1);
|
||||
|
||||
NitroxVector3 beforeChildLocalPosition = new NitroxVector3(75, -10, 13.333f);
|
||||
NitroxQuaternion beforeChildRotation = NitroxQuaternion.FromEuler(75, 11, 5);
|
||||
|
||||
NitroxVector3 setGlobalPosition = new NitroxVector3(34.62131f, 45.99337f, -10.77733f);
|
||||
NitroxQuaternion setGlobalRotation = new NitroxQuaternion(0.6743798f, 0.2302919f, 0.02638498f, 0.7010573f);
|
||||
|
||||
NitroxVector3 afterLocalPosition = new NitroxVector3(17, 14, -13);
|
||||
NitroxQuaternion afterLocalRotation = NitroxQuaternion.FromEuler(60, 25, 0);
|
||||
|
||||
NitroxVector3 afterChildGlobalPosition = new NitroxVector3(379.541f, 109.6675f, -167.4466f);
|
||||
NitroxQuaternion afterChildGlobalRotation = NitroxQuaternion.FromEuler(17.81689f, 187.7957f, 151.9425f);
|
||||
|
||||
NitroxTransform grandchild3 = new NitroxTransform(beforeLocalPosition, beforeRotation, new NitroxVector3(2.5f, 2.5f, 2.5f));
|
||||
NitroxTransform grandgrandchild1 = new NitroxTransform(beforeChildLocalPosition, beforeChildRotation, new NitroxVector3(1, 1, 1));
|
||||
|
||||
grandgrandchild1.SetParent(grandchild3, false);
|
||||
grandchild3.SetParent(child1, true);
|
||||
|
||||
grandchild3.Position.Equals(beforeLocalPosition, TOLERANCE).Should().BeTrue($"Expected: {beforeLocalPosition} Found: {grandchild3.Position}");
|
||||
grandchild3.Rotation.Equals(beforeRotation, TOLERANCE).Should().BeTrue($"Expected: {beforeRotation} Found: {grandchild3.Rotation}");
|
||||
|
||||
grandchild3.Position = setGlobalPosition;
|
||||
grandchild3.Rotation = setGlobalRotation;
|
||||
|
||||
grandchild3.LocalPosition.Equals(afterLocalPosition, TOLERANCE).Should().BeTrue($"Expected: {afterLocalPosition} Found: {grandchild3.LocalPosition}");
|
||||
grandchild3.LocalRotation.Equals(afterLocalRotation, TOLERANCE).Should().BeTrue($"Expected: {afterLocalRotation} Found: {grandchild3.LocalRotation}");
|
||||
|
||||
grandgrandchild1.Position.Equals(afterChildGlobalPosition, TOLERANCE).Should().BeTrue($"Expected: {afterChildGlobalPosition} Found: {grandgrandchild1.Position}");
|
||||
grandgrandchild1.Rotation.Equals(afterChildGlobalRotation, TOLERANCE).Should().BeTrue($"Expected: {afterChildGlobalRotation} Found: {grandgrandchild1.Rotation}");
|
||||
}
|
||||
}
|
||||
}
|
140
Nitrox.Test/Model/DataStructures/Util/OptionalTest.cs
Normal file
140
Nitrox.Test/Model/DataStructures/Util/OptionalTest.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
namespace NitroxModel.DataStructures.Util;
|
||||
|
||||
[TestClass]
|
||||
public class OptionalTest
|
||||
{
|
||||
/// <summary>
|
||||
/// These optional additions should be in <see cref="OptionalHasValueDynamicChecks"/> test method but MSTest
|
||||
/// reuses instances which causes <see cref="Optional{T}.HasValue">Optional{T}.HasValue</see> to be called before the new conditions are added.
|
||||
/// </summary>
|
||||
[ClassInitialize]
|
||||
public static void Init(TestContext _)
|
||||
{
|
||||
Optional.ApplyHasValueCondition<Base>(v => v.GetType() == typeof(A) || v.Threshold > 200); // Cheat: allow check if type A to do more complex tests on Optional<T>.HasValue
|
||||
Optional.ApplyHasValueCondition<A>(v => v.Threshold <= 200);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OptionalGet()
|
||||
{
|
||||
Optional<string> op = Optional.Of("test");
|
||||
op.Value.Should().Be("test");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OptionalIsPresent()
|
||||
{
|
||||
Optional<string> op = Optional.Of("test");
|
||||
op.HasValue.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OptionalIsNotPresent()
|
||||
{
|
||||
Optional<string> op = Optional.Empty;
|
||||
op.HasValue.Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OptionalOrElseValidValue()
|
||||
{
|
||||
Optional<string> op = Optional.Of("test");
|
||||
op.OrElse("test2").Should().Be("test");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OptionalOrElseNoValue()
|
||||
{
|
||||
Optional<string> op = Optional.Empty;
|
||||
op.OrElse("test").Should().Be("test");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OptionalEmpty()
|
||||
{
|
||||
Optional<string> op = Optional.Empty;
|
||||
op.HasValue.Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OptionalSetValueNull()
|
||||
{
|
||||
Optional<Random> op = Optional.Of(new Random());
|
||||
Assert.IsTrue(op.HasValue);
|
||||
Assert.ThrowsException<ArgumentNullException>(() => { op = null; }, "Setting optional to null should not be allowed.");
|
||||
op = Optional.Empty;
|
||||
Assert.IsFalse(op.HasValue);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OptionalHasValueDynamicChecks()
|
||||
{
|
||||
Optional<Base> opBase = Optional.Of(new Base());
|
||||
opBase.HasValue.Should().BeTrue();
|
||||
opBase.Value.Threshold.Should().Be(202);
|
||||
|
||||
Optional<A> a = Optional.Of(new A());
|
||||
a.HasValue.Should().BeTrue();
|
||||
|
||||
Optional<A> actuallyB = Optional.Of<A>(new B());
|
||||
actuallyB.HasValue.Should().BeFalse();
|
||||
|
||||
Optional<B> b = Optional.Of(new B());
|
||||
b.HasValue.Should().BeFalse();
|
||||
|
||||
// A check should still happen on Base because Optional<Base> includes more-specific-than-itself checks.
|
||||
Optional<Base> aAsBase = Optional<Base>.Of((Base)a);
|
||||
aAsBase.HasValue.Should().BeTrue();
|
||||
aAsBase.Value.Threshold.Should().Be(200);
|
||||
|
||||
// Optional<object> 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<object> bAsObj = Optional<object>.Of(new B());
|
||||
bAsObj.HasValue.Should().BeFalse();
|
||||
|
||||
// Type C inheritance doesn't allow for type A. But Optional<object> has the check on A. It should skip the A check on C because inheritance doesn't match up.
|
||||
Optional<object> cAsObj = Optional<object>.Of(new C());
|
||||
cAsObj.HasValue.Should().BeTrue();
|
||||
((C)cAsObj.Value).Threshold.Should().Be(203);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OptionalEqualsCheck()
|
||||
{
|
||||
Optional<string> op = Optional.OfNullable<string>(null);
|
||||
Optional<string> op1 = Optional.OfNullable<string>(null);
|
||||
Optional<string> op2 = Optional.Of("Test");
|
||||
Optional<string> op3 = Optional.Of("Test2");
|
||||
Optional<string> 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;
|
||||
}
|
||||
}
|
113
Nitrox.Test/Model/ExtensionsTests.cs
Normal file
113
Nitrox.Test/Model/ExtensionsTests.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
namespace NitroxModel;
|
||||
|
||||
[TestClass]
|
||||
public class ExtensionsTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void RemoveAllFast_ShouldDoNothingWhenEmptyList()
|
||||
{
|
||||
object[] list = [];
|
||||
list.Should().BeEmpty();
|
||||
list.RemoveAllFast((object)null, static (_, _) => true);
|
||||
list.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RemoveAllFast_ShouldDoNothingWhenAlwaysFalsePredicate()
|
||||
{
|
||||
List<string> list = ["one", "two", "three", "four"];
|
||||
list.RemoveAllFast((object)null, static (_, _) => false);
|
||||
list.Should().BeEquivalentTo("one", "two", "three", "four");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RemoveAllFast_ThrowsErrorIfFixedSizeList()
|
||||
{
|
||||
string[] list = ["one", "two", "three"];
|
||||
Assert.ThrowsException<NotSupportedException>(() => list.RemoveAllFast((object)null, static (item, _) => item == "one"));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RemoveAllFast_CanRemoveFirstItem()
|
||||
{
|
||||
List<string> list = ["one", "two", "three"];
|
||||
list.RemoveAllFast((object)null, static (item, _) => item == "one");
|
||||
list.Should().BeEquivalentTo("two", "three");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RemoveAllFast_CanRemoveMidItems()
|
||||
{
|
||||
List<string> list = ["one", "two", "three", "four"];
|
||||
list.RemoveAllFast((object)null, static (item, _) => item == "two" || item == "three");
|
||||
list.Should().BeEquivalentTo("one", "four");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RemoveAllFast_CanRemoveEndItem()
|
||||
{
|
||||
List<string> list = ["one", "two", "three", "four"];
|
||||
list.RemoveAllFast((object)null, static (item, _) => item == "four");
|
||||
list.Should().BeEquivalentTo("one", "two", "three");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RemoveAllFast_CanRemoveAllItems()
|
||||
{
|
||||
List<string> list = ["one", "two", "three", "four"];
|
||||
list.RemoveAllFast((object)null, static (_, _) => true);
|
||||
list.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RemoveAllFast_CanRemoveItemsWithExtraParameterInPredicate()
|
||||
{
|
||||
List<string> list = ["one", "two", "three", "four"];
|
||||
list.RemoveAllFast(3, static (item, length) => item.Length == length);
|
||||
list.Should().BeEquivalentTo("three", "four");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetUniqueNonCombinatoryFlags_ShouldReturnUniqueNonCombinatoryFlags()
|
||||
{
|
||||
TestEnumFlags.ALL.GetUniqueNonCombinatoryFlags().Should().BeEquivalentTo([TestEnumFlags.A, TestEnumFlags.B, TestEnumFlags.C, TestEnumFlags.D, TestEnumFlags.E, TestEnumFlags.F]);
|
||||
TestEnumFlags.CDEF.GetUniqueNonCombinatoryFlags().Should().BeEquivalentTo([TestEnumFlags.C, TestEnumFlags.D, TestEnumFlags.E, TestEnumFlags.F]);
|
||||
TestEnumFlags.E.GetUniqueNonCombinatoryFlags().Should().BeEquivalentTo([TestEnumFlags.E]);
|
||||
TestEnumFlags.NONE.GetUniqueNonCombinatoryFlags().Should().BeEmpty();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetUniqueNonCombinatorFlags_ShouldReturnAllUniquesWhenAllBitsSet()
|
||||
{
|
||||
((TestEnumFlags)int.MaxValue).GetUniqueNonCombinatoryFlags().Should().BeEquivalentTo([TestEnumFlags.A, TestEnumFlags.B, TestEnumFlags.C, TestEnumFlags.D, TestEnumFlags.E, TestEnumFlags.F]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetCommandArgs()
|
||||
{
|
||||
Array.Empty<string>().GetCommandArgs("").Should().BeEmpty();
|
||||
Array.Empty<string>().GetCommandArgs("--something").Should().BeEmpty();
|
||||
new[] { "/bin/nitrox.dll", "--save", "My World" }.GetCommandArgs("--save").Should().BeEquivalentTo("My World");
|
||||
new[] { "--nitrox", @"C:\a\path" }.GetCommandArgs("--nitrox").Should().BeEquivalentTo(@"C:\a\path");
|
||||
new[] { "blabla", "--other=test", "--nitrox", @"C:\a\path" }.GetCommandArgs("--nitrox").Should().BeEquivalentTo(@"C:\a\path");
|
||||
new[] { "blabla", "--other=test", "--nitrox", @"C:\a\path" }.GetCommandArgs("--other").Should().BeEquivalentTo("test");
|
||||
new[] { "blabla", "--other=test", "other2", "--nitrox", @"C:\a\path" }.GetCommandArgs("--other").Should().BeEquivalentTo("test");
|
||||
new[] { "blabla", "--other", "test", "other2", "--nitrox", @"C:\a\path" }.GetCommandArgs("--other").Should().BeEquivalentTo("test", "other2");
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum TestEnumFlags
|
||||
{
|
||||
NONE = 0,
|
||||
F = 1 << 5,
|
||||
A = 1 << 0,
|
||||
B = 1 << 1,
|
||||
C = 1 << 2,
|
||||
D = 1 << 3,
|
||||
E = 1 << 4,
|
||||
AB = A | B,
|
||||
CD = C | D,
|
||||
CDEF = CD | E | F,
|
||||
ALL = AB | CDEF
|
||||
}
|
||||
}
|
24
Nitrox.Test/Model/Helper/KeyValueStoreTest.cs
Normal file
24
Nitrox.Test/Model/Helper/KeyValueStoreTest.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace NitroxModel.Helper;
|
||||
|
||||
[TestClass]
|
||||
public class KeyValueStoreTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void SetAndReadValue()
|
||||
{
|
||||
const string TEST_KEY = "test";
|
||||
|
||||
KeyValueStore.Instance.SetValue(TEST_KEY, -50);
|
||||
Assert.AreEqual(-50, KeyValueStore.Instance.GetValue<int>(TEST_KEY));
|
||||
|
||||
KeyValueStore.Instance.SetValue(TEST_KEY, 1337);
|
||||
Assert.AreEqual(1337, KeyValueStore.Instance.GetValue<int>(TEST_KEY));
|
||||
|
||||
// Cleanup
|
||||
KeyValueStore.Instance.DeleteKey(TEST_KEY);
|
||||
Assert.IsNull(KeyValueStore.Instance.GetValue<int?>(TEST_KEY));
|
||||
Assert.IsFalse(KeyValueStore.Instance.KeyExists(TEST_KEY));
|
||||
}
|
||||
}
|
68
Nitrox.Test/Model/Helper/NetHelperTest.cs
Normal file
68
Nitrox.Test/Model/Helper/NetHelperTest.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace NitroxModel.Helper;
|
||||
|
||||
[TestClass]
|
||||
public class NetHelperTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void ShouldMatchPrivateIps()
|
||||
{
|
||||
// Tested subnet ranges that are reserved for private networks:
|
||||
// 10.0.0.0/8
|
||||
// 127.0.0.0/8
|
||||
// 172.16.0.0/12
|
||||
// 192.0.0.0/24
|
||||
// 192.168.0.0/16
|
||||
// 198.18.0.0/15
|
||||
|
||||
IPAddress.Parse("10.0.0.0").IsPrivate().Should().BeTrue();
|
||||
IPAddress.Parse("10.0.0.255").IsPrivate().Should().BeTrue();
|
||||
IPAddress.Parse("172.31.255.255").IsPrivate().Should().BeTrue();
|
||||
IPAddress.Parse("172.31.255.255").IsPrivate().Should().BeTrue();
|
||||
IPAddress.Parse("192.0.0.255").IsPrivate().Should().BeTrue();
|
||||
IPAddress.Parse("192.168.2.1").IsPrivate().Should().BeTrue();
|
||||
IPAddress.Parse("192.168.2.254").IsPrivate().Should().BeTrue();
|
||||
IPAddress.Parse("192.168.2.255").IsPrivate().Should().BeTrue();
|
||||
IPAddress.Parse("198.18.0.1").IsPrivate().Should().BeTrue();
|
||||
IPAddress.Parse("198.19.255.255").IsPrivate().Should().BeTrue();
|
||||
|
||||
IPAddress.Parse("9.255.255.255").IsPrivate().Should().BeFalse();
|
||||
IPAddress.Parse("91.63.176.12").IsPrivate().Should().BeFalse();
|
||||
IPAddress.Parse("172.32.0.1").IsPrivate().Should().BeFalse();
|
||||
IPAddress.Parse("192.0.1.0").IsPrivate().Should().BeFalse();
|
||||
IPAddress.Parse("198.17.255.255").IsPrivate().Should().BeFalse();
|
||||
IPAddress.Parse("198.20.0.0").IsPrivate().Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldMatchLocalhostIps()
|
||||
{
|
||||
IPAddress GetSlightlyDifferentIp(IPAddress address)
|
||||
{
|
||||
if (address.AddressFamily != AddressFamily.InterNetwork)
|
||||
{
|
||||
throw new Exception("Only supports IPv4");
|
||||
}
|
||||
byte[] bytes = address.GetAddressBytes();
|
||||
unchecked
|
||||
{
|
||||
while (bytes[3] is < 1 or > 253)
|
||||
{
|
||||
bytes[3]++;
|
||||
}
|
||||
bytes[3]++;
|
||||
}
|
||||
return new IPAddress(bytes);
|
||||
}
|
||||
|
||||
IPAddress.Parse("127.0.0.1").IsLocalhost().Should().BeTrue();
|
||||
IPAddress.Parse("127.0.0.2").IsLocalhost().Should().BeTrue();
|
||||
IPAddress.Parse("192.168.0.255").IsLocalhost().Should().BeFalse();
|
||||
NetHelper.GetLanIp().IsLocalhost().Should().BeTrue();
|
||||
IPAddress differentIp = GetSlightlyDifferentIp(NetHelper.GetLanIp());
|
||||
differentIp.Should().NotBeEquivalentTo(NetHelper.GetLanIp());
|
||||
differentIp.IsLocalhost().Should().BeFalse();
|
||||
}
|
||||
}
|
97
Nitrox.Test/Model/Helper/ReflectTest.cs
Normal file
97
Nitrox.Test/Model/Helper/ReflectTest.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using NitroxClient.MonoBehaviours.Gui.Input;
|
||||
|
||||
namespace NitroxModel.Helper
|
||||
{
|
||||
[TestClass]
|
||||
public class ReflectTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void Method()
|
||||
{
|
||||
// Get static method.
|
||||
MethodInfo staticMethod = Reflect.Method(() => AbusedClass.StaticMethodReturnsInt());
|
||||
staticMethod.Should().NotBeNull();
|
||||
staticMethod.ReturnType.Should().Be<int>();
|
||||
staticMethod.Name.Should().BeEquivalentTo(nameof(AbusedClass.StaticMethodReturnsInt));
|
||||
staticMethod.Invoke(null, Array.Empty<object>());
|
||||
// Extra check for method with parameters, just to be safe.
|
||||
staticMethod = Reflect.Method(() => AbusedClass.StaticMethodHasParams("", null));
|
||||
staticMethod.Should().NotBeNull();
|
||||
staticMethod.ReturnType.Should().Be<string>();
|
||||
staticMethod.Name.Should().BeEquivalentTo(nameof(AbusedClass.StaticMethodHasParams));
|
||||
staticMethod.GetParameters().Should().OnlyHaveUniqueItems();
|
||||
staticMethod.GetParameters()[0].Name.Should().BeEquivalentTo("myValue");
|
||||
staticMethod.GetParameters()[0].ParameterType.Should().Be<string>();
|
||||
staticMethod.GetParameters()[1].ParameterType.Should().Be<Process>();
|
||||
staticMethod.Invoke(null, new[] { "hello, reflection", (object)null }).Should().BeEquivalentTo("hello, reflection");
|
||||
|
||||
// Get instance method.
|
||||
MethodInfo instanceMethod = Reflect.Method((AbusedClass t) => t.Method());
|
||||
instanceMethod.Should().NotBeNull();
|
||||
instanceMethod.ReturnType.Should().Be<int>();
|
||||
instanceMethod.Name.Should().BeEquivalentTo(nameof(AbusedClass.Method));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Field()
|
||||
{
|
||||
// Get static field.
|
||||
FieldInfo staticField = Reflect.Field(() => AbusedClass.StaticField);
|
||||
staticField.Name.Should().BeEquivalentTo(nameof(AbusedClass.StaticField));
|
||||
staticField.FieldType.Should().Be<int>();
|
||||
// Get instance field.
|
||||
FieldInfo instanceField = Reflect.Field((AbusedClass t) => t.InstanceField);
|
||||
instanceField.Name.Should().BeEquivalentTo(nameof(AbusedClass.InstanceField));
|
||||
instanceField.FieldType.Should().Be<int>();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Property()
|
||||
{
|
||||
// Get static property.
|
||||
PropertyInfo staticProperty = Reflect.Property(() => AbusedClass.StaticProperty);
|
||||
staticProperty.Name.Should().BeEquivalentTo(nameof(AbusedClass.StaticProperty));
|
||||
staticProperty.PropertyType.Should().Be<int>();
|
||||
// Get instance property.
|
||||
PropertyInfo instanceProperty = Reflect.Property((AbusedClass t) => t.InstanceProperty);
|
||||
instanceProperty.Name.Should().BeEquivalentTo(nameof(AbusedClass.InstanceProperty));
|
||||
instanceProperty.PropertyType.Should().Be<int>();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Constructor()
|
||||
{
|
||||
ConstructorInfo method = Reflect.Constructor(() => new KeyBindingManager());
|
||||
method.DeclaringType.Should().Be<KeyBindingManager>();
|
||||
}
|
||||
|
||||
private class AbusedClass
|
||||
{
|
||||
public static readonly int StaticReadOnlyField = 1;
|
||||
public static int StaticField = 2;
|
||||
public int InstanceField = 3;
|
||||
public static int StaticProperty { get; set; } = 4;
|
||||
public int InstanceProperty { get; set; } = 5;
|
||||
|
||||
public static int StaticMethodReturnsInt()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
public static string StaticMethodHasParams(string myValue, Process process)
|
||||
{
|
||||
return myValue;
|
||||
}
|
||||
|
||||
public int Method()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
79
Nitrox.Test/Model/Packets/PacketsSerializableTest.cs
Normal file
79
Nitrox.Test/Model/Packets/PacketsSerializableTest.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using BinaryPack.Attributes;
|
||||
using KellermanSoftware.CompareNetObjects;
|
||||
using KellermanSoftware.CompareNetObjects.TypeComparers;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Nitrox.Test.Helper.Faker;
|
||||
using NitroxModel_Subnautica.Logger;
|
||||
using NitroxModel.DataStructures;
|
||||
|
||||
namespace NitroxModel.Packets;
|
||||
|
||||
[TestClass]
|
||||
public class PacketsSerializableTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void InitSerializerTest()
|
||||
{
|
||||
Packet.InitSerializer();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PacketSerializationTest()
|
||||
{
|
||||
ComparisonConfig config = new();
|
||||
config.SkipInvalidIndexers = true;
|
||||
config.AttributesToIgnore.Add(typeof(IgnoredMemberAttribute));
|
||||
config.CustomComparers.Add(new CustomComparer<NitroxId, NitroxId>((id1, id2) => id1.Equals(id2)));
|
||||
CompareLogic comparer = new(config);
|
||||
|
||||
IEnumerable<Type> types = typeof(Packet).Assembly.GetTypes().Concat(typeof(SubnauticaInGameLogger).Assembly.GetTypes());
|
||||
Type[] packetTypes = types.Where(p => typeof(Packet).IsAssignableFrom(p) && p.IsClass && !p.IsAbstract).ToArray();
|
||||
|
||||
// We want to ignore packets with no members when using ShouldNotCompare
|
||||
Type[] emptyPackets = packetTypes.Where(t => !t.GetMembers(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Any(member => member.MemberType is MemberTypes.Field or MemberTypes.Property &&
|
||||
!member.GetCustomAttributes<IgnoredMemberAttribute>().Any()))
|
||||
.ToArray();
|
||||
|
||||
// We generate two different versions of each packet to verify comparison is actually working
|
||||
List<(Packet, Packet)> generatedPackets = new();
|
||||
|
||||
foreach (Type type in packetTypes)
|
||||
{
|
||||
dynamic faker = NitroxFaker.GetOrCreateFaker(type);
|
||||
|
||||
Packet packet = faker.Generate();
|
||||
Packet packet2 = null;
|
||||
|
||||
if (!emptyPackets.Contains(type))
|
||||
{
|
||||
ComparisonResult result;
|
||||
do
|
||||
{
|
||||
packet2 = faker.Generate();
|
||||
result = comparer.Compare(packet, packet2);
|
||||
} while (result == null || result.AreEqual);
|
||||
}
|
||||
|
||||
generatedPackets.Add(new ValueTuple<Packet, Packet>(packet, packet2));
|
||||
}
|
||||
|
||||
Packet.InitSerializer();
|
||||
|
||||
foreach (ValueTuple<Packet, Packet> packet in generatedPackets)
|
||||
{
|
||||
Packet deserialized = Packet.Deserialize(packet.Item1.Serialize());
|
||||
|
||||
packet.Item1.ShouldCompare(deserialized, $"with {packet.Item1.GetType()}", config);
|
||||
|
||||
if (!emptyPackets.Contains(packet.Item1.GetType()))
|
||||
{
|
||||
packet.Item2.ShouldNotCompare(deserialized, $"with {packet.Item1.GetType()}", config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
118
Nitrox.Test/Model/Packets/Processors/PacketProcessorTest.cs
Normal file
118
Nitrox.Test/Model/Packets/Processors/PacketProcessorTest.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Nitrox.Test;
|
||||
using NitroxClient;
|
||||
using NitroxClient.Communication.Packets.Processors.Abstract;
|
||||
using NitroxModel.Core;
|
||||
using NitroxModel.Packets.Processors.Abstract;
|
||||
using NitroxServer;
|
||||
using NitroxServer.Communication.Packets;
|
||||
using NitroxServer.Communication.Packets.Processors;
|
||||
using NitroxServer.Communication.Packets.Processors.Abstract;
|
||||
using NitroxServer_Subnautica;
|
||||
|
||||
namespace NitroxModel.Packets.Processors
|
||||
{
|
||||
[TestClass]
|
||||
public class PacketProcessorTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void ClientPacketProcessorSanity()
|
||||
{
|
||||
typeof(ClientPacketProcessor<>).Assembly.GetTypes()
|
||||
.Where(p => typeof(PacketProcessor).IsAssignableFrom(p) && p.IsClass && !p.IsAbstract)
|
||||
.ToList()
|
||||
.ForEach(processor =>
|
||||
{
|
||||
// Make sure that each packet-processor is derived from the ClientPacketProcessor class,
|
||||
// so that it's packet-type can be determined.
|
||||
Assert.IsNotNull(processor.BaseType, $"{processor} does not derive from any type!");
|
||||
Assert.IsTrue(processor.BaseType.IsGenericType, $"{processor} does not derive from a generic type!");
|
||||
Assert.IsTrue(processor.BaseType.IsAssignableToGenericType(typeof(ClientPacketProcessor<>)), $"{processor} does not derive from ClientPacketProcessor!");
|
||||
|
||||
// Check constructor availability:
|
||||
int numCtors = processor.GetConstructors().Length;
|
||||
Assert.IsTrue(numCtors == 1, $"{processor} should have exactly 1 constructor! (has {numCtors})");
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ServerPacketProcessorSanity()
|
||||
{
|
||||
typeof(PacketHandler).Assembly.GetTypes()
|
||||
.Where(p => typeof(PacketProcessor).IsAssignableFrom(p) && p.IsClass && !p.IsAbstract)
|
||||
.ToList()
|
||||
.ForEach(processor =>
|
||||
{
|
||||
// Make sure that each packet-processor is derived from the ClientPacketProcessor class,
|
||||
// so that it's packet-type can be determined.
|
||||
Assert.IsNotNull(processor.BaseType, $"{processor} does not derive from any type!");
|
||||
Assert.IsTrue(processor.BaseType.IsGenericType, $"{processor} does not derive from a generic type!");
|
||||
Assert.IsTrue(processor.BaseType.IsAssignableToGenericType(typeof(AuthenticatedPacketProcessor<>)) ||
|
||||
processor.BaseType.IsAssignableToGenericType(typeof(UnauthenticatedPacketProcessor<>)), $"{processor} does not derive from (Un)AuthenticatedPacketProcessor!");
|
||||
|
||||
// Check constructor availability:
|
||||
int numCtors = processor.GetConstructors().Length;
|
||||
Assert.IsTrue(numCtors == 1, $"{processor} should have exactly 1 constructor! (has {numCtors})");
|
||||
|
||||
// Unable to check parameters, these are defined in PacketHandler.ctor
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SameAmountOfServerPacketProcessors()
|
||||
{
|
||||
IEnumerable<Type> processors = typeof(PacketHandler).Assembly.GetTypes()
|
||||
.Where(p => typeof(PacketProcessor).IsAssignableFrom(p) && p.IsClass && !p.IsAbstract);
|
||||
ServerAutoFacRegistrar serverDependencyRegistrar = new ServerAutoFacRegistrar();
|
||||
NitroxServiceLocator.InitializeDependencyContainer(serverDependencyRegistrar);
|
||||
NitroxServiceLocator.BeginNewLifetimeScope();
|
||||
|
||||
List<Type> packetTypes = typeof(DefaultServerPacketProcessor).Assembly.GetTypes()
|
||||
.Where(p => typeof(PacketProcessor).IsAssignableFrom(p) && p.IsClass && !p.IsAbstract)
|
||||
.ToList();
|
||||
|
||||
int both = packetTypes.Count;
|
||||
Assert.AreEqual(processors.Count(), both,
|
||||
$"Not all(Un) AuthenticatedPacketProcessors have been discovered by the runtime code (auth + unauth: {both} out of {processors.Count()}). Perhaps the runtime matching code is too strict, or a processor does not derive from ClientPacketProcessor (and will hence not be detected).");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AllPacketsAreHandled()
|
||||
{
|
||||
List<Type> packetTypes = typeof(DefaultServerPacketProcessor).Assembly.GetTypes()
|
||||
.Where(p => typeof(PacketProcessor).IsAssignableFrom(p) && p.IsClass && !p.IsAbstract)
|
||||
.ToList();
|
||||
|
||||
List<Type> abstractProcessorTypes = new();
|
||||
|
||||
abstractProcessorTypes.AddRange(typeof(ClientPacketProcessor<>)
|
||||
.Assembly.GetTypes()
|
||||
.Where(p => p.IsClass && p.IsAbstract && p.IsAssignableToGenericType(typeof(ClientPacketProcessor<>))));
|
||||
|
||||
abstractProcessorTypes.AddRange(typeof(AuthenticatedPacketProcessor<>)
|
||||
.Assembly.GetTypes()
|
||||
.Where(p => p.IsClass && p.IsAbstract && (p.IsAssignableToGenericType(typeof(AuthenticatedPacketProcessor<>)) || p.IsAssignableToGenericType(typeof(UnauthenticatedPacketProcessor<>)))));
|
||||
|
||||
NitroxServiceLocator.InitializeDependencyContainer(new ClientAutoFacRegistrar(), new SubnauticaServerAutoFacRegistrar(), new TestAutoFacRegistrar());
|
||||
NitroxServiceLocator.BeginNewLifetimeScope();
|
||||
|
||||
foreach (Type packet in typeof(Packet).Assembly.GetTypes().Where(p => typeof(Packet).IsAssignableFrom(p) && p.IsClass && !p.IsAbstract).ToList())
|
||||
{
|
||||
Assert.IsTrue(packetTypes.Contains(packet) || abstractProcessorTypes.Any(genericProcessor =>
|
||||
{
|
||||
Type processorType = genericProcessor.MakeGenericType(packet);
|
||||
return NitroxServiceLocator.LocateOptionalService(processorType).HasValue;
|
||||
}), $"Packet of type '{packet}' should have at least one processor.");
|
||||
}
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
NitroxServiceLocator.EndCurrentLifetimeScope();
|
||||
}
|
||||
}
|
||||
}
|
39
Nitrox.Test/Model/Platforms/OS/Windows/RegistryTest.cs
Normal file
39
Nitrox.Test/Model/Platforms/OS/Windows/RegistryTest.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading.Tasks;
|
||||
using Nitrox.Test.Model.Platforms;
|
||||
|
||||
namespace NitroxModel.Platforms.OS.Windows;
|
||||
|
||||
[TestClass]
|
||||
[SupportedOSPlatform("windows")]
|
||||
public class RegistryTest
|
||||
{
|
||||
[OSTestMethod("windows")]
|
||||
public async Task WaitsForRegistryKeyToExist()
|
||||
{
|
||||
const string PATH_TO_KEY = @"SOFTWARE\Nitrox\test";
|
||||
|
||||
RegistryEx.Write(PATH_TO_KEY, 0);
|
||||
Task<bool> readTask = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await RegistryEx.CompareWaitAsync<int>(PATH_TO_KEY,
|
||||
v => v == 1337,
|
||||
TimeSpan.FromSeconds(5));
|
||||
return true;
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
RegistryEx.Write(PATH_TO_KEY, 1337);
|
||||
Assert.IsTrue(await readTask);
|
||||
|
||||
// Cleanup (we can keep "Nitrox" key intact).
|
||||
RegistryEx.Delete(PATH_TO_KEY);
|
||||
Assert.IsNull(RegistryEx.Read<string>(PATH_TO_KEY));
|
||||
}
|
||||
}
|
32
Nitrox.Test/Model/Platforms/OSTestMethodAttribute.cs
Normal file
32
Nitrox.Test/Model/Platforms/OSTestMethodAttribute.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace Nitrox.Test.Model.Platforms;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class OSTestMethodAttribute : TestMethodAttribute
|
||||
{
|
||||
public string Platform { get;}
|
||||
|
||||
/// <summary>
|
||||
/// Test method attribute, that will only run the test on the specified platform.
|
||||
/// </summary>
|
||||
/// <param name="platform">case insensitive platform, i.e: linux, windows, osx</param>
|
||||
public OSTestMethodAttribute(string platform)
|
||||
{
|
||||
Platform = platform;
|
||||
}
|
||||
|
||||
public override TestResult[] Execute(ITestMethod testMethod)
|
||||
{
|
||||
if (!OperatingSystem.IsOSPlatform(Platform))
|
||||
{
|
||||
return [
|
||||
new TestResult()
|
||||
{
|
||||
Outcome = UnitTestOutcome.Inconclusive,
|
||||
TestContextMessages = $"This test can only be run on {Platform}"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
return base.Execute(testMethod);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user