221 lines
12 KiB
C#
221 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
|
|
using DataLoader;
|
|
using Svelto.Tasks;
|
|
using Unity.Mathematics;
|
|
|
|
using TechbloxModdingAPI.App;
|
|
using TechbloxModdingAPI.Tests;
|
|
using TechbloxModdingAPI.Utility;
|
|
|
|
namespace TechbloxModdingAPI.Blocks
|
|
{
|
|
#if TEST
|
|
/// <summary>
|
|
/// Block test cases. Not accessible in release versions.
|
|
/// </summary>
|
|
[APITestClass]
|
|
public static class BlockTests
|
|
{
|
|
[APITestCase(TestType.Game)] //At least one block must be placed for simulation to work
|
|
public static void TestPlaceNew()
|
|
{
|
|
Block newBlock = Block.PlaceNew(BlockIDs.Cube, float3.zero);
|
|
Assert.NotNull(newBlock.Id, "Newly placed block is missing Id. This should be populated when the block is placed.", "Newly placed block Id is not null, block successfully placed.");
|
|
}
|
|
|
|
[APITestCase(TestType.EditMode)]
|
|
public static void TestInitProperty()
|
|
{
|
|
Block newBlock = Block.PlaceNew(BlockIDs.Cube, float3.zero + 2);
|
|
if (!Assert.CloseTo(newBlock.Position, (float3.zero + 2), $"Newly placed block at {newBlock.Position} is expected at {Unity.Mathematics.float3.zero + 2}.", "Newly placed block position matches.")) return;
|
|
//Assert.Equal(newBlock.Exists, true, "Newly placed block does not exist, possibly because Sync() skipped/missed/failed.", "Newly placed block exists, Sync() successful.");
|
|
}
|
|
|
|
[APITestCase(TestType.EditMode)]
|
|
public static void TestBlockIDCoverage()
|
|
{
|
|
Assert.Equal(
|
|
FullGameFields._dataDb.GetValues<CubeListData>().Keys.Select(ushort.Parse).OrderBy(id => id)
|
|
.SequenceEqual(Enum.GetValues(typeof(BlockIDs)).Cast<ushort>().OrderBy(id => id)
|
|
.Except(new[] {(ushort) BlockIDs.Invalid})), true,
|
|
"Block ID enum is different than the known block types, update needed.",
|
|
"Block ID enum matches the known block types.");
|
|
}
|
|
|
|
[APITestCase(TestType.EditMode)]
|
|
public static void TestBlockIDs()
|
|
{
|
|
float3 pos = new float3();
|
|
foreach (BlockIDs id in Enum.GetValues(typeof(BlockIDs)))
|
|
{
|
|
if (id == BlockIDs.Invalid) continue;
|
|
try
|
|
{
|
|
Block.PlaceNew(id, pos);
|
|
pos += 0.2f;
|
|
}
|
|
catch (Exception e)
|
|
{ //Only print failed case
|
|
Assert.Fail($"Failed to place block type {id}: {e}");
|
|
return;
|
|
}
|
|
}
|
|
|
|
Assert.Pass("Placing all possible block types succeeded.");
|
|
}
|
|
|
|
[APITestCase(TestType.EditMode)]
|
|
public static IEnumerator<TaskContract> TestBlockProperties()
|
|
{ //Uses the result of the previous test case
|
|
var blocks = Game.CurrentGame().GetBlocksInGame();
|
|
yield return Yield.It;
|
|
for (var index = 0; index < blocks.Length; index++)
|
|
{
|
|
if (index % 50 == 0) yield return Yield.It; //The material or flipped status can only be changed 130 times per submission
|
|
var block = blocks[index];
|
|
if (!block.Exists) continue;
|
|
foreach (var property in block.GetType().GetProperties())
|
|
{
|
|
//Includes specialised block properties
|
|
if (property.SetMethod == null) continue;
|
|
var testValues = new (Type, object, Predicate<object>)[]
|
|
{
|
|
//(type, default value, predicate or null for equality)
|
|
(typeof(long), 3, null),
|
|
(typeof(int), 4, null),
|
|
(typeof(double), 5.2f, obj => Math.Abs((double) obj - 5.2f) < float.Epsilon),
|
|
(typeof(float), 5.2f, obj => Math.Abs((float) obj - 5.2f) < float.Epsilon),
|
|
(typeof(bool), true, obj => (bool) obj),
|
|
(typeof(string), "Test", obj => (string) obj == "Test"), //String equality check
|
|
(typeof(float3), (float3) 2, obj => math.all((float3) obj - 2 < (float3) float.Epsilon)),
|
|
(typeof(BlockColor), new BlockColor(BlockColors.Aqua, 2), null),
|
|
(typeof(float4), (float4) 5, obj => math.all((float4) obj - 5 < (float4) float.Epsilon))
|
|
};
|
|
var propType = property.PropertyType;
|
|
if (!propType.IsValueType) continue;
|
|
(object valueToUse, Predicate<object> predicateToUse) = (null, null);
|
|
foreach (var (type, value, predicate) in testValues)
|
|
{
|
|
if (type.IsAssignableFrom(propType))
|
|
{
|
|
valueToUse = value;
|
|
predicateToUse = predicate ?? (obj => Equals(obj, value));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (propType.IsEnum)
|
|
{
|
|
var values = propType.GetEnumValues();
|
|
valueToUse = values.GetValue(values.Length / 2);
|
|
predicateToUse = val => Equals(val, valueToUse);
|
|
}
|
|
|
|
if (valueToUse == null)
|
|
{
|
|
Assert.Fail($"Property {block.GetType().Name}.{property.Name} has an unknown type {propType}, test needs fixing.");
|
|
yield break;
|
|
}
|
|
|
|
property.SetValue(block, valueToUse);
|
|
object got = property.GetValue(block);
|
|
var attr = property.GetCustomAttribute<TestValueAttribute>();
|
|
if (!predicateToUse(got) && (attr == null || !Equals(attr.PossibleValue, got)))
|
|
{
|
|
Assert.Fail($"Property {block.GetType().Name}.{property.Name} value {got} does not equal {valueToUse} for block {block}.");
|
|
yield break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Assert.Pass("Setting all possible properties of all registered API block types succeeded.");
|
|
}
|
|
|
|
[APITestCase(TestType.EditMode)]
|
|
public static IEnumerator<TaskContract> TestDefaultValue()
|
|
{
|
|
for (int i = 0; i < 2; i++)
|
|
{ //Tests shared defaults
|
|
var block = Block.PlaceNew(BlockIDs.Cube, 1);
|
|
while (!block.Exists)
|
|
yield return Yield.It;
|
|
block.Remove();
|
|
while (block.Exists)
|
|
yield return Yield.It;
|
|
if(!Assert.Equal(block.Position, default,
|
|
$"Block position default value {block.Position} is incorrect, should be 0.",
|
|
$"Block position default value {block.Position} matches default."))
|
|
yield break;
|
|
block.Position = 4;
|
|
}
|
|
}
|
|
|
|
[APITestCase(TestType.EditMode)]
|
|
public static void TestDampedSpring()
|
|
{
|
|
Block newBlock = Block.PlaceNew(BlockIDs.DampedSpring, Unity.Mathematics.float3.zero + 1);
|
|
DampedSpring b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
|
|
Assert.Errorless(() => { b = (DampedSpring) newBlock; }, "Casting block to DampedSpring raised an exception: ", "Casting block to DampedSpring completed without issue.");
|
|
if (!Assert.CloseTo(b.Stiffness, 1f, $"DampedSpring.Stiffness {b.Stiffness} does not equal default value, possibly because it failed silently.", "DampedSpring.Stiffness is close enough to default.")) return;
|
|
if (!Assert.CloseTo(b.Damping, 0.1f, $"DampedSpring.Damping {b.Damping} does not equal default value, possibly because it failed silently.", "DampedSpring.Damping is close enough to default.")) return;
|
|
if (!Assert.CloseTo(b.MaxExtension, 0.3f, $"DampedSpring.MaxExtension {b.MaxExtension} does not equal default value, possibly because it failed silently.", "DampedSpring.MaxExtension is close enough to default.")) return;
|
|
}
|
|
|
|
/*[APITestCase(TestType.Game)]
|
|
public static void TestMusicBlock1()
|
|
{
|
|
Block newBlock = Block.PlaceNew(BlockIDs.MusicBlock, Unity.Mathematics.float3.zero + 2);
|
|
MusicBlock b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
|
|
Assert.Errorless(() => { b = newBlock.Specialise<MusicBlock>(); }, "Block.Specialize<MusicBlock>() raised an exception: ", "Block.Specialize<MusicBlock>() completed without issue.");
|
|
if (!Assert.NotNull(b, "Block.Specialize<MusicBlock>() returned null, possibly because it failed silently.", "Specialized MusicBlock is not null.")) return;
|
|
if (!Assert.CloseTo(b.Volume, 100f, $"MusicBlock.Volume {b.Volume} does not equal default value, possibly because it failed silently.", "MusicBlock.Volume is close enough to default.")) return;
|
|
if (!Assert.Equal(b.TrackIndex, 0, $"MusicBlock.TrackIndex {b.TrackIndex} does not equal default value, possibly because it failed silently.", "MusicBlock.TrackIndex is equal to default.")) return;
|
|
_musicBlock = b;
|
|
}
|
|
|
|
private static MusicBlock _musicBlock;
|
|
|
|
[APITestCase(TestType.EditMode)]
|
|
public static void TestMusicBlock2()
|
|
{
|
|
//Block newBlock = Block.GetLastPlacedBlock();
|
|
var b = _musicBlock;
|
|
if (!Assert.NotNull(b, "Block.Specialize<MusicBlock>() returned null, possibly because it failed silently.", "Specialized MusicBlock is not null.")) return;
|
|
b.IsPlaying = true; // play sfx
|
|
if (!Assert.Equal(b.IsPlaying, true, $"MusicBlock.IsPlaying {b.IsPlaying} does not equal true, possibly because it failed silently.", "MusicBlock.IsPlaying is set properly.")) return;
|
|
if (!Assert.Equal(b.ChannelType, ChannelType.None, $"MusicBlock.ChannelType {b.ChannelType} does not equal default value, possibly because it failed silently.", "MusicBlock.ChannelType is equal to default.")) return;
|
|
//Assert.Log(b.Track.ToString());
|
|
if (!Assert.Equal(b.Track.ToString(), new Guid("3237ff8f-f5f2-4f84-8144-496ca280f8c0").ToString(), $"MusicBlock.Track {b.Track} does not equal default value, possibly because it failed silently.", "MusicBlock.Track is equal to default.")) return;
|
|
}
|
|
|
|
[APITestCase(TestType.EditMode)]
|
|
public static void TestLogicGate()
|
|
{
|
|
Block newBlock = Block.PlaceNew(BlockIDs.NOTLogicBlock, Unity.Mathematics.float3.zero + 1);
|
|
LogicGate b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
|
|
Assert.Errorless(() => { b = newBlock.Specialise<LogicGate>(); }, "Block.Specialize<LogicGate>() raised an exception: ", "Block.Specialize<LogicGate>() completed without issue.");
|
|
if (!Assert.NotNull(b, "Block.Specialize<LogicGate>() returned null, possibly because it failed silently.", "Specialized LogicGate is not null.")) return;
|
|
if (!Assert.Equal(b.InputCount, 1u, $"LogicGate.InputCount {b.InputCount} does not equal default value, possibly because it failed silently.", "LogicGate.InputCount is default.")) return;
|
|
if (!Assert.Equal(b.OutputCount, 1u, $"LogicGate.OutputCount {b.OutputCount} does not equal default value, possibly because it failed silently.", "LogicGate.OutputCount is default.")) return;
|
|
if (!Assert.NotNull(b, "Block.Specialize<LogicGate>() returned null, possibly because it failed silently.", "Specialized LogicGate is not null.")) return;
|
|
//if (!Assert.Equal(b.PortName(0, true), "Input", $"LogicGate.PortName(0, input:true) {b.PortName(0, true)} does not equal default value, possibly because it failed silently.", "LogicGate.PortName(0, input:true) is close enough to default.")) return;
|
|
LogicGate target = null;
|
|
if (!Assert.Errorless(() => { target = Block.PlaceNew<LogicGate>(BlockIDs.ANDLogicBlock, Unity.Mathematics.float3.zero + 2); })) return;
|
|
Wire newWire = null;
|
|
if (!Assert.Errorless(() => { newWire = b.Connect(0, target, 0);})) return;
|
|
if (!Assert.NotNull(newWire, "SignalingBlock.Connect(...) returned null, possible because it failed silently.", "SignalingBlock.Connect(...) returned a non-null value.")) return;
|
|
}*/
|
|
|
|
/*[APITestCase(TestType.EditMode)]
|
|
public static void TestSpecialiseError()
|
|
{
|
|
Block newBlock = Block.PlaceNew(BlockIDs.Bench, new float3(1, 1, 1));
|
|
if (Assert.Errorful<BlockTypeException>(() => newBlock.Specialise<MusicBlock>(), "Block.Specialise<MusicBlock>() was expected to error on a bench block.", "Block.Specialise<MusicBlock>() errored as expected for a bench block.")) return;
|
|
}*/
|
|
}
|
|
#endif
|
|
}
|