1package term
2
3import (
4 "errors"
5 "os"
6 "syscall"
7 "testing"
8 "time"
9
10 "github.com/bnema/vev/internal/domain"
11)
12
13func TestResizeLoop_CoalescesBurstToOneEmission(t *testing.T) {
14 // Fill the signal channel with a burst before the loop starts
15 // consuming: draining is synchronous and deterministic here since
16 // nothing else sends concurrently.
17 sig := make(chan os.Signal, 8)
18 for range 5 {
19 sig <- syscall.SIGWINCH
20 }
21
22 out := make(chan domain.Geometry)
23 quit := make(chan struct{})
24
25 var calls int
26 getGeometry := func() (domain.Geometry, error) {
27 calls++
28 return domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}, PixelWidth: 800, PixelHeight: 480}, nil
29 }
30
31 go resizeLoop(sig, out, quit, getGeometry)
32
33 got := <-out
34 want := domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}, PixelWidth: 800, PixelHeight: 480}
35 if got != want {
36 t.Fatalf("emitted size = %+v, want %+v", got, want)
37 }
38
39 select {
40 case sz2, ok := <-out:
41 t.Fatalf("unexpected second emission (ok=%v): %+v", ok, sz2)
42 case <-time.After(50 * time.Millisecond):
43 }
44
45 if calls != 1 {
46 t.Fatalf("getSize called %d times, want 1 (burst should coalesce)", calls)
47 }
48
49 close(quit)
50 if _, ok := <-out; ok {
51 t.Fatalf("expected out to be closed after quit")
52 }
53}
54
55func TestResizeLoop_GetSizeErrorSkipsEmission(t *testing.T) {
56 sig := make(chan os.Signal) // unbuffered: sends rendezvous with the loop's receive
57 out := make(chan domain.Geometry)
58 quit := make(chan struct{})
59
60 var calls int
61 firstGetSize := make(chan struct{})
62 getGeometry := func() (domain.Geometry, error) {
63 calls++
64 if calls == 1 {
65 close(firstGetSize)
66 return domain.Geometry{}, errors.New("boom")
67 }
68 return domain.Geometry{Size: domain.Size{Cols: 10, Rows: 5}}, nil
69 }
70
71 go resizeLoop(sig, out, quit, getGeometry)
72
73 sig <- syscall.SIGWINCH // errors; no emission
74 <-firstGetSize // wait until the failed signal has been handled
75 sig <- syscall.SIGWINCH // succeeds
76
77 got := <-out
78 want := domain.Geometry{Size: domain.Size{Cols: 10, Rows: 5}}
79 if got != want {
80 t.Fatalf("emitted size = %+v, want %+v", got, want)
81 }
82 if calls != 2 {
83 t.Fatalf("getSize called %d times, want 2", calls)
84 }
85
86 close(quit)
87 if _, ok := <-out; ok {
88 t.Fatalf("expected out to be closed after quit")
89 }
90}
91
92func TestResizeLoop_QuitClosesOutImmediately(t *testing.T) {
93 sig := make(chan os.Signal)
94 out := make(chan domain.Geometry)
95 quit := make(chan struct{})
96
97 getGeometry := func() (domain.Geometry, error) {
98 t.Fatalf("getGeometry should not be called")
99 return domain.Geometry{}, nil
100 }
101
102 go resizeLoop(sig, out, quit, getGeometry)
103 close(quit)
104
105 select {
106 case _, ok := <-out:
107 if ok {
108 t.Fatalf("expected out to be closed, got a value")
109 }
110 case <-time.After(time.Second):
111 t.Fatalf("out was not closed after quit")
112 }
113}
114
115func TestDrainSignals_RemovesQueuedSignalsOnly(t *testing.T) {
116 sig := make(chan os.Signal, 4)
117 sig <- syscall.SIGWINCH
118 sig <- syscall.SIGWINCH
119 sig <- syscall.SIGWINCH
120
121 drainSignals(sig)
122
123 select {
124 case s := <-sig:
125 t.Fatalf("expected sig to be empty after drain, got %v", s)
126 default:
127 }
128}