Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions addons/talo/apis/debounced_api.gd
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ var _is_executing: bool
var _is_queued: bool
var _pending_waiters: Array[UpdateWaiter] = []

func _init(base_path: String, leading: bool = true) -> void:
func _init(base_path: String) -> void:
super(base_path)
_update_timer = TaloDebounceTimer.new(_on_debounce_fired, leading)
_update_timer = TaloDebounceTimer.new(_on_debounce_fired)
add_child(_update_timer)

func _debounce() -> void:
Expand Down
2 changes: 1 addition & 1 deletion addons/talo/apis/health_check_api.gd
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ enum HealthCheckStatus {

var _cached_result := HealthCheckStatus.UNKNOWN
var _can_ping := true
var _timer := TaloDebounceTimer.new(func (): _can_ping = true, false)
var _timer := TaloDebounceTimer.new(func (): _can_ping = true)

func _ready() -> void:
add_child(_timer)
Expand Down
27 changes: 3 additions & 24 deletions addons/talo/utils/debounce_timer.gd
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,18 @@ class_name TaloDebounceTimer extends Timer
## A one-shot timer with the wait_time equal to the debounce_timer_seconds setting. The debounce() function will throttle callback invocations.

var _callback: Callable
var _has_pending: bool
var _leading: bool

func _init(callback: Callable, leading: bool = true) -> void:
func _init(callback: Callable) -> void:
one_shot = true
ignore_time_scale = true
wait_time = Talo.settings.debounce_timer_seconds

_callback = callback
_leading = leading
timeout.connect(_on_leading_timeout if leading else _on_timeout)
timeout.connect(_on_timeout)

func _on_timeout() -> void:
_callback.call()

func _on_leading_timeout() -> void:
if _has_pending:
_has_pending = false
_callback.call()

## In leading mode: fire immediately on the first call, and again on timeout only if subsequent calls were made.
## In trailing mode: fire on timeout for every debounce window that had at least one call.
## Fire on timeout for every debounce window that had at least one call.
func debounce() -> void:
if _leading:
_handle_leading_debounce()
start()

func _handle_leading_debounce() -> void:
if is_stopped():
_callback.call()
else:
_has_pending = true

func stop() -> void:
super.stop()
_has_pending = false
30 changes: 4 additions & 26 deletions test/apis/debounced_api_test.gd
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ class TestHarness extends TaloDebouncedAPI:
var operation_result: Variant
var operation_count: int

func _init(leading: bool = false) -> void:
super._init("/v1/test", leading)
func _init() -> void:
super._init("/v1/test")

func _run_debounced_update() -> Variant:
operation_count += 1
Expand Down Expand Up @@ -37,8 +37,8 @@ func before_test() -> void:
func after_test() -> void:
Talo.settings.debounce_timer_seconds = 1.0

func _make_harness(result: Variant, leading: bool = false) -> TestHarness:
var harness: TestHarness = auto_free(TestHarness.new(leading))
func _make_harness(result: Variant) -> TestHarness:
var harness: TestHarness = auto_free(TestHarness.new())
harness.operation_result = result
add_child(harness)
monitor_signals(harness)
Expand Down Expand Up @@ -123,28 +123,6 @@ func test_failed_update_resolves_waiter_and_returns_failure() -> void:
assert_int(result).is_equal(TaloDebouncedAPI.FlushResult.FAILURE)
assert_bool(waiter.result.success).is_false()

func test_leading_mode_fires_immediately_on_first_call() -> void:
var harness := _make_harness(TaloFixtures.make_player(), true)

# (first) leading call fires immediately
var first := harness.queue_update()
@warning_ignore("redundant_await")
await assert_signal(harness).is_emitted(harness.operation_started, 1)

# (second) trailing call within window merges into one execution
var second := harness.queue_update()

_release_after(harness, 0.05)
_release_after(harness, 0.10)
var result := await harness.flush_updates()

assert_int(harness.operation_count).is_equal(2)
assert_int(result).is_equal(TaloDebouncedAPI.FlushResult.SUCCESS)
assert_bool(first.result.success).is_true()
assert_bool(second.result.success).is_true()
assert_bool(harness._is_queued).is_false()
assert_bool(harness._is_executing).is_false()

func test_flush_returns_nothing_pending_when_nothing_queued() -> void:
var harness := _make_harness(TaloFixtures.make_player())
var result := await harness.flush_updates()
Expand Down