test(daemon): cover pane resize control targeting
- commit
- 9ebcbf7ac21daeefd0a495dde78bdd4e4ccf67e7
- author
- brice · · 2026-07-25 04:41 UTC
- parent
- cbd555e
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +49 -2
M internal/usecase/command/registry_test.go +7 -2
| @@ -117,8 +117,13 @@ func TestControlHandlersValidateAndDelegate(t *testing.T) { | |||
| 117 | 117 | }{ | |
| 118 | 118 | {name: "zero argument mutation delegates", slug: "split-right", wantCall: "split-right"}, | |
| 119 | 119 | {name: "zero argument mutation rejects args", slug: "split-right", args: []string{"extra"}, wantErr: ErrInvalidArguments}, | |
| 120 | - | {name: "resize mutation delegates", slug: "grow-pane-width", wantCall: "grow-pane-width"}, | |
| 121 | - | {name: "equalize mutation delegates", slug: "equalize-panes", wantCall: "equalize-panes"}, | |
| 120 | + | {name: "grow width delegates", slug: "grow-pane-width", wantCall: "grow-pane-width"}, | |
| 121 | + | {name: "shrink width delegates", slug: "shrink-pane-width", wantCall: "shrink-pane-width"}, | |
| 122 | + | {name: "grow height delegates", slug: "grow-pane-height", wantCall: "grow-pane-height"}, | |
| 123 | + | {name: "shrink height delegates", slug: "shrink-pane-height", wantCall: "shrink-pane-height"}, | |
| 124 | + | {name: "equalize delegates", slug: "equalize-panes", wantCall: "equalize-panes"}, | |
| 125 | + | {name: "resize mutation rejects arguments", slug: "grow-pane-width", args: []string{"extra"}, wantErr: ErrInvalidArguments}, | |
| 126 | + | {name: "equalize rejects arguments", slug: "equalize-panes", args: []string{"extra"}, wantErr: ErrInvalidArguments}, | |
| 122 | 127 | {name: "rename delegates name", slug: "rename-tab", args: []string{"editor"}, wantCall: "rename-tab:editor"}, | |
| 123 | 128 | {name: "rename rejects empty name", slug: "rename-tab", args: []string{""}, wantErr: ErrInvalidArguments}, | |
| 124 | 129 | {name: "toast delegates severity", slug: "toast", args: []string{"-l", "warn", "hello"}, wantCall: "toast:warn:hello"}, | |
| @@ -117,8 +117,13 @@ func TestControlHandlersValidateAndDelegate(t *testing.T) { | |||||
| 117 | }{ | 117 | }{ | ||
| 118 | {name: "zero argument mutation delegates", slug: "split-right", wantCall: "split-right"}, | 118 | {name: "zero argument mutation delegates", slug: "split-right", wantCall: "split-right"}, | ||
| 119 | {name: "zero argument mutation rejects args", slug: "split-right", args: []string{"extra"}, wantErr: ErrInvalidArguments}, | 119 | {name: "zero argument mutation rejects args", slug: "split-right", args: []string{"extra"}, wantErr: ErrInvalidArguments}, | ||
| 120 | - | {name: "resize mutation delegates", slug: "grow-pane-width", wantCall: "grow-pane-width"}, | 120 | + | {name: "grow width delegates", slug: "grow-pane-width", wantCall: "grow-pane-width"}, |
| 121 | - | {name: "equalize mutation delegates", slug: "equalize-panes", wantCall: "equalize-panes"}, | 121 | + | {name: "shrink width delegates", slug: "shrink-pane-width", wantCall: "shrink-pane-width"}, |
| 122 | + | {name: "grow height delegates", slug: "grow-pane-height", wantCall: "grow-pane-height"}, | |||
| 123 | + | {name: "shrink height delegates", slug: "shrink-pane-height", wantCall: "shrink-pane-height"}, | |||
| 124 | + | {name: "equalize delegates", slug: "equalize-panes", wantCall: "equalize-panes"}, | |||
| 125 | + | {name: "resize mutation rejects arguments", slug: "grow-pane-width", args: []string{"extra"}, wantErr: ErrInvalidArguments}, | |||
| 126 | + | {name: "equalize rejects arguments", slug: "equalize-panes", args: []string{"extra"}, wantErr: ErrInvalidArguments}, | |||
| 122 | {name: "rename delegates name", slug: "rename-tab", args: []string{"editor"}, wantCall: "rename-tab:editor"}, | 127 | {name: "rename delegates name", slug: "rename-tab", args: []string{"editor"}, wantCall: "rename-tab:editor"}, | ||
| 123 | {name: "rename rejects empty name", slug: "rename-tab", args: []string{""}, wantErr: ErrInvalidArguments}, | 128 | {name: "rename rejects empty name", slug: "rename-tab", args: []string{""}, wantErr: ErrInvalidArguments}, | ||
| 124 | {name: "toast delegates severity", slug: "toast", args: []string{"-l", "warn", "hello"}, wantCall: "toast:warn:hello"}, | 129 | {name: "toast delegates severity", slug: "toast", args: []string{"-l", "warn", "hello"}, wantCall: "toast:warn:hello"}, | ||
M internal/usecase/daemon/control_test.go +28 -0
| @@ -240,6 +240,34 @@ func TestHandleCommandSelfTargetsNonActiveTabAndPane(t *testing.T) { | |||
| 240 | 240 | active.mu.Unlock() | |
| 241 | 241 | invoking.mu.Lock() | |
| 242 | 242 | require.Len(t, invoking.panes, 2) | |
| 243 | + | beforeFocus := invoking.tree.Focus | |
| 244 | + | beforeGeneration := invoking.layoutGeneration | |
| 245 | + | invoking.mu.Unlock() | |
| 246 | + | ||
| 247 | + | grow := sendCommand(t, d, ports.CommandRequest{ | |
| 248 | + | Slug: "grow-pane-width", Self: true, | |
| 249 | + | TargetSession: "work", TargetTab: "t_invoking", TargetPane: "p_invoking", | |
| 250 | + | }) | |
| 251 | + | require.True(t, grow.OK, grow.Text) | |
| 252 | + | invoking.mu.Lock() | |
| 253 | + | require.Equal(t, beforeFocus, invoking.tree.Focus, "targeted resize must not refocus the tab") | |
| 254 | + | require.Equal(t, beforeGeneration+1, invoking.layoutGeneration) | |
| 255 | + | require.NotZero(t, invoking.tree.Root.Children[0].Weight, "resize must update the invoking tab's shares") | |
| 256 | + | invoking.mu.Unlock() | |
| 257 | + | active.mu.Lock() | |
| 258 | + | require.Len(t, active.panes, 1, "--self resize must not mutate the active tab") | |
| 259 | + | active.mu.Unlock() | |
| 260 | + | ||
| 261 | + | equalize := sendCommand(t, d, ports.CommandRequest{ | |
| 262 | + | Slug: "equalize-panes", Self: true, | |
| 263 | + | TargetSession: "work", TargetTab: "t_invoking", TargetPane: "p_invoking", | |
| 264 | + | }) | |
| 265 | + | require.True(t, equalize.OK, equalize.Text) | |
| 266 | + | invoking.mu.Lock() | |
| 267 | + | require.Equal(t, beforeFocus, invoking.tree.Focus, "targeted equalize must not refocus the tab") | |
| 268 | + | for _, child := range invoking.tree.Root.Children { | |
| 269 | + | require.Zero(t, child.Weight, "equalize must clear each target-tab share") | |
| 270 | + | } | |
| 243 | 271 | invoking.mu.Unlock() | |
| 244 | 272 | } | |
| 245 | 273 | ||
| @@ -240,6 +240,34 @@ func TestHandleCommandSelfTargetsNonActiveTabAndPane(t *testing.T) { | |||||
| 240 | active.mu.Unlock() | 240 | active.mu.Unlock() | ||
| 241 | invoking.mu.Lock() | 241 | invoking.mu.Lock() | ||
| 242 | require.Len(t, invoking.panes, 2) | 242 | require.Len(t, invoking.panes, 2) | ||
| 243 | + | beforeFocus := invoking.tree.Focus | |||
| 244 | + | beforeGeneration := invoking.layoutGeneration | |||
| 245 | + | invoking.mu.Unlock() | |||
| 246 | + | ||||
| 247 | + | grow := sendCommand(t, d, ports.CommandRequest{ | |||
| 248 | + | Slug: "grow-pane-width", Self: true, | |||
| 249 | + | TargetSession: "work", TargetTab: "t_invoking", TargetPane: "p_invoking", | |||
| 250 | + | }) | |||
| 251 | + | require.True(t, grow.OK, grow.Text) | |||
| 252 | + | invoking.mu.Lock() | |||
| 253 | + | require.Equal(t, beforeFocus, invoking.tree.Focus, "targeted resize must not refocus the tab") | |||
| 254 | + | require.Equal(t, beforeGeneration+1, invoking.layoutGeneration) | |||
| 255 | + | require.NotZero(t, invoking.tree.Root.Children[0].Weight, "resize must update the invoking tab's shares") | |||
| 256 | + | invoking.mu.Unlock() | |||
| 257 | + | active.mu.Lock() | |||
| 258 | + | require.Len(t, active.panes, 1, "--self resize must not mutate the active tab") | |||
| 259 | + | active.mu.Unlock() | |||
| 260 | + | ||||
| 261 | + | equalize := sendCommand(t, d, ports.CommandRequest{ | |||
| 262 | + | Slug: "equalize-panes", Self: true, | |||
| 263 | + | TargetSession: "work", TargetTab: "t_invoking", TargetPane: "p_invoking", | |||
| 264 | + | }) | |||
| 265 | + | require.True(t, equalize.OK, equalize.Text) | |||
| 266 | + | invoking.mu.Lock() | |||
| 267 | + | require.Equal(t, beforeFocus, invoking.tree.Focus, "targeted equalize must not refocus the tab") | |||
| 268 | + | for _, child := range invoking.tree.Root.Children { | |||
| 269 | + | require.Zero(t, child.Weight, "equalize must clear each target-tab share") | |||
| 270 | + | } | |||
| 243 | invoking.mu.Unlock() | 271 | invoking.mu.Unlock() | ||
| 244 | } | 272 | } | ||
| 245 | 273 | ||||
M internal/usecase/daemon/resize_mode_test.go +14 -0
| @@ -83,6 +83,20 @@ func TestResizeControlHeadlessAndErrors(t *testing.T) { | |||
| 83 | 83 | require.Empty(t, equalized.Output) | |
| 84 | 84 | } | |
| 85 | 85 | ||
| 86 | + | func TestEnterResizeModeRejectsPaneOutsideSplit(t *testing.T) { | |
| 87 | + | factory := &controlPTYFactory{} | |
| 88 | + | d := newTestDaemon(t, factory, stubClock{}) | |
| 89 | + | t.Cleanup(func() { factory.close(); d.sessWg.Wait() }) | |
| 90 | + | sess := addControlSession(d, "work", "t_work", "p_work") | |
| 91 | + | ac := &attachedClient{output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | |
| 92 | + | ac.initOverlays() | |
| 93 | + | ac.setSession(sess) | |
| 94 | + | ||
| 95 | + | err := d.enterResizeMode(sess, ac) | |
| 96 | + | require.ErrorIs(t, err, layout.ErrNotInSplit) | |
| 97 | + | require.False(t, ac.overlays.resizeModeActive()) | |
| 98 | + | } | |
| 99 | + | ||
| 86 | 100 | func TestResizeModeStateRenderingAndMouseConsumption(t *testing.T) { | |
| 87 | 101 | factory := &controlPTYFactory{} | |
| 88 | 102 | d := newTestDaemon(t, factory, stubClock{}) | |
| @@ -83,6 +83,20 @@ func TestResizeControlHeadlessAndErrors(t *testing.T) { | |||||
| 83 | require.Empty(t, equalized.Output) | 83 | require.Empty(t, equalized.Output) | ||
| 84 | } | 84 | } | ||
| 85 | 85 | ||||
| 86 | + | func TestEnterResizeModeRejectsPaneOutsideSplit(t *testing.T) { | |||
| 87 | + | factory := &controlPTYFactory{} | |||
| 88 | + | d := newTestDaemon(t, factory, stubClock{}) | |||
| 89 | + | t.Cleanup(func() { factory.close(); d.sessWg.Wait() }) | |||
| 90 | + | sess := addControlSession(d, "work", "t_work", "p_work") | |||
| 91 | + | ac := &attachedClient{output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | |||
| 92 | + | ac.initOverlays() | |||
| 93 | + | ac.setSession(sess) | |||
| 94 | + | ||||
| 95 | + | err := d.enterResizeMode(sess, ac) | |||
| 96 | + | require.ErrorIs(t, err, layout.ErrNotInSplit) | |||
| 97 | + | require.False(t, ac.overlays.resizeModeActive()) | |||
| 98 | + | } | |||
| 99 | + | ||||
| 86 | func TestResizeModeStateRenderingAndMouseConsumption(t *testing.T) { | 100 | func TestResizeModeStateRenderingAndMouseConsumption(t *testing.T) { | ||
| 87 | factory := &controlPTYFactory{} | 101 | factory := &controlPTYFactory{} | ||
| 88 | d := newTestDaemon(t, factory, stubClock{}) | 102 | d := newTestDaemon(t, factory, stubClock{}) | ||