mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-09-02 16:31:56 +03:00
test: add idleTracker and connIdleTimeout tests
Cover shared idle tracker behavior: - tracker lifecycle (new, idle after timeout, touch resets) - read/write with data touches tracker - read retries on timeout when tracker is not idle - read closes on timeout when tracker is idle - shared tracker prevents false timeout across directions
This commit is contained in:
@@ -16,6 +16,12 @@ import (
|
|||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type netTimeoutError struct{}
|
||||||
|
|
||||||
|
func (e netTimeoutError) Error() string { return "i/o timeout" }
|
||||||
|
func (e netTimeoutError) Timeout() bool { return true }
|
||||||
|
func (e netTimeoutError) Temporary() bool { return true }
|
||||||
|
|
||||||
type ConnRewindBaseConn struct {
|
type ConnRewindBaseConn struct {
|
||||||
testlib.EssentialsConnMock
|
testlib.EssentialsConnMock
|
||||||
|
|
||||||
@@ -291,6 +297,141 @@ func (suite *ConnProxyProtocolTestSuite) TearDownTest() {
|
|||||||
suite.targetConnMock.AssertExpectations(suite.T())
|
suite.targetConnMock.AssertExpectations(suite.T())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IdleTrackerTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *IdleTrackerTestSuite) TestNewNotIdle() {
|
||||||
|
tracker := newIdleTracker(time.Second)
|
||||||
|
suite.False(tracker.isIdle())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *IdleTrackerTestSuite) TestIdleAfterTimeout() {
|
||||||
|
tracker := newIdleTracker(10 * time.Millisecond)
|
||||||
|
time.Sleep(20 * time.Millisecond)
|
||||||
|
|
||||||
|
suite.True(tracker.isIdle())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *IdleTrackerTestSuite) TestTouchResetsIdle() {
|
||||||
|
tracker := newIdleTracker(50 * time.Millisecond)
|
||||||
|
time.Sleep(30 * time.Millisecond)
|
||||||
|
|
||||||
|
tracker.touch()
|
||||||
|
|
||||||
|
suite.False(tracker.isIdle())
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnIdleTimeoutTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
|
||||||
|
connMock *testlib.EssentialsConnMock
|
||||||
|
tracker *idleTracker
|
||||||
|
conn connIdleTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ConnIdleTimeoutTestSuite) SetupTest() {
|
||||||
|
suite.connMock = &testlib.EssentialsConnMock{}
|
||||||
|
suite.tracker = newIdleTracker(time.Second)
|
||||||
|
suite.conn = connIdleTimeout{
|
||||||
|
Conn: suite.connMock,
|
||||||
|
tracker: suite.tracker,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ConnIdleTimeoutTestSuite) TearDownTest() {
|
||||||
|
suite.connMock.AssertExpectations(suite.T())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ConnIdleTimeoutTestSuite) TestReadOk() {
|
||||||
|
suite.connMock.On("SetReadDeadline", mock.Anything).Return(nil)
|
||||||
|
suite.connMock.On("Read", mock.Anything).Once().Return(5, nil)
|
||||||
|
|
||||||
|
n, err := suite.conn.Read(make([]byte, 10))
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(5, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ConnIdleTimeoutTestSuite) TestReadNonTimeoutErr() {
|
||||||
|
suite.connMock.On("SetReadDeadline", mock.Anything).Return(nil)
|
||||||
|
suite.connMock.On("Read", mock.Anything).Once().Return(0, io.EOF)
|
||||||
|
|
||||||
|
n, err := suite.conn.Read(make([]byte, 10))
|
||||||
|
suite.True(errors.Is(err, io.EOF))
|
||||||
|
suite.Equal(0, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ConnIdleTimeoutTestSuite) TestReadTimeoutRetriesWhenNotIdle() {
|
||||||
|
suite.connMock.On("SetReadDeadline", mock.Anything).Return(nil)
|
||||||
|
suite.connMock.On("Read", mock.Anything).Once().Return(0, netTimeoutError{})
|
||||||
|
suite.connMock.On("Read", mock.Anything).Once().Return(5, nil)
|
||||||
|
|
||||||
|
n, err := suite.conn.Read(make([]byte, 10))
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(5, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ConnIdleTimeoutTestSuite) TestReadTimeoutClosesWhenIdle() {
|
||||||
|
suite.tracker = newIdleTracker(time.Millisecond)
|
||||||
|
suite.conn = connIdleTimeout{
|
||||||
|
Conn: suite.connMock,
|
||||||
|
tracker: suite.tracker,
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(5 * time.Millisecond)
|
||||||
|
|
||||||
|
suite.connMock.On("SetReadDeadline", mock.Anything).Return(nil)
|
||||||
|
suite.connMock.On("Read", mock.Anything).Once().Return(0, netTimeoutError{})
|
||||||
|
|
||||||
|
n, err := suite.conn.Read(make([]byte, 10))
|
||||||
|
suite.Equal(0, n)
|
||||||
|
|
||||||
|
netErr, ok := err.(net.Error) //nolint: errorlint
|
||||||
|
suite.True(ok)
|
||||||
|
suite.True(netErr.Timeout())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ConnIdleTimeoutTestSuite) TestSharedTrackerPreventsFalseTimeout() {
|
||||||
|
connMock2 := &testlib.EssentialsConnMock{}
|
||||||
|
conn2 := connIdleTimeout{
|
||||||
|
Conn: connMock2,
|
||||||
|
tracker: suite.tracker,
|
||||||
|
}
|
||||||
|
|
||||||
|
connMock2.On("SetWriteDeadline", mock.Anything).Return(nil)
|
||||||
|
connMock2.On("Write", mock.Anything).Once().Return(5, nil)
|
||||||
|
|
||||||
|
_, _ = conn2.Write(make([]byte, 5))
|
||||||
|
|
||||||
|
suite.connMock.On("SetReadDeadline", mock.Anything).Return(nil)
|
||||||
|
suite.connMock.On("Read", mock.Anything).Once().Return(0, netTimeoutError{})
|
||||||
|
suite.connMock.On("Read", mock.Anything).Once().Return(3, nil)
|
||||||
|
|
||||||
|
n, err := suite.conn.Read(make([]byte, 10))
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(3, n)
|
||||||
|
|
||||||
|
connMock2.AssertExpectations(suite.T())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ConnIdleTimeoutTestSuite) TestWriteOk() {
|
||||||
|
suite.connMock.On("SetWriteDeadline", mock.Anything).Return(nil)
|
||||||
|
suite.connMock.On("Write", mock.Anything).Once().Return(5, nil)
|
||||||
|
|
||||||
|
n, err := suite.conn.Write(make([]byte, 5))
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(5, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ConnIdleTimeoutTestSuite) TestWriteErr() {
|
||||||
|
suite.connMock.On("SetWriteDeadline", mock.Anything).Return(nil)
|
||||||
|
suite.connMock.On("Write", mock.Anything).Once().Return(0, io.EOF)
|
||||||
|
|
||||||
|
n, err := suite.conn.Write(make([]byte, 5))
|
||||||
|
suite.True(errors.Is(err, io.EOF))
|
||||||
|
suite.Equal(0, n)
|
||||||
|
}
|
||||||
|
|
||||||
func TestConnTraffic(t *testing.T) {
|
func TestConnTraffic(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
suite.Run(t, &ConnTrafficTestSuite{})
|
suite.Run(t, &ConnTrafficTestSuite{})
|
||||||
@@ -305,3 +446,13 @@ func TestConnProxyProtocol(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
suite.Run(t, &ConnProxyProtocolTestSuite{})
|
suite.Run(t, &ConnProxyProtocolTestSuite{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIdleTracker(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
suite.Run(t, &IdleTrackerTestSuite{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnIdleTimeout(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
suite.Run(t, &ConnIdleTimeoutTestSuite{})
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user