Skip to content

Commit d1bf8c3

Browse files
committed
[FAB-14944] Fix Data race in TestSend
The test has a data race because it re-programs a mock in a non memory coherent (and wishful thinking) way, and the goroutine that sends the messages isn't effected by the mock synchronization attempts in the test, because they happen after the data race occurs. This change set fixes the data race by programming the stream's mock only once, and having it dynamically read the returned values while holding a lock, which synchronizes between the goroutine that sends down the stream and the test goroutine. Ran this 10,000 times with data race detector and it doesn't fail now. Change-Id: I8a0030586da9102e8539c9f7b8699c63c6565894 Signed-off-by: yacovm <yacovm@il.ibm.com>
1 parent bae6b94 commit d1bf8c3

File tree

1 file changed

+33
-36
lines changed

1 file changed

+33
-36
lines changed

orderer/common/cluster/rpc_test.go

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"context"
1111
"io"
1212
"sync"
13+
"sync/atomic"
1314
"testing"
1415
"time"
1516

@@ -111,25 +112,6 @@ func TestSend(t *testing.T) {
111112
},
112113
}
113114

114-
comm := &mocks.Communicator{}
115-
stream := &mocks.StepClient{}
116-
client := &mocks.ClusterClient{}
117-
118-
resetMocks := func() {
119-
// When a mock invokes a method from a different goroutine,
120-
// it records this invocation. However - we overwrite the recording
121-
// in this function.
122-
123-
// Call a setter method on the mock, so it will
124-
// lock itself and thus synchronize the recordings
125-
// being done by goroutines from previous test cases.
126-
127-
stream.Mock.On("bla")
128-
stream.Mock = mock.Mock{}
129-
client.Mock = mock.Mock{}
130-
comm.Mock = mock.Mock{}
131-
}
132-
133115
submit := func(rpc *cluster.RPC) error {
134116
err := rpc.SendSubmit(1, submitRequest)
135117
return err
@@ -139,16 +121,35 @@ func TestSend(t *testing.T) {
139121
return rpc.SendConsensus(1, consensusRequest)
140122
}
141123

142-
for _, testCase := range []struct {
124+
type testCase struct {
143125
name string
144126
method func(rpc *cluster.RPC) error
145-
sendReturns interface{}
127+
sendReturns error
146128
sendCalledWith *orderer.StepRequest
147129
receiveReturns []interface{}
148130
stepReturns []interface{}
149131
remoteError error
150132
expectedErr string
151-
}{
133+
}
134+
135+
l := &sync.Mutex{}
136+
var tst testCase
137+
138+
sent := make(chan struct{})
139+
140+
var sendCalls uint32
141+
142+
stream := &mocks.StepClient{}
143+
stream.On("Context", mock.Anything).Return(context.Background())
144+
stream.On("Send", mock.Anything).Return(func(*orderer.StepRequest) error {
145+
l.Lock()
146+
defer l.Unlock()
147+
sent <- struct{}{}
148+
atomic.AddUint32(&sendCalls, 1)
149+
return tst.sendReturns
150+
})
151+
152+
for _, tst := range []testCase{
152153
{
153154
name: "Send and Receive submit succeed",
154155
method: submit,
@@ -194,18 +195,15 @@ func TestSend(t *testing.T) {
194195
expectedErr: "deadline exceeded",
195196
},
196197
} {
197-
testCase := testCase
198+
l.Lock()
199+
testCase := tst
200+
l.Unlock()
201+
198202
t.Run(testCase.name, func(t *testing.T) {
203+
atomic.StoreUint32(&sendCalls, 0)
199204
isSend := testCase.receiveReturns == nil
200-
defer resetMocks()
201-
var sent sync.WaitGroup
202-
sent.Add(1)
203-
204-
stream.On("Context", mock.Anything).Return(context.Background())
205-
stream.On("Send", mock.Anything).Run(func(_ mock.Arguments) {
206-
sent.Done()
207-
}).Return(testCase.sendReturns)
208-
stream.On("Recv").Return(testCase.receiveReturns...)
205+
comm := &mocks.Communicator{}
206+
client := &mocks.ClusterClient{}
209207
client.On("Step", mock.Anything).Return(testCase.stepReturns...)
210208
rm := &cluster.RemoteContext{
211209
Metrics: cluster.NewMetrics(&disabled.Provider{}),
@@ -229,8 +227,7 @@ func TestSend(t *testing.T) {
229227

230228
err = testCase.method(rpc)
231229
if testCase.remoteError == nil && testCase.stepReturns[1] == nil {
232-
sent.Wait()
233-
sent.Add(1)
230+
<-sent
234231
}
235232

236233
if testCase.stepReturns[1] == nil && testCase.remoteError == nil {
@@ -245,11 +242,11 @@ func TestSend(t *testing.T) {
245242
// to Send() were made
246243
err := testCase.method(rpc)
247244
if testCase.expectedErr == "" {
248-
sent.Wait()
245+
<-sent
249246
}
250247

251248
assert.NoError(t, err)
252-
stream.AssertNumberOfCalls(t, "Send", 2)
249+
assert.Equal(t, int(atomic.LoadUint32(&sendCalls)), 2)
253250
client.AssertNumberOfCalls(t, "Step", 1)
254251
}
255252
})

0 commit comments

Comments
 (0)