Files
TiedUp-/src/test/java/com/tiedup/remake/rig/anim/PoseTest.java
notevil 1fa291563c 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
2026-04-23 09:11:32 +02:00

190 lines
6.7 KiB
Java

/*
* © 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");
}
}