1package app
2
3import (
4 "context"
5 "testing"
6
7 "github.com/stretchr/testify/mock"
8
9 portsmocks "github.com/bnema/neferwl/internal/mocks/ports"
10 "github.com/bnema/neferwl/internal/ports"
11)
12
13// Wayland feeds a started output its contents: Attach comes after the
14// output registered and got its security state, the output runs on what
15// Attach returned, Detach of that same handle follows its end, and a
16// restarted connector attaches again.
17func TestOutputSetAttachesAndDetachesContents(t *testing.T) {
18 ctx, cancel := context.WithCancel(context.Background())
19 defer cancel()
20 set := newOutputSet(ctx, nil)
21 events := make(chan ports.SecurityBackendEvent, 4)
22 set.wireSecurity(outputChannels{securityEvents: events})
23 attacher := newMockcontentAttacher(t)
24 set.attach = attacher
25 rets := []*portsmocks.MockContentReturn{portsmocks.NewMockContentReturn(t), portsmocks.NewMockContentReturn(t)}
26 feed := make(chan *ports.ContentBatch, 1)
27 var order []string
28 type seen struct {
29 contents <-chan *ports.ContentBatch
30 returns ports.ContentReturn
31 security ports.SecurityState
32 }
33 started := make(chan seen, 2)
34 run := func(ctx context.Context, _ <-chan ports.Scene, contents <-chan *ports.ContentBatch, returns ports.ContentReturn, _ <-chan ports.CursorChange, _ <-chan ports.CaptureRequest, security <-chan ports.SecurityState, _ ports.OutputInstance) error {
35 started <- seen{contents, returns, <-security}
36 <-ctx.Done()
37 return nil
38 }
39 attaches := 0
40 attacher.EXPECT().AttachOutput("DP-1").RunAndReturn(func(string) (<-chan *ports.ContentBatch, ports.ContentReturn) {
41 order = append(order, "attach")
42 ret := rets[attaches]
43 attaches++
44 // The output is registered with the security backend first.
45 if len(events) != 1 {
46 t.Errorf("attached with %d registrations queued, want 1", len(events))
47 }
48 return feed, ret
49 }).Times(2)
50 var detached []ports.ContentReturn
51 attacher.EXPECT().DetachOutput(mock.Anything).Run(func(feed ports.ContentReturn) {
52 order = append(order, "detach")
53 detached = append(detached, feed)
54 }).Times(2)
55 for round := range 2 {
56 if !set.start(ctx, "DP-1", run) {
57 t.Fatal("start failed")
58 }
59 got := <-started
60 if got.contents != (<-chan *ports.ContentBatch)(feed) || got.returns != ports.ContentReturn(rets[round]) {
61 t.Fatalf("round %d: output got another feed", round)
62 }
63 <-events
64 set.outs["DP-1"].stop()
65 if err := set.finish("DP-1"); err != nil {
66 t.Fatal(err)
67 }
68 }
69 for round := range detached {
70 if detached[round] != ports.ContentReturn(rets[round]) {
71 t.Fatalf("round %d: detached another feed than the one attached", round)
72 }
73 }
74 want := []string{"attach", "detach", "attach", "detach"}
75 if len(order) != len(want) {
76 t.Fatalf("order %v, want %v", order, want)
77 }
78 for i := range want {
79 if order[i] != want[i] {
80 t.Fatalf("order %v, want %v", order, want)
81 }
82 }
83}
84
85// wait detaches the outputs it stops.
86func TestOutputSetWaitDetachesContents(t *testing.T) {
87 ctx, cancel := context.WithCancel(context.Background())
88 defer cancel()
89 set := newOutputSet(ctx, nil)
90 attacher := newMockcontentAttacher(t)
91 set.attach = attacher
92 ret := portsmocks.NewMockContentReturn(t)
93 attacher.EXPECT().AttachOutput("A").Return(nil, ret).Once()
94 attacher.EXPECT().DetachOutput(ports.ContentReturn(ret)).Return().Once()
95 set.start(ctx, "A", func(ctx context.Context, _ <-chan ports.Scene, _ <-chan *ports.ContentBatch, _ ports.ContentReturn, _ <-chan ports.CursorChange, _ <-chan ports.CaptureRequest, _ <-chan ports.SecurityState, _ ports.OutputInstance) error {
96 <-ctx.Done()
97 return nil
98 })
99 if err := set.wait(); err != nil {
100 t.Fatal(err)
101 }
102}