Merge pull request #97 from bnema/fix/restore-pty-test-race
test(daemon): synchronize restore PTY observations
1 file · +28 -7
M internal/usecase/daemon/snapshot_restore_test.go +28 -7
| @@ -94,12 +94,13 @@ func TestRestoreSessionUsesDaemonFallbackEnvironmentAndShellBeforeAttach(t *test | |||
| 94 | 94 | require.NoError(t, d.restoreSession(context.Background(), snapcodec.Session{Name: "work", Tabs: []snapcodec.Tab{{ | |
| 95 | 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) | |
| 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) | |
| 99 | 100 | require.Equal(t, []string{ | |
| 100 | 101 | "ORDINARY=preserved", "SHELL=/usr/local/bin/daemon-shell", "RAW", | |
| 101 | 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 | 105 | d.baseEnv[0] = "ORDINARY=changed" | |
| 105 | 106 | d.mu.Lock() | |
| @@ -120,19 +121,28 @@ func TestRestoreSessionUsesDaemonFallbackEnvironmentAndShellBeforeAttach(t *test | |||
| 120 | 121 | }, tr) | |
| 121 | 122 | require.NoError(t, err) | |
| 122 | 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 | 125 | restored.mu.Lock() | |
| 127 | 126 | attachedTab := restored.tabs[1] | |
| 128 | 127 | restored.mu.Unlock() | |
| 129 | 128 | attachedTab.mu.Lock() | |
| 130 | 129 | attachedPane := attachedTab.panes["pane-1"] | |
| 130 | + | attachedTabStableID := attachedTab.stableID | |
| 131 | + | attachedPaneStableID := attachedPane.stableID | |
| 131 | 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 | 142 | require.Equal(t, []string{ | |
| 133 | 143 | "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) | |
| 136 | 146 | require.NoError(t, d.killSession(restored, ports.ReasonSessionKilled, false)) | |
| 137 | 147 | d.sessWg.Wait() | |
| 138 | 148 | } | |
| @@ -609,6 +619,17 @@ func (f *restorePTYFactory) Open(ctx context.Context, command string, _ []string | |||
| 609 | 619 | 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 | 633 | type restorePTY struct { | |
| 613 | 634 | mu sync.Mutex | |
| 614 | 635 | 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, nil | 619 | 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.Mutex | 634 | mu sync.Mutex | ||
| 614 | writes []string | 635 | writes []string | ||