Audit-10 : add rig/ test coverage (37 new tests)
Added: - TransformSheetTest (10 tests) : binary search getInterpolationInfo edge cases (empty, negative-wrap, exact boundary, multi-keyframe segment), maxFrameTime sentinel, copyAll/copy subrange - PoseTest (10 tests) : interpolatePose merge + lerp correctness at 0/0.5/1, orElseEmpty fallback, load() all three LoadOperation modes, disableAllJoints - LivingMotionIsSameTest (7 tests) : IDLE==INACTION symmetry, uniqueness of universalOrdinal across all LivingMotions values - TimePairListTest (7 tests) : odd-arg rejection, empty list, inclusive begin / exclusive end boundary, multi-pair gap - RigAnimationTickHandlerTest (2 tests) : resetLoggedErrors idempotency Skipped (MC runtime dep): - TiedUpCapabilityEventsTest : AttachCapabilitiesEvent + live ForgeEventBus - EntityPatchProviderInvalidateTest : LazyOptional is a Forge runtime class - LivingEntityPatch.onConstructed : requires real LivingEntity hierarchy - RigAnimationTickHandler.tickPlayer/maybePlayIdle : require TiedUpCapabilities.getEntityPatch + ClientAnimator + LivingEntityPatch Bug flagged (no fix) : - TransformSheet.getFirstFrame() calls copy(0,2) without guarding size >= 2; a single-keyframe sheet would throw ArrayIndexOutOfBoundsException
This commit is contained in:
189
src/test/java/com/tiedup/remake/rig/anim/PoseTest.java
Normal file
189
src/test/java/com/tiedup/remake/rig/anim/PoseTest.java
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
|
||||
*/
|
||||
|
||||
package com.tiedup.remake.rig.anim;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.tiedup.remake.rig.armature.JointTransform;
|
||||
import com.tiedup.remake.rig.math.Vec3f;
|
||||
|
||||
/**
|
||||
* Tests de {@link Pose} : interpolatePose, orElseEmpty, load (SET / OVERWRITE /
|
||||
* APPEND_ABSENT), disableAllJoints.
|
||||
*
|
||||
* Aucun MC runtime requis — Pose utilise uniquement des collections Java et
|
||||
* JointTransform (JOML + Vec3f, pas de Minecraft bootstrap).
|
||||
*/
|
||||
class PoseTest {
|
||||
|
||||
// --- helpers ---
|
||||
|
||||
private static JointTransform translation(float x, float y, float z) {
|
||||
return JointTransform.translation(new Vec3f(x, y, z));
|
||||
}
|
||||
|
||||
// --- orElseEmpty ---
|
||||
|
||||
/** Un joint absent retourne JointTransform.empty() (pas null). */
|
||||
@Test
|
||||
void orElseEmpty_unknownJoint_returnsEmpty() {
|
||||
Pose pose = new Pose();
|
||||
JointTransform result = pose.orElseEmpty("nonexistent");
|
||||
assertNotNull(result, "orElseEmpty ne doit pas retourner null");
|
||||
// Verifier que la translation est zeros (empty = identite)
|
||||
assertEquals(0.0f, result.translation().x, 1e-5f);
|
||||
assertEquals(0.0f, result.translation().y, 1e-5f);
|
||||
assertEquals(0.0f, result.translation().z, 1e-5f);
|
||||
}
|
||||
|
||||
/** Un joint present retourne son transform, pas le fallback. */
|
||||
@Test
|
||||
void orElseEmpty_knownJoint_returnsTransform() {
|
||||
Pose pose = new Pose();
|
||||
JointTransform jt = translation(1.0f, 2.0f, 3.0f);
|
||||
pose.putJointData("spine", jt);
|
||||
assertEquals(jt, pose.orElseEmpty("spine"));
|
||||
}
|
||||
|
||||
// --- interpolatePose ---
|
||||
|
||||
/**
|
||||
* interpolatePose a progression 0.0 doit retourner les valeurs de pose1.
|
||||
*/
|
||||
@Test
|
||||
void interpolatePose_progressionZero_returnsFirstPose() {
|
||||
Pose pose1 = new Pose();
|
||||
pose1.putJointData("arm", translation(0.0f, 0.0f, 0.0f));
|
||||
|
||||
Pose pose2 = new Pose();
|
||||
pose2.putJointData("arm", translation(1.0f, 0.0f, 0.0f));
|
||||
|
||||
Pose result = Pose.interpolatePose(pose1, pose2, 0.0f);
|
||||
assertTrue(result.hasTransform("arm"));
|
||||
assertEquals(0.0f, result.get("arm").translation().x, 1e-5f);
|
||||
}
|
||||
|
||||
/**
|
||||
* interpolatePose a progression 1.0 doit retourner les valeurs de pose2.
|
||||
*/
|
||||
@Test
|
||||
void interpolatePose_progressionOne_returnsSecondPose() {
|
||||
Pose pose1 = new Pose();
|
||||
pose1.putJointData("arm", translation(0.0f, 0.0f, 0.0f));
|
||||
|
||||
Pose pose2 = new Pose();
|
||||
pose2.putJointData("arm", translation(10.0f, 0.0f, 0.0f));
|
||||
|
||||
Pose result = Pose.interpolatePose(pose1, pose2, 1.0f);
|
||||
assertEquals(10.0f, result.get("arm").translation().x, 1e-4f);
|
||||
}
|
||||
|
||||
/**
|
||||
* interpolatePose fusionne les joints des deux poses.
|
||||
* Un joint present dans pose2 seulement doit apparaitre dans le resultat
|
||||
* (interpole avec JointTransform.empty() comme pose1).
|
||||
*/
|
||||
@Test
|
||||
void interpolatePose_mergesJointsFromBothPoses() {
|
||||
Pose pose1 = new Pose();
|
||||
pose1.putJointData("head", translation(0.0f, 1.0f, 0.0f));
|
||||
|
||||
Pose pose2 = new Pose();
|
||||
pose2.putJointData("hand", translation(2.0f, 0.0f, 0.0f));
|
||||
|
||||
Pose result = Pose.interpolatePose(pose1, pose2, 0.5f);
|
||||
// Les deux joints doivent etre presents
|
||||
assertTrue(result.hasTransform("head"), "head from pose1 doit etre dans le resultat");
|
||||
assertTrue(result.hasTransform("hand"), "hand from pose2 doit etre dans le resultat");
|
||||
}
|
||||
|
||||
/**
|
||||
* interpolatePose a 0.5 doit etre a mi-chemin de la translation.
|
||||
*/
|
||||
@Test
|
||||
void interpolatePose_halfProgression_midpointTranslation() {
|
||||
Pose pose1 = new Pose();
|
||||
pose1.putJointData("leg", translation(0.0f, 0.0f, 0.0f));
|
||||
|
||||
Pose pose2 = new Pose();
|
||||
pose2.putJointData("leg", translation(4.0f, 0.0f, 0.0f));
|
||||
|
||||
Pose result = Pose.interpolatePose(pose1, pose2, 0.5f);
|
||||
assertEquals(2.0f, result.get("leg").translation().x, 1e-4f);
|
||||
}
|
||||
|
||||
// --- load(LoadOperation) ---
|
||||
|
||||
/** SET efface les joints existants puis copie depuis la source. */
|
||||
@Test
|
||||
void load_set_replacesAllJoints() {
|
||||
Pose dest = new Pose();
|
||||
dest.putJointData("old_joint", JointTransform.empty());
|
||||
|
||||
Pose src = new Pose();
|
||||
src.putJointData("new_joint", translation(1.0f, 0.0f, 0.0f));
|
||||
|
||||
dest.load(src, Pose.LoadOperation.SET);
|
||||
|
||||
assertFalse(dest.hasTransform("old_joint"),
|
||||
"SET doit supprimer les joints de la destination avant fusion");
|
||||
assertTrue(dest.hasTransform("new_joint"),
|
||||
"SET doit copier les joints source");
|
||||
}
|
||||
|
||||
/** OVERWRITE ecrase les joints communs sans supprimer les joints uniquement dans dest. */
|
||||
@Test
|
||||
void load_overwrite_keepsExistingAndOverwritesCommon() {
|
||||
Pose dest = new Pose();
|
||||
dest.putJointData("kept", JointTransform.empty());
|
||||
dest.putJointData("shared", translation(0.0f, 0.0f, 0.0f));
|
||||
|
||||
Pose src = new Pose();
|
||||
src.putJointData("shared", translation(5.0f, 0.0f, 0.0f));
|
||||
|
||||
dest.load(src, Pose.LoadOperation.OVERWRITE);
|
||||
|
||||
assertTrue(dest.hasTransform("kept"),
|
||||
"OVERWRITE doit conserver les joints de dest non presents dans src");
|
||||
assertEquals(5.0f, dest.get("shared").translation().x, 1e-4f,
|
||||
"OVERWRITE doit ecraser le joint commun avec la valeur src");
|
||||
}
|
||||
|
||||
/** APPEND_ABSENT n'ajoute que les joints absents de dest. */
|
||||
@Test
|
||||
void load_appendAbsent_doesNotOverwriteExisting() {
|
||||
Pose dest = new Pose();
|
||||
dest.putJointData("existing", translation(9.0f, 0.0f, 0.0f));
|
||||
|
||||
Pose src = new Pose();
|
||||
src.putJointData("existing", translation(1.0f, 0.0f, 0.0f));
|
||||
src.putJointData("added", translation(2.0f, 0.0f, 0.0f));
|
||||
|
||||
dest.load(src, Pose.LoadOperation.APPEND_ABSENT);
|
||||
|
||||
assertEquals(9.0f, dest.get("existing").translation().x, 1e-4f,
|
||||
"APPEND_ABSENT ne doit pas ecraser un joint existant");
|
||||
assertTrue(dest.hasTransform("added"),
|
||||
"APPEND_ABSENT doit ajouter les joints absents");
|
||||
}
|
||||
|
||||
// --- disableAllJoints ---
|
||||
|
||||
/** disableAllJoints vide la map de transforms. */
|
||||
@Test
|
||||
void disableAllJoints_clearsTransforms() {
|
||||
Pose pose = new Pose();
|
||||
pose.putJointData("head", JointTransform.empty());
|
||||
pose.putJointData("arm", JointTransform.empty());
|
||||
pose.disableAllJoints();
|
||||
assertTrue(pose.getJointTransformData().isEmpty(),
|
||||
"disableAllJoints doit vider la map de transforms");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user