spawn_placement_test.go RawHistoryFolder
1package core
2
3import (
4 "context"
5 "strings"
6 "testing"
7 "time"
8
9 "github.com/bnema/neferwl/internal/ports"
10)
11
12// A monitor can lose a named workspace while its slot is still configured.
13// The claim must remain pending, but its window follows ordinary placement.
14func TestSlotClaimWithoutWorkspace(t *testing.T) {
15 // A config message would remove both the workspace and its slot.
16 // Exercise the registry's missing-destination branch directly instead.
17 cfg := ports.Config{}
18 cfg.Keyboard.CmdKey = "alt"
19 cfg.Layout.MaxColumns = 1
20 c, err := New(cfg, Channels{Scenes: make(chan []ports.Scene, 1)})
21 if err != nil {
22 t.Fatal(err)
23 }
24 key := slotKey{workspace: "gone", index: 1}
25 c.slots[key] = &slotState{argv: []string{"foot"}}
26 req := c.spawnSlot(key)
27 token := strings.TrimPrefix(req.Env[0], ports.SlotEnv+"=")
28 c.placement.place(c, ports.WindowMapped{ID: 21, Slot: token})
29 if !c.placement.slotPending(key) {
30 t.Fatal("unplaceable slot consumed its token")
31 }
32 if s, _ := c.screenOf(21); s == nil {
33 t.Fatal("window did not map normally")
34 }
35 // Claim status is also reflected on the client command port.
36 commands := make(chan ports.ClientCommand, 1)
37 c.ch.Commands = commands
38 if err := c.publishPending(context.Background()); err != nil {
39 t.Fatal(err)
40 }
41 if got := (<-commands).(ports.SlotsPending); !got.Pending {
42 t.Fatal("pending claim was not retained")
43 }
44}
45
46func TestExpiredTerminalTokenMapsNormally(t *testing.T) {
47 cfg := ports.Config{}
48 cfg.Keyboard.CmdKey = "alt"
49 cfg.Layout.MaxColumns = 1
50 c, err := New(cfg, Channels{Scenes: make(chan []ports.Scene, 1)})
51 if err != nil {
52 t.Fatal(err)
53 }
54 // Keep a different, focused workspace for the ordinary-window fallback.
55 c.cur().mon.AddWindow(1)
56 target := c.cur().mon.Current()
57 c.cur().mon.show(c.cur().mon.Workspaces[1])
58 focused := c.cur().mon.Current()
59 if focused == target {
60 t.Fatal("focus did not move to another workspace")
61 }
62 t0 := time.Unix(123, 0)
63 req := c.placement.request([]string{"foot"}, spawnTarget{terminal: target, at: t0})
64 token := strings.TrimPrefix(req.Env[0], ports.SlotEnv+"=")
65 c.placement.expireTerminals(t0.Add(termRetry))
66 if _, ok := c.placement.pending[token]; !ok {
67 t.Fatal("claim expired at retry boundary")
68 }
69 c.placement.expireTerminals(t0.Add(termRetry + time.Nanosecond))
70 if _, ok := c.placement.pending[token]; ok {
71 t.Fatal("claim survived past retry boundary")
72 }
73 c.placement.place(c, ports.WindowMapped{ID: 51, Slot: token})
74 if _, w := c.screenOf(51); w != focused || target.has(51) {
75 t.Fatal("expired token did not map onto focused workspace")
76 }
77}