refactor: clean GSD comments and translate remaining Java sources to English
- Reduce javadocs to one-liners across config/region/physics/tick/viz/plugin root - Translate residual French comments; no behavioural change - Tests adjusted where assertions referenced French strings
This commit is contained in:
@@ -15,15 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* Round-trip tests for {@link GravityFlipConfig#CODEC}. Critical guarantees:
|
||||
* <ul>
|
||||
* <li>Regions list is always non-null (empty by default).</li>
|
||||
* <li>List elements survive encode -> decode in order.</li>
|
||||
* <li>Decoded list is MUTABLE — Phase 4 command handlers depend on this.
|
||||
* Guard against an accidental {@code List.of(arr)} regression.</li>
|
||||
* </ul>
|
||||
*/
|
||||
/** Round-trip tests for {@link GravityFlipConfig#CODEC} — non-null regions list, order preserved, list remains mutable. */
|
||||
class GravityFlipConfigCodecTest {
|
||||
|
||||
@Test
|
||||
@@ -49,7 +41,6 @@ class GravityFlipConfigCodecTest {
|
||||
@Test
|
||||
void roundTripOfEmptyListYieldsNonNullEmptyList() {
|
||||
GravityFlipConfig src = new GravityFlipConfig();
|
||||
// src.regions is the default empty ArrayList.
|
||||
GravityFlipConfig decoded = roundTrip(src);
|
||||
|
||||
assertNotNull(decoded.getRegions(), "decoded regions list must never be null");
|
||||
@@ -63,8 +54,7 @@ class GravityFlipConfigCodecTest {
|
||||
|
||||
GravityFlipConfig decoded = roundTrip(src);
|
||||
|
||||
// CRITICAL: must not throw UnsupportedOperationException.
|
||||
// Phase 4 commands (define / delete / toggle) all mutate this list.
|
||||
// Must not throw UnsupportedOperationException — command handlers depend on a mutable list.
|
||||
assertDoesNotThrow(() -> decoded.getRegions().add(region("added", 2, 2, 2, 3, 3, 3)));
|
||||
assertDoesNotThrow(() -> decoded.getRegions().remove(0));
|
||||
assertTrue(decoded.getRegions() instanceof ArrayList,
|
||||
@@ -86,7 +76,6 @@ class GravityFlipConfigCodecTest {
|
||||
return GravityFlipConfig.CODEC.decode(encoded, info);
|
||||
}
|
||||
|
||||
// Suppress unused-import warning if List is not directly referenced in any final assertion.
|
||||
@SuppressWarnings("unused")
|
||||
private static List<GravityFlipRegion> typeAnchor() { return null; }
|
||||
}
|
||||
|
||||
@@ -10,10 +10,7 @@ import java.util.UUID;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* Pure-data tests for {@link FallDamageGuard} — no Hytale runtime dependency.
|
||||
* Covers entry / in-region / exit / grace-window / re-entry / FallDamage=true override.
|
||||
*/
|
||||
/** Pure-data tests for {@link FallDamageGuard} — entry, in-region, exit, grace-window, re-entry. */
|
||||
class FallDamageGuardTest {
|
||||
|
||||
@Test
|
||||
@@ -69,8 +66,7 @@ class FallDamageGuardTest {
|
||||
GravityFlipRegion region = region(false, 2500);
|
||||
guard.markInRegion(uuid, region);
|
||||
guard.markExit(uuid, region, 1000L);
|
||||
guard.markInRegion(uuid, region); // re-enter
|
||||
// In-region again with FallDamage=false → immediate suppression, grace reset.
|
||||
guard.markInRegion(uuid, region);
|
||||
assertTrue(guard.shouldSuppressFallDamage(uuid, 1500L));
|
||||
}
|
||||
|
||||
@@ -82,7 +78,6 @@ class FallDamageGuardTest {
|
||||
GravityFlipRegion allowed = region(true, 2500);
|
||||
guard.markInRegion(uuid, suppressed);
|
||||
guard.markExit(uuid, suppressed, 1000L);
|
||||
// New region has FallDamage=true → override immediately.
|
||||
guard.markInRegion(uuid, allowed);
|
||||
assertFalse(guard.shouldSuppressFallDamage(uuid, 1500L));
|
||||
}
|
||||
|
||||
@@ -8,14 +8,7 @@ import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Pure-diff + tracker-semantics tests for {@link GravityApplier}.
|
||||
*
|
||||
* <p>Pas de mocks (pattern Phase 02-02 deviation #4). Pas de runtime Hytale requis —
|
||||
* les tests 5 et 6 utilisent le hook package-private {@code __updateTrackerForTest} et
|
||||
* la vue {@code previouslyInvertedView()} pour valider la sémantique du tracker sans
|
||||
* toucher à {@code World} / {@code Store}.
|
||||
*/
|
||||
/** Pure-diff + tracker-semantics tests for {@link GravityApplier}. */
|
||||
class GravityApplierDiffTest {
|
||||
|
||||
@Test
|
||||
@@ -73,16 +66,12 @@ class GravityApplierDiffTest {
|
||||
applier.__updateTrackerForTest(new HashSet<>(Set.of(c)));
|
||||
assertEquals(Set.of(c), applier.previouslyInvertedView());
|
||||
|
||||
// View is immutable.
|
||||
Set<UUID> view = applier.previouslyInvertedView();
|
||||
assertThrows(UnsupportedOperationException.class, () -> view.add(UUID.randomUUID()));
|
||||
}
|
||||
|
||||
// NOTE (Rule 3 deviation — Plan 03-02) : les tests suivants ciblent la seam pure
|
||||
// `buildFlaggedDecision(double, double, boolean)` au lieu de `buildPhysicsValuesWithFlag`
|
||||
// parce que le static init de `PhysicsValues` déclenche un `ExceptionInInitializerError`
|
||||
// hors runtime Hytale (dépendance ModuleRegistry). La décomposition pure garantit la
|
||||
// sémantique attendue (mass/drag préservés, flag = target) sans couplage ECS.
|
||||
// These tests target the pure seam buildFlaggedDecision because PhysicsValues static init
|
||||
// triggers ExceptionInInitializerError outside the Hytale runtime.
|
||||
|
||||
@Test
|
||||
void buildFlaggedDecisionPreservesMassAndDrag() {
|
||||
|
||||
@@ -11,15 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* Round-trip tests for {@link GravityFlipRegion#CODEC}. Verifies the codec preserves
|
||||
* the legacy Name + Box + Enabled fields across encode -> decode cycles via the BSON
|
||||
* intermediate representation, and (Plan 03-04) the 6 optional tuning fields :
|
||||
* FallDamage, GracePeriodMs, VerticalForce, AffectPlayers, AffectNpcs, AffectItems.
|
||||
*
|
||||
* <p>Back-compat invariant (test {@link #roundTripPreservesDefaultsWhenNewFieldsAbsent}) :
|
||||
* a BSON encoded without the 6 new keys must decode with all Java defaults preserved.
|
||||
*/
|
||||
/** Round-trip tests for {@link GravityFlipRegion#CODEC} covering legacy + optional tuning + visualization fields. */
|
||||
class GravityFlipRegionCodecTest {
|
||||
|
||||
@Test
|
||||
@@ -68,11 +60,9 @@ class GravityFlipRegionCodecTest {
|
||||
assertEquals("", decoded.getName(), "empty name must survive round-trip without substitution");
|
||||
}
|
||||
|
||||
// ---------- Plan 03-04 : 6 nouveaux champs optionnels ----------
|
||||
|
||||
@Test
|
||||
void roundTripPreservesDefaultsWhenNewFieldsAbsent() {
|
||||
// Region construite via constructeur legacy 3-arg (comme un regions.json legacy).
|
||||
// Legacy 3-arg constructor simulates an old regions.json.
|
||||
GravityFlipRegion src = new GravityFlipRegion(
|
||||
"legacy",
|
||||
new Box(new Vector3d(0, 0, 0), new Vector3d(1, 1, 1)),
|
||||
@@ -80,7 +70,6 @@ class GravityFlipRegionCodecTest {
|
||||
|
||||
GravityFlipRegion decoded = roundTrip(src);
|
||||
|
||||
// Tous les 6 nouveaux champs doivent exposer leurs defaults Java.
|
||||
assertFalse(decoded.isFallDamage(), "default FallDamage=false");
|
||||
assertEquals(2500, decoded.getGracePeriodMs(), "default GracePeriodMs=2500");
|
||||
assertEquals(0.1, decoded.getVerticalForce(), 1e-9, "default VerticalForce=0.1");
|
||||
@@ -119,7 +108,6 @@ class GravityFlipRegionCodecTest {
|
||||
src.setAffectPlayers(false);
|
||||
GravityFlipRegion decoded = roundTrip(src);
|
||||
assertFalse(decoded.isAffectPlayers());
|
||||
// Les autres filtres restent à true (non-clobber).
|
||||
assertTrue(decoded.isAffectNpcs());
|
||||
assertTrue(decoded.isAffectItems());
|
||||
}
|
||||
@@ -144,8 +132,6 @@ class GravityFlipRegionCodecTest {
|
||||
assertFalse(decoded.isAffectItems());
|
||||
}
|
||||
|
||||
// ---------- Plan 03-05 : 4 visualization fields ----------
|
||||
|
||||
@Test
|
||||
void roundTripPreservesVisualFields() {
|
||||
GravityFlipRegion src = baseRegion();
|
||||
@@ -164,8 +150,6 @@ class GravityFlipRegionCodecTest {
|
||||
|
||||
@Test
|
||||
void roundTripPreservesVisualDefaultsWhenFieldsAbsent() {
|
||||
// Region construite via constructeur legacy 3-arg — simule un regions.json legacy
|
||||
// (ni 03-04 ni 03-05 présents).
|
||||
GravityFlipRegion src = new GravityFlipRegion(
|
||||
"legacy-viz",
|
||||
new Box(new Vector3d(0, 0, 0), new Vector3d(1, 1, 1)),
|
||||
|
||||
@@ -18,14 +18,8 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Pure-math + concurrency tests for {@link RegionRegistry}.
|
||||
*
|
||||
* <p>JDK 25 + Mockito + Hytale's {@code World} class is a bad combination — Mockito's inline
|
||||
* MockMaker (the only one that can mock final classes) triggers static init of the supertype
|
||||
* {@code PluginBase}, which fails outside a real server because {@code HytaleLogger} requires
|
||||
* the JUL log manager to be set first. Therefore all snapshot tests use the package-private
|
||||
* {@code publishSnapshotByKey} / {@code currentSnapshotByKey} hooks with {@code Object}
|
||||
* sentinels, never a real or mocked {@code World}.
|
||||
* Pure-math + concurrency tests for {@link RegionRegistry}. Snapshot tests use the package-private
|
||||
* publishSnapshotByKey / currentSnapshotByKey hooks because Mockito cannot mock World under JDK 25.
|
||||
*/
|
||||
class RegionRegistryTest {
|
||||
|
||||
@@ -114,11 +108,9 @@ class RegionRegistryTest {
|
||||
cfg.getRegions().add(new GravityFlipRegion("a", box(), true));
|
||||
RegionRegistry reg = new RegionRegistry(cfg);
|
||||
|
||||
// Reader captures the immutable list before the swap.
|
||||
var before = reg.enabled();
|
||||
assertEquals(1, before.size());
|
||||
|
||||
// Mutator swaps via refreshFromConfig.
|
||||
cfg.getRegions().add(new GravityFlipRegion("b", box(), true));
|
||||
reg.refreshFromConfig(cfg);
|
||||
|
||||
@@ -128,7 +120,6 @@ class RegionRegistryTest {
|
||||
assertEquals(1, before.size());
|
||||
}
|
||||
|
||||
/** Minimal RegionSnapshot for the publish/read tests; world() is unused (returns null). */
|
||||
private static final class StubSnapshot implements RegionSnapshot {
|
||||
private final Map<GravityFlipRegion, Collection<Ref<EntityStore>>> by;
|
||||
private final long tick;
|
||||
|
||||
@@ -9,10 +9,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Scheduler-timing tests for {@link RegionTickLoop}. Use the {@code Runnable} overload so the
|
||||
* tests don't wait the 2s production initial delay and don't need a real {@code World}.
|
||||
*/
|
||||
/** Scheduler-timing tests for {@link RegionTickLoop} using the Runnable overload. */
|
||||
class RegionTickLoopTest {
|
||||
|
||||
@Test
|
||||
@@ -32,7 +29,7 @@ class RegionTickLoopTest {
|
||||
RegionTickLoop loop = new RegionTickLoop(reg, t -> {});
|
||||
AtomicInteger count = new AtomicInteger();
|
||||
loop.startWithDelay(0L, (Runnable) count::incrementAndGet);
|
||||
Thread.sleep(300); // ~3 ticks
|
||||
Thread.sleep(300);
|
||||
long t0 = System.nanoTime();
|
||||
loop.stop();
|
||||
long elapsedMs = (System.nanoTime() - t0) / 1_000_000;
|
||||
@@ -52,7 +49,7 @@ class RegionTickLoopTest {
|
||||
int n = count.incrementAndGet();
|
||||
if (n == 1) throw new RuntimeException("boom on first tick");
|
||||
});
|
||||
Thread.sleep(500); // expect ~5 ticks despite the first throwing
|
||||
Thread.sleep(500);
|
||||
loop.stop();
|
||||
assertTrue(count.get() >= 3, "scheduler died after first throw; count=" + count.get());
|
||||
assertNotNull(capturedFirst.get(), "errorHandler was not invoked");
|
||||
|
||||
@@ -10,23 +10,18 @@ import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Tests for {@link ParticleEdgeEmitter}. Verifies the 12-edge AABB emission
|
||||
* contract — no diagonals, no interior points, corner-dedup, density clamping.
|
||||
*/
|
||||
/** Tests for {@link ParticleEdgeEmitter} — 12-edge AABB emission, no diagonals, corner dedup, density clamping. */
|
||||
class ParticleEdgeEmitterTest {
|
||||
|
||||
private static final double EPS = 1e-9;
|
||||
|
||||
@Test
|
||||
void unitBox_density1_returnsExactly8Corners() {
|
||||
// 1x1x1 box, density=1 → each edge of length 1 → ceil(1*1)=1 → max(2,1)=2
|
||||
// points per edge (endpoints only), dedup → 8 corners total.
|
||||
// 1x1x1 box, density=1 -> each edge of length 1 -> 2 points/edge (endpoints), deduped -> 8 corners.
|
||||
Box b = new Box(new Vector3d(0, 0, 0), new Vector3d(1, 1, 1));
|
||||
List<Vector3d> pts = ParticleEdgeEmitter.edgePoints(b, 1.0);
|
||||
assertEquals(8, pts.size(), "unit box at density=1 should emit exactly 8 corner points");
|
||||
|
||||
// All 8 canonical corners present.
|
||||
Set<String> expected = new HashSet<>();
|
||||
for (double x : new double[]{0, 1})
|
||||
for (double y : new double[]{0, 1})
|
||||
@@ -39,30 +34,26 @@ class ParticleEdgeEmitterTest {
|
||||
|
||||
@Test
|
||||
void largeBox_density1_allPointsOnBoxSurfaceAndOnEdges() {
|
||||
// 10x10x10 box, density=1 → 11 points per edge (incl. endpoints).
|
||||
Box b = new Box(new Vector3d(0, 0, 0), new Vector3d(10, 10, 10));
|
||||
List<Vector3d> pts = ParticleEdgeEmitter.edgePoints(b, 1.0);
|
||||
|
||||
// Edge membership: each point must lie on ≥ 2 of the 6 box planes
|
||||
// (i.e. at least 2 of its coords are on {min, max} of their axis).
|
||||
// Edge membership: each point must lie on at least 2 of the 6 box planes.
|
||||
for (Vector3d p : pts) {
|
||||
int onPlane = 0;
|
||||
if (approx(p.x, 0) || approx(p.x, 10)) onPlane++;
|
||||
if (approx(p.y, 0) || approx(p.y, 10)) onPlane++;
|
||||
if (approx(p.z, 0) || approx(p.z, 10)) onPlane++;
|
||||
assertTrue(onPlane >= 2,
|
||||
"point " + p + " must be on ≥ 2 box planes (edge membership), was on " + onPlane);
|
||||
"point " + p + " must be on >= 2 box planes (edge membership), was on " + onPlane);
|
||||
}
|
||||
|
||||
// Sanity: no duplicate points (corners must be deduped).
|
||||
Set<String> keys = new HashSet<>();
|
||||
for (Vector3d p : pts) {
|
||||
assertTrue(keys.add(key(p.x, p.y, p.z)),
|
||||
"duplicate point " + p + " — corners should be dedup'd");
|
||||
"duplicate point " + p + " — corners should be deduped");
|
||||
}
|
||||
|
||||
// Expected count: ceil(10*1) = 10 points/edge (incl. endpoints) → 8 interior/edge.
|
||||
// Total = 8 corners + 12 edges * 8 interior = 8 + 96 = 104.
|
||||
// 10 points/edge (incl. endpoints) -> 8 interior/edge. Total = 8 corners + 12 * 8 = 104.
|
||||
assertEquals(104, pts.size());
|
||||
}
|
||||
|
||||
@@ -70,12 +61,9 @@ class ParticleEdgeEmitterTest {
|
||||
void density_zeroClampedToMin_density1000ClampedToMax() {
|
||||
Box b = new Box(new Vector3d(0, 0, 0), new Vector3d(1, 1, 1));
|
||||
|
||||
// density=0 → clamp to 0.1 → per-edge ceil(1*0.1)=1 → max(2,1)=2 endpoints only.
|
||||
List<Vector3d> lo = ParticleEdgeEmitter.edgePoints(b, 0.0);
|
||||
assertEquals(8, lo.size(), "density=0 should clamp to MIN_DENSITY, yielding 8 corners on unit box");
|
||||
|
||||
// density=1000 → clamp to 10 → per-edge ceil(1*10)=10 → 10 total points/edge.
|
||||
// 8 corners + 12 * 8 interior = 8 + 96 = 104.
|
||||
List<Vector3d> hi = ParticleEdgeEmitter.edgePoints(b, 1000.0);
|
||||
assertEquals(104, hi.size(), "density=1000 should clamp to MAX_DENSITY=10");
|
||||
}
|
||||
@@ -87,14 +75,11 @@ class ParticleEdgeEmitterTest {
|
||||
assertEquals(8, pts.size());
|
||||
}
|
||||
|
||||
// ---------- helpers ----------
|
||||
|
||||
private static boolean approx(double a, double b) {
|
||||
return Math.abs(a - b) < EPS;
|
||||
}
|
||||
|
||||
private static String key(double x, double y, double z) {
|
||||
// Round to 6 decimals to avoid floating-point noise in the dedup check.
|
||||
return String.format("%.6f,%.6f,%.6f", x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,11 +23,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Tests unitaires pour {@link RegionVisualizer}. Le {@code World} n'est jamais touché
|
||||
* (pas mockable sous JDK 25) : le {@code WorldExecutor} injecté exécute la lambda
|
||||
* inline sans ré-entrer dans World, et le {@code DebugEmitter} pousse dans une liste.
|
||||
*/
|
||||
/** Unit tests for {@link RegionVisualizer} with injected WorldExecutor/DebugEmitter so World is never touched. */
|
||||
class RegionVisualizerTest {
|
||||
|
||||
private static final class Call {
|
||||
@@ -38,8 +34,6 @@ class RegionVisualizerTest {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- parseColor ----------
|
||||
|
||||
@Test
|
||||
void parseColor_validHex() {
|
||||
Vector3f c = RegionVisualizer.parseColor("#FF8800");
|
||||
@@ -59,8 +53,6 @@ class RegionVisualizerTest {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- normalizeMode / flagsForMode ----------
|
||||
|
||||
@Test
|
||||
void parseMode_unknown_fallsBackToOutline() {
|
||||
assertEquals("Outline", RegionVisualizer.normalizeMode("Blah"));
|
||||
@@ -76,7 +68,6 @@ class RegionVisualizerTest {
|
||||
assertEquals(DebugUtils.FLAG_NO_WIREFRAME, RegionVisualizer.flagsForMode("Faces"));
|
||||
assertEquals(DebugUtils.FLAG_NONE, RegionVisualizer.flagsForMode("Both"));
|
||||
assertEquals(DebugUtils.FLAG_NONE, RegionVisualizer.flagsForMode("Particles"));
|
||||
// unknown → Outline
|
||||
assertEquals(DebugUtils.FLAG_NO_SOLID, RegionVisualizer.flagsForMode("xxx"));
|
||||
}
|
||||
|
||||
@@ -85,8 +76,6 @@ class RegionVisualizerTest {
|
||||
assertEquals("Particles", RegionVisualizer.normalizeMode("Particles"));
|
||||
}
|
||||
|
||||
// ---------- Particles branch ----------
|
||||
|
||||
@Test
|
||||
void visualize_particlesMode_callsParticleEmitterOncePerEdgePoint() {
|
||||
List<String> particleCalls = new ArrayList<>();
|
||||
@@ -98,14 +87,12 @@ class RegionVisualizerTest {
|
||||
(w, r) -> r.run(),
|
||||
() -> 0L);
|
||||
|
||||
// unit box at density=1 → 8 corner points emitted.
|
||||
GravityFlipRegion r = region("pz", "#FFFFFF", "Particles", 1000, 0.5);
|
||||
r.setVisualParticleId("Dust_Sparkles_Fine");
|
||||
r.setVisualParticleDensity(1.0);
|
||||
|
||||
viz.visualize(null, snapshotOf(r));
|
||||
assertEquals(8, particleCalls.size(), "unit box + density=1 → 8 corner emissions");
|
||||
// All calls use the requested id (validation falls open in test context).
|
||||
assertEquals(8, particleCalls.size(), "unit box + density=1 -> 8 corner emissions");
|
||||
for (String call : particleCalls) {
|
||||
assertTrue(call.startsWith("Dust_Sparkles_Fine@"), "unexpected call: " + call);
|
||||
}
|
||||
@@ -113,31 +100,26 @@ class RegionVisualizerTest {
|
||||
|
||||
@Test
|
||||
void particleDefaults_absentInConstructedRegion() {
|
||||
// Defaults must match the codec's documented defaults (03-06).
|
||||
GravityFlipRegion r = new GravityFlipRegion("x",
|
||||
new Box(new Vector3d(0, 0, 0), new Vector3d(1, 1, 1)), true);
|
||||
assertEquals("Torch_Fire", r.getVisualParticleId());
|
||||
assertEquals(0.3, r.getVisualParticleDensity(), 1e-9);
|
||||
}
|
||||
|
||||
// ---------- matrixFromBox ----------
|
||||
|
||||
@Test
|
||||
void matrix_boxNonCubic() {
|
||||
Box b = new Box(new Vector3d(0, 0, 0), new Vector3d(2, 4, 6));
|
||||
Matrix4d m = RegionVisualizer.matrixFromBox(b);
|
||||
double[] d = m.getData();
|
||||
// column-major : scale en diag [0][5][10], translation en [12][13][14]
|
||||
// column-major: scale on diagonal [0][5][10], translation on [12][13][14].
|
||||
assertEquals(2.0, d[0], 1e-9);
|
||||
assertEquals(4.0, d[5], 1e-9);
|
||||
assertEquals(6.0, d[10], 1e-9);
|
||||
assertEquals(1.0, d[12], 1e-9); // center x = 1
|
||||
assertEquals(2.0, d[13], 1e-9); // center y = 2
|
||||
assertEquals(3.0, d[14], 1e-9); // center z = 3
|
||||
assertEquals(1.0, d[12], 1e-9);
|
||||
assertEquals(2.0, d[13], 1e-9);
|
||||
assertEquals(3.0, d[14], 1e-9);
|
||||
}
|
||||
|
||||
// ---------- visualize : throttling / modes / skip ----------
|
||||
|
||||
@Test
|
||||
void visualize_throttlingSkipsSecondCallWithinWindow() {
|
||||
List<Call> calls = new ArrayList<>();
|
||||
@@ -152,15 +134,15 @@ class RegionVisualizerTest {
|
||||
RegionSnapshot snap = snapshotOf(r);
|
||||
|
||||
viz.visualize(null, snap);
|
||||
assertEquals(1, calls.size(), "premier tick émet");
|
||||
assertEquals(1, calls.size(), "first tick emits");
|
||||
|
||||
clock.set(1_500L); // +500ms < 1000 refreshMs
|
||||
clock.set(1_500L);
|
||||
viz.visualize(null, snap);
|
||||
assertEquals(1, calls.size(), "deuxième tick throttle");
|
||||
assertEquals(1, calls.size(), "second tick is throttled");
|
||||
|
||||
clock.set(2_100L); // +1100ms >= 1000
|
||||
clock.set(2_100L);
|
||||
viz.visualize(null, snap);
|
||||
assertEquals(2, calls.size(), "troisième tick ré-émet après refreshMs");
|
||||
assertEquals(2, calls.size(), "third tick re-emits after refreshMs");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -193,7 +175,7 @@ class RegionVisualizerTest {
|
||||
assertEquals(DebugShape.Cube, c.shape);
|
||||
assertEquals(DebugUtils.FLAG_NO_WIREFRAME, c.flags);
|
||||
assertEquals(0.75f, c.opacity, 1e-6);
|
||||
// TTL = 1000 * 1.2 / 1000 = 1.2s
|
||||
// TTL = 1000 * 1.2 / 1000 = 1.2s.
|
||||
assertEquals(1.2f, c.time, 1e-3);
|
||||
}
|
||||
|
||||
@@ -201,14 +183,14 @@ class RegionVisualizerTest {
|
||||
void visualize_clampsOpacityOutOfRange() {
|
||||
List<Call> calls = new ArrayList<>();
|
||||
RegionVisualizer viz = newViz(calls, 0L);
|
||||
GravityFlipRegion r = region("z1", "#00FF00", "Both", 1000, 2.5); // > 1 → clamp à 1
|
||||
GravityFlipRegion r = region("z1", "#00FF00", "Both", 1000, 2.5);
|
||||
viz.visualize(null, snapshotOf(r));
|
||||
assertEquals(1.0f, calls.get(0).opacity, 1e-6);
|
||||
}
|
||||
|
||||
@Test
|
||||
void visualize_clampsRefreshFloorBelowMin() {
|
||||
// refreshMs = 10 < MIN_REFRESH_MS (100) → effectif = 100ms
|
||||
// refreshMs=10 < MIN_REFRESH_MS(100) -> effective = 100ms.
|
||||
List<Call> calls = new ArrayList<>();
|
||||
AtomicLong clock = new AtomicLong(0L);
|
||||
RegionVisualizer viz = new RegionVisualizer(
|
||||
@@ -218,16 +200,14 @@ class RegionVisualizerTest {
|
||||
clock::get);
|
||||
GravityFlipRegion r = region("z1", "#00FF00", "Outline", 10, 0.5);
|
||||
viz.visualize(null, snapshotOf(r));
|
||||
clock.set(50L); // 50ms < 100 plancher
|
||||
clock.set(50L);
|
||||
viz.visualize(null, snapshotOf(r));
|
||||
assertEquals(1, calls.size(), "plancher 100ms protège contre flood");
|
||||
assertEquals(1, calls.size(), "100ms floor protects against flood");
|
||||
clock.set(150L);
|
||||
viz.visualize(null, snapshotOf(r));
|
||||
assertEquals(2, calls.size());
|
||||
}
|
||||
|
||||
// ---------- helpers ----------
|
||||
|
||||
private static RegionVisualizer newViz(List<Call> calls, long now) {
|
||||
AtomicLong clock = new AtomicLong(now);
|
||||
return new RegionVisualizer(
|
||||
|
||||
Reference in New Issue
Block a user