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, outputRoutes{})
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, feed outputFeed) error {
35 contents, returns, security := feed.Contents, feed.Returns, feed.Security
36 started <- seen{contents, returns, <-security}
37 <-ctx.Done()
38 return nil
39 }
40 attaches := 0
41 attacher.EXPECT().AttachOutput("DP-1").RunAndReturn(func(string) (<-chan *ports.ContentBatch, ports.ContentReturn) {
42 order = append(order, "attach")
43 ret := rets[attaches]
44 attaches++
45 // The output is registered with the security backend first.
46 if len(events) != 1 {
47 t.Errorf("attached with %d registrations queued, want 1", len(events))
48 }
49 return feed, ret
50 }).Times(2)
51 var detached []ports.ContentReturn
52 attacher.EXPECT().DetachOutput(mock.Anything).Run(func(feed ports.ContentReturn) {
53 order = append(order, "detach")
54 detached = append(detached, feed)
55 }).Times(2)
56 for round := range 2 {
57 if !set.start(ctx, "DP-1", run) {
58 t.Fatal("start failed")
59 }
60 got := <-started
61 if got.contents != (<-chan *ports.ContentBatch)(feed) || got.returns != ports.ContentReturn(rets[round]) {
62 t.Fatalf("round %d: output got another feed", round)
63 }
64 <-events
65 set.outs["DP-1"].stop()
66 if err := set.finish("DP-1"); err != nil {
67 t.Fatal(err)
68 }
69 }
70 for round := range detached {
71 if detached[round] != ports.ContentReturn(rets[round]) {
72 t.Fatalf("round %d: detached another feed than the one attached", round)
73 }
74 }
75 want := []string{"attach", "detach", "attach", "detach"}
76 if len(order) != len(want) {
77 t.Fatalf("order %v, want %v", order, want)
78 }
79 for i := range want {
80 if order[i] != want[i] {
81 t.Fatalf("order %v, want %v", order, want)
82 }
83 }
84}
85
86// wait detaches the outputs it stops.
87func TestOutputSetWaitDetachesContents(t *testing.T) {
88 ctx, cancel := context.WithCancel(context.Background())
89 defer cancel()
90 set := newOutputSet(ctx, nil, outputRoutes{})
91 attacher := newMockcontentAttacher(t)
92 set.attach = attacher
93 ret := portsmocks.NewMockContentReturn(t)
94 attacher.EXPECT().AttachOutput("A").Return(nil, ret).Once()
95 attacher.EXPECT().DetachOutput(ports.ContentReturn(ret)).Return().Once()
96 set.start(ctx, "A", func(ctx context.Context, feed outputFeed) error {
97 <-ctx.Done()
98 return nil
99 })
100 if err := set.wait(); err != nil {
101 t.Fatal(err)
102 }
103}