diff options
Diffstat (limited to 'internal/engine/tick_test.go')
| -rw-r--r-- | internal/engine/tick_test.go | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/internal/engine/tick_test.go b/internal/engine/tick_test.go new file mode 100644 index 0000000..520ef19 --- /dev/null +++ b/internal/engine/tick_test.go @@ -0,0 +1,62 @@ +package engine + +import ( + "sync" + "testing" +) + +// TestProcessTickFiresInSubscriptionOrder asserts the engine invokes due +// tick subscribers in ascending subscription-ID order, i.e. the order in +// which they registered. This is the determinism contract that callers +// (notably the game's master subscriber at cmd/thoi/main.go, which subscribes +// first as ID 1 and must fire before combat/aggro/respawn per-tick callbacks) +// increasingly rely on. +func TestProcessTickFiresInSubscriptionOrder(t *testing.T) { + e := New() + var mu sync.Mutex + var order []int + + // Three interval-1 subscribers. Each appends its own index. All return + // true to stay subscribed (we Unsubscribe at the end). + id1 := e.Subscribe(1, func() bool { + mu.Lock() + order = append(order, 1) + mu.Unlock() + return true + }) + id2 := e.Subscribe(1, func() bool { + mu.Lock() + order = append(order, 2) + mu.Unlock() + return true + }) + id3 := e.Subscribe(1, func() bool { + mu.Lock() + order = append(order, 3) + mu.Unlock() + return true + }) + + if id1 >= id2 || id2 >= id3 { + t.Fatalf("ID monotonicity broken: got %d, %d, %d", id1, id2, id3) + } + + // processTick is synchronous: it invokes every due interval-1 subscriber + // inline in turn before returning. So `order` is fully populated here. + e.processTick() + + mu.Lock() + got := append([]int(nil), order...) + mu.Unlock() + + e.Unsubscribe(id1) + e.Unsubscribe(id2) + e.Unsubscribe(id3) + + if len(got) != 3 { + t.Fatalf("expected 3 subscriber firings, got %d: %v", len(got), got) + } + if got[0] != 1 || got[1] != 2 || got[2] != 3 { + t.Errorf("subscribers fired out of registration order: got %v, want [1 2 3]", got) + } +} |
