1package app
2
3import (
4 "bytes"
5 "context"
6 "errors"
7 "io"
8 "testing"
9
10 "github.com/stretchr/testify/require"
11
12 "github.com/bnema/nefercap/internal/adapters/wayland"
13 "github.com/bnema/nefercap/internal/adapters/wayland/testserver"
14 "github.com/bnema/nefercap/internal/logging"
15 "github.com/bnema/nefercap/internal/ports"
16)
17
18func sourceFor(t *testing.T, cfg testserver.Config) (context.Context, *bytes.Buffer, *wayland.Source, ports.Target) {
19 t.Helper()
20 var logs bytes.Buffer
21 ctx := logging.With(context.Background(), &logs, false)
22 srv := testserver.Start(t, cfg)
23 source, err := wayland.New(ctx, srv.Path)
24 require.NoError(t, err)
25 t.Cleanup(func() { _ = source.Close() })
26 outs, err := source.Outputs(ctx)
27 require.NoError(t, err)
28 return ctx, &logs, source, ports.Target{OutputID: outs[0].ID}
29}
30
31func TestStartExclusionWithExtension(t *testing.T) {
32 ctx, _, source, target := sourceFor(t, testserver.Config{NeferwlExclusion: true})
33 token, withHUD, err := startExclusion(ctx, io.Discard, source, target)
34 require.NoError(t, err)
35 require.True(t, withHUD)
36 require.Equal(t, testserver.Token, token)
37}
38
39func TestStartExclusionWithoutExtensionHasNoHUD(t *testing.T) {
40 ctx, _, source, target := sourceFor(t, testserver.Config{})
41 token, withHUD, err := startExclusion(ctx, io.Discard, source, target)
42 require.NoError(t, err)
43 require.False(t, withHUD)
44 require.Empty(t, token)
45}
46
47// A busy exclusion records without HUD and logs it at warn.
48func TestStartExclusionBusyFallsBackWithoutHUD(t *testing.T) {
49 ctx, logs, source, target := sourceFor(t, testserver.Config{NeferwlExclusion: true, ExclusionFail: 1})
50 var notices bytes.Buffer
51 token, withHUD, err := startExclusion(ctx, ¬ices, source, target)
52 require.NoError(t, err)
53 require.False(t, withHUD)
54 require.Empty(t, token)
55 require.Contains(t, notices.String(), "recording without indicator")
56 require.Contains(t, logs.String(), `"level":"warn"`)
57 require.Contains(t, logs.String(), "recording without indicator")
58}
59
60// Other failures stay fatal.
61func TestStartExclusionOtherErrorsAreFatal(t *testing.T) {
62 ctx, _, source, _ := sourceFor(t, testserver.Config{NeferwlExclusion: true})
63 _, _, err := startExclusion(ctx, io.Discard, source, ports.Target{OutputID: 9999})
64 require.ErrorIs(t, err, ports.ErrOutputNotFound)
65 require.False(t, errors.Is(err, wayland.ErrExclusionUnavailable))
66}
67
68// Any other refusal reason is fatal too, never a fallback.
69func TestStartExclusionOtherReasonIsFatal(t *testing.T) {
70 ctx, _, source, target := sourceFor(t, testserver.Config{NeferwlExclusion: true, ExclusionFail: 4})
71 var notices bytes.Buffer
72 _, withHUD, err := startExclusion(ctx, ¬ices, source, target)
73 require.ErrorIs(t, err, wayland.ErrExclusionUnavailable)
74 require.False(t, errors.Is(err, wayland.ErrExclusionBusy))
75 require.False(t, withHUD)
76 require.Empty(t, notices.String())
77}
78
79// A session the compositor stops is fatal, not a fallback: no second session.
80func TestStartExclusionStoppedSessionIsFatal(t *testing.T) {
81 ctx, _, source, target := sourceFor(t, testserver.Config{NeferwlExclusion: true, ExclusionFail: 2})
82 _, _, err := startExclusion(ctx, io.Discard, source, target)
83 require.ErrorIs(t, err, ports.ErrCaptureStopped)
84 require.False(t, errors.Is(err, wayland.ErrExclusionUnavailable))
85}
86
87// A compositor that refuses the client stops the session at creation: the
88// error says so and nothing retries.
89func TestStartExclusionRefusedClientIsFatal(t *testing.T) {
90 ctx, _, source, target := sourceFor(t, testserver.Config{NeferwlExclusion: true, StopAtCreate: true})
91 _, _, err := startExclusion(ctx, io.Discard, source, target)
92 require.ErrorIs(t, err, ports.ErrCaptureStopped)
93 require.ErrorContains(t, err, "/etc/neferwl/capture-allow")
94}