idle_test.go

 1package fetcher
 2
 3import (
 4	"errors"
 5	"sync/atomic"
 6	"testing"
 7	"time"
 8
 9	"github.com/floatpane/matcha/config"
10)
11
12func TestIdleWatcher_StopAllAndWait_TracksReplacedGoroutines(t *testing.T) {
13	w := NewIdleWatcher(make(chan IdleUpdate))
14	stopCh := make(chan struct{})
15	doneCh := make(chan struct{})
16	var exits atomic.Int64
17
18	w.wg.Add(1)
19	go func() {
20		defer w.wg.Done()
21		defer close(doneCh)
22		<-stopCh
23		exits.Add(1)
24	}()
25
26	w.watchers["acct"] = &accountIdle{
27		account: &config.Account{ID: "acct"},
28		stop:    stopCh,
29		done:    doneCh,
30	}
31
32	if err := w.StopAllAndWaitTimeout(time.Second); err != nil {
33		t.Fatalf("StopAllAndWaitTimeout returned error: %v", err)
34	}
35	if got := exits.Load(); got != 1 {
36		t.Fatalf("expected synthetic watcher to exit once, got %d", got)
37	}
38}
39
40func TestIdleWatcher_StopAllAndWaitTimeout_ReturnsOnSlowExit(t *testing.T) {
41	w := NewIdleWatcher(make(chan IdleUpdate))
42	stopCh := make(chan struct{})
43	doneCh := make(chan struct{})
44	release := make(chan struct{})
45	exited := make(chan struct{})
46
47	w.wg.Add(1)
48	go func() {
49		defer w.wg.Done()
50		defer close(doneCh)
51		defer close(exited)
52		<-release
53	}()
54
55	w.watchers["acct"] = &accountIdle{
56		account: &config.Account{ID: "acct"},
57		stop:    stopCh,
58		done:    doneCh,
59	}
60
61	err := w.StopAllAndWaitTimeout(50 * time.Millisecond)
62	if !errors.Is(err, ErrStopTimeout) {
63		t.Fatalf("expected ErrStopTimeout, got %v", err)
64	}
65
66	close(release)
67	select {
68	case <-exited:
69	case <-time.After(time.Second):
70		t.Fatal("synthetic watcher did not exit during cleanup")
71	}
72}