extends "res://tests/test_case.gd" const InputLeadController = preload("res://scripts/input_lead_controller.gd") func test_starts_at_minimum() -> void: var c := InputLeadController.new() assert_eq(c.lead, InputLeadController.LEAD_MIN, "initial lead") func test_unknown_depth_is_a_normal_tick() -> void: var c := InputLeadController.new() assert_eq(c.update(-1), 1, "no snapshot info yet -> ordinary +1 seq increment") assert_eq(c.lead, InputLeadController.LEAD_MIN, "lead unchanged with no info") func test_healthy_depth_is_a_normal_tick_and_no_immediate_release() -> void: var c := InputLeadController.new() for i in 10: assert_eq(c.update(1), 1, "healthy depth -> ordinary +1 tick %d" % i) assert_eq(c.lead, InputLeadController.LEAD_MIN, "release needs 2s clean, not 10 ticks") # §3.3: "on any starve, increase by up to 3 immediately" — but debounced by # MIN_CHANGE_INTERVAL_TICKS so it isn't literally same-tick. func test_starve_triggers_fast_attack_after_debounce_floor() -> void: var c := InputLeadController.new() var deltas: Array[int] = [] for i in InputLeadController.MIN_CHANGE_INTERVAL_TICKS: deltas.append(c.update(0)) # Every tick before the debounce floor is an ordinary +1 (no jump yet). for i in InputLeadController.MIN_CHANGE_INTERVAL_TICKS - 1: assert_eq(deltas[i], 1, "no lead change before the debounce floor, tick %d" % i) assert_eq(deltas[InputLeadController.MIN_CHANGE_INTERVAL_TICKS - 1], 4, "attack fires on the debounce-floor tick: +1 ordinary + 3 skip") assert_eq(c.lead, InputLeadController.LEAD_MIN + 3, "lead jumped by 3") func test_repeated_starvation_climbs_toward_max_and_clamps() -> void: var c := InputLeadController.new() # Enough sustained starvation to trigger several attack steps. for i in InputLeadController.MIN_CHANGE_INTERVAL_TICKS * 6: c.update(0) assert_eq(c.lead, InputLeadController.LEAD_MAX, "clamps at LEAD_MAX under sustained starvation, never exceeds it") func test_release_requires_both_clean_surplus_and_its_own_interval() -> void: var c := InputLeadController.new() # Force lead above minimum first via one attack step. for i in InputLeadController.MIN_CHANGE_INTERVAL_TICKS: c.update(0) var lead_after_attack := c.lead assert_true(lead_after_attack > InputLeadController.LEAD_MIN, "lead raised above minimum before testing release") # Fewer than CLEAN_SURPLUS_TICKS of surplus depth (above TARGET_DEPTH): # must not release yet. for i in InputLeadController.CLEAN_SURPLUS_TICKS - 1: c.update(InputLeadController.TARGET_DEPTH + 1) assert_eq(c.lead, lead_after_attack, "no release before 2s of clean surplus has elapsed") # One more surplus tick crosses the clean-surplus threshold AND the # release interval (both are already satisfied by now since the # debounce timer has been running the whole time) -> releases by 1. var delta := c.update(InputLeadController.TARGET_DEPTH + 1) assert_eq(delta, 0, "release tick duplicates rather than incrementing seq") assert_eq(c.lead, lead_after_attack - 1, "lead released by exactly 1") func test_release_stops_at_minimum() -> void: var c := InputLeadController.new() # Sustained surplus depth with lead already at LEAD_MIN: `lead` itself # must never drop below the floor, but release must still fire # (duplicate a seq) once its own timing conditions are met, since a # real reported surplus at floor lead is exactly the "backlog this # controller never caused" case — capping `lead` is cosmetic, it must # not also block the seq-duplicate action that drains real depth. var released := false for i in InputLeadController.CLEAN_SURPLUS_TICKS * 3: if c.update(InputLeadController.TARGET_DEPTH + 1) == 0: released = true assert_eq(c.lead, InputLeadController.LEAD_MIN, "lead never drops below the floor, tick %d" % i) assert_true(released, "release still fires (duplicates a seq) even though lead itself is pinned at minimum") func test_starve_resets_clean_surplus_counter() -> void: var c := InputLeadController.new() for i in InputLeadController.MIN_CHANGE_INTERVAL_TICKS: c.update(0) # raise lead above minimum via one attack step var lead_after_attack := c.lead # Some, but not all, of a clean surplus window — and well under the # 30-tick attack debounce floor too, so the interrupting starve below # can't accidentally retrigger a second attack step of its own. var partial_clean_ticks := 10 for i in partial_clean_ticks: c.update(InputLeadController.TARGET_DEPTH + 1) c.update(0) # a lone starve tick, resetting _clean_surplus_ticks assert_eq(c.lead, lead_after_attack, "the lone starve tick was too soon after the last change to trigger another attack") # A full clean window from this fresh starting point is required before # release fires — one tick short must not be enough. for i in InputLeadController.CLEAN_SURPLUS_TICKS - 1: c.update(InputLeadController.TARGET_DEPTH + 1) assert_eq(c.lead, lead_after_attack, "the starve interruption forced a fresh 2s clean window, so no release yet") c.update(InputLeadController.TARGET_DEPTH + 1) assert_eq(c.lead, lead_after_attack - 1, "release finally fires once a full fresh clean window has elapsed since the interruption") # A first attempt at fixing this gated the whole release branch on `lead > # LEAD_MIN` — this controller's own memory of past attacks — so a backlog # it did NOT itself create (a server hitch, persistent client/server clock # drift, a burst re-delivery) was never drained: lead stayed at 1 forever # even while the server kept reporting a deep, real backlog, and — because # that gate blocked the seq-duplicate action too, not just lead's own # bookkeeping — the actual buffered depth was never drained either. A # second adversarial review caught that the depth check added alongside # it didn't remove the old gate, just sat next to it. This reproduces the # scenario directly: lead never attacks (depth is never reported as a # starve, <= 0), yet release must still fire from sustained real surplus # alone, even while lead itself stays pinned at its floor throughout. func test_release_drains_a_backlog_it_never_caused_itself() -> void: var c := InputLeadController.new() assert_eq(c.lead, InputLeadController.LEAD_MIN, "starts at minimum, never attacked") # A large, externally-caused surplus (e.g. right after the server's own # ring-overflow resync) reported for well over 2s — lead never moves # via attack since depth is never <= 0. var released_at_floor := false for i in InputLeadController.CLEAN_SURPLUS_TICKS + InputLeadController.RELEASE_INTERVAL_TICKS: if c.update(10) == 0: released_at_floor = true assert_eq(c.lead, InputLeadController.LEAD_MIN, "lead's own bookkeeping never drops below its floor") assert_true(released_at_floor, "release still fires (duplicates a seq, actually draining real depth) even while lead is pinned at the floor") # Raise it above the floor via one real attack, then confirm sustained # external surplus (not self-caused) still drains it back down. for i in InputLeadController.MIN_CHANGE_INTERVAL_TICKS: c.update(0) var lead_after_attack := c.lead assert_true(lead_after_attack > InputLeadController.LEAD_MIN, "attack raised lead") var released := false for i in InputLeadController.CLEAN_SURPLUS_TICKS + InputLeadController.RELEASE_INTERVAL_TICKS: if c.update(10) == 0: released = true break assert_true(released, "sustained externally-caused surplus (depth=10) must eventually trigger a release") assert_true(c.lead < lead_after_attack, "lead actually decreased in response to real depth, not just internal bookkeeping")