github.com/lirm/aeron-go@v0.0.0-20230415210743-920325491dc4/aeron/subscription_test.go (about) 1 // Copyright 2022 Steven Stern 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package aeron 16 17 import ( 18 "github.com/lirm/aeron-go/aeron/atomic" 19 "github.com/lirm/aeron-go/aeron/counters" 20 "github.com/lirm/aeron-go/aeron/logbuffer" 21 "github.com/lirm/aeron-go/aeron/logbuffer/term" 22 "github.com/stretchr/testify/mock" 23 "github.com/stretchr/testify/suite" 24 "math" 25 "testing" 26 ) 27 28 const ( 29 Channel = "aeron:udp?endpoint=localhost:40124" 30 StreamId = int32(1002) 31 RegistrationId = int64(10) 32 ChannelStatusId = int32(100) 33 ReadBufferCapacity = 1024 34 FragmentCountLimit = math.MaxInt32 35 ) 36 37 type SubscriptionTestSuite struct { 38 suite.Suite 39 headerLength int32 // Effectively a const, but DataFrameHeader declares it in a struct 40 atomicReadBuffer *atomic.Buffer 41 cc *MockReceivingConductor 42 fragmentHandlerMock *term.MockFragmentHandler 43 fragmentHandler term.FragmentHandler // References the mock's func. Helps readability 44 imageOne *MockImage 45 imageTwo *MockImage 46 header *logbuffer.Header 47 sub *Subscription 48 } 49 50 func (s *SubscriptionTestSuite) SetupTest() { 51 s.headerLength = logbuffer.DataFrameHeader.Length 52 s.atomicReadBuffer = atomic.MakeBuffer(make([]byte, s.headerLength), s.headerLength) 53 s.cc = NewMockReceivingConductor(s.T()) 54 s.fragmentHandlerMock = term.NewMockFragmentHandler(s.T()) 55 s.fragmentHandler = s.fragmentHandlerMock.Execute 56 s.imageOne = NewMockImage(s.T()) 57 s.imageTwo = NewMockImage(s.T()) 58 s.header = new(logbuffer.Header) // Unused so no need to initialize 59 s.sub = NewSubscription(s.cc, Channel, RegistrationId, StreamId, ChannelStatusId, nil, nil) 60 } 61 62 func (s *SubscriptionTestSuite) TestShouldEnsureTheSubscriptionIsOpenWhenPolling() { 63 s.cc.On("releaseSubscription", RegistrationId, mock.Anything).Return(nil) 64 65 s.Require().NoError(s.sub.Close()) 66 s.Assert().True(s.sub.IsClosed()) 67 } 68 69 func (s *SubscriptionTestSuite) TestShouldReadNothingWhenNoImages() { 70 fragments := s.sub.Poll(s.fragmentHandler, 1) 71 s.Assert().Equal(0, fragments) 72 } 73 74 func (s *SubscriptionTestSuite) TestShouldReadNothingWhenThereIsNoData() { 75 s.sub.addImage(s.imageOne) 76 s.imageOne.On("Poll", mock.Anything, mock.Anything).Return(0, nil) 77 78 fragments := s.sub.Poll(s.fragmentHandler, 1) 79 s.Assert().Equal(0, fragments) 80 } 81 82 func (s *SubscriptionTestSuite) TestShouldReadData() { 83 s.sub.addImage(s.imageOne) 84 85 s.fragmentHandlerMock.On("Execute", 86 s.atomicReadBuffer, s.headerLength, ReadBufferCapacity-s.headerLength, s.header) 87 88 s.imageOne.On("Poll", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { 89 handler := args.Get(0).(term.FragmentHandler) 90 handler(s.atomicReadBuffer, s.headerLength, ReadBufferCapacity-s.headerLength, s.header) 91 }).Return(1, nil) 92 93 fragments := s.sub.Poll(s.fragmentHandler, FragmentCountLimit) 94 s.Assert().Equal(1, fragments) 95 } 96 97 func (s *SubscriptionTestSuite) TestShouldReadDataFromMultipleSources() { 98 s.sub.addImage(s.imageOne) 99 s.sub.addImage(s.imageTwo) 100 101 s.imageOne.On("Poll", mock.Anything, mock.Anything).Return(1, nil) 102 s.imageTwo.On("Poll", mock.Anything, mock.Anything).Return(1, nil) 103 104 fragments := s.sub.Poll(s.fragmentHandler, FragmentCountLimit) 105 s.Assert().Equal(2, fragments) 106 } 107 108 // TODO: Implement resolveChannel set of tests. 109 110 func TestSubscription(t *testing.T) { 111 suite.Run(t, new(SubscriptionTestSuite)) 112 } 113 114 // Everything below is auto generated by mockery using this command: 115 // mockery --name=ReceivingConductor --inpackage --structname=MockReceivingConductor --print 116 117 // MockReceivingConductor is an autogenerated mock type for the ReceivingConductor type 118 type MockReceivingConductor struct { 119 mock.Mock 120 } 121 122 // AddRcvDestination provides a mock function with given fields: registrationID, endpointChannel 123 func (_m *MockReceivingConductor) AddRcvDestination(registrationID int64, endpointChannel string) error { 124 ret := _m.Called(registrationID, endpointChannel) 125 126 var r0 error 127 if rf, ok := ret.Get(0).(func(int64, string) error); ok { 128 r0 = rf(registrationID, endpointChannel) 129 } else { 130 r0 = ret.Error(0) 131 } 132 133 return r0 134 } 135 136 // CounterReader provides a mock function with given fields: 137 func (_m *MockReceivingConductor) CounterReader() *counters.Reader { 138 ret := _m.Called() 139 140 var r0 *counters.Reader 141 if rf, ok := ret.Get(0).(func() *counters.Reader); ok { 142 r0 = rf() 143 } else { 144 if ret.Get(0) != nil { 145 r0 = ret.Get(0).(*counters.Reader) 146 } 147 } 148 149 return r0 150 } 151 152 // RemoveRcvDestination provides a mock function with given fields: registrationID, endpointChannel 153 func (_m *MockReceivingConductor) RemoveRcvDestination(registrationID int64, endpointChannel string) error { 154 ret := _m.Called(registrationID, endpointChannel) 155 156 var r0 error 157 if rf, ok := ret.Get(0).(func(int64, string) error); ok { 158 r0 = rf(registrationID, endpointChannel) 159 } else { 160 r0 = ret.Error(0) 161 } 162 163 return r0 164 } 165 166 // releaseSubscription provides a mock function with given fields: regID, images 167 func (_m *MockReceivingConductor) releaseSubscription(regID int64, images []Image) error { 168 ret := _m.Called(regID, images) 169 170 var r0 error 171 if rf, ok := ret.Get(0).(func(int64, []Image) error); ok { 172 r0 = rf(regID, images) 173 } else { 174 r0 = ret.Error(0) 175 } 176 177 return r0 178 } 179 180 type mockConstructorTestingTNewMockReceivingConductor interface { 181 mock.TestingT 182 Cleanup(func()) 183 } 184 185 // NewMockReceivingConductor creates a new instance of MockReceivingConductor. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 186 func NewMockReceivingConductor(t mockConstructorTestingTNewMockReceivingConductor) *MockReceivingConductor { 187 mock := &MockReceivingConductor{} 188 mock.Mock.Test(t) 189 190 t.Cleanup(func() { mock.AssertExpectations(t) }) 191 192 return mock 193 }