UnitTests/Kv3Tests.cs

Unit tests for VMDL KV3 parsing and bulk editing. Tests parse a sample KV3 VMDL string, verify finding RenderMeshFile and RenderMeshList objects, apply scale and alignment changes via VmdlBulkEditor, test unit conversions for scale, no-op behavior when values match, and reading first entry properties.

File Access
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ModelPro.Vmdl;

[TestClass]
public class Kv3Tests
{
	const string SampleVmdl = """
	<!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32aa0d} format:modeldoc30:version{8c2d7a91-9c42-4bf0-883a-5a3b1762d4f1} -->
	{
		rootNode = 
		{
			_class = "RootNode"
			children = 
			[
				{
					_class = "RenderMeshList"
					children = 
					[
						{
							_class = "RenderMeshFile"
							name = "Torso_LOD0"
							children = 
							[
								{
									_class = "RenderMeshMarkup"
									use_expensive_tangents = true
								},
							]
							filename = "models/citizen/citizen.fbx"
							import_translation = [ 0.0, 0.0, 0.0 ]
							import_rotation = [ 0.0, 0.0, 0.0 ]
							import_scale = 1.0
							align_origin_x_type = "None"
							align_origin_y_type = "None"
							align_origin_z_type = "None"
							parent_bone = ""
							import_filter = 
							{
								exclude_by_default = true
								exception_list = 
								[
									"CitizenTorso_LOD0",
								]
							}
						},
						{
							_class = "RenderMeshFile"
							name = "Arms_LOD0"
							filename = "models/citizen/citizen_arms.fbx"
							import_scale = 1.0
							align_origin_x_type = "None"
							align_origin_y_type = "None"
							align_origin_z_type = "None"
						},
					]
				},
			]
			model_archetype = ""
		}
	}
	""";

	[TestMethod]
	public void FindsMeshEntries()
	{
		var doc = Kv3Document.Parse( SampleVmdl );
		Assert.AreEqual( 2, doc.FindObjects( "RenderMeshFile" ).Count );
		Assert.AreEqual( 1, doc.FindObjects( "RenderMeshList" ).Count );
	}

	[TestMethod]
	public void AppliesScaleAndAlign()
	{
		var editor = VmdlBulkEditor.Load( SampleVmdl );
		Assert.AreEqual( 2, editor.MeshEntryCount );

		var props = new MeshEntryProperties
		{
			ImportScale = 0.5f,
			AlignOriginX = AlignOrigin.Center,
			AlignOriginY = AlignOrigin.Mins,
			AlignOriginZ = AlignOrigin.Maxs
		};

		Assert.IsTrue( editor.Apply( props ) );

		var result = Kv3Document.Parse( editor.Source );
		var entries = result.FindObjects( "RenderMeshFile" );
		Assert.AreEqual( 2, entries.Count );

		foreach ( var entry in entries )
		{
			Assert.AreEqual( "0.5", (entry.FindField( "import_scale" ).Value as Kv3Scalar)?.Value );
			Assert.AreEqual( "Center", (entry.FindField( "align_origin_x_type" ).Value as Kv3Scalar)?.Value );
			Assert.AreEqual( "Mins", (entry.FindField( "align_origin_y_type" ).Value as Kv3Scalar)?.Value );
			Assert.AreEqual( "Maxs", (entry.FindField( "align_origin_z_type" ).Value as Kv3Scalar)?.Value );
		}
	}

	[TestMethod]
	public void ConvertsScaleUnits()
	{
		// The value in the field is the import_scale multiplier written to the vmdl.
		Assert.AreEqual( 1.0f, new ScaleInput { Value = 1, Unit = ScaleUnit.Inches }.ToImportScale(), 0.0001f );
		Assert.AreEqual( 0.3937f, new ScaleInput { Value = 0.3937f, Unit = ScaleUnit.Centimeters }.ToImportScale(), 0.0001f );

		// Switching 1 inch to cm re-expresses it as 0.3937 (1 cm = 0.3937 inches).
		var inches = new ScaleInput { Value = 1, Unit = ScaleUnit.Inches };
		var cm = inches.ConvertTo( ScaleUnit.Centimeters );
		Assert.AreEqual( 0.3937f, cm.Value, 0.0001f );
		Assert.AreEqual( 0.3937f, cm.ToImportScale(), 0.0001f );

		// A cm model (import_scale 0.3937) reads back as itself.
		var fromCm = ScaleInput.FromImportScale( 0.3937f, ScaleUnit.Centimeters );
		Assert.AreEqual( 0.3937f, fromCm.Value, 0.0001f );
		Assert.AreEqual( 0.3937f, fromCm.ToImportScale(), 0.0001f );
	}

	[TestMethod]
	public void NoOpWhenValuesMatch()
	{
		var editor = VmdlBulkEditor.Load( SampleVmdl );

		var props = new MeshEntryProperties
		{
			ImportScale = 1.0f,
			AlignOriginX = AlignOrigin.None,
			AlignOriginY = AlignOrigin.None,
			AlignOriginZ = AlignOrigin.None
		};

		Assert.IsFalse( editor.Apply( props ) );
		Assert.AreEqual( SampleVmdl, editor.Source );
	}

	[TestMethod]
	public void ReadsFirstEntryProperties()
	{
		var editor = VmdlBulkEditor.Load( SampleVmdl );
		var props = editor.ReadFirstEntryProperties();

		Assert.AreEqual( 1.0f, props.ImportScale );
		Assert.AreEqual( AlignOrigin.None, props.AlignOriginX );
		Assert.AreEqual( AlignOrigin.None, props.AlignOriginY );
		Assert.AreEqual( AlignOrigin.None, props.AlignOriginZ );
	}
}