1package wayland
2
3import (
4 "bytes"
5 "context"
6 "math"
7 "os"
8 "testing"
9 "time"
10
11 "github.com/bnema/neferwl/internal/ports"
12 "github.com/bnema/zerowrap"
13)
14
15func TestSyncWaitAlreadySignalled(t *testing.T) {
16 dev := newMocksyncobjDevice(t)
17 dev.EXPECT().signalled(uint32(7), uint64(4)).Return(true, nil).Once()
18 sw, err := newSyncWaiter(func() {})
19 if err != nil {
20 t.Fatal(err)
21 }
22 defer sw.pipeR.Close()
23 defer sw.close()
24 w, err := sw.watch(syncPoint{tl: &timeline{dev: dev, handle: 7}, point: 4})
25 if err != nil || !sw.fired(w) {
26 t.Fatalf("wait: %v, ready: %v", err, sw.fired(w))
27 }
28}
29
30// An invalid poll result is an error, never permission to publish.
31func TestImplicitPollErrorRejectedAndWarnOnce(t *testing.T) {
32 sw, err := newSyncWaiter(func() {})
33 if err != nil {
34 t.Fatal(err)
35 }
36 defer sw.pipeR.Close()
37 defer sw.close()
38 var output bytes.Buffer
39 sw.log = zerowrap.New(zerowrap.Config{Output: &output})
40 // An fd number no process can own polls as POLLNVAL; its finalizer
41 // cannot close a reused descriptor.
42 b := &ports.DMABuf{Planes: []ports.DMABufPlane{{File: os.NewFile(uintptr(math.MaxInt32), "invalid")}}}
43 var waits [4]*syncWait
44 for range 2 {
45 if err := sw.watchImplicit(b, &waits); err == nil || waits[0] != nil {
46 t.Fatalf("poll error should reject without registering wait: %v %+v", err, waits)
47 }
48 }
49 if n := bytes.Count(output.Bytes(), []byte("implicit fence poll error")); n != 1 {
50 t.Fatalf("poll warnings %d: %s", n, output.String())
51 }
52}
53
54// POLLHUP with no POLLIN on a watched fd must wake the surface too.
55func TestImplicitPollHangupWakes(t *testing.T) {
56 sw, err := newSyncWaiter(func() {})
57 if err != nil {
58 t.Fatal(err)
59 }
60 ctx, cancel := context.WithCancel(context.Background())
61 done := make(chan struct{})
62 go func() { sw.run(ctx); close(done) }()
63 defer func() { cancel(); sw.close(); <-done }()
64 r, wr, err := os.Pipe()
65 if err != nil {
66 t.Fatal(err)
67 }
68 defer r.Close()
69 b := &ports.DMABuf{Planes: []ports.DMABufPlane{{File: r}}}
70 var waits [4]*syncWait
71 if err := sw.watchImplicit(b, &waits); err != nil || waits[0] == nil {
72 t.Fatalf("watch: %v", err)
73 }
74 wr.Close()
75 deadline := time.Now().Add(time.Second)
76 for !sw.fired(waits[0]) && time.Now().Before(deadline) {
77 time.Sleep(time.Millisecond)
78 }
79 if !sw.fired(waits[0]) || !waits[0].failed {
80 t.Fatal("hangup must wake waiter as failed, not ready to publish")
81 }
82}
83
84func TestImplicitWaitAndDrop(t *testing.T) {
85 sw, err := newSyncWaiter(func() {})
86 if err != nil {
87 t.Fatal(err)
88 }
89 ctx, cancel := context.WithCancel(context.Background())
90 done := make(chan struct{})
91 go func() { sw.run(ctx); close(done) }()
92 defer func() { cancel(); sw.close(); <-done }()
93 r, wr, err := os.Pipe()
94 if err != nil {
95 t.Fatal(err)
96 }
97 defer r.Close()
98 defer wr.Close()
99 b := &ports.DMABuf{Planes: []ports.DMABufPlane{{File: r}}}
100 var waits [4]*syncWait
101 err = sw.watchImplicit(b, &waits)
102 if err != nil || waits[0] == nil || sw.fired(waits[0]) {
103 t.Fatalf("not ready: %v", err)
104 }
105 sw.cancel(waits[0])
106 waits = [4]*syncWait{}
107 err = sw.watchImplicit(b, &waits)
108 if err != nil || waits[0] == nil || sw.fired(waits[0]) {
109 t.Fatalf("not ready: %v", err)
110 }
111 if _, err := wr.Write([]byte{1}); err != nil {
112 t.Fatal(err)
113 }
114 deadline := time.Now().Add(time.Second)
115 for !sw.fired(waits[0]) && time.Now().Before(deadline) {
116 time.Sleep(time.Millisecond)
117 }
118 if !sw.fired(waits[0]) {
119 t.Fatal("readable buffer never became ready")
120 }
121 // A newly attached readable buffer is ready without a poll goroutine hop.
122 waits = [4]*syncWait{}
123 err = sw.watchImplicit(b, &waits)
124 if err != nil || waits[0] != nil {
125 t.Fatalf("ready: %v, waits: %+v", err, waits)
126 }
127}