1package headlessinput
2
3import (
4 "context"
5 "strings"
6 "testing"
7
8 "github.com/bnema/neferwl/internal/adapters/logging"
9 "github.com/bnema/neferwl/internal/adapters/xkb"
10 "github.com/bnema/neferwl/internal/ports"
11)
12
13func TestScript(t *testing.T) {
14 for _, tt := range []struct {
15 line string
16 names []string
17 codes []uint32
18 }{
19 {"type Hi!", []string{"Shift_L", "H", "H", "Shift_L", "i", "i", "Shift_L", "exclam", "exclam", "Shift_L"}, nil},
20 {"key Shift+A", []string{"Shift_L", "A", "A", "Shift_L"}, []uint32{42, 30, 30, 42}},
21 {"key Super+Return", []string{"Super_L", "Return", "Return", "Super_L"}, []uint32{125, 28, 28, 125}},
22 } {
23 t.Run(tt.line, func(t *testing.T) {
24 km, err := xkb.New(xkb.RMLVO{Layout: "us"})
25 if err != nil {
26 if strings.Contains(err.Error(), "libxkbcommon.so.0") {
27 t.Skip(err)
28 }
29 t.Fatal(err)
30 }
31 script := make(chan string, 1)
32 script <- tt.line
33 close(script)
34 input := make(chan ports.InputEvent, 32)
35 if err := Run(context.Background(), km, nil, script, input, nil, nil, logging.For(context.Background(), "input")); err != nil {
36 t.Fatal(err)
37 }
38 if len(input) != len(tt.names) {
39 t.Fatalf("events=%d want %d", len(input), len(tt.names))
40 }
41 for i, name := range tt.names {
42 ev := (<-input).(ports.KeyEvent)
43 if ev.Keysym != name {
44 t.Errorf("event %d: %s want %s", i, ev.Keysym, name)
45 }
46 if tt.codes != nil && ev.Keycode != tt.codes[i] {
47 t.Errorf("code %d: %d", i, ev.Keycode)
48 }
49 if tt.line == "key Super+Return" && i == 1 && ev.Mods&ports.ModSuper == 0 {
50 t.Error("Return missing Super")
51 }
52 if tt.line == "key Super+Return" && i == 3 && ev.Mods&ports.ModSuper != 0 {
53 t.Error("Super not released")
54 }
55 }
56 })
57 }
58}
59
60func TestPointerScript(t *testing.T) {
61 km, err := xkb.New(xkb.RMLVO{Layout: "us"})
62 if err != nil {
63 t.Skip(err)
64 }
65 script := make(chan string, 8)
66 for _, line := range []string{"move 12.5 45", "click", "down right", "up right", "swipe sideways", "swipe left", "swipe4 up"} {
67 script <- line
68 }
69 close(script)
70 input := make(chan ports.InputEvent, 32)
71 if err := Run(context.Background(), km, nil, script, input, nil, nil, logging.For(context.Background(), "input")); err != nil {
72 t.Fatal(err)
73 }
74 if len(input) != 5+2*(swipeSteps+2) {
75 t.Fatalf("events: %d", len(input))
76 }
77 m := (<-input).(ports.PointerMotion)
78 if m.X != 12.5 || m.Y != 45 {
79 t.Fatal(m)
80 }
81 for _, want := range []ports.PointerButton{{Button: 0x110, Pressed: true}, {Button: 0x110}, {Button: 0x111, Pressed: true}, {Button: 0x111}} {
82 got := (<-input).(ports.PointerButton)
83 if got.Button != want.Button || got.Pressed != want.Pressed {
84 t.Fatalf("got %+v want %+v", got, want)
85 }
86 }
87 begin := (<-input).(ports.SwipeBegin)
88 if begin.Fingers != 3 {
89 t.Fatal(begin)
90 }
91 last := begin.Time
92 for range swipeSteps {
93 u := (<-input).(ports.SwipeUpdate)
94 if u.DX >= 0 || u.DY != 0 || u.Time <= last {
95 t.Fatalf("update %+v after %v", u, last)
96 }
97 last = u.Time
98 }
99 if e := (<-input).(ports.SwipeEnd); e.Cancelled || e.Time != last {
100 t.Fatal(e)
101 }
102 if b := (<-input).(ports.SwipeBegin); b.Fingers != 4 {
103 t.Fatal(b)
104 }
105 if u := (<-input).(ports.SwipeUpdate); u.DY >= 0 || u.DX != 0 {
106 t.Fatal(u)
107 }
108}
109
110// The cursor is placed in target pixels: after the output transform.
111func TestMoveCursorTarget(t *testing.T) {
112 km, err := xkb.New(xkb.RMLVO{Layout: "us"})
113 if err != nil {
114 t.Skip(err)
115 }
116 layouts := make(chan ports.Layout)
117 script := make(chan string)
118 input := make(chan ports.InputEvent, 4)
119 type pos struct {
120 out string
121 x, y float64
122 }
123 moved := make(chan pos, 1)
124 done := make(chan error, 1)
125 go func() {
126 done <- Run(context.Background(), km, nil, script, input, layouts, func(o string, x, y float64) { moved <- pos{o, x, y} }, logging.For(context.Background(), "input"))
127 }()
128 // Unbuffered: the layout is taken before the move is read.
129 layouts <- ports.Layout{{Info: ports.OutputInfo{Name: "A", Width: 200, Height: 100}, Scale: 1, Transform: 1, Width: 100, Height: 200}}
130 script <- "move 10 20"
131 if got, want := <-moved, (pos{"A", 20, 90}); got != want {
132 t.Fatalf("got %+v want %+v", got, want)
133 }
134 close(script)
135 if err := <-done; err != nil {
136 t.Fatal(err)
137 }
138}