fix(remote): address PR review findings
- commit
- 8d55edc4f465eea315dd7befdda45d4bbf2e8438
- author
- brice · · 2026-07-07 08:54 UTC
- parent
- 2e8f4cf
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
4 files · +141 -10
M internal/adapters/dgram/transport.go +1 -1
| @@ -501,7 +501,7 @@ func (t *Transport) resendPending() { | |||
| 501 | 501 | } | |
| 502 | 502 | t.mu.Lock() | |
| 503 | 503 | for seq, p := range t.pending { | |
| 504 | - | if p.last.IsZero() || now.Sub(p.last) >= defaultResend { | |
| 504 | + | if p.last.IsZero() || now.Sub(p.last) >= t.resendAfter { | |
| 505 | 505 | p.last = now | |
| 506 | 506 | resend = append(resend, struct { | |
| 507 | 507 | seq uint64 | |
| @@ -501,7 +501,7 @@ func (t *Transport) resendPending() { | |||||
| 501 | } | 501 | } | ||
| 502 | t.mu.Lock() | 502 | t.mu.Lock() | ||
| 503 | for seq, p := range t.pending { | 503 | for seq, p := range t.pending { | ||
| 504 | - | if p.last.IsZero() || now.Sub(p.last) >= defaultResend { | 504 | + | if p.last.IsZero() || now.Sub(p.last) >= t.resendAfter { |
| 505 | p.last = now | 505 | p.last = now | ||
| 506 | resend = append(resend, struct { | 506 | resend = append(resend, struct { | ||
| 507 | seq uint64 | 507 | seq uint64 | ||
M internal/adapters/dgram/transport_test.go +13 -5
| @@ -169,7 +169,6 @@ func TestAuthenticatedPeerChangeUpdatesPeerAndEmitsConnected(t *testing.T) { | |||
| 169 | 169 | a.setLinkState(ports.LinkStateOffline, nil) | |
| 170 | 170 | <-a.LinkEvents() | |
| 171 | 171 | aPC.in <- packet{[]byte("not authenticated"), testAddr("evil")} | |
| 172 | - | time.Sleep(30 * time.Millisecond) | |
| 173 | 172 | if a.Peer().String() != old { | |
| 174 | 173 | t.Fatalf("unauthenticated packet changed peer to %v", a.Peer()) | |
| 175 | 174 | } | |
| @@ -178,10 +177,7 @@ func TestAuthenticatedPeerChangeUpdatesPeerAndEmitsConnected(t *testing.T) { | |||
| 178 | 177 | frags, _ := pdgram.FragmentPayload(9, rec, pdgram.DefaultMTU) | |
| 179 | 178 | raw, _ := pdgram.MarshalFragment(frags[0]) | |
| 180 | 179 | aPC.in <- packet{c.Seal(2, 99, raw, nil), testAddr("evil")} | |
| 181 | - | time.Sleep(30 * time.Millisecond) | |
| 182 | - | if a.Peer().String() != "evil" { | |
| 183 | - | t.Fatalf("authenticated packet did not rehome: %v", a.Peer()) | |
| 184 | - | } | |
| 180 | + | waitPeer(t, a, "evil") | |
| 185 | 181 | select { | |
| 186 | 182 | case ev := <-a.LinkEvents(): | |
| 187 | 183 | if ev.State != ports.LinkStateConnected { | |
| @@ -520,6 +516,18 @@ func recvWithin(t *testing.T, tr *Transport, d time.Duration) ports.Frame { | |||
| 520 | 516 | } | |
| 521 | 517 | } | |
| 522 | 518 | ||
| 519 | + | func waitPeer(t *testing.T, tr *Transport, want string) { | |
| 520 | + | t.Helper() | |
| 521 | + | deadline := time.Now().Add(time.Second) | |
| 522 | + | for time.Now().Before(deadline) { | |
| 523 | + | if tr.Peer().String() == want { | |
| 524 | + | return | |
| 525 | + | } | |
| 526 | + | time.Sleep(time.Millisecond) | |
| 527 | + | } | |
| 528 | + | t.Fatalf("peer=%v, want %s", tr.Peer(), want) | |
| 529 | + | } | |
| 530 | + | ||
| 523 | 531 | func recvErrWithin(t *testing.T, tr *Transport, d time.Duration) (ports.Frame, error) { | |
| 524 | 532 | t.Helper() | |
| 525 | 533 | type result struct { | |
| @@ -169,7 +169,6 @@ func TestAuthenticatedPeerChangeUpdatesPeerAndEmitsConnected(t *testing.T) { | |||||
| 169 | a.setLinkState(ports.LinkStateOffline, nil) | 169 | a.setLinkState(ports.LinkStateOffline, nil) | ||
| 170 | <-a.LinkEvents() | 170 | <-a.LinkEvents() | ||
| 171 | aPC.in <- packet{[]byte("not authenticated"), testAddr("evil")} | 171 | aPC.in <- packet{[]byte("not authenticated"), testAddr("evil")} | ||
| 172 | - | time.Sleep(30 * time.Millisecond) | |||
| 173 | if a.Peer().String() != old { | 172 | if a.Peer().String() != old { | ||
| 174 | t.Fatalf("unauthenticated packet changed peer to %v", a.Peer()) | 173 | t.Fatalf("unauthenticated packet changed peer to %v", a.Peer()) | ||
| 175 | } | 174 | } | ||
| @@ -178,10 +177,7 @@ func TestAuthenticatedPeerChangeUpdatesPeerAndEmitsConnected(t *testing.T) { | |||||
| 178 | frags, _ := pdgram.FragmentPayload(9, rec, pdgram.DefaultMTU) | 177 | frags, _ := pdgram.FragmentPayload(9, rec, pdgram.DefaultMTU) | ||
| 179 | raw, _ := pdgram.MarshalFragment(frags[0]) | 178 | raw, _ := pdgram.MarshalFragment(frags[0]) | ||
| 180 | aPC.in <- packet{c.Seal(2, 99, raw, nil), testAddr("evil")} | 179 | aPC.in <- packet{c.Seal(2, 99, raw, nil), testAddr("evil")} | ||
| 181 | - | time.Sleep(30 * time.Millisecond) | 180 | + | waitPeer(t, a, "evil") |
| 182 | - | if a.Peer().String() != "evil" { | |||
| 183 | - | t.Fatalf("authenticated packet did not rehome: %v", a.Peer()) | |||
| 184 | - | } | |||
| 185 | select { | 181 | select { | ||
| 186 | case ev := <-a.LinkEvents(): | 182 | case ev := <-a.LinkEvents(): | ||
| 187 | if ev.State != ports.LinkStateConnected { | 183 | if ev.State != ports.LinkStateConnected { | ||
| @@ -520,6 +516,18 @@ func recvWithin(t *testing.T, tr *Transport, d time.Duration) ports.Frame { | |||||
| 520 | } | 516 | } | ||
| 521 | } | 517 | } | ||
| 522 | 518 | ||||
| 519 | + | func waitPeer(t *testing.T, tr *Transport, want string) { | |||
| 520 | + | t.Helper() | |||
| 521 | + | deadline := time.Now().Add(time.Second) | |||
| 522 | + | for time.Now().Before(deadline) { | |||
| 523 | + | if tr.Peer().String() == want { | |||
| 524 | + | return | |||
| 525 | + | } | |||
| 526 | + | time.Sleep(time.Millisecond) | |||
| 527 | + | } | |||
| 528 | + | t.Fatalf("peer=%v, want %s", tr.Peer(), want) | |||
| 529 | + | } | |||
| 530 | + | ||||
| 523 | func recvErrWithin(t *testing.T, tr *Transport, d time.Duration) (ports.Frame, error) { | 531 | func recvErrWithin(t *testing.T, tr *Transport, d time.Duration) (ports.Frame, error) { | ||
| 524 | t.Helper() | 532 | t.Helper() | ||
| 525 | type result struct { | 533 | type result struct { | ||
M internal/ports/mocks/mocks.go +117 -0
| @@ -15,6 +15,123 @@ import ( | |||
| 15 | 15 | mock "github.com/stretchr/testify/mock" | |
| 16 | 16 | ) | |
| 17 | 17 | ||
| 18 | + | // NewMockLinkStateReporter creates a new instance of MockLinkStateReporter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 19 | + | // The first argument is typically a *testing.T value. | |
| 20 | + | func NewMockLinkStateReporter(t interface { | |
| 21 | + | mock.TestingT | |
| 22 | + | Cleanup(func()) | |
| 23 | + | }) *MockLinkStateReporter { | |
| 24 | + | mock := &MockLinkStateReporter{} | |
| 25 | + | mock.Mock.Test(t) | |
| 26 | + | ||
| 27 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 28 | + | ||
| 29 | + | return mock | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | // MockLinkStateReporter is an autogenerated mock type for the LinkStateReporter type | |
| 33 | + | type MockLinkStateReporter struct { | |
| 34 | + | mock.Mock | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | type MockLinkStateReporter_Expecter struct { | |
| 38 | + | mock *mock.Mock | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | func (_m *MockLinkStateReporter) EXPECT() *MockLinkStateReporter_Expecter { | |
| 42 | + | return &MockLinkStateReporter_Expecter{mock: &_m.Mock} | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | // LinkEvents provides a mock function for the type MockLinkStateReporter | |
| 46 | + | func (_mock *MockLinkStateReporter) LinkEvents() <-chan ports.LinkEvent { | |
| 47 | + | ret := _mock.Called() | |
| 48 | + | ||
| 49 | + | if len(ret) == 0 { | |
| 50 | + | panic("no return value specified for LinkEvents") | |
| 51 | + | } | |
| 52 | + | ||
| 53 | + | var r0 <-chan ports.LinkEvent | |
| 54 | + | if returnFunc, ok := ret.Get(0).(func() <-chan ports.LinkEvent); ok { | |
| 55 | + | r0 = returnFunc() | |
| 56 | + | } else { | |
| 57 | + | if ret.Get(0) != nil { | |
| 58 | + | r0 = ret.Get(0).(<-chan ports.LinkEvent) | |
| 59 | + | } | |
| 60 | + | } | |
| 61 | + | return r0 | |
| 62 | + | } | |
| 63 | + | ||
| 64 | + | // MockLinkStateReporter_LinkEvents_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LinkEvents' | |
| 65 | + | type MockLinkStateReporter_LinkEvents_Call struct { | |
| 66 | + | *mock.Call | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | // LinkEvents is a helper method to define mock.On call | |
| 70 | + | func (_e *MockLinkStateReporter_Expecter) LinkEvents() *MockLinkStateReporter_LinkEvents_Call { | |
| 71 | + | return &MockLinkStateReporter_LinkEvents_Call{Call: _e.mock.On("LinkEvents")} | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | func (_c *MockLinkStateReporter_LinkEvents_Call) Run(run func()) *MockLinkStateReporter_LinkEvents_Call { | |
| 75 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 76 | + | run() | |
| 77 | + | }) | |
| 78 | + | return _c | |
| 79 | + | } | |
| 80 | + | ||
| 81 | + | func (_c *MockLinkStateReporter_LinkEvents_Call) Return(linkEventCh <-chan ports.LinkEvent) *MockLinkStateReporter_LinkEvents_Call { | |
| 82 | + | _c.Call.Return(linkEventCh) | |
| 83 | + | return _c | |
| 84 | + | } | |
| 85 | + | ||
| 86 | + | func (_c *MockLinkStateReporter_LinkEvents_Call) RunAndReturn(run func() <-chan ports.LinkEvent) *MockLinkStateReporter_LinkEvents_Call { | |
| 87 | + | _c.Call.Return(run) | |
| 88 | + | return _c | |
| 89 | + | } | |
| 90 | + | ||
| 91 | + | // LinkState provides a mock function for the type MockLinkStateReporter | |
| 92 | + | func (_mock *MockLinkStateReporter) LinkState() ports.LinkState { | |
| 93 | + | ret := _mock.Called() | |
| 94 | + | ||
| 95 | + | if len(ret) == 0 { | |
| 96 | + | panic("no return value specified for LinkState") | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | var r0 ports.LinkState | |
| 100 | + | if returnFunc, ok := ret.Get(0).(func() ports.LinkState); ok { | |
| 101 | + | r0 = returnFunc() | |
| 102 | + | } else { | |
| 103 | + | r0 = ret.Get(0).(ports.LinkState) | |
| 104 | + | } | |
| 105 | + | return r0 | |
| 106 | + | } | |
| 107 | + | ||
| 108 | + | // MockLinkStateReporter_LinkState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LinkState' | |
| 109 | + | type MockLinkStateReporter_LinkState_Call struct { | |
| 110 | + | *mock.Call | |
| 111 | + | } | |
| 112 | + | ||
| 113 | + | // LinkState is a helper method to define mock.On call | |
| 114 | + | func (_e *MockLinkStateReporter_Expecter) LinkState() *MockLinkStateReporter_LinkState_Call { | |
| 115 | + | return &MockLinkStateReporter_LinkState_Call{Call: _e.mock.On("LinkState")} | |
| 116 | + | } | |
| 117 | + | ||
| 118 | + | func (_c *MockLinkStateReporter_LinkState_Call) Run(run func()) *MockLinkStateReporter_LinkState_Call { | |
| 119 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 120 | + | run() | |
| 121 | + | }) | |
| 122 | + | return _c | |
| 123 | + | } | |
| 124 | + | ||
| 125 | + | func (_c *MockLinkStateReporter_LinkState_Call) Return(linkState ports.LinkState) *MockLinkStateReporter_LinkState_Call { | |
| 126 | + | _c.Call.Return(linkState) | |
| 127 | + | return _c | |
| 128 | + | } | |
| 129 | + | ||
| 130 | + | func (_c *MockLinkStateReporter_LinkState_Call) RunAndReturn(run func() ports.LinkState) *MockLinkStateReporter_LinkState_Call { | |
| 131 | + | _c.Call.Return(run) | |
| 132 | + | return _c | |
| 133 | + | } | |
| 134 | + | ||
| 18 | 135 | // NewMockPTY creates a new instance of MockPTY. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 19 | 136 | // The first argument is typically a *testing.T value. | |
| 20 | 137 | func NewMockPTY(t interface { | |
| @@ -15,6 +15,123 @@ import ( | |||||
| 15 | mock "github.com/stretchr/testify/mock" | 15 | mock "github.com/stretchr/testify/mock" | ||
| 16 | ) | 16 | ) | ||
| 17 | 17 | ||||
| 18 | + | // NewMockLinkStateReporter creates a new instance of MockLinkStateReporter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |||
| 19 | + | // The first argument is typically a *testing.T value. | |||
| 20 | + | func NewMockLinkStateReporter(t interface { | |||
| 21 | + | mock.TestingT | |||
| 22 | + | Cleanup(func()) | |||
| 23 | + | }) *MockLinkStateReporter { | |||
| 24 | + | mock := &MockLinkStateReporter{} | |||
| 25 | + | mock.Mock.Test(t) | |||
| 26 | + | ||||
| 27 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |||
| 28 | + | ||||
| 29 | + | return mock | |||
| 30 | + | } | |||
| 31 | + | ||||
| 32 | + | // MockLinkStateReporter is an autogenerated mock type for the LinkStateReporter type | |||
| 33 | + | type MockLinkStateReporter struct { | |||
| 34 | + | mock.Mock | |||
| 35 | + | } | |||
| 36 | + | ||||
| 37 | + | type MockLinkStateReporter_Expecter struct { | |||
| 38 | + | mock *mock.Mock | |||
| 39 | + | } | |||
| 40 | + | ||||
| 41 | + | func (_m *MockLinkStateReporter) EXPECT() *MockLinkStateReporter_Expecter { | |||
| 42 | + | return &MockLinkStateReporter_Expecter{mock: &_m.Mock} | |||
| 43 | + | } | |||
| 44 | + | ||||
| 45 | + | // LinkEvents provides a mock function for the type MockLinkStateReporter | |||
| 46 | + | func (_mock *MockLinkStateReporter) LinkEvents() <-chan ports.LinkEvent { | |||
| 47 | + | ret := _mock.Called() | |||
| 48 | + | ||||
| 49 | + | if len(ret) == 0 { | |||
| 50 | + | panic("no return value specified for LinkEvents") | |||
| 51 | + | } | |||
| 52 | + | ||||
| 53 | + | var r0 <-chan ports.LinkEvent | |||
| 54 | + | if returnFunc, ok := ret.Get(0).(func() <-chan ports.LinkEvent); ok { | |||
| 55 | + | r0 = returnFunc() | |||
| 56 | + | } else { | |||
| 57 | + | if ret.Get(0) != nil { | |||
| 58 | + | r0 = ret.Get(0).(<-chan ports.LinkEvent) | |||
| 59 | + | } | |||
| 60 | + | } | |||
| 61 | + | return r0 | |||
| 62 | + | } | |||
| 63 | + | ||||
| 64 | + | // MockLinkStateReporter_LinkEvents_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LinkEvents' | |||
| 65 | + | type MockLinkStateReporter_LinkEvents_Call struct { | |||
| 66 | + | *mock.Call | |||
| 67 | + | } | |||
| 68 | + | ||||
| 69 | + | // LinkEvents is a helper method to define mock.On call | |||
| 70 | + | func (_e *MockLinkStateReporter_Expecter) LinkEvents() *MockLinkStateReporter_LinkEvents_Call { | |||
| 71 | + | return &MockLinkStateReporter_LinkEvents_Call{Call: _e.mock.On("LinkEvents")} | |||
| 72 | + | } | |||
| 73 | + | ||||
| 74 | + | func (_c *MockLinkStateReporter_LinkEvents_Call) Run(run func()) *MockLinkStateReporter_LinkEvents_Call { | |||
| 75 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 76 | + | run() | |||
| 77 | + | }) | |||
| 78 | + | return _c | |||
| 79 | + | } | |||
| 80 | + | ||||
| 81 | + | func (_c *MockLinkStateReporter_LinkEvents_Call) Return(linkEventCh <-chan ports.LinkEvent) *MockLinkStateReporter_LinkEvents_Call { | |||
| 82 | + | _c.Call.Return(linkEventCh) | |||
| 83 | + | return _c | |||
| 84 | + | } | |||
| 85 | + | ||||
| 86 | + | func (_c *MockLinkStateReporter_LinkEvents_Call) RunAndReturn(run func() <-chan ports.LinkEvent) *MockLinkStateReporter_LinkEvents_Call { | |||
| 87 | + | _c.Call.Return(run) | |||
| 88 | + | return _c | |||
| 89 | + | } | |||
| 90 | + | ||||
| 91 | + | // LinkState provides a mock function for the type MockLinkStateReporter | |||
| 92 | + | func (_mock *MockLinkStateReporter) LinkState() ports.LinkState { | |||
| 93 | + | ret := _mock.Called() | |||
| 94 | + | ||||
| 95 | + | if len(ret) == 0 { | |||
| 96 | + | panic("no return value specified for LinkState") | |||
| 97 | + | } | |||
| 98 | + | ||||
| 99 | + | var r0 ports.LinkState | |||
| 100 | + | if returnFunc, ok := ret.Get(0).(func() ports.LinkState); ok { | |||
| 101 | + | r0 = returnFunc() | |||
| 102 | + | } else { | |||
| 103 | + | r0 = ret.Get(0).(ports.LinkState) | |||
| 104 | + | } | |||
| 105 | + | return r0 | |||
| 106 | + | } | |||
| 107 | + | ||||
| 108 | + | // MockLinkStateReporter_LinkState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LinkState' | |||
| 109 | + | type MockLinkStateReporter_LinkState_Call struct { | |||
| 110 | + | *mock.Call | |||
| 111 | + | } | |||
| 112 | + | ||||
| 113 | + | // LinkState is a helper method to define mock.On call | |||
| 114 | + | func (_e *MockLinkStateReporter_Expecter) LinkState() *MockLinkStateReporter_LinkState_Call { | |||
| 115 | + | return &MockLinkStateReporter_LinkState_Call{Call: _e.mock.On("LinkState")} | |||
| 116 | + | } | |||
| 117 | + | ||||
| 118 | + | func (_c *MockLinkStateReporter_LinkState_Call) Run(run func()) *MockLinkStateReporter_LinkState_Call { | |||
| 119 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 120 | + | run() | |||
| 121 | + | }) | |||
| 122 | + | return _c | |||
| 123 | + | } | |||
| 124 | + | ||||
| 125 | + | func (_c *MockLinkStateReporter_LinkState_Call) Return(linkState ports.LinkState) *MockLinkStateReporter_LinkState_Call { | |||
| 126 | + | _c.Call.Return(linkState) | |||
| 127 | + | return _c | |||
| 128 | + | } | |||
| 129 | + | ||||
| 130 | + | func (_c *MockLinkStateReporter_LinkState_Call) RunAndReturn(run func() ports.LinkState) *MockLinkStateReporter_LinkState_Call { | |||
| 131 | + | _c.Call.Return(run) | |||
| 132 | + | return _c | |||
| 133 | + | } | |||
| 134 | + | ||||
| 18 | // NewMockPTY creates a new instance of MockPTY. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | 135 | // NewMockPTY creates a new instance of MockPTY. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | ||
| 19 | // The first argument is typically a *testing.T value. | 136 | // The first argument is typically a *testing.T value. | ||
| 20 | func NewMockPTY(t interface { | 137 | func NewMockPTY(t interface { | ||
M internal/usecase/daemon/resume_test.go +10 -4
| @@ -133,18 +133,24 @@ func TestOutputAckGapForcesFullStateRepaint(t *testing.T) { | |||
| 133 | 133 | ac.advanceOutputAck(2) | |
| 134 | 134 | ac.advanceOutputAck(4) | |
| 135 | 135 | require.Equal(t, uint64(3), ac.outputAck.Load(), "stale or future ACKs must not move output state incorrectly") | |
| 136 | + | ac.sendMu.Lock() | |
| 136 | 137 | ac.nextStateNum = 5 | |
| 137 | - | require.True(t, ac.shouldResetOutputState(false), "client ack gap should force dependency-free full repaint") | |
| 138 | + | reset := ac.shouldResetOutputState(false) | |
| 139 | + | require.True(t, reset, "client ack gap should force dependency-free full repaint") | |
| 138 | 140 | ||
| 139 | - | f := ac.nextOutputFrameLocked([]byte("full repaint"), ac.shouldResetOutputState(false)) | |
| 141 | + | f := ac.nextOutputFrameLocked([]byte("full repaint"), reset) | |
| 142 | + | ac.sendMu.Unlock() | |
| 140 | 143 | out, err := ports.UnmarshalOutput(f.Payload) | |
| 141 | 144 | require.NoError(t, err) | |
| 142 | 145 | require.Equal(t, uint64(0), out.BaseStateNum, "full repaint must not depend on dropped prior output states") | |
| 143 | 146 | require.Equal(t, uint64(6), out.NewStateNum) | |
| 144 | 147 | ||
| 145 | 148 | ac.outputAck.Store(out.NewStateNum) | |
| 146 | - | require.False(t, ac.shouldResetOutputState(false), "acked current state can resume incremental output") | |
| 147 | - | incremental := ac.nextOutputFrameLocked([]byte("incremental"), ac.shouldResetOutputState(false)) | |
| 149 | + | ac.sendMu.Lock() | |
| 150 | + | reset = ac.shouldResetOutputState(false) | |
| 151 | + | require.False(t, reset, "acked current state can resume incremental output") | |
| 152 | + | incremental := ac.nextOutputFrameLocked([]byte("incremental"), reset) | |
| 153 | + | ac.sendMu.Unlock() | |
| 148 | 154 | incrementalOut, err := ports.UnmarshalOutput(incremental.Payload) | |
| 149 | 155 | require.NoError(t, err) | |
| 150 | 156 | require.Equal(t, uint64(6), incrementalOut.BaseStateNum) | |
| @@ -133,18 +133,24 @@ func TestOutputAckGapForcesFullStateRepaint(t *testing.T) { | |||||
| 133 | ac.advanceOutputAck(2) | 133 | ac.advanceOutputAck(2) | ||
| 134 | ac.advanceOutputAck(4) | 134 | ac.advanceOutputAck(4) | ||
| 135 | require.Equal(t, uint64(3), ac.outputAck.Load(), "stale or future ACKs must not move output state incorrectly") | 135 | require.Equal(t, uint64(3), ac.outputAck.Load(), "stale or future ACKs must not move output state incorrectly") | ||
| 136 | + | ac.sendMu.Lock() | |||
| 136 | ac.nextStateNum = 5 | 137 | ac.nextStateNum = 5 | ||
| 137 | - | require.True(t, ac.shouldResetOutputState(false), "client ack gap should force dependency-free full repaint") | 138 | + | reset := ac.shouldResetOutputState(false) |
| 139 | + | require.True(t, reset, "client ack gap should force dependency-free full repaint") | |||
| 138 | 140 | ||||
| 139 | - | f := ac.nextOutputFrameLocked([]byte("full repaint"), ac.shouldResetOutputState(false)) | 141 | + | f := ac.nextOutputFrameLocked([]byte("full repaint"), reset) |
| 142 | + | ac.sendMu.Unlock() | |||
| 140 | out, err := ports.UnmarshalOutput(f.Payload) | 143 | out, err := ports.UnmarshalOutput(f.Payload) | ||
| 141 | require.NoError(t, err) | 144 | require.NoError(t, err) | ||
| 142 | require.Equal(t, uint64(0), out.BaseStateNum, "full repaint must not depend on dropped prior output states") | 145 | require.Equal(t, uint64(0), out.BaseStateNum, "full repaint must not depend on dropped prior output states") | ||
| 143 | require.Equal(t, uint64(6), out.NewStateNum) | 146 | require.Equal(t, uint64(6), out.NewStateNum) | ||
| 144 | 147 | ||||
| 145 | ac.outputAck.Store(out.NewStateNum) | 148 | ac.outputAck.Store(out.NewStateNum) | ||
| 146 | - | require.False(t, ac.shouldResetOutputState(false), "acked current state can resume incremental output") | 149 | + | ac.sendMu.Lock() |
| 147 | - | incremental := ac.nextOutputFrameLocked([]byte("incremental"), ac.shouldResetOutputState(false)) | 150 | + | reset = ac.shouldResetOutputState(false) |
| 151 | + | require.False(t, reset, "acked current state can resume incremental output") | |||
| 152 | + | incremental := ac.nextOutputFrameLocked([]byte("incremental"), reset) | |||
| 153 | + | ac.sendMu.Unlock() | |||
| 148 | incrementalOut, err := ports.UnmarshalOutput(incremental.Payload) | 154 | incrementalOut, err := ports.UnmarshalOutput(incremental.Payload) | ||
| 149 | require.NoError(t, err) | 155 | require.NoError(t, err) | ||
| 150 | require.Equal(t, uint64(6), incrementalOut.BaseStateNum) | 156 | require.Equal(t, uint64(6), incrementalOut.BaseStateNum) | ||