1package core
2
3import (
4 "context"
5 "testing"
6 "time"
7
8 "github.com/bnema/neferwl/internal/ports"
9)
10
11func TestArrangeLayers(t *testing.T) {
12 bar := ports.LayerSurface{ID: 1, Layer: ports.LayerTop, Anchor: ports.AnchorTop | ports.AnchorLeft | ports.AnchorRight, Width: 1920, Height: 30, ExclusiveZone: 30}
13 tests := []struct {
14 name string
15 layers []ports.LayerSurface
16 rects []ports.Rect
17 usable ports.Rect
18 }{
19 {"top", []ports.LayerSurface{bar}, []ports.Rect{{W: 1920, H: 30}}, ports.Rect{Y: 30, W: 1920, H: 1050}},
20 {"bottom margin", []ports.LayerSurface{{Anchor: ports.AnchorBottom, Width: 100, Height: 20, ExclusiveZone: 20, Margin: [4]int32{0, 0, 5, 0}}}, []ports.Rect{{X: 910, Y: 1055, W: 100, H: 20}}, ports.Rect{W: 1920, H: 1055}},
21 {"left dock", []ports.LayerSurface{{Anchor: ports.AnchorLeft | ports.AnchorTop | ports.AnchorBottom, Width: 40, Height: 1080, ExclusiveZone: 40}}, []ports.Rect{{W: 40, H: 1080}}, ports.Rect{X: 40, W: 1880, H: 1080}},
22 {"center", []ports.LayerSurface{{Width: 100, Height: 40, Layer: ports.LayerOverlay}}, []ports.Rect{{X: 910, Y: 520, W: 100, H: 40}}, ports.Rect{W: 1920, H: 1080}},
23 {"opposing horizontal anchors with margins", []ports.LayerSurface{{Anchor: ports.AnchorLeft | ports.AnchorRight, Width: 100, Height: 20, Margin: [4]int32{0, 30, 0, 10}}}, []ports.Rect{{X: 900, Y: 530, W: 100, H: 20}}, ports.Rect{W: 1920, H: 1080}},
24 {"top only", []ports.LayerSurface{{Anchor: ports.AnchorTop, Width: 100, Height: 20, Margin: [4]int32{7, 0, 0, 0}}}, []ports.Rect{{X: 910, Y: 7, W: 100, H: 20}}, ports.Rect{W: 1920, H: 1080}},
25 {"full span between margins", []ports.LayerSurface{{Anchor: ports.AnchorLeft | ports.AnchorRight | ports.AnchorTop, Height: 20, Margin: [4]int32{7, 30, 0, 10}}}, []ports.Rect{{X: 10, Y: 7, W: 1880, H: 20}}, ports.Rect{W: 1920, H: 1080}},
26 {"opposing vertical anchors with margins", []ports.LayerSurface{{Anchor: ports.AnchorTop | ports.AnchorBottom, Width: 100, Height: 20, Margin: [4]int32{10, 0, 30, 0}}}, []ports.Rect{{X: 910, Y: 520, W: 100, H: 20}}, ports.Rect{W: 1920, H: 1080}},
27 {"minus one", []ports.LayerSurface{bar, {Layer: ports.LayerTop, Anchor: ports.AnchorTop, Width: 100, Height: 20, ExclusiveZone: -1}}, []ports.Rect{{W: 1920, H: 30}, {X: 910, W: 100, H: 20}}, ports.Rect{Y: 30, W: 1920, H: 1050}},
28 {"stack", []ports.LayerSurface{bar, {Layer: ports.LayerTop, Anchor: bar.Anchor, Width: 1920, Height: 20, ExclusiveZone: 20}}, []ports.Rect{{W: 1920, H: 30}, {Y: 30, W: 1920, H: 20}}, ports.Rect{Y: 50, W: 1920, H: 1030}},
29 }
30 for _, tt := range tests {
31 t.Run(tt.name, func(t *testing.T) {
32 got, u := arrangeLayers(1920, 1080, tt.layers)
33 if u != tt.usable || len(got) != len(tt.rects) {
34 t.Fatalf("got %v %v", got, u)
35 }
36 for i, r := range tt.rects {
37 if got[i].Rect != r {
38 t.Fatalf("rect %d: %v want %v", i, got[i].Rect, r)
39 }
40 }
41 })
42 }
43 layers := []ports.LayerSurface{{ID: 1, Layer: ports.LayerOverlay}, {ID: 2, Layer: ports.LayerBottom}, {ID: 3, Layer: ports.LayerBackground}, {ID: 4, Layer: ports.LayerTop}, {ID: 5, Layer: ports.LayerBottom}}
44 got, _ := arrangeLayers(100, 100, layers)
45 for i, id := range []ports.WindowID{3, 2, 5, 4, 1} {
46 if got[i].ID != id {
47 t.Fatalf("order: %v", got)
48 }
49 }
50}
51
52func TestLayerChangedChannels(t *testing.T) {
53 client := make(chan ports.ClientEvent)
54 output := make(chan ports.OutputEvent)
55 commands := make(chan ports.ClientCommand, 16)
56 scenes := make(chan []ports.Scene, 1)
57 cfg := ports.Config{}
58 cfg.Keyboard.CmdKey = "super"
59 cfg.Layout.MaxColumns = 2
60 c, err := New(cfg, Channels{Client: client, Output: output, Commands: commands, Scenes: scenes})
61 if err != nil {
62 t.Fatal(err)
63 }
64 ctx, cancel := context.WithCancel(context.Background())
65 defer cancel()
66 done := make(chan struct{})
67 go func() { defer close(done); _ = c.Run(ctx) }()
68 recv := func() ports.Scene {
69 t.Helper()
70 select {
71 case s := <-scenes:
72 return s[0]
73 case <-time.After(time.Second):
74 t.Fatal("scene timeout")
75 return ports.Scene{}
76 }
77 }
78 output <- ports.OutputAdded{Info: ports.OutputInfo{Name: "OUT-1", Width: 1920, Height: 1080}}
79 recv()
80 client <- ports.WindowMapped{ID: 1}
81 before := recv()
82 for len(commands) > 0 {
83 <-commands
84 }
85 bar := ports.LayerSurface{ID: 2, Layer: ports.LayerTop, Anchor: ports.AnchorTop | ports.AnchorLeft | ports.AnchorRight, Width: 1920, Height: 30, ExclusiveZone: 30}
86 client <- ports.LayerChanged{Layers: []ports.LayerSurface{bar}}
87 scene := recv()
88 if len(scene.Layers) != 1 || scene.Layers[0].Rect != (ports.Rect{W: 1920, H: 30}) || len(scene.Windows) != 1 || scene.Windows[0].Rect.Y < 30 {
89 t.Fatalf("scene: %v", scene)
90 }
91 if scene.Windows[0].Rect.H != before.Windows[0].Rect.H-30 {
92 t.Fatalf("height: %v vs %v", scene.Windows, before.Windows)
93 }
94 select {
95 case v := <-commands:
96 cfg, ok := v.(ports.ConfigureWindow)
97 if !ok || cfg.ID != 1 || cfg.Height != scene.Windows[0].Rect.H {
98 t.Fatalf("configure: %v", v)
99 }
100 case <-time.After(time.Second):
101 t.Fatal("configure timeout")
102 }
103 scene.Layers[0].Rect.Y = 99
104 client <- ports.LayerChanged{Layers: []ports.LayerSurface{bar}}
105 if next := recv(); next.Layers[0].Rect.Y != 0 {
106 t.Fatalf("scene alias: %v", next)
107 }
108 cancel()
109 <-done
110}