First pass increasing test coverage (#209)

* point tests

* Mesh Tests and removal of unused functions

* Vector tests

* more tests
This commit is contained in:
Jedd Morgan
2025-01-21 14:19:10 +00:00
committed by GitHub
parent bafd130ece
commit 3a71a10cf1
9 changed files with 443 additions and 132 deletions
+1 -2
View File
@@ -254,8 +254,7 @@ dotnet_diagnostic.ca1506.severity = warning # Avoid excessive class coupling
dotnet_diagnostic.ca1507.severity = warning # Use nameof in place of string
dotnet_diagnostic.ca1508.severity = warning # Avoid dead conditional code
dotnet_diagnostic.ca1509.severity = warning # Invalid entry in code metrics configuration file
dotnet_diagnostic.ca1861.severity = none # Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861)
dotnet_diagnostic.ca1861.severity = suggestion # Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861)
# Performance rules
+2 -1
View File
@@ -1,2 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=QL/@EntryIndexedValue">QL</s:String></wpf:ResourceDictionary>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=QL/@EntryIndexedValue">QL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XYZ/@EntryIndexedValue">XYZ</s:String></wpf:ResourceDictionary>
+28 -72
View File
@@ -1,24 +1,46 @@
using System.Diagnostics.Contracts;
using Speckle.Newtonsoft.Json;
using Speckle.Objects.Other;
using Speckle.Objects.Utils;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Models;
namespace Speckle.Objects.Geometry;
/// <remarks><a href="https://speckle.notion.site/Objects-Geometry-Mesh-9b0bf5ab92bf42f58bf2fe3922d2efca">More docs on notion</a></remarks>
[SpeckleType("Objects.Geometry.Mesh")]
public class Mesh : Base, IHasBoundingBox, IHasVolume, IHasArea, ITransformable<Mesh>
{
/// <summary>
/// Flat list of vertex data (flat <c>x,y,z,x,y,z...</c> list)
/// </summary>
[DetachProperty, Chunkable(31250)]
public required List<double> vertices { get; set; }
/// <summary>
/// Flat list of face data<br/>
/// Each face starts with the length of the face (e.g. 3 in the case of triangles), followed by that many indices
/// </summary>
/// <remarks>
/// N-gons are supported, but large values of n (> ~50) tend to cause significant performance problems for consumers (e.g. HostApps and <see cref="MeshTriangulationHelper"/>.
/// </remarks>
/// <example>
/// <code>[
/// 3, 0, 1, 2, //first face, a triangle (3-gon)
/// 4, 1, 2, 3, 4, //second face, a quad (4-gon)
/// 6, 4, 5, 6, 7, 8, 9, //third face, an n-gon (6-gon)
/// ];</code></example>
[DetachProperty, Chunkable(62500)]
public required List<int> faces { get; set; }
/// <summary> Vertex colors as ARGB <see cref="int"/>s</summary>
/// <summary>Vertex colors as ARGB <see cref="int"/>s</summary>
/// <remarks>Expected that there are either 1 color per vertex, or an empty <see cref="List{T}"/></remarks>
[DetachProperty, Chunkable(62500)]
public List<int> colors { get; set; } = new();
/// <summary>Flat list of texture coordinates (flat <c>u,v,u,v,u,v...</c> list)</summary>
/// <remarks>Expected that there are either 1 texture coordinate per vertex, or an empty <see cref="List{T}"/></remarks>
[DetachProperty, Chunkable(31250)]
public List<double> textureCoordinates { get; set; } = new();
@@ -85,8 +107,6 @@ public class Mesh : Base, IHasBoundingBox, IHasVolume, IHasArea, ITransformable<
return res;
}
#region Convenience Methods
[JsonIgnore]
public int VerticesCount => vertices.Count / 3;
@@ -98,6 +118,8 @@ public class Mesh : Base, IHasBoundingBox, IHasVolume, IHasArea, ITransformable<
/// </summary>
/// <param name="index">The index of the vertex</param>
/// <returns>Vertex as a <see cref="Point"/></returns>
/// <remarks>It is usually recommended to instead consume the <see cref="vertices"/> list manually for better performance</remarks>
[Pure]
public Point GetPoint(int index)
{
index *= 3;
@@ -106,6 +128,8 @@ public class Mesh : Base, IHasBoundingBox, IHasVolume, IHasArea, ITransformable<
/// <returns><see cref="vertices"/> as list of <see cref="Point"/>s</returns>
/// <exception cref="SpeckleException">when list is malformed</exception>
/// <remarks>It is usually recommended to instead consume the <see cref="vertices"/> list manually for better performance</remarks>
[Pure]
public List<Point> GetPoints()
{
if (vertices.Count % 3 != 0)
@@ -129,78 +153,10 @@ public class Mesh : Base, IHasBoundingBox, IHasVolume, IHasArea, ITransformable<
/// </summary>
/// <param name="index">The index of the texture coordinate</param>
/// <returns>Texture coordinate as a <see cref="ValueTuple{T1, T2}"/></returns>
[Pure]
public (double, double) GetTextureCoordinate(int index)
{
index *= 2;
return (textureCoordinates[index], textureCoordinates[index + 1]);
}
/// <summary>
/// If not already so, this method will align <see cref="Mesh.vertices"/>
/// such that a vertex and its corresponding texture coordinates have the same index.
/// This alignment is what is expected by most applications.<br/>
/// </summary>
/// <remarks>
/// If the calling application expects
/// <code>vertices.count == textureCoordinates.count</code>
/// Then this method should be called by the <c>MeshToNative</c> method before parsing <see cref="Mesh.vertices"/> and <see cref="Mesh.faces"/>
/// to ensure compatibility with geometry originating from applications that map <see cref="Mesh.vertices"/> to <see cref="Mesh.textureCoordinates"/> using vertex instance index (rather than vertex index)
/// <br/>
/// <see cref="Mesh.vertices"/>, <see cref="Mesh.colors"/>, and <see cref="faces"/> lists will be modified to contain no shared vertices (vertices shared between polygons)
/// </remarks>
public void AlignVerticesWithTexCoordsByIndex()
{
if (textureCoordinates.Count == 0)
{
return;
}
if (TextureCoordinatesCount == VerticesCount)
{
return; //Tex-coords already aligned as expected
}
var facesUnique = new List<int>(faces.Count);
var verticesUnique = new List<double>(TextureCoordinatesCount * 3);
bool hasColors = colors.Count > 0;
var colorsUnique = hasColors ? new List<int>(TextureCoordinatesCount) : null;
int nIndex = 0;
while (nIndex < faces.Count)
{
int n = faces[nIndex];
if (n < 3)
{
n += 3; // 0 -> 3, 1 -> 4
}
if (nIndex + n >= faces.Count)
{
break; //Malformed face list
}
facesUnique.Add(n);
for (int i = 1; i <= n; i++)
{
int vertIndex = faces[nIndex + i];
int newVertIndex = verticesUnique.Count / 3;
var (x, y, z) = GetPoint(vertIndex);
verticesUnique.Add(x);
verticesUnique.Add(y);
verticesUnique.Add(z);
colorsUnique?.Add(colors[vertIndex]);
facesUnique.Add(newVertIndex);
}
nIndex += n + 1;
}
vertices = verticesUnique;
colors = colorsUnique ?? colors;
faces = facesUnique;
}
#endregion
}
+2 -30
View File
@@ -86,15 +86,9 @@ public class Vector : Base, IHasBoundingBox, ITransformable<Vector>
/// Returns the coordinates of this <see cref="Vector"/> as a list of numbers
/// </summary>
/// <returns>A list of coordinates {x, y, z} </returns>
public List<double> ToList()
{
return new List<double> { x, y, z };
}
public List<double> ToList() => [x, y, z];
public Point ToPoint()
{
return new Point(x, y, z, units, applicationId);
}
public Point ToPoint() => new(x, y, z, units, applicationId);
/// <summary>
/// Creates a new vector based on a list of coordinates and the unit they're drawn in.
@@ -176,11 +170,6 @@ public class Vector : Base, IHasBoundingBox, ITransformable<Vector>
return new Vector(x, y, z, units: u.units);
}
public static double Angle(Vector u, Vector v)
{
return Math.Acos(DotProduct(u, v) / (u.Length * v.Length));
}
/// <summary>
/// Compute and return a unit vector from this vector
/// </summary>
@@ -205,23 +194,6 @@ public class Vector : Base, IHasBoundingBox, ITransformable<Vector>
return this;
}
/// <summary>
/// Returns a normalized copy of this vector.
/// </summary>
/// <returns>A copy of this vector unitized.</returns>
public Vector Unit()
{
return this / Length;
}
/// <summary>
/// Constructs a new <see cref="Vector"/> from a <see cref="Point"/>
/// </summary>
/// <param name="point">The point whose coordinates will be used</param>
/// <param name="applicationId">The unique application ID of the object.</param>
[Obsolete($"Use {nameof(Point.ToVector)}", true)]
public Vector(Point point, string? applicationId = null) { }
/// <summary>
/// Gets or sets the coordinates of the vector
/// </summary>
+3 -2
View File
@@ -1,4 +1,5 @@
using System.Diagnostics.Contracts;
using Speckle.Sdk.Dependencies;
namespace Speckle.Sdk.Common;
@@ -19,7 +20,7 @@ public static class Units
/// <summary>US Survey foot, now not supported by Speckle, kept privately for backwards compatibility</summary>
private const string USFeet = "us_ft";
internal static readonly List<string> SupportedUnits = new()
internal static readonly IReadOnlyCollection<string> SupportedUnits = new[]
{
Millimeters,
Centimeters,
@@ -30,7 +31,7 @@ public static class Units
Yards,
Miles,
None,
};
}.Freeze();
/// <param name="unit"></param>
/// <returns><see langword="true"/> if <paramref name="unit"/> is a recognised/supported unit string, otherwise <see langword="false"/></returns>
@@ -6,22 +6,7 @@ namespace Speckle.Objects.Tests.Unit.Geometry;
public class MeshTests
{
private static readonly Mesh[] TestCaseSource = { CreateBlenderStylePolygon(), CreateRhinoStylePolygon() };
[Theory]
[MemberData(nameof(GetTestCaseSource))]
public void CanAlignVertices(Mesh inPolygon)
{
inPolygon.AlignVerticesWithTexCoordsByIndex();
inPolygon.VerticesCount.Should().Be(inPolygon.TextureCoordinatesCount);
var expectedPolygon = CreateRhinoStylePolygon();
inPolygon.vertices.Should().BeEquivalentTo(expectedPolygon.vertices);
inPolygon.faces.Should().BeEquivalentTo(expectedPolygon.faces);
inPolygon.textureCoordinates.Should().BeEquivalentTo(expectedPolygon.textureCoordinates);
}
private static readonly Mesh[] TestCaseSource = { CreateRhinoStylePolygon(), CreateEmpty() };
public static IEnumerable<object[]> GetTestCaseSource() => TestCaseSource.Select(mesh => new object[] { mesh });
@@ -36,14 +21,51 @@ public class MeshTests
};
}
private static Mesh CreateBlenderStylePolygon()
private static Mesh CreateEmpty()
{
return new Mesh
{
vertices = new List<double> { 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0 },
faces = new List<int> { 3, 0, 1, 2, 3, 0, 2, 3 },
textureCoordinates = new List<double> { 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0 },
vertices = [],
faces = [],
textureCoordinates = [],
units = Units.Meters,
};
}
[Theory]
[MemberData(nameof(GetTestCaseSource))]
public void GetTextureCoordinate_ReturnsCorrectUVValue(Mesh testCase)
{
for (int i = 0, j = 0; i < testCase.textureCoordinates.Count; i += 2, j++)
{
var (u, v) = testCase.GetTextureCoordinate(j);
u.Should().Be(testCase.textureCoordinates[i]);
v.Should().Be(testCase.textureCoordinates[i + 1]);
}
Assert.Throws<ArgumentOutOfRangeException>(() => testCase.GetTextureCoordinate(testCase.textureCoordinates.Count));
}
[Theory]
[MemberData(nameof(GetTestCaseSource))]
public void GetPoints_ReturnsVerticesAsPoints(Mesh testCase)
{
testCase.VerticesCount.Should().Be(testCase.vertices.Count / 3);
var getPoints = testCase.GetPoints();
var getPoint = Enumerable.Range(0, testCase.VerticesCount).Select(i => testCase.GetPoint(i));
//Test each point has correct units
getPoints.Select(x => x.units).Should().AllBe(testCase.units).And.HaveCount(testCase.VerticesCount);
getPoints.Select(x => x.units).Should().AllBe(testCase.units).And.HaveCount(testCase.VerticesCount);
//Convert back to flat list
var expected = testCase.vertices;
var getPointsList = getPoints.SelectMany(x => x.ToList());
var getPointList = getPoint.SelectMany(x => x.ToList());
getPointsList.Should().BeEquivalentTo(expected);
getPointList.Should().BeEquivalentTo(expected);
}
}
@@ -1,6 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using FluentAssertions;
using Speckle.DoubleNumerics;
using Speckle.Objects.Geometry;
using Speckle.Objects.Other;
using Speckle.Sdk.Common;
namespace Speckle.Objects.Tests.Unit.Geometry;
@@ -64,4 +66,131 @@ public class PointTests
(p1 == p2).Should().Be(expectedResult);
}
[Fact]
public void TestDistanceTo()
{
//Arrange
var p1 = new Point(1, 0, 0, units: Units.Meters);
var p2 = new Point(0, 0, 0, units: Units.Meters);
//Act
var result = p1.DistanceTo(p2);
//Assert
result.Should().Be(1);
}
private static IReadOnlyList<Matrix4x4> MatrixTestData =>
[
Matrix4x4.Identity,
Matrix4x4.CreateScale(1, 2, 3),
Matrix4x4.CreateTranslation(100, 10, 0),
Matrix4x4.CreateRotationZ(1),
];
private static IReadOnlyList<Point> PointTestData =>
[
new(1, 2, 3, Units.Meters),
new(0.5, 100.5, 123.123, Units.Meters),
new(1, 2, 3, Units.Meters, applicationId: "Test me!"),
new(0, 0, 0, Units.Feet),
];
public static TheoryData<Point> PointTestCases() => new(PointTestData);
public static TheoryData<Matrix4x4, Point> TransformTestCases()
{
TheoryData<Matrix4x4, Point> testCases = new();
for (int i = 0; i < PointTestData.Count; i++)
{
testCases.Add(MatrixTestData[i], PointTestData[i]);
}
return testCases;
}
[Theory]
[MemberData(nameof(TransformTestCases))]
public void TransformPoint_SameUnits(Matrix4x4 matrix, Point point)
{
//Arrange
Transform t = new() { matrix = Matrix4x4.Transpose(matrix), units = point.units };
Vector3 expectedVector = Vector3.Transform(new(point.x, point.y, point.z), matrix);
var expectedResult = (expectedVector.X, expectedVector.Y, expectedVector.Z);
//Act
point.TransformTo(t, out Point transformedPoint);
var actualResult = (transformedPoint.x, transformedPoint.y, transformedPoint.z);
//Assert
actualResult.Should().Be(expectedResult);
transformedPoint.applicationId.Should().Be(point.applicationId);
transformedPoint.applicationId.Should().Be(point.applicationId);
transformedPoint.id.Should().Be(point.id);
transformedPoint.units.Should().Be(point.units);
}
[Fact(Skip = "Something clearly wrong with units!!!")]
public void TransformingPoint_ChangeOfUnits()
{
//Arrange
Point point = new(0, 0, 10, Units.Meters);
Transform t = new()
{
matrix = Matrix4x4.Transpose(Matrix4x4.CreateTranslation(1000, 0, 0)),
units = Units.Millimeters,
};
Vector3 expected = new(1, 0, 10);
//Act
point.TransformTo(t, out Point transformedPoint);
//Assert
(double x, double y, double z) = transformedPoint;
transformedPoint.units.Should().Be(point.units);
transformedPoint.applicationId.Should().Be(point.applicationId);
new Vector3(x, y, z).Should().Be(expected);
}
[Theory]
[MemberData(nameof(PointTestCases))]
public void ToVector(Point testCase)
{
var expectedXYZ = (testCase.x, testCase.y, testCase.z);
var expectedUnits = testCase.units;
var expectedApplicationId = testCase.applicationId;
var asVector = testCase.ToVector();
var resultXYZ = (asVector.x, asVector.y, asVector.z);
resultXYZ.Should().Be(expectedXYZ);
asVector.units.Should().Be(expectedUnits);
asVector.applicationId.Should().Be(expectedApplicationId);
}
[Theory]
[MemberData(nameof(PointTestCases))]
public void Deconstruct_Double_Double_Double_String(Point testCase)
{
(double x, double y, double z, string? units) = testCase;
x.Should().Be(testCase.x);
y.Should().Be(testCase.y);
z.Should().Be(testCase.z);
units.Should().Be(testCase.units);
}
[Theory]
[MemberData(nameof(PointTestCases))]
public void Deconstruct_Double_Double_Double(Point testCase)
{
(double x, double y, double z) = testCase;
x.Should().Be(testCase.x);
y.Should().Be(testCase.y);
z.Should().Be(testCase.z);
}
}
@@ -0,0 +1,233 @@
using FluentAssertions;
using Speckle.DoubleNumerics;
using Speckle.Objects.Geometry;
namespace Speckle.Objects.Tests.Unit.Geometry;
public class VectorTests
{
private const float FLOAT_TOLERANCE = 1e-6f;
public static TheoryData<double, double, double, string> TestCases() =>
new()
{
{ 0d, 0d, 0d, "m" },
{ 1d, 2d, 3d, "ft" },
{ 0d, 0d, -1d, "km" },
{ 100d, 0d, -200d, "in" },
{ 123.123d, 456.456d, 5789.789d, "cm" },
{ -123.123d, -456.456d, -5789.789d, "mm" },
};
[Theory]
[MemberData(nameof(TestCases))]
public void Constructors_AreTheSame(double x, double y, double z, string units)
{
const string appId = "asdfasdfasdf";
var pctor = new Vector(x, y, z, units, applicationId: appId);
pctor.x.Should().Be(x);
pctor.y.Should().Be(y);
pctor.z.Should().Be(z);
pctor.units.Should().Be(units);
pctor.applicationId.Should().Be(appId);
var init = new Vector
{
x = x,
y = y,
z = z,
units = units,
applicationId = appId,
};
Assert.Equal(pctor.x, init.x);
Assert.Equal(pctor.y, init.y);
Assert.Equal(pctor.z, init.z);
Assert.Equal(pctor.units, init.units);
Assert.Equal(pctor.applicationId, init.applicationId);
}
[Theory]
[InlineData(1d, 0d, 0d, 1d)]
[InlineData(0d, 2d, 0d, 2d)]
[InlineData(0d, 0d, -3d, 3d)]
[InlineData(1d, 1d, 0d, 1.4142135623730951d)]
public void LengthCalculated(double x, double y, double z, double expected)
{
var testCase = new Vector(x, y, z, "");
var actual = testCase.Length;
actual.Should().Be(expected);
}
[Theory]
[MemberData(nameof(TestCases))]
public void EncodingToAndFromList(double x, double y, double z, string units)
{
var testCase = new Vector(x, y, z, units);
var encoded = testCase.ToList();
encoded.Should().BeEquivalentTo([x, y, z]);
const string NEW_UNIT = "something different...";
var decoded = Vector.FromList(encoded, NEW_UNIT);
decoded.x.Should().Be(x);
decoded.y.Should().Be(y);
decoded.z.Should().Be(z);
decoded.units.Should().Be(NEW_UNIT);
}
[Theory]
[MemberData(nameof(TestCases))]
public void ToPoint(double x, double y, double z, string units)
{
var testCase = new Vector(x, y, z, units, "asdfasdf");
var asPoint = testCase.ToPoint();
asPoint.x.Should().Be(x);
asPoint.y.Should().Be(y);
asPoint.z.Should().Be(z);
asPoint.units.Should().Be(units);
asPoint.applicationId.Should().Be("asdfasdf");
}
[Theory]
[MemberData(nameof(TestCases))]
public void Normalize(double x, double y, double z, string units)
{
var sut = new Vector(x, y, z, units);
var originalLength = sut.Length;
sut.Normalize();
if (!(originalLength > 0))
{
sut.Length.Should().Be(double.NaN);
return;
}
sut.Length.Should().BeApproximately(1, FLOAT_TOLERANCE);
var rescaled = sut * originalLength;
rescaled.x.Should().BeApproximately(x, FLOAT_TOLERANCE);
rescaled.y.Should().BeApproximately(y, FLOAT_TOLERANCE);
rescaled.z.Should().BeApproximately(z, FLOAT_TOLERANCE);
rescaled.units.Should().Be(units);
}
[Theory]
[MemberData(nameof(TestCases))]
public void Negate(double x, double y, double z, string units)
{
var sut = new Vector(x, y, z, units);
var originalLength = sut.Length;
sut.Negate();
sut.Length.Should().Be(originalLength);
var rescaled = sut.Negate();
rescaled.x.Should().BeApproximately(x, FLOAT_TOLERANCE);
rescaled.y.Should().BeApproximately(y, FLOAT_TOLERANCE);
rescaled.z.Should().BeApproximately(z, FLOAT_TOLERANCE);
rescaled.units.Should().Be(units);
}
[Theory]
[MemberData(nameof(TestCases))]
public void TestAddition(double x, double y, double z, string units)
{
var operand1 = new Vector(x, y, z, units);
var operand2 = new Vector(x, y, z, units);
var result = operand1 + operand2;
double[] expected = [x + x, y + y, z + z];
result.ToList().Should().BeEquivalentTo(expected);
result.units.Should().BeEquivalentTo(units);
}
[Theory]
[MemberData(nameof(TestCases))]
public void TestSubtraction(double x, double y, double z, string units)
{
var operand1 = new Vector(x, y, z, units);
var operand2 = new Vector(x, y, z, units);
var result = operand1 - operand2;
double[] expected = [x - x, y - y, z - z];
result.ToList().Should().BeEquivalentTo(expected);
result.units.Should().BeEquivalentTo(units);
}
[Theory]
[MemberData(nameof(TestCases))]
public void TestDivision(double x, double y, double z, string units)
{
var operand1 = new Vector(x, y, z, units);
const int OPERAND2 = 2;
var result = operand1 / OPERAND2;
double[] expected = [x / OPERAND2, y / OPERAND2, z / OPERAND2];
result.ToList().Should().BeEquivalentTo(expected);
result.units.Should().BeEquivalentTo(units);
}
[Theory]
[MemberData(nameof(TestCases))]
public void TestMultiplication(double x, double y, double z, string units)
{
var operand1 = new Vector(x, y, z, units);
const int OPERAND2 = 2;
var result = operand1 * OPERAND2;
double[] expected = [x * OPERAND2, y * OPERAND2, z * OPERAND2];
result.ToList().Should().BeEquivalentTo(expected);
result.units.Should().BeEquivalentTo(units);
}
[Theory]
[MemberData(nameof(TestCases))]
public void TestDotProduct(double x, double y, double z, string units)
{
var operand1 = new Vector(x, y, z, units);
var operand2 = new Vector(x, y, z, units);
var result = Vector.DotProduct(operand1, operand2);
double expected = Vector3.Dot(new Vector3(x, y, z), new Vector3(x, y, z));
result.Should().BeApproximately(expected, FLOAT_TOLERANCE);
}
[Theory]
[MemberData(nameof(TestCases))]
public void TestCrossProduct(double x, double y, double z, string units)
{
var operand1 = new Vector(x, y, z, units);
var operand2 = new Vector(x, y, z, units);
var result = Vector.CrossProduct(operand1, operand2);
var expected = Vector3.Cross(new Vector3(x, y, z), new Vector3(x, y, z));
result.x.Should().BeApproximately(expected.X, FLOAT_TOLERANCE);
result.y.Should().BeApproximately(expected.Y, FLOAT_TOLERANCE);
result.z.Should().BeApproximately(expected.Z, FLOAT_TOLERANCE);
}
[Theory]
[MemberData(nameof(TestCases))]
[Obsolete("Tests Obsolete legacy behaviour to maintain backwards json compatibility with ~2.13? data")]
public void TestLegacyValueProp(double x, double y, double z, string _)
{
var vector = Activator.CreateInstance<Vector>();
vector.value = [x, y, z];
vector.x.Should().Be(x);
vector.y.Should().Be(y);
vector.z.Should().Be(z);
}
}
@@ -8,11 +8,9 @@ public class UnitsTest
{
private const double EPS = 0.00022;
public static List<string> OfficiallySupportedUnits => Units.SupportedUnits;
public static List<string> NotSupportedUnits => ["feeters", "liters", "us_ft"];
public static List<string?> ConversionSupport => Units.SupportedUnits.Concat([null]).ToList();
public static IReadOnlyCollection<string> OfficiallySupportedUnits => Units.SupportedUnits;
public static IReadOnlyCollection<string> NotSupportedUnits => ["feeters", "liters", "us_ft"];
public static IReadOnlyCollection<string?> ConversionSupport => [.. Units.SupportedUnits, null];
[Theory]
[MemberData(nameof(ConversionSupportGenerator))]