1package visual
2
3import (
4 "testing"
5 "time"
6 "unicode/utf8"
7
8 "github.com/bnema/neferclient"
9 "github.com/stretchr/testify/require"
10)
11
12func TestFadeAlphaInterpolatesAndArrives(t *testing.T) {
13 t0 := time.Unix(100, 0)
14 a, done := fadeAlpha(0, 1, t0, 2*time.Second, t0.Add(time.Second))
15 require.InDelta(t, 0.5, a, 1e-9)
16 require.False(t, done)
17 a, done = fadeAlpha(0, 1, t0, 2*time.Second, t0.Add(3*time.Second))
18 require.Equal(t, 1.0, a)
19 require.True(t, done)
20 _, done = fadeAlpha(1, 0, t0, 0, t0)
21 require.True(t, done)
22}
23
24// Surfaces share the model but draw at different instants: a surface that
25// drew just after the end must not stop redraws for one that drew just before.
26func TestFadeKeepsTickingUntilEverySurfaceDrewTheEnd(t *testing.T) {
27 m := newFadeModel()
28 m.send(fadeCmd{black: true, dur: time.Second})
29 t0 := m.base.Add(time.Minute)
30 a, done := m.step(t0)
31 require.Equal(t, 0.0, a)
32 require.False(t, done)
33
34 before := t0.Add(time.Second - time.Millisecond)
35 after := t0.Add(time.Second + time.Millisecond)
36 _, done = m.step(before) // surface A
37 require.False(t, done)
38 a, done = m.step(after) // surface B reaches the end
39 require.Equal(t, 1.0, a)
40 require.True(t, done)
41 require.True(t, m.animatingAt(after), "surface A still needs a redraw")
42 require.False(t, m.animatingAt(t0.Add(time.Hour)))
43}
44
45func TestFadeLayerIsClickThroughOverlayOnEveryOutput(t *testing.T) {
46 l := fadeLayer("DP-1")
47 require.Equal(t, "DP-1", l.Output)
48 require.Equal(t, neferclient.LayerOverlay, l.Level)
49 require.Equal(t, neferclient.KeyboardNone, l.Keyboard)
50 require.Equal(t, int32(-1), l.ExclusiveZone)
51 require.NotNil(t, l.InputRects)
52 require.Empty(t, l.InputRects)
53 all := neferclient.AnchorTop | neferclient.AnchorBottom | neferclient.AnchorLeft | neferclient.AnchorRight
54 require.Equal(t, all, l.Anchors)
55}
56
57func TestHintsShowStatusThenPromptNotesAndFitTheBox(t *testing.T) {
58 for _, tc := range []struct {
59 s lockStatus
60 p promptKind
61 want string
62 }{
63 {lockFailed, promptFallback, "Denied"},
64 {lockFailed, promptMore, "Denied"},
65 {lockBusy, promptPIN, "..."},
66 {lockIdle, promptFallback, "PIN off, use password"},
67 {lockIdle, promptUnavailable, "Authentication unavailable"},
68 {lockIdle, promptMore, "Enter again"},
69 {lockIdle, promptPassword, " "},
70 {lockIdle, promptPIN, " "},
71 } {
72 got := hintFor(tc.s, tc.p)
73 require.Equal(t, tc.want, got)
74 require.LessOrEqual(t, utf8.RuneCountInString(got), maxHintRunes, got)
75 }
76}
77
78func TestMaskIsCappedToTheField(t *testing.T) {
79 mask, _ := maskFor(lockState{Mask: 3}, promptPIN)
80 require.Equal(t, "●●●", mask)
81 mask, _ = maskFor(lockState{Mask: 3}, promptFallback)
82 require.Equal(t, "•••_", mask)
83 mask, _ = maskFor(lockState{Mask: 512}, promptPIN)
84 require.Equal(t, maxPINMask, utf8.RuneCountInString(mask))
85 mask, _ = maskFor(lockState{Mask: 512}, promptPassword)
86 require.Equal(t, maxPasswordMask+1, utf8.RuneCountInString(mask))
87}
88
89func TestFieldsAreCenteredInTheBox(t *testing.T) {
90 for _, w := range []int{passwordW, pinW} {
91 outer := w + 2*fieldPadX + fieldEdge
92 require.Equal(t, boxContentW-outer-fieldMargin(w), fieldMargin(w), w)
93 }
94}
95
96func TestClockWakeStopsWhenDone(t *testing.T) {
97 done := make(chan struct{})
98 stopped := make(chan struct{})
99 go func() { defer close(stopped); clockWake(done, make(chan struct{})) }()
100 close(done)
101 <-stopped
102}