refactor(daemon): drop the legacy attachment size field and clock fallbacks
attachedClient now keeps its window size only in geometry. noteAttention reads the daemon clock without a wall-clock fallback, and the test stub clock reports a fixed non-zero instant. The test-only Daemon.childEnv helper is removed in favour of childEnvFrom.
- commit
- 6fb053b192f640d001033c260df797a03c5b0802
- author
- brice · · 2026-10-08 05:03 UTC
- parent
- 4c16dd8
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
31 files · +97 -108
M internal/usecase/daemon/attachment_effect_gate_test.go +10 -10
| @@ -198,11 +198,11 @@ func TestKillSessionAcquisitionTimeoutDoesNotPublishPartialInvalidation(t *testi | |||
| 198 | 198 | first.replaceTransport(firstTransport) | |
| 199 | 199 | ||
| 200 | 200 | blockedTransport := &closeTrackingTransport{} | |
| 201 | - | blocked := &attachedClient{tr: blockedTransport, output: newOutputStateStream(), size: first.size} | |
| 201 | + | blocked := &attachedClient{tr: blockedTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: first.geometry.Size}} | |
| 202 | 202 | blocked.initOverlays() | |
| 203 | 203 | blocked.setSession(sess) | |
| 204 | 204 | laterTransport := &closeTrackingTransport{} | |
| 205 | - | later := &attachedClient{tr: laterTransport, output: newOutputStateStream(), size: first.size} | |
| 205 | + | later := &attachedClient{tr: laterTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: first.geometry.Size}} | |
| 206 | 206 | later.initOverlays() | |
| 207 | 207 | later.setSession(sess) | |
| 208 | 208 | sess.mu.Lock() | |
| @@ -320,7 +320,7 @@ func TestKillSessionInterruptsOnlyExactParticipantBlockedSends(t *testing.T) { | |||
| 320 | 320 | snatched.installTestAttachmentCapability(sess.captureAttachmentCapability(snatched, snatchedTransport)) | |
| 321 | 321 | ||
| 322 | 322 | activeTransport := newTeardownBlockingTransport() | |
| 323 | - | active := &attachedClient{tr: activeTransport, output: newOutputStateStream(), size: snatched.size} | |
| 323 | + | active := &attachedClient{tr: activeTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: snatched.geometry.Size}} | |
| 324 | 324 | active.initOverlays() | |
| 325 | 325 | transition, err := d.transitionAttachment(attachmentTransitionRequest{ | |
| 326 | 326 | target: sess, next: active, expectedTransport: active.transportSnapshot(), ready: true, | |
| @@ -329,7 +329,7 @@ func TestKillSessionInterruptsOnlyExactParticipantBlockedSends(t *testing.T) { | |||
| 329 | 329 | defer d.deferAttachmentTransitionCleanups(transition) | |
| 330 | 330 | ||
| 331 | 331 | unrelatedTransport := &closeTrackingTransport{} | |
| 332 | - | unrelatedClient := &attachedClient{tr: unrelatedTransport, output: newOutputStateStream(), size: active.size} | |
| 332 | + | unrelatedClient := &attachedClient{tr: unrelatedTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: active.geometry.Size}} | |
| 333 | 333 | unrelatedClient.initOverlays() | |
| 334 | 334 | unrelated := &session{sessionCore: sessionCore{id: "unrelated", name: "unrelated", attachments: map[*attachedClient]struct{}{unrelatedClient: {}}}, ctx: sess.ctx, cancel: func() {}} | |
| 335 | 335 | unrelatedClient.setSession(unrelated) | |
| @@ -505,7 +505,7 @@ func TestAttachmentEffectGateReplacementInterruptsBlockedOldRenderBeforePublicat | |||
| 505 | 505 | <-oldTransport.sendEntered | |
| 506 | 506 | ||
| 507 | 507 | newTransport := &closeTrackingTransport{} | |
| 508 | - | next := &attachedClient{tr: newTransport, output: newOutputStateStream(), size: old.size} | |
| 508 | + | next := &attachedClient{tr: newTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: old.geometry.Size}} | |
| 509 | 509 | next.initOverlays() | |
| 510 | 510 | transitionDone := make(chan attachmentTransitionResult, 1) | |
| 511 | 511 | transitionErr := make(chan error, 1) | |
| @@ -554,7 +554,7 @@ func TestAttachmentEffectGateAdmittedEffectCompletesBeforeConflictingTransition( | |||
| 554 | 554 | ticket, ok := old.beginAttachmentEffect(token) | |
| 555 | 555 | require.True(t, ok) | |
| 556 | 556 | ||
| 557 | - | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), size: old.size} | |
| 557 | + | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), geometry: domain.Geometry{Size: old.geometry.Size}} | |
| 558 | 558 | next.initOverlays() | |
| 559 | 559 | done := make(chan error, 1) | |
| 560 | 560 | go func() { | |
| @@ -668,7 +668,7 @@ func TestAttachmentEffectGateAdmittedActiveEffectsFinishBeforeReplacement(t *tes | |||
| 668 | 668 | }() | |
| 669 | 669 | <-admitted | |
| 670 | 670 | ||
| 671 | - | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), size: old.size} | |
| 671 | + | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), geometry: domain.Geometry{Size: old.geometry.Size}} | |
| 672 | 672 | next.initOverlays() | |
| 673 | 673 | transitionDone := make(chan error, 1) | |
| 674 | 674 | go func() { | |
| @@ -817,7 +817,7 @@ func TestPickerDeleteDoesNotDeleteSourceAfterInitiatorReplacement(t *testing.T) | |||
| 817 | 817 | }() | |
| 818 | 818 | <-admissionEnded | |
| 819 | 819 | ||
| 820 | - | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), size: old.size} | |
| 820 | + | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), geometry: domain.Geometry{Size: old.geometry.Size}} | |
| 821 | 821 | next.initOverlays() | |
| 822 | 822 | result, err := d.transitionAttachment(attachmentTransitionRequest{ | |
| 823 | 823 | target: sess, next: next, expectedTransport: next.transportSnapshot(), ready: true, | |
| @@ -873,7 +873,7 @@ func TestAttachmentEffectGateAdmittedFirstPaintFinishesBeforeReplacement(t *test | |||
| 873 | 873 | go func() { paintDone <- d.firstPaintForTransition(token) }() | |
| 874 | 874 | <-admitted | |
| 875 | 875 | ||
| 876 | - | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), size: old.size} | |
| 876 | + | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), geometry: domain.Geometry{Size: old.geometry.Size}} | |
| 877 | 877 | next.initOverlays() | |
| 878 | 878 | transitionDone := make(chan error, 1) | |
| 879 | 879 | go func() { | |
| @@ -897,7 +897,7 @@ func TestAttachmentEffectGateReversedConcurrentTransitionsDoNotDeadlock(t *testi | |||
| 897 | 897 | first.captureAttachmentCapability(a, aTransport) | |
| 898 | 898 | ||
| 899 | 899 | bTransport := newDatagramTestTransport() | |
| 900 | - | b := &attachedClient{tr: bTransport, output: newOutputStateStream(), size: a.size} | |
| 900 | + | b := &attachedClient{tr: bTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: a.geometry.Size}} | |
| 901 | 901 | b.initOverlays() | |
| 902 | 902 | second := &session{sessionCore: sessionCore{id: "second", name: "second", attachments: map[*attachedClient]struct{}{b: {}}}} | |
| 903 | 903 | b.setSession(second) | |
| @@ -198,11 +198,11 @@ func TestKillSessionAcquisitionTimeoutDoesNotPublishPartialInvalidation(t *testi | |||||
| 198 | first.replaceTransport(firstTransport) | 198 | first.replaceTransport(firstTransport) | ||
| 199 | 199 | ||||
| 200 | blockedTransport := &closeTrackingTransport{} | 200 | blockedTransport := &closeTrackingTransport{} | ||
| 201 | - | blocked := &attachedClient{tr: blockedTransport, output: newOutputStateStream(), size: first.size} | 201 | + | blocked := &attachedClient{tr: blockedTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: first.geometry.Size}} |
| 202 | blocked.initOverlays() | 202 | blocked.initOverlays() | ||
| 203 | blocked.setSession(sess) | 203 | blocked.setSession(sess) | ||
| 204 | laterTransport := &closeTrackingTransport{} | 204 | laterTransport := &closeTrackingTransport{} | ||
| 205 | - | later := &attachedClient{tr: laterTransport, output: newOutputStateStream(), size: first.size} | 205 | + | later := &attachedClient{tr: laterTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: first.geometry.Size}} |
| 206 | later.initOverlays() | 206 | later.initOverlays() | ||
| 207 | later.setSession(sess) | 207 | later.setSession(sess) | ||
| 208 | sess.mu.Lock() | 208 | sess.mu.Lock() | ||
| @@ -320,7 +320,7 @@ func TestKillSessionInterruptsOnlyExactParticipantBlockedSends(t *testing.T) { | |||||
| 320 | snatched.installTestAttachmentCapability(sess.captureAttachmentCapability(snatched, snatchedTransport)) | 320 | snatched.installTestAttachmentCapability(sess.captureAttachmentCapability(snatched, snatchedTransport)) | ||
| 321 | 321 | ||||
| 322 | activeTransport := newTeardownBlockingTransport() | 322 | activeTransport := newTeardownBlockingTransport() | ||
| 323 | - | active := &attachedClient{tr: activeTransport, output: newOutputStateStream(), size: snatched.size} | 323 | + | active := &attachedClient{tr: activeTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: snatched.geometry.Size}} |
| 324 | active.initOverlays() | 324 | active.initOverlays() | ||
| 325 | transition, err := d.transitionAttachment(attachmentTransitionRequest{ | 325 | transition, err := d.transitionAttachment(attachmentTransitionRequest{ | ||
| 326 | target: sess, next: active, expectedTransport: active.transportSnapshot(), ready: true, | 326 | target: sess, next: active, expectedTransport: active.transportSnapshot(), ready: true, | ||
| @@ -329,7 +329,7 @@ func TestKillSessionInterruptsOnlyExactParticipantBlockedSends(t *testing.T) { | |||||
| 329 | defer d.deferAttachmentTransitionCleanups(transition) | 329 | defer d.deferAttachmentTransitionCleanups(transition) | ||
| 330 | 330 | ||||
| 331 | unrelatedTransport := &closeTrackingTransport{} | 331 | unrelatedTransport := &closeTrackingTransport{} | ||
| 332 | - | unrelatedClient := &attachedClient{tr: unrelatedTransport, output: newOutputStateStream(), size: active.size} | 332 | + | unrelatedClient := &attachedClient{tr: unrelatedTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: active.geometry.Size}} |
| 333 | unrelatedClient.initOverlays() | 333 | unrelatedClient.initOverlays() | ||
| 334 | unrelated := &session{sessionCore: sessionCore{id: "unrelated", name: "unrelated", attachments: map[*attachedClient]struct{}{unrelatedClient: {}}}, ctx: sess.ctx, cancel: func() {}} | 334 | unrelated := &session{sessionCore: sessionCore{id: "unrelated", name: "unrelated", attachments: map[*attachedClient]struct{}{unrelatedClient: {}}}, ctx: sess.ctx, cancel: func() {}} | ||
| 335 | unrelatedClient.setSession(unrelated) | 335 | unrelatedClient.setSession(unrelated) | ||
| @@ -505,7 +505,7 @@ func TestAttachmentEffectGateReplacementInterruptsBlockedOldRenderBeforePublicat | |||||
| 505 | <-oldTransport.sendEntered | 505 | <-oldTransport.sendEntered | ||
| 506 | 506 | ||||
| 507 | newTransport := &closeTrackingTransport{} | 507 | newTransport := &closeTrackingTransport{} | ||
| 508 | - | next := &attachedClient{tr: newTransport, output: newOutputStateStream(), size: old.size} | 508 | + | next := &attachedClient{tr: newTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: old.geometry.Size}} |
| 509 | next.initOverlays() | 509 | next.initOverlays() | ||
| 510 | transitionDone := make(chan attachmentTransitionResult, 1) | 510 | transitionDone := make(chan attachmentTransitionResult, 1) | ||
| 511 | transitionErr := make(chan error, 1) | 511 | transitionErr := make(chan error, 1) | ||
| @@ -554,7 +554,7 @@ func TestAttachmentEffectGateAdmittedEffectCompletesBeforeConflictingTransition( | |||||
| 554 | ticket, ok := old.beginAttachmentEffect(token) | 554 | ticket, ok := old.beginAttachmentEffect(token) | ||
| 555 | require.True(t, ok) | 555 | require.True(t, ok) | ||
| 556 | 556 | ||||
| 557 | - | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), size: old.size} | 557 | + | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), geometry: domain.Geometry{Size: old.geometry.Size}} |
| 558 | next.initOverlays() | 558 | next.initOverlays() | ||
| 559 | done := make(chan error, 1) | 559 | done := make(chan error, 1) | ||
| 560 | go func() { | 560 | go func() { | ||
| @@ -668,7 +668,7 @@ func TestAttachmentEffectGateAdmittedActiveEffectsFinishBeforeReplacement(t *tes | |||||
| 668 | }() | 668 | }() | ||
| 669 | <-admitted | 669 | <-admitted | ||
| 670 | 670 | ||||
| 671 | - | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), size: old.size} | 671 | + | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), geometry: domain.Geometry{Size: old.geometry.Size}} |
| 672 | next.initOverlays() | 672 | next.initOverlays() | ||
| 673 | transitionDone := make(chan error, 1) | 673 | transitionDone := make(chan error, 1) | ||
| 674 | go func() { | 674 | go func() { | ||
| @@ -817,7 +817,7 @@ func TestPickerDeleteDoesNotDeleteSourceAfterInitiatorReplacement(t *testing.T) | |||||
| 817 | }() | 817 | }() | ||
| 818 | <-admissionEnded | 818 | <-admissionEnded | ||
| 819 | 819 | ||||
| 820 | - | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), size: old.size} | 820 | + | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), geometry: domain.Geometry{Size: old.geometry.Size}} |
| 821 | next.initOverlays() | 821 | next.initOverlays() | ||
| 822 | result, err := d.transitionAttachment(attachmentTransitionRequest{ | 822 | result, err := d.transitionAttachment(attachmentTransitionRequest{ | ||
| 823 | target: sess, next: next, expectedTransport: next.transportSnapshot(), ready: true, | 823 | target: sess, next: next, expectedTransport: next.transportSnapshot(), ready: true, | ||
| @@ -873,7 +873,7 @@ func TestAttachmentEffectGateAdmittedFirstPaintFinishesBeforeReplacement(t *test | |||||
| 873 | go func() { paintDone <- d.firstPaintForTransition(token) }() | 873 | go func() { paintDone <- d.firstPaintForTransition(token) }() | ||
| 874 | <-admitted | 874 | <-admitted | ||
| 875 | 875 | ||||
| 876 | - | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), size: old.size} | 876 | + | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), geometry: domain.Geometry{Size: old.geometry.Size}} |
| 877 | next.initOverlays() | 877 | next.initOverlays() | ||
| 878 | transitionDone := make(chan error, 1) | 878 | transitionDone := make(chan error, 1) | ||
| 879 | go func() { | 879 | go func() { | ||
| @@ -897,7 +897,7 @@ func TestAttachmentEffectGateReversedConcurrentTransitionsDoNotDeadlock(t *testi | |||||
| 897 | first.captureAttachmentCapability(a, aTransport) | 897 | first.captureAttachmentCapability(a, aTransport) | ||
| 898 | 898 | ||||
| 899 | bTransport := newDatagramTestTransport() | 899 | bTransport := newDatagramTestTransport() | ||
| 900 | - | b := &attachedClient{tr: bTransport, output: newOutputStateStream(), size: a.size} | 900 | + | b := &attachedClient{tr: bTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: a.geometry.Size}} |
| 901 | b.initOverlays() | 901 | b.initOverlays() | ||
| 902 | second := &session{sessionCore: sessionCore{id: "second", name: "second", attachments: map[*attachedClient]struct{}{b: {}}}} | 902 | second := &session{sessionCore: sessionCore{id: "second", name: "second", attachments: map[*attachedClient]struct{}{b: {}}}} | ||
| 903 | b.setSession(second) | 903 | b.setSession(second) | ||
M internal/usecase/daemon/attachment_output_test.go +2 -2
| @@ -122,7 +122,7 @@ func TestAttachmentOutputEpochsAreAttachmentLocalAndPreparedFramesAreFenced(t *t | |||
| 122 | 122 | fillOutputStateRows(frame, []string{"abc"}) | |
| 123 | 123 | newAttachment := func() *attachmentOutput { | |
| 124 | 124 | stream := newOutputStateStream() | |
| 125 | - | ac := &attachedClient{output: stream, size: domain.Size{Cols: 3, Rows: 1}} | |
| 125 | + | ac := &attachedClient{output: stream, geometry: domain.Geometry{Size: domain.Size{Cols: 3, Rows: 1}}} | |
| 126 | 126 | stream.attachment = ac | |
| 127 | 127 | return stream | |
| 128 | 128 | } | |
| @@ -195,7 +195,7 @@ func TestPreparedOutputDropsReplacedConnectionAndView(t *testing.T) { | |||
| 195 | 195 | } { | |
| 196 | 196 | t.Run(tt.name, func(t *testing.T) { | |
| 197 | 197 | stream := newOutputStateStream() | |
| 198 | - | ac := &attachedClient{output: stream, size: domain.Size{Cols: 3, Rows: 1}} | |
| 198 | + | ac := &attachedClient{output: stream, geometry: domain.Geometry{Size: domain.Size{Cols: 3, Rows: 1}}} | |
| 199 | 199 | stream.attachment = ac | |
| 200 | 200 | frame := renderer.NewFrame(3, 1) | |
| 201 | 201 | fillOutputStateRows(frame, []string{"abc"}) | |
| @@ -122,7 +122,7 @@ func TestAttachmentOutputEpochsAreAttachmentLocalAndPreparedFramesAreFenced(t *t | |||||
| 122 | fillOutputStateRows(frame, []string{"abc"}) | 122 | fillOutputStateRows(frame, []string{"abc"}) | ||
| 123 | newAttachment := func() *attachmentOutput { | 123 | newAttachment := func() *attachmentOutput { | ||
| 124 | stream := newOutputStateStream() | 124 | stream := newOutputStateStream() | ||
| 125 | - | ac := &attachedClient{output: stream, size: domain.Size{Cols: 3, Rows: 1}} | 125 | + | ac := &attachedClient{output: stream, geometry: domain.Geometry{Size: domain.Size{Cols: 3, Rows: 1}}} |
| 126 | stream.attachment = ac | 126 | stream.attachment = ac | ||
| 127 | return stream | 127 | return stream | ||
| 128 | } | 128 | } | ||
| @@ -195,7 +195,7 @@ func TestPreparedOutputDropsReplacedConnectionAndView(t *testing.T) { | |||||
| 195 | } { | 195 | } { | ||
| 196 | t.Run(tt.name, func(t *testing.T) { | 196 | t.Run(tt.name, func(t *testing.T) { | ||
| 197 | stream := newOutputStateStream() | 197 | stream := newOutputStateStream() | ||
| 198 | - | ac := &attachedClient{output: stream, size: domain.Size{Cols: 3, Rows: 1}} | 198 | + | ac := &attachedClient{output: stream, geometry: domain.Geometry{Size: domain.Size{Cols: 3, Rows: 1}}} |
| 199 | stream.attachment = ac | 199 | stream.attachment = ac | ||
| 200 | frame := renderer.NewFrame(3, 1) | 200 | frame := renderer.NewFrame(3, 1) | ||
| 201 | fillOutputStateRows(frame, []string{"abc"}) | 201 | fillOutputStateRows(frame, []string{"abc"}) | ||
M internal/usecase/daemon/attachment_registry_test.go +2 -2
| @@ -77,7 +77,7 @@ func TestAttachmentRegistryRepairsRemovedStableTarget(t *testing.T) { | |||
| 77 | 77 | func TestTabForAttachmentRepairRebasesOutputBeforePublishingRevision(t *testing.T) { | |
| 78 | 78 | sess := registryTestSession() | |
| 79 | 79 | ac := registryTestAttachment(1) | |
| 80 | - | ac.size = domain.Size{Cols: 80, Rows: 23} | |
| 80 | + | ac.geometry.Size = domain.Size{Cols: 80, Rows: 23} | |
| 81 | 81 | ac.output = newOutputStateStream() | |
| 82 | 82 | ac.output.attachment = ac | |
| 83 | 83 | require.True(t, sess.registerAttachment(ac)) | |
| @@ -105,7 +105,7 @@ func TestTabForAttachmentRepairRebasesOutputBeforePublishingRevision(t *testing. | |||
| 105 | 105 | func TestPrepareRemovedTabViewRebasesOutputBeforeSideEffect(t *testing.T) { | |
| 106 | 106 | sess := registryTestSession() | |
| 107 | 107 | ac := registryTestAttachment(1) | |
| 108 | - | ac.size = domain.Size{Cols: 80, Rows: 23} | |
| 108 | + | ac.geometry.Size = domain.Size{Cols: 80, Rows: 23} | |
| 109 | 109 | ac.output = newOutputStateStream() | |
| 110 | 110 | ac.output.attachment = ac | |
| 111 | 111 | require.True(t, sess.registerAttachment(ac)) | |
| @@ -77,7 +77,7 @@ func TestAttachmentRegistryRepairsRemovedStableTarget(t *testing.T) { | |||||
| 77 | func TestTabForAttachmentRepairRebasesOutputBeforePublishingRevision(t *testing.T) { | 77 | func TestTabForAttachmentRepairRebasesOutputBeforePublishingRevision(t *testing.T) { | ||
| 78 | sess := registryTestSession() | 78 | sess := registryTestSession() | ||
| 79 | ac := registryTestAttachment(1) | 79 | ac := registryTestAttachment(1) | ||
| 80 | - | ac.size = domain.Size{Cols: 80, Rows: 23} | 80 | + | ac.geometry.Size = domain.Size{Cols: 80, Rows: 23} |
| 81 | ac.output = newOutputStateStream() | 81 | ac.output = newOutputStateStream() | ||
| 82 | ac.output.attachment = ac | 82 | ac.output.attachment = ac | ||
| 83 | require.True(t, sess.registerAttachment(ac)) | 83 | require.True(t, sess.registerAttachment(ac)) | ||
| @@ -105,7 +105,7 @@ func TestTabForAttachmentRepairRebasesOutputBeforePublishingRevision(t *testing. | |||||
| 105 | func TestPrepareRemovedTabViewRebasesOutputBeforeSideEffect(t *testing.T) { | 105 | func TestPrepareRemovedTabViewRebasesOutputBeforeSideEffect(t *testing.T) { | ||
| 106 | sess := registryTestSession() | 106 | sess := registryTestSession() | ||
| 107 | ac := registryTestAttachment(1) | 107 | ac := registryTestAttachment(1) | ||
| 108 | - | ac.size = domain.Size{Cols: 80, Rows: 23} | 108 | + | ac.geometry.Size = domain.Size{Cols: 80, Rows: 23} |
| 109 | ac.output = newOutputStateStream() | 109 | ac.output = newOutputStateStream() | ||
| 110 | ac.output.attachment = ac | 110 | ac.output.attachment = ac | ||
| 111 | require.True(t, sess.registerAttachment(ac)) | 111 | require.True(t, sess.registerAttachment(ac)) | ||
M internal/usecase/daemon/attachment_render_test.go +7 -7
| @@ -118,7 +118,7 @@ func TestAttachmentResizeKeepsPeerWindowAndExpandsSharedContent(t *testing.T) { | |||
| 118 | 118 | require.True(t, sess.repairAttachmentView(second)) | |
| 119 | 119 | ||
| 120 | 120 | tb := sess.tabs[0] | |
| 121 | - | firstSize := first.size | |
| 121 | + | firstSize := first.geometry.Size | |
| 122 | 122 | secondRevision := second.viewSnapshot().revision | |
| 123 | 123 | secondEpoch := second.output.currentEpoch() | |
| 124 | 124 | ||
| @@ -136,8 +136,8 @@ func TestAttachmentResizeKeepsPeerWindowAndExpandsSharedContent(t *testing.T) { | |||
| 136 | 136 | tb.mu.Lock() | |
| 137 | 137 | require.Equal(t, domain.Size{Cols: 120, Rows: 48}, tb.size) | |
| 138 | 138 | tb.mu.Unlock() | |
| 139 | - | require.Equal(t, firstSize, first.size) | |
| 140 | - | require.Equal(t, domain.Size{Cols: 120, Rows: 50}, second.size) | |
| 139 | + | require.Equal(t, firstSize, first.geometry.Size) | |
| 140 | + | require.Equal(t, domain.Size{Cols: 120, Rows: 50}, second.geometry.Size) | |
| 141 | 141 | require.Equal(t, secondRevision+1, second.viewSnapshot().revision) | |
| 142 | 142 | require.Greater(t, second.output.currentEpoch(), secondEpoch) | |
| 143 | 143 | } | |
| @@ -292,8 +292,8 @@ func TestAttachmentResizeUsesLatestClaimedSessionGeometry(t *testing.T) { | |||
| 292 | 292 | tb.mu.Lock() | |
| 293 | 293 | require.Equal(t, domain.Size{Cols: 120, Rows: 48}, tb.size) | |
| 294 | 294 | tb.mu.Unlock() | |
| 295 | - | require.Equal(t, domain.Size{Cols: 80, Rows: 24}, first.size) | |
| 296 | - | require.Equal(t, domain.Size{Cols: 120, Rows: 50}, second.size) | |
| 295 | + | require.Equal(t, domain.Size{Cols: 80, Rows: 24}, first.geometry.Size) | |
| 296 | + | require.Equal(t, domain.Size{Cols: 120, Rows: 50}, second.geometry.Size) | |
| 297 | 297 | ||
| 298 | 298 | // A same-size resize is still a claim, so the smaller peer can take | |
| 299 | 299 | // authority without changing its local window state first. | |
| @@ -308,8 +308,8 @@ func TestAttachmentResizeUsesLatestClaimedSessionGeometry(t *testing.T) { | |||
| 308 | 308 | tb.mu.Lock() | |
| 309 | 309 | require.Equal(t, domain.Size{Cols: 70, Rows: 18}, tb.size) | |
| 310 | 310 | tb.mu.Unlock() | |
| 311 | - | require.Equal(t, domain.Size{Cols: 70, Rows: 20}, first.size) | |
| 312 | - | require.Equal(t, domain.Size{Cols: 90, Rows: 30}, second.size) | |
| 311 | + | require.Equal(t, domain.Size{Cols: 70, Rows: 20}, first.geometry.Size) | |
| 312 | + | require.Equal(t, domain.Size{Cols: 90, Rows: 30}, second.geometry.Size) | |
| 313 | 313 | ||
| 314 | 314 | // Detaching the latest claimant falls back to the most recent remaining | |
| 315 | 315 | // attachment claim rather than reverting to the historical maximum. | |
| @@ -118,7 +118,7 @@ func TestAttachmentResizeKeepsPeerWindowAndExpandsSharedContent(t *testing.T) { | |||||
| 118 | require.True(t, sess.repairAttachmentView(second)) | 118 | require.True(t, sess.repairAttachmentView(second)) | ||
| 119 | 119 | ||||
| 120 | tb := sess.tabs[0] | 120 | tb := sess.tabs[0] | ||
| 121 | - | firstSize := first.size | 121 | + | firstSize := first.geometry.Size |
| 122 | secondRevision := second.viewSnapshot().revision | 122 | secondRevision := second.viewSnapshot().revision | ||
| 123 | secondEpoch := second.output.currentEpoch() | 123 | secondEpoch := second.output.currentEpoch() | ||
| 124 | 124 | ||||
| @@ -136,8 +136,8 @@ func TestAttachmentResizeKeepsPeerWindowAndExpandsSharedContent(t *testing.T) { | |||||
| 136 | tb.mu.Lock() | 136 | tb.mu.Lock() | ||
| 137 | require.Equal(t, domain.Size{Cols: 120, Rows: 48}, tb.size) | 137 | require.Equal(t, domain.Size{Cols: 120, Rows: 48}, tb.size) | ||
| 138 | tb.mu.Unlock() | 138 | tb.mu.Unlock() | ||
| 139 | - | require.Equal(t, firstSize, first.size) | 139 | + | require.Equal(t, firstSize, first.geometry.Size) |
| 140 | - | require.Equal(t, domain.Size{Cols: 120, Rows: 50}, second.size) | 140 | + | require.Equal(t, domain.Size{Cols: 120, Rows: 50}, second.geometry.Size) |
| 141 | require.Equal(t, secondRevision+1, second.viewSnapshot().revision) | 141 | require.Equal(t, secondRevision+1, second.viewSnapshot().revision) | ||
| 142 | require.Greater(t, second.output.currentEpoch(), secondEpoch) | 142 | require.Greater(t, second.output.currentEpoch(), secondEpoch) | ||
| 143 | } | 143 | } | ||
| @@ -292,8 +292,8 @@ func TestAttachmentResizeUsesLatestClaimedSessionGeometry(t *testing.T) { | |||||
| 292 | tb.mu.Lock() | 292 | tb.mu.Lock() | ||
| 293 | require.Equal(t, domain.Size{Cols: 120, Rows: 48}, tb.size) | 293 | require.Equal(t, domain.Size{Cols: 120, Rows: 48}, tb.size) | ||
| 294 | tb.mu.Unlock() | 294 | tb.mu.Unlock() | ||
| 295 | - | require.Equal(t, domain.Size{Cols: 80, Rows: 24}, first.size) | 295 | + | require.Equal(t, domain.Size{Cols: 80, Rows: 24}, first.geometry.Size) |
| 296 | - | require.Equal(t, domain.Size{Cols: 120, Rows: 50}, second.size) | 296 | + | require.Equal(t, domain.Size{Cols: 120, Rows: 50}, second.geometry.Size) |
| 297 | 297 | ||||
| 298 | // A same-size resize is still a claim, so the smaller peer can take | 298 | // A same-size resize is still a claim, so the smaller peer can take | ||
| 299 | // authority without changing its local window state first. | 299 | // authority without changing its local window state first. | ||
| @@ -308,8 +308,8 @@ func TestAttachmentResizeUsesLatestClaimedSessionGeometry(t *testing.T) { | |||||
| 308 | tb.mu.Lock() | 308 | tb.mu.Lock() | ||
| 309 | require.Equal(t, domain.Size{Cols: 70, Rows: 18}, tb.size) | 309 | require.Equal(t, domain.Size{Cols: 70, Rows: 18}, tb.size) | ||
| 310 | tb.mu.Unlock() | 310 | tb.mu.Unlock() | ||
| 311 | - | require.Equal(t, domain.Size{Cols: 70, Rows: 20}, first.size) | 311 | + | require.Equal(t, domain.Size{Cols: 70, Rows: 20}, first.geometry.Size) |
| 312 | - | require.Equal(t, domain.Size{Cols: 90, Rows: 30}, second.size) | 312 | + | require.Equal(t, domain.Size{Cols: 90, Rows: 30}, second.geometry.Size) |
| 313 | 313 | ||||
| 314 | // Detaching the latest claimant falls back to the most recent remaining | 314 | // Detaching the latest claimant falls back to the most recent remaining | ||
| 315 | // attachment claim rather than reverting to the historical maximum. | 315 | // attachment claim rather than reverting to the historical maximum. | ||
M internal/usecase/daemon/attention.go +1 -4
| @@ -19,10 +19,7 @@ func (d *Daemon) noteAttention(sess *session, tb *tab) { | |||
| 19 | 19 | return | |
| 20 | 20 | } | |
| 21 | 21 | ||
| 22 | - | now := d.clock.Now() | |
| 23 | - | if now.IsZero() { | |
| 24 | - | now = time.Now() | |
| 25 | - | } | |
| 22 | + | now := d.daemonNow() | |
| 26 | 23 | ||
| 27 | 24 | sess.mu.Lock() | |
| 28 | 25 | if !tb.attention { | |
| @@ -19,10 +19,7 @@ func (d *Daemon) noteAttention(sess *session, tb *tab) { | |||||
| 19 | return | 19 | return | ||
| 20 | } | 20 | } | ||
| 21 | 21 | ||||
| 22 | - | now := d.clock.Now() | 22 | + | now := d.daemonNow() |
| 23 | - | if now.IsZero() { | |||
| 24 | - | now = time.Now() | |||
| 25 | - | } | |||
| 26 | 23 | ||||
| 27 | sess.mu.Lock() | 24 | sess.mu.Lock() | ||
| 28 | if !tb.attention { | 25 | if !tb.attention { | ||
M internal/usecase/daemon/attention_test.go +1 -1
| @@ -172,7 +172,7 @@ func TestNoteAttentionDoesNotBlockOnWedgedOtherClient(t *testing.T) { | |||
| 172 | 172 | return nil | |
| 173 | 173 | }).Maybe() | |
| 174 | 174 | trW.EXPECT().Close().Return(nil).Maybe() | |
| 175 | - | acW := &attachedClient{tr: trW, output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | |
| 175 | + | acW := &attachedClient{tr: trW, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} | |
| 176 | 176 | acW.initOverlays() | |
| 177 | 177 | sctxW, cancelW := context.WithCancel(d.serveCtx) | |
| 178 | 178 | t.Cleanup(cancelW) | |
| @@ -172,7 +172,7 @@ func TestNoteAttentionDoesNotBlockOnWedgedOtherClient(t *testing.T) { | |||||
| 172 | return nil | 172 | return nil | ||
| 173 | }).Maybe() | 173 | }).Maybe() | ||
| 174 | trW.EXPECT().Close().Return(nil).Maybe() | 174 | trW.EXPECT().Close().Return(nil).Maybe() | ||
| 175 | - | acW := &attachedClient{tr: trW, output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | 175 | + | acW := &attachedClient{tr: trW, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} |
| 176 | acW.initOverlays() | 176 | acW.initOverlays() | ||
| 177 | sctxW, cancelW := context.WithCancel(d.serveCtx) | 177 | sctxW, cancelW := context.WithCancel(d.serveCtx) | ||
| 178 | t.Cleanup(cancelW) | 178 | t.Cleanup(cancelW) | ||
M internal/usecase/daemon/bar_refresh_test.go +14 -14
| @@ -64,7 +64,7 @@ func TestBarScriptRefreshIntervalClampingTickAndLastGoodOnFailure(t *testing.T) | |||
| 64 | 64 | r := &fakeBarRunner{outs: []string{"top1", "bot1", "top2", "bot2"}} | |
| 65 | 65 | d := newBarRefreshTestDaemon(r, 100*time.Millisecond) | |
| 66 | 66 | sess := newBarRefreshTestSession() | |
| 67 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 120, Rows: 24}}) | |
| 67 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 120, Rows: 24}}}) | |
| 68 | 68 | ||
| 69 | 69 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(0, 0), false)) | |
| 70 | 70 | waitBarRefreshIdle(t, d) | |
| @@ -91,7 +91,7 @@ func TestBarScriptContextUsesFirstOrderedPaneAndClientCols(t *testing.T) { | |||
| 91 | 91 | sess := newBarRefreshTestSession() | |
| 92 | 92 | sess.name = "work" | |
| 93 | 93 | sess.cwd = "/repo" | |
| 94 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 132, Rows: 40}}) | |
| 94 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 132, Rows: 40}}}) | |
| 95 | 95 | sess.tabs[0].size = domain.Size{Cols: 132, Rows: 38} | |
| 96 | 96 | active := sess.tabs[0].focusedPane() | |
| 97 | 97 | active.stableID = "pane-default" | |
| @@ -130,7 +130,7 @@ func TestBarScriptRefreshSkipsRunnerWhenCommandsDisabled(t *testing.T) { | |||
| 130 | 130 | d.barScripts.cfg.topRight = "" | |
| 131 | 131 | d.barScripts.cfg.bottomRight = "" | |
| 132 | 132 | sess := newBarRefreshTestSession() | |
| 133 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | |
| 133 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) | |
| 134 | 134 | d.barScripts.outputs[sess.id] = barScriptOutputs{topRight: "old", bottomRight: "old"} | |
| 135 | 135 | ||
| 136 | 136 | require.False(t, d.refreshBarScriptsIfDue(sess, time.Unix(0, 0), true)) | |
| @@ -145,7 +145,7 @@ func TestDefaultBarConfigDoesNotInvokeRunner(t *testing.T) { | |||
| 145 | 145 | d := newBarRefreshTestDaemon(r, time.Second) | |
| 146 | 146 | d.barScripts.cfg = barConfigFromDomain(domain.Defaults().Bar) | |
| 147 | 147 | sess := newBarRefreshTestSession() | |
| 148 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | |
| 148 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) | |
| 149 | 149 | ||
| 150 | 150 | require.False(t, d.refreshBarScriptsIfDue(sess, time.Unix(0, 0), true)) | |
| 151 | 151 | require.Empty(t, r.calls) | |
| @@ -155,7 +155,7 @@ func TestBarScriptForcedRefreshRespectsMinimumInterval(t *testing.T) { | |||
| 155 | 155 | r := &fakeBarRunner{outs: []string{"top1", "bottom1", "top2", "bottom2"}} | |
| 156 | 156 | d := newBarRefreshTestDaemon(r, 5*time.Second) | |
| 157 | 157 | sess := newBarRefreshTestSession() | |
| 158 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | |
| 158 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) | |
| 159 | 159 | ||
| 160 | 160 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(10, 0), true)) | |
| 161 | 161 | waitBarRefreshIdle(t, d) | |
| @@ -172,7 +172,7 @@ func TestBarScriptContextChangeDebouncesUntilMinimumInterval(t *testing.T) { | |||
| 172 | 172 | r := &fakeBarRunner{outs: []string{"top1", "bottom1", "top2", "bottom2"}} | |
| 173 | 173 | d := newBarRefreshTestDaemon(r, 5*time.Second) | |
| 174 | 174 | sess := newBarRefreshTestSession() | |
| 175 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | |
| 175 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) | |
| 176 | 176 | ||
| 177 | 177 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Now(), false)) | |
| 178 | 178 | waitBarRefreshIdle(t, d) | |
| @@ -190,7 +190,7 @@ func TestBarScriptRefreshForcesWhenContextChanges(t *testing.T) { | |||
| 190 | 190 | r := &fakeBarRunner{outs: []string{"top1", "bottom1", "top2", "bottom2"}} | |
| 191 | 191 | d := newBarRefreshTestDaemon(r, 5*time.Second) | |
| 192 | 192 | sess := newBarRefreshTestSession() | |
| 193 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | |
| 193 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) | |
| 194 | 194 | ||
| 195 | 195 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(0, 0), false)) | |
| 196 | 196 | waitBarRefreshIdle(t, d) | |
| @@ -218,7 +218,7 @@ func TestRefreshBarScriptsAllSessionsForcesRun(t *testing.T) { | |||
| 218 | 218 | r := &fakeBarRunner{outs: []string{"top1", "bottom1"}} | |
| 219 | 219 | d := newBarRefreshTestDaemon(r, 60*time.Second) | |
| 220 | 220 | sess := newBarRefreshTestSession() | |
| 221 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | |
| 221 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) | |
| 222 | 222 | d.sessions = map[domain.SessionID]*session{sess.id: sess} | |
| 223 | 223 | ||
| 224 | 224 | // Verifies refreshBarScriptsAllSessions iterates every live session and | |
| @@ -251,7 +251,7 @@ func TestBarScriptFailureLogsAndRetainsLastGood(t *testing.T) { | |||
| 251 | 251 | d := newBarRefreshTestDaemon(r, time.Second) | |
| 252 | 252 | d.log = slog.New(slog.NewTextHandler(&logs, nil)) | |
| 253 | 253 | sess := newBarRefreshTestSession() | |
| 254 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | |
| 254 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) | |
| 255 | 255 | ||
| 256 | 256 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(10, 0), true)) | |
| 257 | 257 | waitBarRefreshIdle(t, d) | |
| @@ -286,7 +286,7 @@ func TestBarScriptRunConsumesPendingAfterRunningClears(t *testing.T) { | |||
| 286 | 286 | r := newBlockingBarRunner([]string{"top1", "bottom1", "top2", "bottom2"}) | |
| 287 | 287 | d := newBarRefreshTestDaemon(r, time.Second) | |
| 288 | 288 | sess := newBarRefreshTestSession() | |
| 289 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | |
| 289 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) | |
| 290 | 290 | ||
| 291 | 291 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(10, 0), true)) | |
| 292 | 292 | select { | |
| @@ -336,7 +336,7 @@ func TestBarScriptFailureLogsOnceUntilSignatureChanges(t *testing.T) { | |||
| 336 | 336 | d.log = logger | |
| 337 | 337 | d.baseEnv = []string{"PATH=/usr/bin"} | |
| 338 | 338 | sess := newBarRefreshTestSession() | |
| 339 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | |
| 339 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) | |
| 340 | 340 | ||
| 341 | 341 | readBuf := func() string { | |
| 342 | 342 | mu.Lock() | |
| @@ -419,11 +419,11 @@ func TestBarScriptRefreshIsPerSession(t *testing.T) { | |||
| 419 | 419 | a := newBarRefreshTestSession() | |
| 420 | 420 | a.id = "a" | |
| 421 | 421 | a.name = "a" | |
| 422 | - | a.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | |
| 422 | + | a.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) | |
| 423 | 423 | b := newBarRefreshTestSession() | |
| 424 | 424 | b.id = "b" | |
| 425 | 425 | b.name = "b" | |
| 426 | - | b.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | |
| 426 | + | b.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) | |
| 427 | 427 | ||
| 428 | 428 | require.True(t, d.refreshBarScriptsIfDue(a, time.Unix(0, 0), false)) | |
| 429 | 429 | waitBarRefreshIdle(t, d) | |
| @@ -644,7 +644,7 @@ func TestBarScriptsUseSessionEnvNotDaemonBaseEnv(t *testing.T) { | |||
| 644 | 644 | d.baseEnv = tc.baseEnv | |
| 645 | 645 | sess := newBarRefreshTestSession() | |
| 646 | 646 | sess.env = tc.sessEnv | |
| 647 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | |
| 647 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) | |
| 648 | 648 | ||
| 649 | 649 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(0, 0), true)) | |
| 650 | 650 | waitBarRefreshIdle(t, d) | |
| @@ -64,7 +64,7 @@ func TestBarScriptRefreshIntervalClampingTickAndLastGoodOnFailure(t *testing.T) | |||||
| 64 | r := &fakeBarRunner{outs: []string{"top1", "bot1", "top2", "bot2"}} | 64 | r := &fakeBarRunner{outs: []string{"top1", "bot1", "top2", "bot2"}} | ||
| 65 | d := newBarRefreshTestDaemon(r, 100*time.Millisecond) | 65 | d := newBarRefreshTestDaemon(r, 100*time.Millisecond) | ||
| 66 | sess := newBarRefreshTestSession() | 66 | sess := newBarRefreshTestSession() | ||
| 67 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 120, Rows: 24}}) | 67 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 120, Rows: 24}}}) |
| 68 | 68 | ||||
| 69 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(0, 0), false)) | 69 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(0, 0), false)) | ||
| 70 | waitBarRefreshIdle(t, d) | 70 | waitBarRefreshIdle(t, d) | ||
| @@ -91,7 +91,7 @@ func TestBarScriptContextUsesFirstOrderedPaneAndClientCols(t *testing.T) { | |||||
| 91 | sess := newBarRefreshTestSession() | 91 | sess := newBarRefreshTestSession() | ||
| 92 | sess.name = "work" | 92 | sess.name = "work" | ||
| 93 | sess.cwd = "/repo" | 93 | sess.cwd = "/repo" | ||
| 94 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 132, Rows: 40}}) | 94 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 132, Rows: 40}}}) |
| 95 | sess.tabs[0].size = domain.Size{Cols: 132, Rows: 38} | 95 | sess.tabs[0].size = domain.Size{Cols: 132, Rows: 38} | ||
| 96 | active := sess.tabs[0].focusedPane() | 96 | active := sess.tabs[0].focusedPane() | ||
| 97 | active.stableID = "pane-default" | 97 | active.stableID = "pane-default" | ||
| @@ -130,7 +130,7 @@ func TestBarScriptRefreshSkipsRunnerWhenCommandsDisabled(t *testing.T) { | |||||
| 130 | d.barScripts.cfg.topRight = "" | 130 | d.barScripts.cfg.topRight = "" | ||
| 131 | d.barScripts.cfg.bottomRight = "" | 131 | d.barScripts.cfg.bottomRight = "" | ||
| 132 | sess := newBarRefreshTestSession() | 132 | sess := newBarRefreshTestSession() | ||
| 133 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | 133 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) |
| 134 | d.barScripts.outputs[sess.id] = barScriptOutputs{topRight: "old", bottomRight: "old"} | 134 | d.barScripts.outputs[sess.id] = barScriptOutputs{topRight: "old", bottomRight: "old"} | ||
| 135 | 135 | ||||
| 136 | require.False(t, d.refreshBarScriptsIfDue(sess, time.Unix(0, 0), true)) | 136 | require.False(t, d.refreshBarScriptsIfDue(sess, time.Unix(0, 0), true)) | ||
| @@ -145,7 +145,7 @@ func TestDefaultBarConfigDoesNotInvokeRunner(t *testing.T) { | |||||
| 145 | d := newBarRefreshTestDaemon(r, time.Second) | 145 | d := newBarRefreshTestDaemon(r, time.Second) | ||
| 146 | d.barScripts.cfg = barConfigFromDomain(domain.Defaults().Bar) | 146 | d.barScripts.cfg = barConfigFromDomain(domain.Defaults().Bar) | ||
| 147 | sess := newBarRefreshTestSession() | 147 | sess := newBarRefreshTestSession() | ||
| 148 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | 148 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) |
| 149 | 149 | ||||
| 150 | require.False(t, d.refreshBarScriptsIfDue(sess, time.Unix(0, 0), true)) | 150 | require.False(t, d.refreshBarScriptsIfDue(sess, time.Unix(0, 0), true)) | ||
| 151 | require.Empty(t, r.calls) | 151 | require.Empty(t, r.calls) | ||
| @@ -155,7 +155,7 @@ func TestBarScriptForcedRefreshRespectsMinimumInterval(t *testing.T) { | |||||
| 155 | r := &fakeBarRunner{outs: []string{"top1", "bottom1", "top2", "bottom2"}} | 155 | r := &fakeBarRunner{outs: []string{"top1", "bottom1", "top2", "bottom2"}} | ||
| 156 | d := newBarRefreshTestDaemon(r, 5*time.Second) | 156 | d := newBarRefreshTestDaemon(r, 5*time.Second) | ||
| 157 | sess := newBarRefreshTestSession() | 157 | sess := newBarRefreshTestSession() | ||
| 158 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | 158 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) |
| 159 | 159 | ||||
| 160 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(10, 0), true)) | 160 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(10, 0), true)) | ||
| 161 | waitBarRefreshIdle(t, d) | 161 | waitBarRefreshIdle(t, d) | ||
| @@ -172,7 +172,7 @@ func TestBarScriptContextChangeDebouncesUntilMinimumInterval(t *testing.T) { | |||||
| 172 | r := &fakeBarRunner{outs: []string{"top1", "bottom1", "top2", "bottom2"}} | 172 | r := &fakeBarRunner{outs: []string{"top1", "bottom1", "top2", "bottom2"}} | ||
| 173 | d := newBarRefreshTestDaemon(r, 5*time.Second) | 173 | d := newBarRefreshTestDaemon(r, 5*time.Second) | ||
| 174 | sess := newBarRefreshTestSession() | 174 | sess := newBarRefreshTestSession() | ||
| 175 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | 175 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) |
| 176 | 176 | ||||
| 177 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Now(), false)) | 177 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Now(), false)) | ||
| 178 | waitBarRefreshIdle(t, d) | 178 | waitBarRefreshIdle(t, d) | ||
| @@ -190,7 +190,7 @@ func TestBarScriptRefreshForcesWhenContextChanges(t *testing.T) { | |||||
| 190 | r := &fakeBarRunner{outs: []string{"top1", "bottom1", "top2", "bottom2"}} | 190 | r := &fakeBarRunner{outs: []string{"top1", "bottom1", "top2", "bottom2"}} | ||
| 191 | d := newBarRefreshTestDaemon(r, 5*time.Second) | 191 | d := newBarRefreshTestDaemon(r, 5*time.Second) | ||
| 192 | sess := newBarRefreshTestSession() | 192 | sess := newBarRefreshTestSession() | ||
| 193 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | 193 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) |
| 194 | 194 | ||||
| 195 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(0, 0), false)) | 195 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(0, 0), false)) | ||
| 196 | waitBarRefreshIdle(t, d) | 196 | waitBarRefreshIdle(t, d) | ||
| @@ -218,7 +218,7 @@ func TestRefreshBarScriptsAllSessionsForcesRun(t *testing.T) { | |||||
| 218 | r := &fakeBarRunner{outs: []string{"top1", "bottom1"}} | 218 | r := &fakeBarRunner{outs: []string{"top1", "bottom1"}} | ||
| 219 | d := newBarRefreshTestDaemon(r, 60*time.Second) | 219 | d := newBarRefreshTestDaemon(r, 60*time.Second) | ||
| 220 | sess := newBarRefreshTestSession() | 220 | sess := newBarRefreshTestSession() | ||
| 221 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | 221 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) |
| 222 | d.sessions = map[domain.SessionID]*session{sess.id: sess} | 222 | d.sessions = map[domain.SessionID]*session{sess.id: sess} | ||
| 223 | 223 | ||||
| 224 | // Verifies refreshBarScriptsAllSessions iterates every live session and | 224 | // Verifies refreshBarScriptsAllSessions iterates every live session and | ||
| @@ -251,7 +251,7 @@ func TestBarScriptFailureLogsAndRetainsLastGood(t *testing.T) { | |||||
| 251 | d := newBarRefreshTestDaemon(r, time.Second) | 251 | d := newBarRefreshTestDaemon(r, time.Second) | ||
| 252 | d.log = slog.New(slog.NewTextHandler(&logs, nil)) | 252 | d.log = slog.New(slog.NewTextHandler(&logs, nil)) | ||
| 253 | sess := newBarRefreshTestSession() | 253 | sess := newBarRefreshTestSession() | ||
| 254 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | 254 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) |
| 255 | 255 | ||||
| 256 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(10, 0), true)) | 256 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(10, 0), true)) | ||
| 257 | waitBarRefreshIdle(t, d) | 257 | waitBarRefreshIdle(t, d) | ||
| @@ -286,7 +286,7 @@ func TestBarScriptRunConsumesPendingAfterRunningClears(t *testing.T) { | |||||
| 286 | r := newBlockingBarRunner([]string{"top1", "bottom1", "top2", "bottom2"}) | 286 | r := newBlockingBarRunner([]string{"top1", "bottom1", "top2", "bottom2"}) | ||
| 287 | d := newBarRefreshTestDaemon(r, time.Second) | 287 | d := newBarRefreshTestDaemon(r, time.Second) | ||
| 288 | sess := newBarRefreshTestSession() | 288 | sess := newBarRefreshTestSession() | ||
| 289 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | 289 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) |
| 290 | 290 | ||||
| 291 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(10, 0), true)) | 291 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(10, 0), true)) | ||
| 292 | select { | 292 | select { | ||
| @@ -336,7 +336,7 @@ func TestBarScriptFailureLogsOnceUntilSignatureChanges(t *testing.T) { | |||||
| 336 | d.log = logger | 336 | d.log = logger | ||
| 337 | d.baseEnv = []string{"PATH=/usr/bin"} | 337 | d.baseEnv = []string{"PATH=/usr/bin"} | ||
| 338 | sess := newBarRefreshTestSession() | 338 | sess := newBarRefreshTestSession() | ||
| 339 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | 339 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) |
| 340 | 340 | ||||
| 341 | readBuf := func() string { | 341 | readBuf := func() string { | ||
| 342 | mu.Lock() | 342 | mu.Lock() | ||
| @@ -419,11 +419,11 @@ func TestBarScriptRefreshIsPerSession(t *testing.T) { | |||||
| 419 | a := newBarRefreshTestSession() | 419 | a := newBarRefreshTestSession() | ||
| 420 | a.id = "a" | 420 | a.id = "a" | ||
| 421 | a.name = "a" | 421 | a.name = "a" | ||
| 422 | - | a.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | 422 | + | a.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) |
| 423 | b := newBarRefreshTestSession() | 423 | b := newBarRefreshTestSession() | ||
| 424 | b.id = "b" | 424 | b.id = "b" | ||
| 425 | b.name = "b" | 425 | b.name = "b" | ||
| 426 | - | b.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | 426 | + | b.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) |
| 427 | 427 | ||||
| 428 | require.True(t, d.refreshBarScriptsIfDue(a, time.Unix(0, 0), false)) | 428 | require.True(t, d.refreshBarScriptsIfDue(a, time.Unix(0, 0), false)) | ||
| 429 | waitBarRefreshIdle(t, d) | 429 | waitBarRefreshIdle(t, d) | ||
| @@ -644,7 +644,7 @@ func TestBarScriptsUseSessionEnvNotDaemonBaseEnv(t *testing.T) { | |||||
| 644 | d.baseEnv = tc.baseEnv | 644 | d.baseEnv = tc.baseEnv | ||
| 645 | sess := newBarRefreshTestSession() | 645 | sess := newBarRefreshTestSession() | ||
| 646 | sess.env = tc.sessEnv | 646 | sess.env = tc.sessEnv | ||
| 647 | - | sess.registerAttachment(&attachedClient{size: domain.Size{Cols: 80, Rows: 24}}) | 647 | + | sess.registerAttachment(&attachedClient{geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}}) |
| 648 | 648 | ||||
| 649 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(0, 0), true)) | 649 | require.True(t, d.refreshBarScriptsIfDue(sess, time.Unix(0, 0), true)) | ||
| 650 | waitBarRefreshIdle(t, d) | 650 | waitBarRefreshIdle(t, d) | ||
M internal/usecase/daemon/client.go +2 -6
| @@ -94,7 +94,6 @@ type attachedClient struct { | |||
| 94 | 94 | // sizeMu lets shared-geometry snapshots read attachment windows without | |
| 95 | 95 | // waiting behind a blocked transport send. | |
| 96 | 96 | sizeMu sync.RWMutex | |
| 97 | - | size domain.Size // retained for legacy fixtures; live paths publish geometry | |
| 98 | 97 | geometry domain.Geometry // controlling-terminal geometry for this Attachment | |
| 99 | 98 | keys *keys.Router | |
| 100 | 99 | // view is attachment-local navigation state. It is never inferred from a | |
| @@ -181,7 +180,7 @@ func (ac *attachedClient) sizeSnapshot() domain.Size { | |||
| 181 | 180 | } | |
| 182 | 181 | ac.sizeMu.RLock() | |
| 183 | 182 | defer ac.sizeMu.RUnlock() | |
| 184 | - | return ac.size | |
| 183 | + | return ac.geometry.Size | |
| 185 | 184 | } | |
| 186 | 185 | ||
| 187 | 186 | func (ac *attachedClient) setSize(size domain.Size) { | |
| @@ -189,7 +188,6 @@ func (ac *attachedClient) setSize(size domain.Size) { | |||
| 189 | 188 | return | |
| 190 | 189 | } | |
| 191 | 190 | ac.sizeMu.Lock() | |
| 192 | - | ac.size = size | |
| 193 | 191 | ac.geometry.Size = size | |
| 194 | 192 | ac.geometry = ac.geometry.NormalizePixels() | |
| 195 | 193 | ac.sizeMu.Unlock() | |
| @@ -204,7 +202,7 @@ func (ac *attachedClient) geometrySnapshot() domain.Geometry { | |||
| 204 | 202 | if ac.geometry.Valid() { | |
| 205 | 203 | return ac.geometry | |
| 206 | 204 | } | |
| 207 | - | return domain.Geometry{Size: ac.size} | |
| 205 | + | return domain.Geometry{Size: ac.geometry.Size} | |
| 208 | 206 | } | |
| 209 | 207 | ||
| 210 | 208 | func (ac *attachedClient) setGeometry(geometry domain.Geometry) { | |
| @@ -213,7 +211,6 @@ func (ac *attachedClient) setGeometry(geometry domain.Geometry) { | |||
| 213 | 211 | } | |
| 214 | 212 | geometry = geometry.NormalizePixels() | |
| 215 | 213 | ac.sizeMu.Lock() | |
| 216 | - | ac.size = geometry.Size | |
| 217 | 214 | ac.geometry = geometry | |
| 218 | 215 | ac.sizeMu.Unlock() | |
| 219 | 216 | } | |
| @@ -772,7 +769,6 @@ func (d *Daemon) prepareAttachedClientLocked(sess *session, tr ports.ServerConne | |||
| 772 | 769 | ac := &attachedClient{ | |
| 773 | 770 | tr: tr, | |
| 774 | 771 | output: output, | |
| 775 | - | size: geometry.Size, | |
| 776 | 772 | geometry: geometry, | |
| 777 | 773 | view: attachmentView{windowRows: geometry.Rows, windowSet: true}, | |
| 778 | 774 | clientID: opts.clientID, | |
| @@ -94,7 +94,6 @@ type attachedClient struct { | |||||
| 94 | // sizeMu lets shared-geometry snapshots read attachment windows without | 94 | // sizeMu lets shared-geometry snapshots read attachment windows without | ||
| 95 | // waiting behind a blocked transport send. | 95 | // waiting behind a blocked transport send. | ||
| 96 | sizeMu sync.RWMutex | 96 | sizeMu sync.RWMutex | ||
| 97 | - | size domain.Size // retained for legacy fixtures; live paths publish geometry | |||
| 98 | geometry domain.Geometry // controlling-terminal geometry for this Attachment | 97 | geometry domain.Geometry // controlling-terminal geometry for this Attachment | ||
| 99 | keys *keys.Router | 98 | keys *keys.Router | ||
| 100 | // view is attachment-local navigation state. It is never inferred from a | 99 | // view is attachment-local navigation state. It is never inferred from a | ||
| @@ -181,7 +180,7 @@ func (ac *attachedClient) sizeSnapshot() domain.Size { | |||||
| 181 | } | 180 | } | ||
| 182 | ac.sizeMu.RLock() | 181 | ac.sizeMu.RLock() | ||
| 183 | defer ac.sizeMu.RUnlock() | 182 | defer ac.sizeMu.RUnlock() | ||
| 184 | - | return ac.size | 183 | + | return ac.geometry.Size |
| 185 | } | 184 | } | ||
| 186 | 185 | ||||
| 187 | func (ac *attachedClient) setSize(size domain.Size) { | 186 | func (ac *attachedClient) setSize(size domain.Size) { | ||
| @@ -189,7 +188,6 @@ func (ac *attachedClient) setSize(size domain.Size) { | |||||
| 189 | return | 188 | return | ||
| 190 | } | 189 | } | ||
| 191 | ac.sizeMu.Lock() | 190 | ac.sizeMu.Lock() | ||
| 192 | - | ac.size = size | |||
| 193 | ac.geometry.Size = size | 191 | ac.geometry.Size = size | ||
| 194 | ac.geometry = ac.geometry.NormalizePixels() | 192 | ac.geometry = ac.geometry.NormalizePixels() | ||
| 195 | ac.sizeMu.Unlock() | 193 | ac.sizeMu.Unlock() | ||
| @@ -204,7 +202,7 @@ func (ac *attachedClient) geometrySnapshot() domain.Geometry { | |||||
| 204 | if ac.geometry.Valid() { | 202 | if ac.geometry.Valid() { | ||
| 205 | return ac.geometry | 203 | return ac.geometry | ||
| 206 | } | 204 | } | ||
| 207 | - | return domain.Geometry{Size: ac.size} | 205 | + | return domain.Geometry{Size: ac.geometry.Size} |
| 208 | } | 206 | } | ||
| 209 | 207 | ||||
| 210 | func (ac *attachedClient) setGeometry(geometry domain.Geometry) { | 208 | func (ac *attachedClient) setGeometry(geometry domain.Geometry) { | ||
| @@ -213,7 +211,6 @@ func (ac *attachedClient) setGeometry(geometry domain.Geometry) { | |||||
| 213 | } | 211 | } | ||
| 214 | geometry = geometry.NormalizePixels() | 212 | geometry = geometry.NormalizePixels() | ||
| 215 | ac.sizeMu.Lock() | 213 | ac.sizeMu.Lock() | ||
| 216 | - | ac.size = geometry.Size | |||
| 217 | ac.geometry = geometry | 214 | ac.geometry = geometry | ||
| 218 | ac.sizeMu.Unlock() | 215 | ac.sizeMu.Unlock() | ||
| 219 | } | 216 | } | ||
| @@ -772,7 +769,6 @@ func (d *Daemon) prepareAttachedClientLocked(sess *session, tr ports.ServerConne | |||||
| 772 | ac := &attachedClient{ | 769 | ac := &attachedClient{ | ||
| 773 | tr: tr, | 770 | tr: tr, | ||
| 774 | output: output, | 771 | output: output, | ||
| 775 | - | size: geometry.Size, | |||
| 776 | geometry: geometry, | 772 | geometry: geometry, | ||
| 777 | view: attachmentView{windowRows: geometry.Rows, windowSet: true}, | 773 | view: attachmentView{windowRows: geometry.Rows, windowSet: true}, | ||
| 778 | clientID: opts.clientID, | 774 | clientID: opts.clientID, | ||
M internal/usecase/daemon/copy_scroll_ownership_test.go +2 -1
| @@ -4,6 +4,7 @@ import ( | |||
| 4 | 4 | "testing" | |
| 5 | 5 | ||
| 6 | 6 | renderer "github.com/bnema/vev-vt" | |
| 7 | + | "github.com/bnema/vev/internal/domain" | |
| 7 | 8 | "github.com/bnema/vev/internal/protocol" | |
| 8 | 9 | scopy "github.com/bnema/vev/internal/usecase/copy" | |
| 9 | 10 | "github.com/stretchr/testify/require" | |
| @@ -94,7 +95,7 @@ func TestCopyExitRefreshesAfterOtherAttachmentConsumesDamage(t *testing.T) { | |||
| 94 | 95 | copyBefore := captureTestFrame(ac.render.cache.frame) | |
| 95 | 96 | ||
| 96 | 97 | tr, peerSends := newCapturingTransport(t) | |
| 97 | - | peer := &attachedClient{tr: tr, output: newOutputStateStream(), size: ac.size} | |
| 98 | + | peer := &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: ac.geometry.Size}} | |
| 98 | 99 | peer.output.attachment = peer | |
| 99 | 100 | peer.initOverlays() | |
| 100 | 101 | peer.setSession(sess) | |
| @@ -4,6 +4,7 @@ import ( | |||||
| 4 | "testing" | 4 | "testing" | ||
| 5 | 5 | ||||
| 6 | renderer "github.com/bnema/vev-vt" | 6 | renderer "github.com/bnema/vev-vt" | ||
| 7 | + | "github.com/bnema/vev/internal/domain" | |||
| 7 | "github.com/bnema/vev/internal/protocol" | 8 | "github.com/bnema/vev/internal/protocol" | ||
| 8 | scopy "github.com/bnema/vev/internal/usecase/copy" | 9 | scopy "github.com/bnema/vev/internal/usecase/copy" | ||
| 9 | "github.com/stretchr/testify/require" | 10 | "github.com/stretchr/testify/require" | ||
| @@ -94,7 +95,7 @@ func TestCopyExitRefreshesAfterOtherAttachmentConsumesDamage(t *testing.T) { | |||||
| 94 | copyBefore := captureTestFrame(ac.render.cache.frame) | 95 | copyBefore := captureTestFrame(ac.render.cache.frame) | ||
| 95 | 96 | ||||
| 96 | tr, peerSends := newCapturingTransport(t) | 97 | tr, peerSends := newCapturingTransport(t) | ||
| 97 | - | peer := &attachedClient{tr: tr, output: newOutputStateStream(), size: ac.size} | 98 | + | peer := &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: ac.geometry.Size}} |
| 98 | peer.output.attachment = peer | 99 | peer.output.attachment = peer | ||
| 99 | peer.initOverlays() | 100 | peer.initOverlays() | ||
| 100 | peer.setSession(sess) | 101 | peer.setSession(sess) | ||
M internal/usecase/daemon/daemon_test.go +4 -3
| @@ -26,10 +26,11 @@ import ( | |||
| 26 | 26 | ||
| 27 | 27 | // stubClock returns timers whose channel never fires, so a scheduler under it | |
| 28 | 28 | // blocks in its debounce loop until the session context is cancelled. Used by | |
| 29 | - | // every test that is not specifically exercising the debounce. | |
| 29 | + | // every test that is not specifically exercising the debounce. Now reports a | |
| 30 | + | // fixed non-zero instant so recorded timestamps are deterministic and set. | |
| 30 | 31 | type stubClock struct{} | |
| 31 | 32 | ||
| 32 | - | func (stubClock) Now() time.Time { return time.Time{} } | |
| 33 | + | func (stubClock) Now() time.Time { return time.Unix(1_700_000_000, 0) } | |
| 33 | 34 | func (stubClock) NewTimer(time.Duration) ports.Timer { return stubTimer{} } | |
| 34 | 35 | ||
| 35 | 36 | type stubTimer struct{} | |
| @@ -416,7 +417,7 @@ func newManualSessionWithPTYsClockCleanup(t testing.TB, clock ports.Clock, regis | |||
| 416 | 417 | t.Helper() | |
| 417 | 418 | d := newTestDaemonWithCleanup(t, nil, clock, registerCleanup) | |
| 418 | 419 | tr, sends := newCapturingTransport(t) | |
| 419 | - | ac := &attachedClient{tr: tr, output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | |
| 420 | + | ac := &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} | |
| 420 | 421 | ac.output.attachment = ac | |
| 421 | 422 | ac.initOverlays() | |
| 422 | 423 | sctx, cancel := context.WithCancel(d.serveCtx) | |
| @@ -26,10 +26,11 @@ import ( | |||||
| 26 | 26 | ||||
| 27 | // stubClock returns timers whose channel never fires, so a scheduler under it | 27 | // stubClock returns timers whose channel never fires, so a scheduler under it | ||
| 28 | // blocks in its debounce loop until the session context is cancelled. Used by | 28 | // blocks in its debounce loop until the session context is cancelled. Used by | ||
| 29 | - | // every test that is not specifically exercising the debounce. | 29 | + | // every test that is not specifically exercising the debounce. Now reports a |
| 30 | + | // fixed non-zero instant so recorded timestamps are deterministic and set. | |||
| 30 | type stubClock struct{} | 31 | type stubClock struct{} | ||
| 31 | 32 | ||||
| 32 | - | func (stubClock) Now() time.Time { return time.Time{} } | 33 | + | func (stubClock) Now() time.Time { return time.Unix(1_700_000_000, 0) } |
| 33 | func (stubClock) NewTimer(time.Duration) ports.Timer { return stubTimer{} } | 34 | func (stubClock) NewTimer(time.Duration) ports.Timer { return stubTimer{} } | ||
| 34 | 35 | ||||
| 35 | type stubTimer struct{} | 36 | type stubTimer struct{} | ||
| @@ -416,7 +417,7 @@ func newManualSessionWithPTYsClockCleanup(t testing.TB, clock ports.Clock, regis | |||||
| 416 | t.Helper() | 417 | t.Helper() | ||
| 417 | d := newTestDaemonWithCleanup(t, nil, clock, registerCleanup) | 418 | d := newTestDaemonWithCleanup(t, nil, clock, registerCleanup) | ||
| 418 | tr, sends := newCapturingTransport(t) | 419 | tr, sends := newCapturingTransport(t) | ||
| 419 | - | ac := &attachedClient{tr: tr, output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | 420 | + | ac := &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} |
| 420 | ac.output.attachment = ac | 421 | ac.output.attachment = ac | ||
| 421 | ac.initOverlays() | 422 | ac.initOverlays() | ||
| 422 | sctx, cancel := context.WithCancel(d.serveCtx) | 423 | sctx, cancel := context.WithCancel(d.serveCtx) | ||
M internal/usecase/daemon/final_acceptance_test.go +9 -9
| @@ -19,9 +19,9 @@ func TestAcceptancePaletteNewTabUsesSessionGeometryAfterAttachmentResize(t *test | |||
| 19 | 19 | ||
| 20 | 20 | secondTransport, _ := newCapturingTransport(t) | |
| 21 | 21 | second := &attachedClient{ | |
| 22 | - | tr: secondTransport, | |
| 23 | - | output: newOutputStateStream(), | |
| 24 | - | size: domain.Size{Cols: 80, Rows: 24}, | |
| 22 | + | tr: secondTransport, | |
| 23 | + | output: newOutputStateStream(), | |
| 24 | + | geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}, | |
| 25 | 25 | } | |
| 26 | 26 | second.initOverlays() | |
| 27 | 27 | second.clientID[0] = 2 | |
| @@ -85,7 +85,7 @@ func TestAcceptancePaletteNewTabUsesSessionGeometryAfterAttachmentResize(t *test | |||
| 85 | 85 | first.sendMu.Unlock() | |
| 86 | 86 | require.Equal(t, firstEpoch, gotFirstEpoch, "new tab render crossed attachment boundary") | |
| 87 | 87 | second.sendMu.Lock() | |
| 88 | - | gotSecondSize := second.size | |
| 88 | + | gotSecondSize := second.geometry.Size | |
| 89 | 89 | second.sendMu.Unlock() | |
| 90 | 90 | require.Equal(t, domain.Size{Cols: 120, Rows: 40}, gotSecondSize) | |
| 91 | 91 | } | |
| @@ -99,9 +99,9 @@ func TestAcceptanceAttachmentStateIsolationAcrossResetResizeAndDetach(t *testing | |||
| 99 | 99 | ||
| 100 | 100 | secondTransport, _ := newCapturingTransport(t) | |
| 101 | 101 | second := &attachedClient{ | |
| 102 | - | tr: secondTransport, | |
| 103 | - | output: newOutputStateStream(), | |
| 104 | - | size: domain.Size{Cols: 80, Rows: 24}, | |
| 102 | + | tr: secondTransport, | |
| 103 | + | output: newOutputStateStream(), | |
| 104 | + | geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}, | |
| 105 | 105 | } | |
| 106 | 106 | second.initOverlays() | |
| 107 | 107 | second.clientID[0] = 2 | |
| @@ -156,8 +156,8 @@ func TestAcceptanceAttachmentStateIsolationAcrossResetResizeAndDetach(t *testing | |||
| 156 | 156 | require.True(t, admitted) | |
| 157 | 157 | require.True(t, d.resizeAttachmentForLease(secondEffect, domain.Size{Cols: 120, Rows: 40})) | |
| 158 | 158 | secondEffect.End() | |
| 159 | - | require.Equal(t, domain.Size{Cols: 120, Rows: 40}, second.size) | |
| 160 | - | require.Equal(t, domain.Size{Cols: 80, Rows: 24}, first.size, "peer resize crossed attachment boundary") | |
| 159 | + | require.Equal(t, domain.Size{Cols: 120, Rows: 40}, second.geometry.Size) | |
| 160 | + | require.Equal(t, domain.Size{Cols: 80, Rows: 24}, first.geometry.Size, "peer resize crossed attachment boundary") | |
| 161 | 161 | sess.mu.Lock() | |
| 162 | 162 | tb := sess.tabs[0] | |
| 163 | 163 | sess.mu.Unlock() | |
| @@ -19,9 +19,9 @@ func TestAcceptancePaletteNewTabUsesSessionGeometryAfterAttachmentResize(t *test | |||||
| 19 | 19 | ||||
| 20 | secondTransport, _ := newCapturingTransport(t) | 20 | secondTransport, _ := newCapturingTransport(t) | ||
| 21 | second := &attachedClient{ | 21 | second := &attachedClient{ | ||
| 22 | - | tr: secondTransport, | 22 | + | tr: secondTransport, |
| 23 | - | output: newOutputStateStream(), | 23 | + | output: newOutputStateStream(), |
| 24 | - | size: domain.Size{Cols: 80, Rows: 24}, | 24 | + | geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}, |
| 25 | } | 25 | } | ||
| 26 | second.initOverlays() | 26 | second.initOverlays() | ||
| 27 | second.clientID[0] = 2 | 27 | second.clientID[0] = 2 | ||
| @@ -85,7 +85,7 @@ func TestAcceptancePaletteNewTabUsesSessionGeometryAfterAttachmentResize(t *test | |||||
| 85 | first.sendMu.Unlock() | 85 | first.sendMu.Unlock() | ||
| 86 | require.Equal(t, firstEpoch, gotFirstEpoch, "new tab render crossed attachment boundary") | 86 | require.Equal(t, firstEpoch, gotFirstEpoch, "new tab render crossed attachment boundary") | ||
| 87 | second.sendMu.Lock() | 87 | second.sendMu.Lock() | ||
| 88 | - | gotSecondSize := second.size | 88 | + | gotSecondSize := second.geometry.Size |
| 89 | second.sendMu.Unlock() | 89 | second.sendMu.Unlock() | ||
| 90 | require.Equal(t, domain.Size{Cols: 120, Rows: 40}, gotSecondSize) | 90 | require.Equal(t, domain.Size{Cols: 120, Rows: 40}, gotSecondSize) | ||
| 91 | } | 91 | } | ||
| @@ -99,9 +99,9 @@ func TestAcceptanceAttachmentStateIsolationAcrossResetResizeAndDetach(t *testing | |||||
| 99 | 99 | ||||
| 100 | secondTransport, _ := newCapturingTransport(t) | 100 | secondTransport, _ := newCapturingTransport(t) | ||
| 101 | second := &attachedClient{ | 101 | second := &attachedClient{ | ||
| 102 | - | tr: secondTransport, | 102 | + | tr: secondTransport, |
| 103 | - | output: newOutputStateStream(), | 103 | + | output: newOutputStateStream(), |
| 104 | - | size: domain.Size{Cols: 80, Rows: 24}, | 104 | + | geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}, |
| 105 | } | 105 | } | ||
| 106 | second.initOverlays() | 106 | second.initOverlays() | ||
| 107 | second.clientID[0] = 2 | 107 | second.clientID[0] = 2 | ||
| @@ -156,8 +156,8 @@ func TestAcceptanceAttachmentStateIsolationAcrossResetResizeAndDetach(t *testing | |||||
| 156 | require.True(t, admitted) | 156 | require.True(t, admitted) | ||
| 157 | require.True(t, d.resizeAttachmentForLease(secondEffect, domain.Size{Cols: 120, Rows: 40})) | 157 | require.True(t, d.resizeAttachmentForLease(secondEffect, domain.Size{Cols: 120, Rows: 40})) | ||
| 158 | secondEffect.End() | 158 | secondEffect.End() | ||
| 159 | - | require.Equal(t, domain.Size{Cols: 120, Rows: 40}, second.size) | 159 | + | require.Equal(t, domain.Size{Cols: 120, Rows: 40}, second.geometry.Size) |
| 160 | - | require.Equal(t, domain.Size{Cols: 80, Rows: 24}, first.size, "peer resize crossed attachment boundary") | 160 | + | require.Equal(t, domain.Size{Cols: 80, Rows: 24}, first.geometry.Size, "peer resize crossed attachment boundary") |
| 161 | sess.mu.Lock() | 161 | sess.mu.Lock() | ||
| 162 | tb := sess.tabs[0] | 162 | tb := sess.tabs[0] | ||
| 163 | sess.mu.Unlock() | 163 | sess.mu.Unlock() | ||
M internal/usecase/daemon/floating_open_test.go +1 -1
| @@ -226,7 +226,7 @@ func TestFloatingOpenErrorReleasesReturnedPTYBeforePublishingFailure(t *testing. | |||
| 226 | 226 | d := newTestDaemon(t, factory, stubClock{}) | |
| 227 | 227 | tb := newFloatingTestTab(t) | |
| 228 | 228 | tr, _ := newCapturingTransport(t) | |
| 229 | - | ac := &attachedClient{tr: tr, output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | |
| 229 | + | ac := &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} | |
| 230 | 230 | ac.initOverlays() | |
| 231 | 231 | sessCtx, cancelSession := context.WithCancel(t.Context()) | |
| 232 | 232 | sess := &session{sessionCore: sessionCore{id: "floating-open-error", name: "work", attachments: map[*attachedClient]struct{}{ac: {}}}, tabs: []*tab{tb}, ctx: sessCtx, cancel: cancelSession} | |
| @@ -226,7 +226,7 @@ func TestFloatingOpenErrorReleasesReturnedPTYBeforePublishingFailure(t *testing. | |||||
| 226 | d := newTestDaemon(t, factory, stubClock{}) | 226 | d := newTestDaemon(t, factory, stubClock{}) | ||
| 227 | tb := newFloatingTestTab(t) | 227 | tb := newFloatingTestTab(t) | ||
| 228 | tr, _ := newCapturingTransport(t) | 228 | tr, _ := newCapturingTransport(t) | ||
| 229 | - | ac := &attachedClient{tr: tr, output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | 229 | + | ac := &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} |
| 230 | ac.initOverlays() | 230 | ac.initOverlays() | ||
| 231 | sessCtx, cancelSession := context.WithCancel(t.Context()) | 231 | sessCtx, cancelSession := context.WithCancel(t.Context()) | ||
| 232 | sess := &session{sessionCore: sessionCore{id: "floating-open-error", name: "work", attachments: map[*attachedClient]struct{}{ac: {}}}, tabs: []*tab{tb}, ctx: sessCtx, cancel: cancelSession} | 232 | sess := &session{sessionCore: sessionCore{id: "floating-open-error", name: "work", attachments: map[*attachedClient]struct{}{ac: {}}}, tabs: []*tab{tb}, ctx: sessCtx, cancel: cancelSession} | ||
M internal/usecase/daemon/floating_test.go +1 -1
| @@ -768,7 +768,7 @@ func TestFloatingAsyncSpawnFailureToastsOnlyForUserOpen(t *testing.T) { | |||
| 768 | 768 | d := newTestDaemon(t, factory, stubClock{}) | |
| 769 | 769 | tb := newFloatingTestTab(t) | |
| 770 | 770 | tr, _ := newCapturingTransport(t) | |
| 771 | - | ac := &attachedClient{tr: tr, output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | |
| 771 | + | ac := &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} | |
| 772 | 772 | ac.initOverlays() | |
| 773 | 773 | sess := &session{sessionCore: sessionCore{id: "floating-spawn", name: "work", attachments: map[*attachedClient]struct{}{ac: {}}}, tabs: []*tab{tb}, ctx: t.Context()} | |
| 774 | 774 | ac.setSession(sess) | |
| @@ -768,7 +768,7 @@ func TestFloatingAsyncSpawnFailureToastsOnlyForUserOpen(t *testing.T) { | |||||
| 768 | d := newTestDaemon(t, factory, stubClock{}) | 768 | d := newTestDaemon(t, factory, stubClock{}) | ||
| 769 | tb := newFloatingTestTab(t) | 769 | tb := newFloatingTestTab(t) | ||
| 770 | tr, _ := newCapturingTransport(t) | 770 | tr, _ := newCapturingTransport(t) | ||
| 771 | - | ac := &attachedClient{tr: tr, output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | 771 | + | ac := &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} |
| 772 | ac.initOverlays() | 772 | ac.initOverlays() | ||
| 773 | sess := &session{sessionCore: sessionCore{id: "floating-spawn", name: "work", attachments: map[*attachedClient]struct{}{ac: {}}}, tabs: []*tab{tb}, ctx: t.Context()} | 773 | sess := &session{sessionCore: sessionCore{id: "floating-spawn", name: "work", attachments: map[*attachedClient]struct{}{ac: {}}}, tabs: []*tab{tb}, ctx: t.Context()} | ||
| 774 | ac.setSession(sess) | 774 | ac.setSession(sess) | ||
M internal/usecase/daemon/input_test.go +1 -1
| @@ -191,7 +191,7 @@ func TestSwitchTabFirstFrameDoesNotReuseSamePaneIDCapture(t *testing.T) { | |||
| 191 | 191 | d.paint(sess, ac, true, nil) | |
| 192 | 192 | first := awaitFrame(t, sends, "Output") | |
| 193 | 193 | firstOutput := unmarshalTestOutput(t, first.Payload) | |
| 194 | - | terminal := vt.NewScreen(ac.size.Cols, ac.size.Rows) | |
| 194 | + | terminal := vt.NewScreen(ac.geometry.Size.Cols, ac.geometry.Size.Rows) | |
| 195 | 195 | terminal.Write(firstOutput.Data) | |
| 196 | 196 | ||
| 197 | 197 | // A clean pane relies on the attachment capture cache for its retained | |
| @@ -191,7 +191,7 @@ func TestSwitchTabFirstFrameDoesNotReuseSamePaneIDCapture(t *testing.T) { | |||||
| 191 | d.paint(sess, ac, true, nil) | 191 | d.paint(sess, ac, true, nil) | ||
| 192 | first := awaitFrame(t, sends, "Output") | 192 | first := awaitFrame(t, sends, "Output") | ||
| 193 | firstOutput := unmarshalTestOutput(t, first.Payload) | 193 | firstOutput := unmarshalTestOutput(t, first.Payload) | ||
| 194 | - | terminal := vt.NewScreen(ac.size.Cols, ac.size.Rows) | 194 | + | terminal := vt.NewScreen(ac.geometry.Size.Cols, ac.geometry.Size.Rows) |
| 195 | terminal.Write(firstOutput.Data) | 195 | terminal.Write(firstOutput.Data) | ||
| 196 | 196 | ||||
| 197 | // A clean pane relies on the attachment capture cache for its retained | 197 | // A clean pane relies on the attachment capture cache for its retained | ||
M internal/usecase/daemon/move_cleanup_test.go +1 -1
| @@ -62,7 +62,7 @@ func TestMovePaneCleanupUsesCapturedSourceAttachment(t *testing.T) { | |||
| 62 | 62 | ||
| 63 | 63 | replacementTransport, _ := newCapturingTransport(t) | |
| 64 | 64 | replacement := &attachedClient{ | |
| 65 | - | tr: replacementTransport, output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}, | |
| 65 | + | tr: replacementTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}, | |
| 66 | 66 | } | |
| 67 | 67 | replacement.initOverlays() | |
| 68 | 68 | replacement.setSession(source) | |
| @@ -62,7 +62,7 @@ func TestMovePaneCleanupUsesCapturedSourceAttachment(t *testing.T) { | |||||
| 62 | 62 | ||||
| 63 | replacementTransport, _ := newCapturingTransport(t) | 63 | replacementTransport, _ := newCapturingTransport(t) | ||
| 64 | replacement := &attachedClient{ | 64 | replacement := &attachedClient{ | ||
| 65 | - | tr: replacementTransport, output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}, | 65 | + | tr: replacementTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}, |
| 66 | } | 66 | } | ||
| 67 | replacement.initOverlays() | 67 | replacement.initOverlays() | ||
| 68 | replacement.setSession(source) | 68 | replacement.setSession(source) | ||
M internal/usecase/daemon/move_gate_race_test.go +1 -1
| @@ -186,7 +186,7 @@ func TestPaletteFinalCloseEndsCurrentEffectBeforeDrainingPeers(t *testing.T) { | |||
| 186 | 186 | d, sess, current, _ := newManualSessionWithPTYs(t, newQuietPTY()) | |
| 187 | 187 | d.attachCoordinator(sess, nil, current, true) | |
| 188 | 188 | otherTransport := &closeTrackingTransport{} | |
| 189 | - | other := &attachedClient{tr: otherTransport, output: newOutputStateStream(), size: current.size} | |
| 189 | + | other := &attachedClient{tr: otherTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: current.geometry.Size}} | |
| 190 | 190 | other.initOverlays() | |
| 191 | 191 | other.setSession(sess) | |
| 192 | 192 | sess.mu.Lock() | |
| @@ -186,7 +186,7 @@ func TestPaletteFinalCloseEndsCurrentEffectBeforeDrainingPeers(t *testing.T) { | |||||
| 186 | d, sess, current, _ := newManualSessionWithPTYs(t, newQuietPTY()) | 186 | d, sess, current, _ := newManualSessionWithPTYs(t, newQuietPTY()) | ||
| 187 | d.attachCoordinator(sess, nil, current, true) | 187 | d.attachCoordinator(sess, nil, current, true) | ||
| 188 | otherTransport := &closeTrackingTransport{} | 188 | otherTransport := &closeTrackingTransport{} | ||
| 189 | - | other := &attachedClient{tr: otherTransport, output: newOutputStateStream(), size: current.size} | 189 | + | other := &attachedClient{tr: otherTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: current.geometry.Size}} |
| 190 | other.initOverlays() | 190 | other.initOverlays() | ||
| 191 | other.setSession(sess) | 191 | other.setSession(sess) | ||
| 192 | sess.mu.Lock() | 192 | sess.mu.Lock() | ||
M internal/usecase/daemon/move_picker_test.go +2 -2
| @@ -390,10 +390,10 @@ func TestMovePickerCompositeFollow(t *testing.T) { | |||
| 390 | 390 | source.tabs = append(source.tabs, newTabWithStableID("remaining", "remaining-pane", pty, movedTab.size)) | |
| 391 | 391 | publishTiledPaneOwners(source, source.tabs[1]) | |
| 392 | 392 | } | |
| 393 | - | peer := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), size: ac.size} | |
| 393 | + | peer := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), geometry: domain.Geometry{Size: ac.geometry.Size}} | |
| 394 | 394 | peer.setSession(source) | |
| 395 | 395 | source.registerAttachment(peer) | |
| 396 | - | destinationPeer := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), size: ac.size} | |
| 396 | + | destinationPeer := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), geometry: domain.Geometry{Size: ac.geometry.Size}} | |
| 397 | 397 | destinationPeer.setSession(destination) | |
| 398 | 398 | destination.registerAttachment(destinationPeer) | |
| 399 | 399 | destination.selectAttachmentTabLocked(destinationPeer, domain.TabStableID(destinationTab.stableID)) | |
| @@ -390,10 +390,10 @@ func TestMovePickerCompositeFollow(t *testing.T) { | |||||
| 390 | source.tabs = append(source.tabs, newTabWithStableID("remaining", "remaining-pane", pty, movedTab.size)) | 390 | source.tabs = append(source.tabs, newTabWithStableID("remaining", "remaining-pane", pty, movedTab.size)) | ||
| 391 | publishTiledPaneOwners(source, source.tabs[1]) | 391 | publishTiledPaneOwners(source, source.tabs[1]) | ||
| 392 | } | 392 | } | ||
| 393 | - | peer := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), size: ac.size} | 393 | + | peer := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), geometry: domain.Geometry{Size: ac.geometry.Size}} |
| 394 | peer.setSession(source) | 394 | peer.setSession(source) | ||
| 395 | source.registerAttachment(peer) | 395 | source.registerAttachment(peer) | ||
| 396 | - | destinationPeer := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), size: ac.size} | 396 | + | destinationPeer := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), geometry: domain.Geometry{Size: ac.geometry.Size}} |
| 397 | destinationPeer.setSession(destination) | 397 | destinationPeer.setSession(destination) | ||
| 398 | destination.registerAttachment(destinationPeer) | 398 | destination.registerAttachment(destinationPeer) | ||
| 399 | destination.selectAttachmentTabLocked(destinationPeer, domain.TabStableID(destinationTab.stableID)) | 399 | destination.selectAttachmentTabLocked(destinationPeer, domain.TabStableID(destinationTab.stableID)) | ||
M internal/usecase/daemon/move_test.go +1 -1
| @@ -142,7 +142,7 @@ func TestMovePaneRetiresSourceParkedClients(t *testing.T) { | |||
| 142 | 142 | movedTab.stableID = "source-tab" | |
| 143 | 143 | ||
| 144 | 144 | transport := &closeTrackingTransport{} | |
| 145 | - | parked := &attachedClient{tr: transport, output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}, resumeCapable: true} | |
| 145 | + | parked := &attachedClient{tr: transport, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}, resumeCapable: true} | |
| 146 | 146 | parked.initOverlays() | |
| 147 | 147 | require.True(t, d.parkAttachment(source, parked)) | |
| 148 | 148 | destinationPTY := newQuietPTY() | |
| @@ -142,7 +142,7 @@ func TestMovePaneRetiresSourceParkedClients(t *testing.T) { | |||||
| 142 | movedTab.stableID = "source-tab" | 142 | movedTab.stableID = "source-tab" | ||
| 143 | 143 | ||||
| 144 | transport := &closeTrackingTransport{} | 144 | transport := &closeTrackingTransport{} | ||
| 145 | - | parked := &attachedClient{tr: transport, output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}, resumeCapable: true} | 145 | + | parked := &attachedClient{tr: transport, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}, resumeCapable: true} |
| 146 | parked.initOverlays() | 146 | parked.initOverlays() | ||
| 147 | require.True(t, d.parkAttachment(source, parked)) | 147 | require.True(t, d.parkAttachment(source, parked)) | ||
| 148 | destinationPTY := newQuietPTY() | 148 | destinationPTY := newQuietPTY() | ||
M internal/usecase/daemon/navigation_attachment_handoff_test.go +1 -1
| @@ -172,7 +172,7 @@ func TestStoppedSessionHandoffDoesNotResumeAfterInitiatorReplacement(t *testing. | |||
| 172 | 172 | t.Fatal("stopped-session handoff did not release its role ticket") | |
| 173 | 173 | } | |
| 174 | 174 | ||
| 175 | - | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), size: old.size} | |
| 175 | + | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), geometry: domain.Geometry{Size: old.geometry.Size}} | |
| 176 | 176 | next.initOverlays() | |
| 177 | 177 | replacement, err := d.transitionAttachment(attachmentTransitionRequest{ | |
| 178 | 178 | target: source, next: next, expectedTransport: next.transportSnapshot(), ready: true, | |
| @@ -172,7 +172,7 @@ func TestStoppedSessionHandoffDoesNotResumeAfterInitiatorReplacement(t *testing. | |||||
| 172 | t.Fatal("stopped-session handoff did not release its role ticket") | 172 | t.Fatal("stopped-session handoff did not release its role ticket") | ||
| 173 | } | 173 | } | ||
| 174 | 174 | ||||
| 175 | - | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), size: old.size} | 175 | + | next := &attachedClient{tr: &closeTrackingTransport{}, output: newOutputStateStream(), geometry: domain.Geometry{Size: old.geometry.Size}} |
| 176 | next.initOverlays() | 176 | next.initOverlays() | ||
| 177 | replacement, err := d.transitionAttachment(attachmentTransitionRequest{ | 177 | replacement, err := d.transitionAttachment(attachmentTransitionRequest{ | ||
| 178 | target: source, next: next, expectedTransport: next.transportSnapshot(), ready: true, | 178 | target: source, next: next, expectedTransport: next.transportSnapshot(), ready: true, | ||
M internal/usecase/daemon/notice_render_test.go +3 -3
| @@ -81,14 +81,14 @@ func containsTopRight(rows []string, text string, minCol int) bool { | |||
| 81 | 81 | func TestPaintComposesNoticeToastTopRightAndExpiresOnTTL(t *testing.T) { | |
| 82 | 82 | clk := newNoticeClock() | |
| 83 | 83 | d, sess, ac, sends := newNoticeFixture(t, clk) | |
| 84 | - | screen := vt.NewScreen(ac.size.Cols, ac.size.Rows) | |
| 84 | + | screen := vt.NewScreen(ac.geometry.Size.Cols, ac.geometry.Size.Rows) | |
| 85 | 85 | ||
| 86 | 86 | d.notify(sess, domain.NoticeError, domain.NoticePaneSpawn, "couldn't open pane", nil) | |
| 87 | 87 | awaitToastCount(t, ac, 1) | |
| 88 | 88 | replayOutputFrames(t, screen, drainAllFrames(sends)) | |
| 89 | 89 | ||
| 90 | 90 | rows := frameRows(captureTestFrame(screen)) | |
| 91 | - | if !containsTopRight(rows, "couldn't open pane", ac.size.Cols/2) { | |
| 91 | + | if !containsTopRight(rows, "couldn't open pane", ac.geometry.Size.Cols/2) { | |
| 92 | 92 | t.Fatalf("composed frame missing toast message top-right:\n%s", strings.Join(rows, "\n")) | |
| 93 | 93 | } | |
| 94 | 94 | ||
| @@ -102,7 +102,7 @@ func TestPaintComposesNoticeToastTopRightAndExpiresOnTTL(t *testing.T) { | |||
| 102 | 102 | } | |
| 103 | 103 | ||
| 104 | 104 | rows = frameRows(captureTestFrame(screen)) | |
| 105 | - | if containsTopRight(rows, "couldn't open pane", ac.size.Cols/2) { | |
| 105 | + | if containsTopRight(rows, "couldn't open pane", ac.geometry.Size.Cols/2) { | |
| 106 | 106 | t.Fatalf("toast message still present after TTL expired:\n%s", strings.Join(rows, "\n")) | |
| 107 | 107 | } | |
| 108 | 108 | } | |
| @@ -81,14 +81,14 @@ func containsTopRight(rows []string, text string, minCol int) bool { | |||||
| 81 | func TestPaintComposesNoticeToastTopRightAndExpiresOnTTL(t *testing.T) { | 81 | func TestPaintComposesNoticeToastTopRightAndExpiresOnTTL(t *testing.T) { | ||
| 82 | clk := newNoticeClock() | 82 | clk := newNoticeClock() | ||
| 83 | d, sess, ac, sends := newNoticeFixture(t, clk) | 83 | d, sess, ac, sends := newNoticeFixture(t, clk) | ||
| 84 | - | screen := vt.NewScreen(ac.size.Cols, ac.size.Rows) | 84 | + | screen := vt.NewScreen(ac.geometry.Size.Cols, ac.geometry.Size.Rows) |
| 85 | 85 | ||||
| 86 | d.notify(sess, domain.NoticeError, domain.NoticePaneSpawn, "couldn't open pane", nil) | 86 | d.notify(sess, domain.NoticeError, domain.NoticePaneSpawn, "couldn't open pane", nil) | ||
| 87 | awaitToastCount(t, ac, 1) | 87 | awaitToastCount(t, ac, 1) | ||
| 88 | replayOutputFrames(t, screen, drainAllFrames(sends)) | 88 | replayOutputFrames(t, screen, drainAllFrames(sends)) | ||
| 89 | 89 | ||||
| 90 | rows := frameRows(captureTestFrame(screen)) | 90 | rows := frameRows(captureTestFrame(screen)) | ||
| 91 | - | if !containsTopRight(rows, "couldn't open pane", ac.size.Cols/2) { | 91 | + | if !containsTopRight(rows, "couldn't open pane", ac.geometry.Size.Cols/2) { |
| 92 | t.Fatalf("composed frame missing toast message top-right:\n%s", strings.Join(rows, "\n")) | 92 | t.Fatalf("composed frame missing toast message top-right:\n%s", strings.Join(rows, "\n")) | ||
| 93 | } | 93 | } | ||
| 94 | 94 | ||||
| @@ -102,7 +102,7 @@ func TestPaintComposesNoticeToastTopRightAndExpiresOnTTL(t *testing.T) { | |||||
| 102 | } | 102 | } | ||
| 103 | 103 | ||||
| 104 | rows = frameRows(captureTestFrame(screen)) | 104 | rows = frameRows(captureTestFrame(screen)) | ||
| 105 | - | if containsTopRight(rows, "couldn't open pane", ac.size.Cols/2) { | 105 | + | if containsTopRight(rows, "couldn't open pane", ac.geometry.Size.Cols/2) { |
| 106 | t.Fatalf("toast message still present after TTL expired:\n%s", strings.Join(rows, "\n")) | 106 | t.Fatalf("toast message still present after TTL expired:\n%s", strings.Join(rows, "\n")) | ||
| 107 | } | 107 | } | ||
| 108 | } | 108 | } | ||
M internal/usecase/daemon/notify_test.go +5 -5
| @@ -278,7 +278,7 @@ func newNoticeFixture(t *testing.T, clk ports.Clock) (*Daemon, *session, *attach | |||
| 278 | 278 | p, _ := newBlockingPTY(t) | |
| 279 | 279 | d := newTestDaemon(t, nil, clk) | |
| 280 | 280 | tr, sends := newCapturingTransport(t) | |
| 281 | - | ac := &attachedClient{tr: tr, output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | |
| 281 | + | ac := &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} | |
| 282 | 282 | ac.initOverlays() | |
| 283 | 283 | sctx, cancel := context.WithCancel(d.serveCtx) | |
| 284 | 284 | wctx, wcancel := context.WithCancel(sctx) | |
| @@ -443,7 +443,7 @@ func TestReportErrorNonBenignIsNeverDropped(t *testing.T) { | |||
| 443 | 443 | func TestNotifyRoutesToSessionClientOnly(t *testing.T) { | |
| 444 | 444 | d, sess, ac, _ := newNoticeFixture(t, newNoticeClock()) | |
| 445 | 445 | ||
| 446 | - | other := &attachedClient{output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | |
| 446 | + | other := &attachedClient{output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} | |
| 447 | 447 | other.initOverlays() | |
| 448 | 448 | sess2 := &session{sessionCore: sessionCore{id: "manual-2", name: "other", attachments: map[*attachedClient]struct{}{other: {}}}, ctx: sess.ctx, cancel: func() {}} | |
| 449 | 449 | other.setSession(sess2) | |
| @@ -533,7 +533,7 @@ func TestSessionScopedNoticeQueuedWhileDetached(t *testing.T) { | |||
| 533 | 533 | clearAttachmentsForTestLocked(sessA) | |
| 534 | 534 | sessA.mu.Unlock() | |
| 535 | 535 | ||
| 536 | - | clientB := &attachedClient{output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | |
| 536 | + | clientB := &attachedClient{output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} | |
| 537 | 537 | clientB.initOverlays() | |
| 538 | 538 | sessB := &session{sessionCore: sessionCore{id: "manual-2", name: "other"}, ctx: sessA.ctx, cancel: func() {}} | |
| 539 | 539 | clientB.setSession(sessB) | |
| @@ -572,7 +572,7 @@ func TestSessionScopedNoticeQueuedWhileDetached(t *testing.T) { | |||
| 572 | 572 | require.Equal(t, "hello from a script", aToasts[0].Message) | |
| 573 | 573 | require.Equal(t, 1, aToasts[0].Count) | |
| 574 | 574 | ||
| 575 | - | secondA := &attachedClient{output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | |
| 575 | + | secondA := &attachedClient{output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} | |
| 576 | 576 | secondA.initOverlays() | |
| 577 | 577 | secondA.setSession(sessA) | |
| 578 | 578 | sessA.mu.Lock() | |
| @@ -586,7 +586,7 @@ func TestSessionScopedNoticeQueuedWhileDetached(t *testing.T) { | |||
| 586 | 586 | func TestNotifyGlobalFansOutToAttachedClients(t *testing.T) { | |
| 587 | 587 | d, sess, ac, _ := newNoticeFixture(t, newNoticeClock()) | |
| 588 | 588 | ||
| 589 | - | second := &attachedClient{output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | |
| 589 | + | second := &attachedClient{output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} | |
| 590 | 590 | second.initOverlays() | |
| 591 | 591 | sess2 := &session{sessionCore: sessionCore{id: "manual-2", name: "other", attachments: map[*attachedClient]struct{}{second: {}}}, ctx: sess.ctx, cancel: func() {}} | |
| 592 | 592 | second.setSession(sess2) | |
| @@ -278,7 +278,7 @@ func newNoticeFixture(t *testing.T, clk ports.Clock) (*Daemon, *session, *attach | |||||
| 278 | p, _ := newBlockingPTY(t) | 278 | p, _ := newBlockingPTY(t) | ||
| 279 | d := newTestDaemon(t, nil, clk) | 279 | d := newTestDaemon(t, nil, clk) | ||
| 280 | tr, sends := newCapturingTransport(t) | 280 | tr, sends := newCapturingTransport(t) | ||
| 281 | - | ac := &attachedClient{tr: tr, output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | 281 | + | ac := &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} |
| 282 | ac.initOverlays() | 282 | ac.initOverlays() | ||
| 283 | sctx, cancel := context.WithCancel(d.serveCtx) | 283 | sctx, cancel := context.WithCancel(d.serveCtx) | ||
| 284 | wctx, wcancel := context.WithCancel(sctx) | 284 | wctx, wcancel := context.WithCancel(sctx) | ||
| @@ -443,7 +443,7 @@ func TestReportErrorNonBenignIsNeverDropped(t *testing.T) { | |||||
| 443 | func TestNotifyRoutesToSessionClientOnly(t *testing.T) { | 443 | func TestNotifyRoutesToSessionClientOnly(t *testing.T) { | ||
| 444 | d, sess, ac, _ := newNoticeFixture(t, newNoticeClock()) | 444 | d, sess, ac, _ := newNoticeFixture(t, newNoticeClock()) | ||
| 445 | 445 | ||||
| 446 | - | other := &attachedClient{output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | 446 | + | other := &attachedClient{output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} |
| 447 | other.initOverlays() | 447 | other.initOverlays() | ||
| 448 | sess2 := &session{sessionCore: sessionCore{id: "manual-2", name: "other", attachments: map[*attachedClient]struct{}{other: {}}}, ctx: sess.ctx, cancel: func() {}} | 448 | sess2 := &session{sessionCore: sessionCore{id: "manual-2", name: "other", attachments: map[*attachedClient]struct{}{other: {}}}, ctx: sess.ctx, cancel: func() {}} | ||
| 449 | other.setSession(sess2) | 449 | other.setSession(sess2) | ||
| @@ -533,7 +533,7 @@ func TestSessionScopedNoticeQueuedWhileDetached(t *testing.T) { | |||||
| 533 | clearAttachmentsForTestLocked(sessA) | 533 | clearAttachmentsForTestLocked(sessA) | ||
| 534 | sessA.mu.Unlock() | 534 | sessA.mu.Unlock() | ||
| 535 | 535 | ||||
| 536 | - | clientB := &attachedClient{output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | 536 | + | clientB := &attachedClient{output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} |
| 537 | clientB.initOverlays() | 537 | clientB.initOverlays() | ||
| 538 | sessB := &session{sessionCore: sessionCore{id: "manual-2", name: "other"}, ctx: sessA.ctx, cancel: func() {}} | 538 | sessB := &session{sessionCore: sessionCore{id: "manual-2", name: "other"}, ctx: sessA.ctx, cancel: func() {}} | ||
| 539 | clientB.setSession(sessB) | 539 | clientB.setSession(sessB) | ||
| @@ -572,7 +572,7 @@ func TestSessionScopedNoticeQueuedWhileDetached(t *testing.T) { | |||||
| 572 | require.Equal(t, "hello from a script", aToasts[0].Message) | 572 | require.Equal(t, "hello from a script", aToasts[0].Message) | ||
| 573 | require.Equal(t, 1, aToasts[0].Count) | 573 | require.Equal(t, 1, aToasts[0].Count) | ||
| 574 | 574 | ||||
| 575 | - | secondA := &attachedClient{output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | 575 | + | secondA := &attachedClient{output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} |
| 576 | secondA.initOverlays() | 576 | secondA.initOverlays() | ||
| 577 | secondA.setSession(sessA) | 577 | secondA.setSession(sessA) | ||
| 578 | sessA.mu.Lock() | 578 | sessA.mu.Lock() | ||
| @@ -586,7 +586,7 @@ func TestSessionScopedNoticeQueuedWhileDetached(t *testing.T) { | |||||
| 586 | func TestNotifyGlobalFansOutToAttachedClients(t *testing.T) { | 586 | func TestNotifyGlobalFansOutToAttachedClients(t *testing.T) { | ||
| 587 | d, sess, ac, _ := newNoticeFixture(t, newNoticeClock()) | 587 | d, sess, ac, _ := newNoticeFixture(t, newNoticeClock()) | ||
| 588 | 588 | ||||
| 589 | - | second := &attachedClient{output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | 589 | + | second := &attachedClient{output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} |
| 590 | second.initOverlays() | 590 | second.initOverlays() | ||
| 591 | sess2 := &session{sessionCore: sessionCore{id: "manual-2", name: "other", attachments: map[*attachedClient]struct{}{second: {}}}, ctx: sess.ctx, cancel: func() {}} | 591 | sess2 := &session{sessionCore: sessionCore{id: "manual-2", name: "other", attachments: map[*attachedClient]struct{}{second: {}}}, ctx: sess.ctx, cancel: func() {}} | ||
| 592 | second.setSession(sess2) | 592 | second.setSession(sess2) | ||
M internal/usecase/daemon/output_state_sync_test.go +2 -2
| @@ -358,7 +358,7 @@ func TestResizeWithoutPTYOutputFlushesOneFullFrameAtDeadline(t *testing.T) { | |||
| 358 | 358 | require.Zero(t, out.Base) | |
| 359 | 359 | require.Contains(t, screenLineText(client, 0), "1") | |
| 360 | 360 | require.Contains(t, screenLineText(client, 23), "work") | |
| 361 | - | require.Equal(t, domain.Size{Cols: 120, Rows: 24}, ac.size) | |
| 361 | + | require.Equal(t, domain.Size{Cols: 120, Rows: 24}, ac.geometry.Size) | |
| 362 | 362 | requireNoOutputFrame(t, sends) | |
| 363 | 363 | } | |
| 364 | 364 | ||
| @@ -392,7 +392,7 @@ func TestResizeBurstFlushesOnlyLatestGeometry(t *testing.T) { | |||
| 392 | 392 | latest.ch <- time.Now() | |
| 393 | 393 | awaitTestCompletion(t, latestDone, "latest resize callback did not complete") | |
| 394 | 394 | awaitFrame(t, sends, "Output") | |
| 395 | - | require.Equal(t, domain.Size{Cols: 120, Rows: 24}, ac.size) | |
| 395 | + | require.Equal(t, domain.Size{Cols: 120, Rows: 24}, ac.geometry.Size) | |
| 396 | 396 | require.Equal(t, 120, testAttachmentTab(sess).focusedPane().screen.Columns()) | |
| 397 | 397 | requireNoOutputFrame(t, sends) | |
| 398 | 398 | } | |
| @@ -358,7 +358,7 @@ func TestResizeWithoutPTYOutputFlushesOneFullFrameAtDeadline(t *testing.T) { | |||||
| 358 | require.Zero(t, out.Base) | 358 | require.Zero(t, out.Base) | ||
| 359 | require.Contains(t, screenLineText(client, 0), "1") | 359 | require.Contains(t, screenLineText(client, 0), "1") | ||
| 360 | require.Contains(t, screenLineText(client, 23), "work") | 360 | require.Contains(t, screenLineText(client, 23), "work") | ||
| 361 | - | require.Equal(t, domain.Size{Cols: 120, Rows: 24}, ac.size) | 361 | + | require.Equal(t, domain.Size{Cols: 120, Rows: 24}, ac.geometry.Size) |
| 362 | requireNoOutputFrame(t, sends) | 362 | requireNoOutputFrame(t, sends) | ||
| 363 | } | 363 | } | ||
| 364 | 364 | ||||
| @@ -392,7 +392,7 @@ func TestResizeBurstFlushesOnlyLatestGeometry(t *testing.T) { | |||||
| 392 | latest.ch <- time.Now() | 392 | latest.ch <- time.Now() | ||
| 393 | awaitTestCompletion(t, latestDone, "latest resize callback did not complete") | 393 | awaitTestCompletion(t, latestDone, "latest resize callback did not complete") | ||
| 394 | awaitFrame(t, sends, "Output") | 394 | awaitFrame(t, sends, "Output") | ||
| 395 | - | require.Equal(t, domain.Size{Cols: 120, Rows: 24}, ac.size) | 395 | + | require.Equal(t, domain.Size{Cols: 120, Rows: 24}, ac.geometry.Size) |
| 396 | require.Equal(t, 120, testAttachmentTab(sess).focusedPane().screen.Columns()) | 396 | require.Equal(t, 120, testAttachmentTab(sess).focusedPane().screen.Columns()) | ||
| 397 | requireNoOutputFrame(t, sends) | 397 | requireNoOutputFrame(t, sends) | ||
| 398 | } | 398 | } | ||
M internal/usecase/daemon/pane_actions_test.go +3 -3
| @@ -660,7 +660,7 @@ func TestFocusDirMovesFocusAndExitsCopyMode(t *testing.T) { | |||
| 660 | 660 | tb.panes["pane-1"].rect = domain.Rect{Width: 20, Height: 10} | |
| 661 | 661 | tb.panes["pane-2"].rect = domain.Rect{X: 21, Width: 20, Height: 10} | |
| 662 | 662 | tr, _ := newCapturingTransport(t) | |
| 663 | - | ac := &attachedClient{tr: tr, output: newOutputStateStream(), size: domain.Size{Cols: 41, Rows: 12}} | |
| 663 | + | ac := &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 41, Rows: 12}}} | |
| 664 | 664 | ac.initOverlays() | |
| 665 | 665 | ac.setSession(sess) | |
| 666 | 666 | require.True(t, sess.registerAttachment(ac)) | |
| @@ -699,7 +699,7 @@ func TestClosePaneRepaintFanoutRespectsAttachmentScope(t *testing.T) { | |||
| 699 | 699 | sends := make([]chan wire.Envelope, 2) | |
| 700 | 700 | for i := range clients { | |
| 701 | 701 | tr, sent := newCapturingTransport(t) | |
| 702 | - | clients[i] = &attachedClient{tr: tr, output: newOutputStateStream(), size: domain.Size{Cols: 41, Rows: 12}} | |
| 702 | + | clients[i] = &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 41, Rows: 12}}} | |
| 703 | 703 | clients[i].clientID[0] = byte(i + 1) | |
| 704 | 704 | clients[i].initOverlays() | |
| 705 | 705 | clients[i].setSession(sess) | |
| @@ -737,7 +737,7 @@ func TestFinalPaneReapClearsCopyModeForEveryAttachment(t *testing.T) { | |||
| 737 | 737 | ||
| 738 | 738 | for i := range 2 { | |
| 739 | 739 | tr, _ := newCapturingTransport(t) | |
| 740 | - | ac := &attachedClient{tr: tr, output: newOutputStateStream(), size: domain.Size{Cols: 41, Rows: 12}} | |
| 740 | + | ac := &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 41, Rows: 12}}} | |
| 741 | 741 | ac.clientID[0] = byte(i + 1) | |
| 742 | 742 | ac.initOverlays() | |
| 743 | 743 | ac.setSession(sess) | |
| @@ -660,7 +660,7 @@ func TestFocusDirMovesFocusAndExitsCopyMode(t *testing.T) { | |||||
| 660 | tb.panes["pane-1"].rect = domain.Rect{Width: 20, Height: 10} | 660 | tb.panes["pane-1"].rect = domain.Rect{Width: 20, Height: 10} | ||
| 661 | tb.panes["pane-2"].rect = domain.Rect{X: 21, Width: 20, Height: 10} | 661 | tb.panes["pane-2"].rect = domain.Rect{X: 21, Width: 20, Height: 10} | ||
| 662 | tr, _ := newCapturingTransport(t) | 662 | tr, _ := newCapturingTransport(t) | ||
| 663 | - | ac := &attachedClient{tr: tr, output: newOutputStateStream(), size: domain.Size{Cols: 41, Rows: 12}} | 663 | + | ac := &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 41, Rows: 12}}} |
| 664 | ac.initOverlays() | 664 | ac.initOverlays() | ||
| 665 | ac.setSession(sess) | 665 | ac.setSession(sess) | ||
| 666 | require.True(t, sess.registerAttachment(ac)) | 666 | require.True(t, sess.registerAttachment(ac)) | ||
| @@ -699,7 +699,7 @@ func TestClosePaneRepaintFanoutRespectsAttachmentScope(t *testing.T) { | |||||
| 699 | sends := make([]chan wire.Envelope, 2) | 699 | sends := make([]chan wire.Envelope, 2) | ||
| 700 | for i := range clients { | 700 | for i := range clients { | ||
| 701 | tr, sent := newCapturingTransport(t) | 701 | tr, sent := newCapturingTransport(t) | ||
| 702 | - | clients[i] = &attachedClient{tr: tr, output: newOutputStateStream(), size: domain.Size{Cols: 41, Rows: 12}} | 702 | + | clients[i] = &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 41, Rows: 12}}} |
| 703 | clients[i].clientID[0] = byte(i + 1) | 703 | clients[i].clientID[0] = byte(i + 1) | ||
| 704 | clients[i].initOverlays() | 704 | clients[i].initOverlays() | ||
| 705 | clients[i].setSession(sess) | 705 | clients[i].setSession(sess) | ||
| @@ -737,7 +737,7 @@ func TestFinalPaneReapClearsCopyModeForEveryAttachment(t *testing.T) { | |||||
| 737 | 737 | ||||
| 738 | for i := range 2 { | 738 | for i := range 2 { | ||
| 739 | tr, _ := newCapturingTransport(t) | 739 | tr, _ := newCapturingTransport(t) | ||
| 740 | - | ac := &attachedClient{tr: tr, output: newOutputStateStream(), size: domain.Size{Cols: 41, Rows: 12}} | 740 | + | ac := &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 41, Rows: 12}}} |
| 741 | ac.clientID[0] = byte(i + 1) | 741 | ac.clientID[0] = byte(i + 1) | ||
| 742 | ac.initOverlays() | 742 | ac.initOverlays() | ||
| 743 | ac.setSession(sess) | 743 | ac.setSession(sess) | ||
M internal/usecase/daemon/performance_benchmark_test.go +5 -5
| @@ -265,7 +265,7 @@ func TestPerformanceFixtureResize(t *testing.T) { | |||
| 265 | 265 | ||
| 266 | 266 | for _, want := range sequence { | |
| 267 | 267 | fixture.resizeTo(want) | |
| 268 | - | require.Equal(t, want, fixture.ac.size) | |
| 268 | + | require.Equal(t, want, fixture.ac.geometry.Size) | |
| 269 | 269 | require.True(t, fixture.resized()) | |
| 270 | 270 | } | |
| 271 | 271 | metrics := fixture.metrics() | |
| @@ -301,7 +301,7 @@ func TestTransactionalResizeMetrics(t *testing.T) { | |||
| 301 | 301 | require.Equal(t, uint64(3), metrics.outputFrames, "commit, resize-failure notice toast, and successful full-reset retry") | |
| 302 | 302 | require.Zero(t, metrics.frameGapEpochs, "every committed epoch emitted its required frame") | |
| 303 | 303 | require.Equal(t, []domain.Size{{Cols: 50, Rows: 28}, {Cols: 50, Rows: 28}}, fixture.pty.requested()) | |
| 304 | - | require.Equal(t, domain.Size{Cols: 100, Rows: 30}, fixture.ac.size) | |
| 304 | + | require.Equal(t, domain.Size{Cols: 100, Rows: 30}, fixture.ac.geometry.Size) | |
| 305 | 305 | } | |
| 306 | 306 | ||
| 307 | 307 | func TestPerformanceFixtureLargeHistoryTopology(t *testing.T) { | |
| @@ -1077,7 +1077,7 @@ func newPerformanceFixtureWithCleanup(t testing.TB, config performanceConfig, re | |||
| 1077 | 1077 | d.clock = clock | |
| 1078 | 1078 | output := &countingOutputTransport{} | |
| 1079 | 1079 | ac.tr = output | |
| 1080 | - | ac.size = config.size | |
| 1080 | + | ac.geometry.Size = config.size | |
| 1081 | 1081 | sess.name = "performance" | |
| 1082 | 1082 | sess.ephemeral = false | |
| 1083 | 1083 | sess.incarnation = domain.IncarnationID{1} | |
| @@ -1300,7 +1300,7 @@ func (f *performanceFixture) retryLatest() { | |||
| 1300 | 1300 | members := make([]resizeMember, 0) | |
| 1301 | 1301 | for _, tb := range tabs { | |
| 1302 | 1302 | tb.mu.Lock() | |
| 1303 | - | plan := prepareTabLayoutForSizeLocked(f.sess, tb, tabSize(f.ac.size)) | |
| 1303 | + | plan := prepareTabLayoutForSizeLocked(f.sess, tb, tabSize(f.ac.geometry.Size)) | |
| 1304 | 1304 | tb.mu.Unlock() | |
| 1305 | 1305 | members = append(members, plan.members...) | |
| 1306 | 1306 | } | |
| @@ -1314,7 +1314,7 @@ func (f *performanceFixture) retryLatest() { | |||
| 1314 | 1314 | } | |
| 1315 | 1315 | ||
| 1316 | 1316 | func (f *performanceFixture) resized() bool { | |
| 1317 | - | if f.ac.size != f.resizedSize { | |
| 1317 | + | if f.ac.geometry.Size != f.resizedSize { | |
| 1318 | 1318 | return false | |
| 1319 | 1319 | } | |
| 1320 | 1320 | for _, tb := range f.sess.tabs { | |
| @@ -265,7 +265,7 @@ func TestPerformanceFixtureResize(t *testing.T) { | |||||
| 265 | 265 | ||||
| 266 | for _, want := range sequence { | 266 | for _, want := range sequence { | ||
| 267 | fixture.resizeTo(want) | 267 | fixture.resizeTo(want) | ||
| 268 | - | require.Equal(t, want, fixture.ac.size) | 268 | + | require.Equal(t, want, fixture.ac.geometry.Size) |
| 269 | require.True(t, fixture.resized()) | 269 | require.True(t, fixture.resized()) | ||
| 270 | } | 270 | } | ||
| 271 | metrics := fixture.metrics() | 271 | metrics := fixture.metrics() | ||
| @@ -301,7 +301,7 @@ func TestTransactionalResizeMetrics(t *testing.T) { | |||||
| 301 | require.Equal(t, uint64(3), metrics.outputFrames, "commit, resize-failure notice toast, and successful full-reset retry") | 301 | require.Equal(t, uint64(3), metrics.outputFrames, "commit, resize-failure notice toast, and successful full-reset retry") | ||
| 302 | require.Zero(t, metrics.frameGapEpochs, "every committed epoch emitted its required frame") | 302 | require.Zero(t, metrics.frameGapEpochs, "every committed epoch emitted its required frame") | ||
| 303 | require.Equal(t, []domain.Size{{Cols: 50, Rows: 28}, {Cols: 50, Rows: 28}}, fixture.pty.requested()) | 303 | require.Equal(t, []domain.Size{{Cols: 50, Rows: 28}, {Cols: 50, Rows: 28}}, fixture.pty.requested()) | ||
| 304 | - | require.Equal(t, domain.Size{Cols: 100, Rows: 30}, fixture.ac.size) | 304 | + | require.Equal(t, domain.Size{Cols: 100, Rows: 30}, fixture.ac.geometry.Size) |
| 305 | } | 305 | } | ||
| 306 | 306 | ||||
| 307 | func TestPerformanceFixtureLargeHistoryTopology(t *testing.T) { | 307 | func TestPerformanceFixtureLargeHistoryTopology(t *testing.T) { | ||
| @@ -1077,7 +1077,7 @@ func newPerformanceFixtureWithCleanup(t testing.TB, config performanceConfig, re | |||||
| 1077 | d.clock = clock | 1077 | d.clock = clock | ||
| 1078 | output := &countingOutputTransport{} | 1078 | output := &countingOutputTransport{} | ||
| 1079 | ac.tr = output | 1079 | ac.tr = output | ||
| 1080 | - | ac.size = config.size | 1080 | + | ac.geometry.Size = config.size |
| 1081 | sess.name = "performance" | 1081 | sess.name = "performance" | ||
| 1082 | sess.ephemeral = false | 1082 | sess.ephemeral = false | ||
| 1083 | sess.incarnation = domain.IncarnationID{1} | 1083 | sess.incarnation = domain.IncarnationID{1} | ||
| @@ -1300,7 +1300,7 @@ func (f *performanceFixture) retryLatest() { | |||||
| 1300 | members := make([]resizeMember, 0) | 1300 | members := make([]resizeMember, 0) | ||
| 1301 | for _, tb := range tabs { | 1301 | for _, tb := range tabs { | ||
| 1302 | tb.mu.Lock() | 1302 | tb.mu.Lock() | ||
| 1303 | - | plan := prepareTabLayoutForSizeLocked(f.sess, tb, tabSize(f.ac.size)) | 1303 | + | plan := prepareTabLayoutForSizeLocked(f.sess, tb, tabSize(f.ac.geometry.Size)) |
| 1304 | tb.mu.Unlock() | 1304 | tb.mu.Unlock() | ||
| 1305 | members = append(members, plan.members...) | 1305 | members = append(members, plan.members...) | ||
| 1306 | } | 1306 | } | ||
| @@ -1314,7 +1314,7 @@ func (f *performanceFixture) retryLatest() { | |||||
| 1314 | } | 1314 | } | ||
| 1315 | 1315 | ||||
| 1316 | func (f *performanceFixture) resized() bool { | 1316 | func (f *performanceFixture) resized() bool { | ||
| 1317 | - | if f.ac.size != f.resizedSize { | 1317 | + | if f.ac.geometry.Size != f.resizedSize { |
| 1318 | return false | 1318 | return false | ||
| 1319 | } | 1319 | } | ||
| 1320 | for _, tb := range f.sess.tabs { | 1320 | for _, tb := range f.sess.tabs { | ||
M internal/usecase/daemon/reduced_color_test.go +1 -1
| @@ -219,7 +219,7 @@ func TestAppliedThemeIsPerAttachment(t *testing.T) { | |||
| 219 | 219 | d, sess, ac16, _ := newManualSessionWithPTYs(t, p) | |
| 220 | 220 | ac16.terminalCapabilities = terminalcap.Capabilities{Color: terminalcap.ColorCapabilities{Mode: terminalcap.ANSI16}} | |
| 221 | 221 | tr, _ := newCapturingTransport(t) | |
| 222 | - | acTrue := &attachedClient{tr: tr, output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | |
| 222 | + | acTrue := &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} | |
| 223 | 223 | acTrue.output.attachment = acTrue | |
| 224 | 224 | acTrue.initOverlays() | |
| 225 | 225 | sess.mu.Lock() | |
| @@ -219,7 +219,7 @@ func TestAppliedThemeIsPerAttachment(t *testing.T) { | |||||
| 219 | d, sess, ac16, _ := newManualSessionWithPTYs(t, p) | 219 | d, sess, ac16, _ := newManualSessionWithPTYs(t, p) | ||
| 220 | ac16.terminalCapabilities = terminalcap.Capabilities{Color: terminalcap.ColorCapabilities{Mode: terminalcap.ANSI16}} | 220 | ac16.terminalCapabilities = terminalcap.Capabilities{Color: terminalcap.ColorCapabilities{Mode: terminalcap.ANSI16}} | ||
| 221 | tr, _ := newCapturingTransport(t) | 221 | tr, _ := newCapturingTransport(t) | ||
| 222 | - | acTrue := &attachedClient{tr: tr, output: newOutputStateStream(), size: domain.Size{Cols: 80, Rows: 24}} | 222 | + | acTrue := &attachedClient{tr: tr, output: newOutputStateStream(), geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}} |
| 223 | acTrue.output.attachment = acTrue | 223 | acTrue.output.attachment = acTrue | ||
| 224 | acTrue.initOverlays() | 224 | acTrue.initOverlays() | ||
| 225 | sess.mu.Lock() | 225 | sess.mu.Lock() | ||
M internal/usecase/daemon/render_coordinator_handshake_test.go +3 -3
| @@ -83,9 +83,9 @@ func TestRenderCoordinatorFanoutDoesNotWaitForSlowTransport(t *testing.T) { | |||
| 83 | 83 | d, sess, healthy, healthySends, _ := newManualTabSession(t, 1) | |
| 84 | 84 | slowTransport := newFanoutBlockingTransport() | |
| 85 | 85 | slow := &attachedClient{ | |
| 86 | - | tr: slowTransport, | |
| 87 | - | output: newOutputStateStream(), | |
| 88 | - | size: domain.Size{Cols: 80, Rows: 24}, | |
| 86 | + | tr: slowTransport, | |
| 87 | + | output: newOutputStateStream(), | |
| 88 | + | geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}, | |
| 89 | 89 | } | |
| 90 | 90 | slow.initOverlays() | |
| 91 | 91 | slow.setSession(sess) | |
| @@ -83,9 +83,9 @@ func TestRenderCoordinatorFanoutDoesNotWaitForSlowTransport(t *testing.T) { | |||||
| 83 | d, sess, healthy, healthySends, _ := newManualTabSession(t, 1) | 83 | d, sess, healthy, healthySends, _ := newManualTabSession(t, 1) | ||
| 84 | slowTransport := newFanoutBlockingTransport() | 84 | slowTransport := newFanoutBlockingTransport() | ||
| 85 | slow := &attachedClient{ | 85 | slow := &attachedClient{ | ||
| 86 | - | tr: slowTransport, | 86 | + | tr: slowTransport, |
| 87 | - | output: newOutputStateStream(), | 87 | + | output: newOutputStateStream(), |
| 88 | - | size: domain.Size{Cols: 80, Rows: 24}, | 88 | + | geometry: domain.Geometry{Size: domain.Size{Cols: 80, Rows: 24}}, |
| 89 | } | 89 | } | ||
| 90 | slow.initOverlays() | 90 | slow.initOverlays() | ||
| 91 | slow.setSession(sess) | 91 | slow.setSession(sess) | ||
M internal/usecase/daemon/render_test.go +2 -2
| @@ -73,7 +73,7 @@ func TestFirstPaintRetainedFloatingPaneEmitsOneReset(t *testing.T) { | |||
| 73 | 73 | defer releasePTY() | |
| 74 | 74 | d, sess, ac, sends := newManualSessionWithPTYs(t, pty) | |
| 75 | 75 | ac.sendMu.Lock() | |
| 76 | - | ac.size = tc.clientSize | |
| 76 | + | ac.geometry.Size = tc.clientSize | |
| 77 | 77 | ac.sendMu.Unlock() | |
| 78 | 78 | ||
| 79 | 79 | // A retained visible popup still needs activation warmup. When the | |
| @@ -404,7 +404,7 @@ func TestPTYReaderRepublishesSynchronizedCompletionAfterAttachmentLifecycle(t *t | |||
| 404 | 404 | rc := d.attachCoordinator(target, nil, detached, true) | |
| 405 | 405 | ||
| 406 | 406 | secondTransport, secondSends := newCapturingTransport(t) | |
| 407 | - | remaining := &attachedClient{tr: secondTransport, output: newOutputStateStream(), size: detached.size} | |
| 407 | + | remaining := &attachedClient{tr: secondTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: detached.geometry.Size}} | |
| 408 | 408 | remaining.initOverlays() | |
| 409 | 409 | remaining.setSession(target) | |
| 410 | 410 | target.mu.Lock() | |
| @@ -73,7 +73,7 @@ func TestFirstPaintRetainedFloatingPaneEmitsOneReset(t *testing.T) { | |||||
| 73 | defer releasePTY() | 73 | defer releasePTY() | ||
| 74 | d, sess, ac, sends := newManualSessionWithPTYs(t, pty) | 74 | d, sess, ac, sends := newManualSessionWithPTYs(t, pty) | ||
| 75 | ac.sendMu.Lock() | 75 | ac.sendMu.Lock() | ||
| 76 | - | ac.size = tc.clientSize | 76 | + | ac.geometry.Size = tc.clientSize |
| 77 | ac.sendMu.Unlock() | 77 | ac.sendMu.Unlock() | ||
| 78 | 78 | ||||
| 79 | // A retained visible popup still needs activation warmup. When the | 79 | // A retained visible popup still needs activation warmup. When the | ||
| @@ -404,7 +404,7 @@ func TestPTYReaderRepublishesSynchronizedCompletionAfterAttachmentLifecycle(t *t | |||||
| 404 | rc := d.attachCoordinator(target, nil, detached, true) | 404 | rc := d.attachCoordinator(target, nil, detached, true) | ||
| 405 | 405 | ||||
| 406 | secondTransport, secondSends := newCapturingTransport(t) | 406 | secondTransport, secondSends := newCapturingTransport(t) | ||
| 407 | - | remaining := &attachedClient{tr: secondTransport, output: newOutputStateStream(), size: detached.size} | 407 | + | remaining := &attachedClient{tr: secondTransport, output: newOutputStateStream(), geometry: domain.Geometry{Size: detached.geometry.Size}} |
| 408 | remaining.initOverlays() | 408 | remaining.initOverlays() | ||
| 409 | remaining.setSession(target) | 409 | remaining.setSession(target) | ||
| 410 | target.mu.Lock() | 410 | target.mu.Lock() | ||
M internal/usecase/daemon/resume_test.go +1 -1
| @@ -1247,7 +1247,7 @@ func TestParkingReleasesPaneCapturesBeforeHeadlessCloseAndResume(t *testing.T) { | |||
| 1247 | 1247 | require.Len(t, sends, 1) | |
| 1248 | 1248 | output := unmarshalTestOutput(t, sends[0].Payload) | |
| 1249 | 1249 | require.Zero(t, output.Base, "resume must start with a complete frame") | |
| 1250 | - | terminal := vt.NewScreen(resumedAC.size.Cols, resumedAC.size.Rows) | |
| 1250 | + | terminal := vt.NewScreen(resumedAC.geometry.Size.Cols, resumedAC.geometry.Size.Rows) | |
| 1251 | 1251 | terminal.Write(output.Data) | |
| 1252 | 1252 | contents := strings.Join(frameRows(terminal), "\n") | |
| 1253 | 1253 | require.Contains(t, contents, "survivor", "resume first paint must contain current headless content") | |
| @@ -1247,7 +1247,7 @@ func TestParkingReleasesPaneCapturesBeforeHeadlessCloseAndResume(t *testing.T) { | |||||
| 1247 | require.Len(t, sends, 1) | 1247 | require.Len(t, sends, 1) | ||
| 1248 | output := unmarshalTestOutput(t, sends[0].Payload) | 1248 | output := unmarshalTestOutput(t, sends[0].Payload) | ||
| 1249 | require.Zero(t, output.Base, "resume must start with a complete frame") | 1249 | require.Zero(t, output.Base, "resume must start with a complete frame") | ||
| 1250 | - | terminal := vt.NewScreen(resumedAC.size.Cols, resumedAC.size.Rows) | 1250 | + | terminal := vt.NewScreen(resumedAC.geometry.Size.Cols, resumedAC.geometry.Size.Rows) |
| 1251 | terminal.Write(output.Data) | 1251 | terminal.Write(output.Data) | ||
| 1252 | contents := strings.Join(frameRows(terminal), "\n") | 1252 | contents := strings.Join(frameRows(terminal), "\n") | ||
| 1253 | require.Contains(t, contents, "survivor", "resume first paint must contain current headless content") | 1253 | require.Contains(t, contents, "survivor", "resume first paint must contain current headless content") | ||
M internal/usecase/daemon/session.go +0 -6
| @@ -2169,12 +2169,6 @@ func (d *Daemon) refreshSessionCwd(sess *session) { | |||
| 2169 | 2169 | markSnapshotDirty(sess) | |
| 2170 | 2170 | } | |
| 2171 | 2171 | ||
| 2172 | - | // childEnv retains the daemon-environment helper for daemon-local legacy callers. | |
| 2173 | - | // Interactive PTY launch paths use childEnvFrom with their session snapshot. | |
| 2174 | - | func (d *Daemon) childEnv(name, tabStableID, paneStableID string) []string { | |
| 2175 | - | return childEnvFrom(d.baseEnv, name, tabStableID, paneStableID) | |
| 2176 | - | } | |
| 2177 | - | ||
| 2178 | 2172 | func copyEnvironment(env []string) []string { | |
| 2179 | 2173 | return append([]string(nil), env...) | |
| 2180 | 2174 | } | |
| @@ -2169,12 +2169,6 @@ func (d *Daemon) refreshSessionCwd(sess *session) { | |||||
| 2169 | markSnapshotDirty(sess) | 2169 | markSnapshotDirty(sess) | ||
| 2170 | } | 2170 | } | ||
| 2171 | 2171 | ||||
| 2172 | - | // childEnv retains the daemon-environment helper for daemon-local legacy callers. | |||
| 2173 | - | // Interactive PTY launch paths use childEnvFrom with their session snapshot. | |||
| 2174 | - | func (d *Daemon) childEnv(name, tabStableID, paneStableID string) []string { | |||
| 2175 | - | return childEnvFrom(d.baseEnv, name, tabStableID, paneStableID) | |||
| 2176 | - | } | |||
| 2177 | - | ||||
| 2178 | func copyEnvironment(env []string) []string { | 2172 | func copyEnvironment(env []string) []string { | ||
| 2179 | return append([]string(nil), env...) | 2173 | return append([]string(nil), env...) | ||
| 2180 | } | 2174 | } | ||
M internal/usecase/daemon/session_test.go +7 -7
| @@ -1255,7 +1255,7 @@ func TestEphemeralPromotionLifecyclePreventsStaleSameNamePaletteTarget(t *testin | |||
| 1255 | 1255 | d.ptys = newFactorySeq(t, firstPTY, secondPTY) | |
| 1256 | 1256 | WithStore(t, store)(d) | |
| 1257 | 1257 | ||
| 1258 | - | first, err := createSessionForTest(d, "0", true, "/tmp", ac.size, terminalEnv{}, d.baseEnv) | |
| 1258 | + | first, err := createSessionForTest(d, "0", true, "/tmp", ac.geometry.Size, terminalEnv{}, d.baseEnv) | |
| 1259 | 1259 | require.NoError(t, err) | |
| 1260 | 1260 | require.NoError(t, d.renameSession(first, "named")) | |
| 1261 | 1261 | staleCreatedAt := first.createdAt | |
| @@ -1263,7 +1263,7 @@ func TestEphemeralPromotionLifecyclePreventsStaleSameNamePaletteTarget(t *testin | |||
| 1263 | 1263 | require.NoError(t, d.killSession(first, protocol.ReasonSessionKilled, true)) | |
| 1264 | 1264 | d.sessWg.Wait() | |
| 1265 | 1265 | ||
| 1266 | - | second, err := createSessionForTest(d, "0", true, "/tmp", ac.size, terminalEnv{}, d.baseEnv) | |
| 1266 | + | second, err := createSessionForTest(d, "0", true, "/tmp", ac.geometry.Size, terminalEnv{}, d.baseEnv) | |
| 1267 | 1267 | require.NoError(t, err) | |
| 1268 | 1268 | require.NoError(t, d.renameSession(second, "named")) | |
| 1269 | 1269 | require.NotEqual(t, staleCreatedAt, second.createdAt) | |
| @@ -1414,7 +1414,7 @@ func TestPickerStoppedTargetKillPurges(t *testing.T) { | |||
| 1414 | 1414 | func TestChildEnvEscapesLegacySessionName(t *testing.T) { | |
| 1415 | 1415 | d := newTestDaemon(t, nil, stubClock{}) | |
| 1416 | 1416 | ||
| 1417 | - | got := d.childEnv("legacy,name=value", "t_alpha", "p_beta") | |
| 1417 | + | got := childEnvFrom(d.baseEnv, "legacy,name=value", "t_alpha", "p_beta") | |
| 1418 | 1418 | ||
| 1419 | 1419 | require.Contains(t, got, "VEV=session=legacy%2Cname%3Dvalue,tab=t_alpha,pane=p_beta") | |
| 1420 | 1420 | } | |
| @@ -1580,7 +1580,7 @@ func TestChildEnvTrueColorCapability(t *testing.T) { | |||
| 1580 | 1580 | d := newTestDaemon(t, nil, stubClock{}) | |
| 1581 | 1581 | d.baseEnv = tt.baseEnv | |
| 1582 | 1582 | ||
| 1583 | - | got := d.childEnv("work", "t_alpha", "p_beta") | |
| 1583 | + | got := childEnvFrom(d.baseEnv, "work", "t_alpha", "p_beta") | |
| 1584 | 1584 | ||
| 1585 | 1585 | for _, want := range tt.wantContain { | |
| 1586 | 1586 | require.Contains(t, got, want) | |
| @@ -1629,7 +1629,7 @@ func TestAttachUpdatesFutureChildEnvTrueColor(t *testing.T) { | |||
| 1629 | 1629 | require.Contains(t, opens[0], "TERM=xterm-256color") | |
| 1630 | 1630 | require.Contains(t, opens[0], "COLORTERM=truecolor") | |
| 1631 | 1631 | require.Contains(t, opens[0], "TERM_PROGRAM=vev") | |
| 1632 | - | require.NoError(t, d.createTab(sess, ac.size)) | |
| 1632 | + | require.NoError(t, d.createTab(sess, ac.geometry.Size)) | |
| 1633 | 1633 | require.Contains(t, opens[1], "TERM=xterm-256color") | |
| 1634 | 1634 | require.Contains(t, opens[1], "COLORTERM=truecolor") | |
| 1635 | 1635 | require.Contains(t, opens[1], "TERM_PROGRAM=vev") | |
| @@ -1675,7 +1675,7 @@ func TestLiveAttachUpdatesFutureChildEnvTrueColor(t *testing.T) { | |||
| 1675 | 1675 | ||
| 1676 | 1676 | require.Contains(t, opens[0], "TERM=xterm-256color") | |
| 1677 | 1677 | require.Contains(t, opens[0], "COLORTERM=truecolor") | |
| 1678 | - | require.NoError(t, d.createTab(sess, ac.size)) | |
| 1678 | + | require.NoError(t, d.createTab(sess, ac.geometry.Size)) | |
| 1679 | 1679 | require.Contains(t, opens[1], "TERM=xterm-256color") | |
| 1680 | 1680 | require.Contains(t, opens[1], "COLORTERM=truecolor") | |
| 1681 | 1681 | require.Contains(t, opens[1], "TERM_PROGRAM=vev") | |
| @@ -1792,7 +1792,7 @@ func TestAttachEnvironmentRefreshesOnlySessionBoundFuturePTYInputs(t *testing.T) | |||
| 1792 | 1792 | require.Equal(t, "/usr/bin/fish", commands[0]) | |
| 1793 | 1793 | require.Equal(t, []string{"SECRET=first", "TERM_PROGRAM_extra=keep", "SHELL=/usr/bin/fish", "A=a=b", "WAYLAND_DISPLAY=wayland-1", "SSH_AUTH_SOCK=/run/first-agent", "TERM=xterm-256color", "COLORTERM=truecolor", "TERM_PROGRAM=vev", "VEV=session=work,tab=" + sess.tabs[0].stableID + ",pane=" + sess.tabs[0].panes["pane-1"].stableID}, envs[0]) | |
| 1794 | 1794 | require.Equal(t, "fish", sess.tabs[0].focusedPaneTitle(false), "the initial tab title identifies the shell requested by its creator") | |
| 1795 | - | require.NoError(t, d.createTab(sess, ac.size)) | |
| 1795 | + | require.NoError(t, d.createTab(sess, ac.geometry.Size)) | |
| 1796 | 1796 | // Only session-bound variables follow the attaching client: the X11-only | |
| 1797 | 1797 | // display group replaces the Wayland one, the absent agent socket is | |
| 1798 | 1798 | // removed, and the creator's shell and ordinary variables stay. | |
| @@ -1255,7 +1255,7 @@ func TestEphemeralPromotionLifecyclePreventsStaleSameNamePaletteTarget(t *testin | |||||
| 1255 | d.ptys = newFactorySeq(t, firstPTY, secondPTY) | 1255 | d.ptys = newFactorySeq(t, firstPTY, secondPTY) | ||
| 1256 | WithStore(t, store)(d) | 1256 | WithStore(t, store)(d) | ||
| 1257 | 1257 | ||||
| 1258 | - | first, err := createSessionForTest(d, "0", true, "/tmp", ac.size, terminalEnv{}, d.baseEnv) | 1258 | + | first, err := createSessionForTest(d, "0", true, "/tmp", ac.geometry.Size, terminalEnv{}, d.baseEnv) |
| 1259 | require.NoError(t, err) | 1259 | require.NoError(t, err) | ||
| 1260 | require.NoError(t, d.renameSession(first, "named")) | 1260 | require.NoError(t, d.renameSession(first, "named")) | ||
| 1261 | staleCreatedAt := first.createdAt | 1261 | staleCreatedAt := first.createdAt | ||
| @@ -1263,7 +1263,7 @@ func TestEphemeralPromotionLifecyclePreventsStaleSameNamePaletteTarget(t *testin | |||||
| 1263 | require.NoError(t, d.killSession(first, protocol.ReasonSessionKilled, true)) | 1263 | require.NoError(t, d.killSession(first, protocol.ReasonSessionKilled, true)) | ||
| 1264 | d.sessWg.Wait() | 1264 | d.sessWg.Wait() | ||
| 1265 | 1265 | ||||
| 1266 | - | second, err := createSessionForTest(d, "0", true, "/tmp", ac.size, terminalEnv{}, d.baseEnv) | 1266 | + | second, err := createSessionForTest(d, "0", true, "/tmp", ac.geometry.Size, terminalEnv{}, d.baseEnv) |
| 1267 | require.NoError(t, err) | 1267 | require.NoError(t, err) | ||
| 1268 | require.NoError(t, d.renameSession(second, "named")) | 1268 | require.NoError(t, d.renameSession(second, "named")) | ||
| 1269 | require.NotEqual(t, staleCreatedAt, second.createdAt) | 1269 | require.NotEqual(t, staleCreatedAt, second.createdAt) | ||
| @@ -1414,7 +1414,7 @@ func TestPickerStoppedTargetKillPurges(t *testing.T) { | |||||
| 1414 | func TestChildEnvEscapesLegacySessionName(t *testing.T) { | 1414 | func TestChildEnvEscapesLegacySessionName(t *testing.T) { | ||
| 1415 | d := newTestDaemon(t, nil, stubClock{}) | 1415 | d := newTestDaemon(t, nil, stubClock{}) | ||
| 1416 | 1416 | ||||
| 1417 | - | got := d.childEnv("legacy,name=value", "t_alpha", "p_beta") | 1417 | + | got := childEnvFrom(d.baseEnv, "legacy,name=value", "t_alpha", "p_beta") |
| 1418 | 1418 | ||||
| 1419 | require.Contains(t, got, "VEV=session=legacy%2Cname%3Dvalue,tab=t_alpha,pane=p_beta") | 1419 | require.Contains(t, got, "VEV=session=legacy%2Cname%3Dvalue,tab=t_alpha,pane=p_beta") | ||
| 1420 | } | 1420 | } | ||
| @@ -1580,7 +1580,7 @@ func TestChildEnvTrueColorCapability(t *testing.T) { | |||||
| 1580 | d := newTestDaemon(t, nil, stubClock{}) | 1580 | d := newTestDaemon(t, nil, stubClock{}) | ||
| 1581 | d.baseEnv = tt.baseEnv | 1581 | d.baseEnv = tt.baseEnv | ||
| 1582 | 1582 | ||||
| 1583 | - | got := d.childEnv("work", "t_alpha", "p_beta") | 1583 | + | got := childEnvFrom(d.baseEnv, "work", "t_alpha", "p_beta") |
| 1584 | 1584 | ||||
| 1585 | for _, want := range tt.wantContain { | 1585 | for _, want := range tt.wantContain { | ||
| 1586 | require.Contains(t, got, want) | 1586 | require.Contains(t, got, want) | ||
| @@ -1629,7 +1629,7 @@ func TestAttachUpdatesFutureChildEnvTrueColor(t *testing.T) { | |||||
| 1629 | require.Contains(t, opens[0], "TERM=xterm-256color") | 1629 | require.Contains(t, opens[0], "TERM=xterm-256color") | ||
| 1630 | require.Contains(t, opens[0], "COLORTERM=truecolor") | 1630 | require.Contains(t, opens[0], "COLORTERM=truecolor") | ||
| 1631 | require.Contains(t, opens[0], "TERM_PROGRAM=vev") | 1631 | require.Contains(t, opens[0], "TERM_PROGRAM=vev") | ||
| 1632 | - | require.NoError(t, d.createTab(sess, ac.size)) | 1632 | + | require.NoError(t, d.createTab(sess, ac.geometry.Size)) |
| 1633 | require.Contains(t, opens[1], "TERM=xterm-256color") | 1633 | require.Contains(t, opens[1], "TERM=xterm-256color") | ||
| 1634 | require.Contains(t, opens[1], "COLORTERM=truecolor") | 1634 | require.Contains(t, opens[1], "COLORTERM=truecolor") | ||
| 1635 | require.Contains(t, opens[1], "TERM_PROGRAM=vev") | 1635 | require.Contains(t, opens[1], "TERM_PROGRAM=vev") | ||
| @@ -1675,7 +1675,7 @@ func TestLiveAttachUpdatesFutureChildEnvTrueColor(t *testing.T) { | |||||
| 1675 | 1675 | ||||
| 1676 | require.Contains(t, opens[0], "TERM=xterm-256color") | 1676 | require.Contains(t, opens[0], "TERM=xterm-256color") | ||
| 1677 | require.Contains(t, opens[0], "COLORTERM=truecolor") | 1677 | require.Contains(t, opens[0], "COLORTERM=truecolor") | ||
| 1678 | - | require.NoError(t, d.createTab(sess, ac.size)) | 1678 | + | require.NoError(t, d.createTab(sess, ac.geometry.Size)) |
| 1679 | require.Contains(t, opens[1], "TERM=xterm-256color") | 1679 | require.Contains(t, opens[1], "TERM=xterm-256color") | ||
| 1680 | require.Contains(t, opens[1], "COLORTERM=truecolor") | 1680 | require.Contains(t, opens[1], "COLORTERM=truecolor") | ||
| 1681 | require.Contains(t, opens[1], "TERM_PROGRAM=vev") | 1681 | require.Contains(t, opens[1], "TERM_PROGRAM=vev") | ||
| @@ -1792,7 +1792,7 @@ func TestAttachEnvironmentRefreshesOnlySessionBoundFuturePTYInputs(t *testing.T) | |||||
| 1792 | require.Equal(t, "/usr/bin/fish", commands[0]) | 1792 | require.Equal(t, "/usr/bin/fish", commands[0]) | ||
| 1793 | require.Equal(t, []string{"SECRET=first", "TERM_PROGRAM_extra=keep", "SHELL=/usr/bin/fish", "A=a=b", "WAYLAND_DISPLAY=wayland-1", "SSH_AUTH_SOCK=/run/first-agent", "TERM=xterm-256color", "COLORTERM=truecolor", "TERM_PROGRAM=vev", "VEV=session=work,tab=" + sess.tabs[0].stableID + ",pane=" + sess.tabs[0].panes["pane-1"].stableID}, envs[0]) | 1793 | require.Equal(t, []string{"SECRET=first", "TERM_PROGRAM_extra=keep", "SHELL=/usr/bin/fish", "A=a=b", "WAYLAND_DISPLAY=wayland-1", "SSH_AUTH_SOCK=/run/first-agent", "TERM=xterm-256color", "COLORTERM=truecolor", "TERM_PROGRAM=vev", "VEV=session=work,tab=" + sess.tabs[0].stableID + ",pane=" + sess.tabs[0].panes["pane-1"].stableID}, envs[0]) | ||
| 1794 | require.Equal(t, "fish", sess.tabs[0].focusedPaneTitle(false), "the initial tab title identifies the shell requested by its creator") | 1794 | require.Equal(t, "fish", sess.tabs[0].focusedPaneTitle(false), "the initial tab title identifies the shell requested by its creator") | ||
| 1795 | - | require.NoError(t, d.createTab(sess, ac.size)) | 1795 | + | require.NoError(t, d.createTab(sess, ac.geometry.Size)) |
| 1796 | // Only session-bound variables follow the attaching client: the X11-only | 1796 | // Only session-bound variables follow the attaching client: the X11-only | ||
| 1797 | // display group replaces the Wayland one, the absent agent socket is | 1797 | // display group replaces the Wayland one, the absent agent socket is | ||
| 1798 | // removed, and the creator's shell and ordinary variables stay. | 1798 | // removed, and the creator's shell and ordinary variables stay. | ||
M internal/usecase/daemon/transactional_resize_test.go +2 -2
| @@ -474,7 +474,7 @@ func TestTransactionalResizeRechecksLeaseAtAttachmentPublication(t *testing.T) { | |||
| 474 | 474 | lease := rc.attachmentLease(ac) | |
| 475 | 475 | epoch := rc.recordResizeRequestForLease(domain.Size{Cols: 100, Rows: 30}, ac, lease) | |
| 476 | 476 | require.NotZero(t, epoch) | |
| 477 | - | initialSize := ac.size | |
| 477 | + | initialSize := ac.geometry.Size | |
| 478 | 478 | ||
| 479 | 479 | entered := make(chan struct{}) | |
| 480 | 480 | release := make(chan struct{}) | |
| @@ -492,7 +492,7 @@ func TestTransactionalResizeRechecksLeaseAtAttachmentPublication(t *testing.T) { | |||
| 492 | 492 | require.NotZero(t, newer) | |
| 493 | 493 | close(release) | |
| 494 | 494 | require.False(t, <-done, "stale resize generation must be rejected") | |
| 495 | - | require.Equal(t, initialSize, ac.size, "stale resize must not publish attachment size") | |
| 495 | + | require.Equal(t, initialSize, ac.geometry.Size, "stale resize must not publish attachment size") | |
| 496 | 496 | if tc.effect == resizeOwnerPostSnapshotDirty { | |
| 497 | 497 | require.False(t, sess.snapDirty.Load(), "stale resize must not publish snapshot dirtiness") | |
| 498 | 498 | } | |
| @@ -474,7 +474,7 @@ func TestTransactionalResizeRechecksLeaseAtAttachmentPublication(t *testing.T) { | |||||
| 474 | lease := rc.attachmentLease(ac) | 474 | lease := rc.attachmentLease(ac) | ||
| 475 | epoch := rc.recordResizeRequestForLease(domain.Size{Cols: 100, Rows: 30}, ac, lease) | 475 | epoch := rc.recordResizeRequestForLease(domain.Size{Cols: 100, Rows: 30}, ac, lease) | ||
| 476 | require.NotZero(t, epoch) | 476 | require.NotZero(t, epoch) | ||
| 477 | - | initialSize := ac.size | 477 | + | initialSize := ac.geometry.Size |
| 478 | 478 | ||||
| 479 | entered := make(chan struct{}) | 479 | entered := make(chan struct{}) | ||
| 480 | release := make(chan struct{}) | 480 | release := make(chan struct{}) | ||
| @@ -492,7 +492,7 @@ func TestTransactionalResizeRechecksLeaseAtAttachmentPublication(t *testing.T) { | |||||
| 492 | require.NotZero(t, newer) | 492 | require.NotZero(t, newer) | ||
| 493 | close(release) | 493 | close(release) | ||
| 494 | require.False(t, <-done, "stale resize generation must be rejected") | 494 | require.False(t, <-done, "stale resize generation must be rejected") | ||
| 495 | - | require.Equal(t, initialSize, ac.size, "stale resize must not publish attachment size") | 495 | + | require.Equal(t, initialSize, ac.geometry.Size, "stale resize must not publish attachment size") |
| 496 | if tc.effect == resizeOwnerPostSnapshotDirty { | 496 | if tc.effect == resizeOwnerPostSnapshotDirty { | ||
| 497 | require.False(t, sess.snapDirty.Load(), "stale resize must not publish snapshot dirtiness") | 497 | require.False(t, sess.snapDirty.Load(), "stale resize must not publish snapshot dirtiness") | ||
| 498 | } | 498 | } | ||