detached 8d17a79

Merge pull request #97 from bnema/fix/restore-pty-test-race

test(daemon): synchronize restore PTY observations
commit
8d17a797a2ca685959f21b049c3fe837e5d1d357
author
bnema · · 2026-07-15 11:23 UTC
committer
GitHub · · 2026-07-15 11:23 UTC
merge of
c4af702 + 8957a00 · diff against the first parent
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
1 file · +28 -7
M internal/usecase/daemon/snapshot_restore_test.go +28 -7
@@ -94,12 +94,13 @@ func TestRestoreSessionUsesDaemonFallbackEnvironmentAndShellBeforeAttach(t *test
9494 require.NoError(t, d.restoreSession(context.Background(), snapcodec.Session{Name: "work", Tabs: []snapcodec.Tab{{
9595 StableID: "tab", Cols: 80, Rows: 24, Tree: layout.NewTree("pane-1"), Panes: []snapcodec.Pane{terminalPane(t, "pane-1", "pane", "/tmp", nil, nil)},
9696 }}}))
97- require.Len(t, factory.opens, 1)
98- require.Equal(t, "/usr/local/bin/daemon-shell", factory.opens[0].command)
97+ initialOpens := factory.snapshotOpens()
98+ require.Len(t, initialOpens, 1)
99+ require.Equal(t, "/usr/local/bin/daemon-shell", initialOpens[0].command)
99100 require.Equal(t, []string{
100101 "ORDINARY=preserved", "SHELL=/usr/local/bin/daemon-shell", "RAW",
101102 "TERM=xterm-256color", "TERM_PROGRAM=vev", "VEV=session=work,tab=tab,pane=pane",
102- }, factory.opens[0].env)
103+ }, initialOpens[0].env)
103104
104105 d.baseEnv[0] = "ORDINARY=changed"
105106 d.mu.Lock()
@@ -120,19 +121,28 @@ func TestRestoreSessionUsesDaemonFallbackEnvironmentAndShellBeforeAttach(t *test
120121 }, tr)
121122 require.NoError(t, err)
122123 require.NoError(t, d.createTab(restored, ac.size))
123- require.Len(t, factory.opens, 2)
124- require.Equal(t, "/bin/attached-shell", factory.opens[1].command)
125124
126125 restored.mu.Lock()
127126 attachedTab := restored.tabs[1]
128127 restored.mu.Unlock()
129128 attachedTab.mu.Lock()
130129 attachedPane := attachedTab.panes["pane-1"]
130+ attachedTabStableID := attachedTab.stableID
131+ attachedPaneStableID := attachedPane.stableID
131132 attachedTab.mu.Unlock()
133+ expectedVEV := "VEV=session=work,tab=" + attachedTabStableID + ",pane=" + attachedPaneStableID
134+ var attachedOpens []restorePTYOpen
135+ for _, open := range factory.snapshotOpens() {
136+ if containsEnv(open.env, expectedVEV) {
137+ attachedOpens = append(attachedOpens, open)
138+ }
139+ }
140+ require.Len(t, attachedOpens, 1)
141+ require.Equal(t, "/bin/attached-shell", attachedOpens[0].command)
132142 require.Equal(t, []string{
133143 "ORDINARY=attached", "SHELL=/bin/attached-shell",
134- "TERM=xterm-256color", "TERM_PROGRAM=vev", "VEV=session=work,tab=" + attachedTab.stableID + ",pane=" + attachedPane.stableID,
135- }, factory.opens[1].env)
144+ "TERM=xterm-256color", "TERM_PROGRAM=vev", expectedVEV,
145+ }, attachedOpens[0].env)
136146 require.NoError(t, d.killSession(restored, ports.ReasonSessionKilled, false))
137147 d.sessWg.Wait()
138148 }
@@ -609,6 +619,17 @@ func (f *restorePTYFactory) Open(ctx context.Context, command string, _ []string
609619 return pty, nil
610620 }
611621
622+func (f *restorePTYFactory) snapshotOpens() []restorePTYOpen {
623+ f.mu.Lock()
624+ defer f.mu.Unlock()
625+ opens := make([]restorePTYOpen, len(f.opens))
626+ copy(opens, f.opens)
627+ for i := range opens {
628+ opens[i].env = append([]string(nil), opens[i].env...)
629+ }
630+ return opens
631+}
632+
612633 type restorePTY struct {
613634 mu sync.Mutex
614635 writes []string
@@ -94,12 +94,13 @@ func TestRestoreSessionUsesDaemonFallbackEnvironmentAndShellBeforeAttach(t *test
94 require.NoError(t, d.restoreSession(context.Background(), snapcodec.Session{Name: "work", Tabs: []snapcodec.Tab{{94 require.NoError(t, d.restoreSession(context.Background(), snapcodec.Session{Name: "work", Tabs: []snapcodec.Tab{{
95 StableID: "tab", Cols: 80, Rows: 24, Tree: layout.NewTree("pane-1"), Panes: []snapcodec.Pane{terminalPane(t, "pane-1", "pane", "/tmp", nil, nil)},95 StableID: "tab", Cols: 80, Rows: 24, Tree: layout.NewTree("pane-1"), Panes: []snapcodec.Pane{terminalPane(t, "pane-1", "pane", "/tmp", nil, nil)},
96 }}}))96 }}}))
97- require.Len(t, factory.opens, 1)97+ initialOpens := factory.snapshotOpens()
98- require.Equal(t, "/usr/local/bin/daemon-shell", factory.opens[0].command)98+ require.Len(t, initialOpens, 1)
99+ require.Equal(t, "/usr/local/bin/daemon-shell", initialOpens[0].command)
99 require.Equal(t, []string{100 require.Equal(t, []string{
100 "ORDINARY=preserved", "SHELL=/usr/local/bin/daemon-shell", "RAW",101 "ORDINARY=preserved", "SHELL=/usr/local/bin/daemon-shell", "RAW",
101 "TERM=xterm-256color", "TERM_PROGRAM=vev", "VEV=session=work,tab=tab,pane=pane",102 "TERM=xterm-256color", "TERM_PROGRAM=vev", "VEV=session=work,tab=tab,pane=pane",
102- }, factory.opens[0].env)103+ }, initialOpens[0].env)
103 104
104 d.baseEnv[0] = "ORDINARY=changed"105 d.baseEnv[0] = "ORDINARY=changed"
105 d.mu.Lock()106 d.mu.Lock()
@@ -120,19 +121,28 @@ func TestRestoreSessionUsesDaemonFallbackEnvironmentAndShellBeforeAttach(t *test
120 }, tr)121 }, tr)
121 require.NoError(t, err)122 require.NoError(t, err)
122 require.NoError(t, d.createTab(restored, ac.size))123 require.NoError(t, d.createTab(restored, ac.size))
123- require.Len(t, factory.opens, 2)
124- require.Equal(t, "/bin/attached-shell", factory.opens[1].command)
125 124
126 restored.mu.Lock()125 restored.mu.Lock()
127 attachedTab := restored.tabs[1]126 attachedTab := restored.tabs[1]
128 restored.mu.Unlock()127 restored.mu.Unlock()
129 attachedTab.mu.Lock()128 attachedTab.mu.Lock()
130 attachedPane := attachedTab.panes["pane-1"]129 attachedPane := attachedTab.panes["pane-1"]
130+ attachedTabStableID := attachedTab.stableID
131+ attachedPaneStableID := attachedPane.stableID
131 attachedTab.mu.Unlock()132 attachedTab.mu.Unlock()
133+ expectedVEV := "VEV=session=work,tab=" + attachedTabStableID + ",pane=" + attachedPaneStableID
134+ var attachedOpens []restorePTYOpen
135+ for _, open := range factory.snapshotOpens() {
136+ if containsEnv(open.env, expectedVEV) {
137+ attachedOpens = append(attachedOpens, open)
138+ }
139+ }
140+ require.Len(t, attachedOpens, 1)
141+ require.Equal(t, "/bin/attached-shell", attachedOpens[0].command)
132 require.Equal(t, []string{142 require.Equal(t, []string{
133 "ORDINARY=attached", "SHELL=/bin/attached-shell",143 "ORDINARY=attached", "SHELL=/bin/attached-shell",
134- "TERM=xterm-256color", "TERM_PROGRAM=vev", "VEV=session=work,tab=" + attachedTab.stableID + ",pane=" + attachedPane.stableID,144+ "TERM=xterm-256color", "TERM_PROGRAM=vev", expectedVEV,
135- }, factory.opens[1].env)145+ }, attachedOpens[0].env)
136 require.NoError(t, d.killSession(restored, ports.ReasonSessionKilled, false))146 require.NoError(t, d.killSession(restored, ports.ReasonSessionKilled, false))
137 d.sessWg.Wait()147 d.sessWg.Wait()
138 }148 }
@@ -609,6 +619,17 @@ func (f *restorePTYFactory) Open(ctx context.Context, command string, _ []string
609 return pty, nil619 return pty, nil
610 }620 }
611 621
622+func (f *restorePTYFactory) snapshotOpens() []restorePTYOpen {
623+ f.mu.Lock()
624+ defer f.mu.Unlock()
625+ opens := make([]restorePTYOpen, len(f.opens))
626+ copy(opens, f.opens)
627+ for i := range opens {
628+ opens[i].env = append([]string(nil), opens[i].env...)
629+ }
630+ return opens
631+}
632+
612 type restorePTY struct {633 type restorePTY struct {
613 mu sync.Mutex634 mu sync.Mutex
614 writes []string635 writes []string

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close