mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-04 20:57:36 +03:00
add some more tests for the client multiplexer
This commit is contained in:
parent
0928e91e4d
commit
b682af20cf
5 changed files with 156 additions and 22 deletions
|
@ -15,9 +15,14 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
clientMuxerOnce sync.Once
|
clientMuxerOnce sync.Once
|
||||||
clientMuxer *clientMultiplexer
|
clientMuxer multiplexer
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type multiplexer interface {
|
||||||
|
AddConn(net.PacketConn) packetHandlerManager
|
||||||
|
AddHandler(net.PacketConn, protocol.ConnectionID, packetHandler) error
|
||||||
|
}
|
||||||
|
|
||||||
// The clientMultiplexer listens on multiple net.PacketConns and dispatches
|
// The clientMultiplexer listens on multiple net.PacketConns and dispatches
|
||||||
// incoming packets to the session handler.
|
// incoming packets to the session handler.
|
||||||
type clientMultiplexer struct {
|
type clientMultiplexer struct {
|
||||||
|
@ -29,7 +34,9 @@ type clientMultiplexer struct {
|
||||||
logger utils.Logger
|
logger utils.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func getClientMultiplexer() *clientMultiplexer {
|
var _ multiplexer = &clientMultiplexer{}
|
||||||
|
|
||||||
|
func getClientMultiplexer() multiplexer {
|
||||||
clientMuxerOnce.Do(func() {
|
clientMuxerOnce.Do(func() {
|
||||||
clientMuxer = &clientMultiplexer{
|
clientMuxer = &clientMultiplexer{
|
||||||
conns: make(map[net.PacketConn]packetHandlerManager),
|
conns: make(map[net.PacketConn]packetHandlerManager),
|
||||||
|
|
|
@ -96,11 +96,11 @@ var _ = Describe("Client Multiplexer", func() {
|
||||||
|
|
||||||
It("ignores packets arriving late for closed sessions", func() {
|
It("ignores packets arriving late for closed sessions", func() {
|
||||||
manager := NewMockPacketHandlerManager(mockCtrl)
|
manager := NewMockPacketHandlerManager(mockCtrl)
|
||||||
origNewPacketHandlerManager := getClientMultiplexer().newPacketHandlerManager
|
origNewPacketHandlerManager := getClientMultiplexer().(*clientMultiplexer).newPacketHandlerManager
|
||||||
defer func() {
|
defer func() {
|
||||||
getClientMultiplexer().newPacketHandlerManager = origNewPacketHandlerManager
|
getClientMultiplexer().(*clientMultiplexer).newPacketHandlerManager = origNewPacketHandlerManager
|
||||||
}()
|
}()
|
||||||
getClientMultiplexer().newPacketHandlerManager = func() packetHandlerManager { return manager }
|
getClientMultiplexer().(*clientMultiplexer).newPacketHandlerManager = func() packetHandlerManager { return manager }
|
||||||
|
|
||||||
conn := newMockPacketConn()
|
conn := newMockPacketConn()
|
||||||
connID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}
|
connID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}
|
||||||
|
|
100
client_test.go
100
client_test.go
|
@ -23,10 +23,12 @@ import (
|
||||||
|
|
||||||
var _ = Describe("Client", func() {
|
var _ = Describe("Client", func() {
|
||||||
var (
|
var (
|
||||||
cl *client
|
cl *client
|
||||||
packetConn *mockPacketConn
|
packetConn *mockPacketConn
|
||||||
addr net.Addr
|
addr net.Addr
|
||||||
connID protocol.ConnectionID
|
connID protocol.ConnectionID
|
||||||
|
mockMultiplexer *MockMultiplexer
|
||||||
|
origMultiplexer multiplexer
|
||||||
|
|
||||||
originalClientSessConstructor func(connection, sessionRunner, string, protocol.VersionNumber, protocol.ConnectionID, *tls.Config, *Config, protocol.VersionNumber, []protocol.VersionNumber, utils.Logger) (quicSession, error)
|
originalClientSessConstructor func(connection, sessionRunner, string, protocol.VersionNumber, protocol.ConnectionID, *tls.Config, *Config, protocol.VersionNumber, []protocol.VersionNumber, utils.Logger) (quicSession, error)
|
||||||
)
|
)
|
||||||
|
@ -59,9 +61,15 @@ var _ = Describe("Client", func() {
|
||||||
conn: &conn{pconn: packetConn, currentAddr: addr},
|
conn: &conn{pconn: packetConn, currentAddr: addr},
|
||||||
logger: utils.DefaultLogger,
|
logger: utils.DefaultLogger,
|
||||||
}
|
}
|
||||||
|
getClientMultiplexer() // make the sync.Once execute
|
||||||
|
// replace the clientMuxer. getClientMultiplexer will now return the MockMultiplexer
|
||||||
|
mockMultiplexer = NewMockMultiplexer(mockCtrl)
|
||||||
|
origMultiplexer = clientMuxer
|
||||||
|
clientMuxer = mockMultiplexer
|
||||||
})
|
})
|
||||||
|
|
||||||
AfterEach(func() {
|
AfterEach(func() {
|
||||||
|
clientMuxer = origMultiplexer
|
||||||
newClientSession = originalClientSessConstructor
|
newClientSession = originalClientSessConstructor
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -137,14 +145,11 @@ var _ = Describe("Client", func() {
|
||||||
Eventually(hostnameChan).Should(Receive(Equal("foobar")))
|
Eventually(hostnameChan).Should(Receive(Equal("foobar")))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("errors when receiving an error from the connection", func() {
|
|
||||||
testErr := errors.New("connection error")
|
|
||||||
packetConn.readErr = testErr
|
|
||||||
_, err := Dial(packetConn, addr, "quic.clemente.io:1337", nil, nil)
|
|
||||||
Expect(err).To(MatchError(testErr))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("returns after the handshake is complete", func() {
|
It("returns after the handshake is complete", func() {
|
||||||
|
manager := NewMockPacketHandlerManager(mockCtrl)
|
||||||
|
mockMultiplexer.EXPECT().AddConn(packetConn).Return(manager)
|
||||||
|
mockMultiplexer.EXPECT().AddHandler(packetConn, gomock.Any(), gomock.Any())
|
||||||
|
|
||||||
run := make(chan struct{})
|
run := make(chan struct{})
|
||||||
newClientSession = func(
|
newClientSession = func(
|
||||||
_ connection,
|
_ connection,
|
||||||
|
@ -160,11 +165,9 @@ var _ = Describe("Client", func() {
|
||||||
) (quicSession, error) {
|
) (quicSession, error) {
|
||||||
sess := NewMockQuicSession(mockCtrl)
|
sess := NewMockQuicSession(mockCtrl)
|
||||||
sess.EXPECT().run().Do(func() { close(run) })
|
sess.EXPECT().run().Do(func() { close(run) })
|
||||||
sess.EXPECT().handlePacket(gomock.Any())
|
|
||||||
runner.onHandshakeComplete(sess)
|
runner.onHandshakeComplete(sess)
|
||||||
return sess, nil
|
return sess, nil
|
||||||
}
|
}
|
||||||
packetConn.dataToRead <- acceptClientVersionPacket(cl.srcConnID)
|
|
||||||
s, err := Dial(packetConn, addr, "quic.clemente.io:1337", nil, nil)
|
s, err := Dial(packetConn, addr, "quic.clemente.io:1337", nil, nil)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(s).ToNot(BeNil())
|
Expect(s).ToNot(BeNil())
|
||||||
|
@ -172,8 +175,11 @@ var _ = Describe("Client", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("returns an error that occurs while waiting for the connection to become secure", func() {
|
It("returns an error that occurs while waiting for the connection to become secure", func() {
|
||||||
|
manager := NewMockPacketHandlerManager(mockCtrl)
|
||||||
|
mockMultiplexer.EXPECT().AddConn(packetConn).Return(manager)
|
||||||
|
mockMultiplexer.EXPECT().AddHandler(packetConn, gomock.Any(), gomock.Any())
|
||||||
|
|
||||||
testErr := errors.New("early handshake error")
|
testErr := errors.New("early handshake error")
|
||||||
handledPacket := make(chan struct{})
|
|
||||||
newClientSession = func(
|
newClientSession = func(
|
||||||
conn connection,
|
conn connection,
|
||||||
_ sessionRunner,
|
_ sessionRunner,
|
||||||
|
@ -187,17 +193,19 @@ var _ = Describe("Client", func() {
|
||||||
_ utils.Logger,
|
_ utils.Logger,
|
||||||
) (quicSession, error) {
|
) (quicSession, error) {
|
||||||
sess := NewMockQuicSession(mockCtrl)
|
sess := NewMockQuicSession(mockCtrl)
|
||||||
sess.EXPECT().handlePacket(gomock.Any()).Do(func(_ *receivedPacket) { close(handledPacket) })
|
|
||||||
sess.EXPECT().run().Return(testErr)
|
sess.EXPECT().run().Return(testErr)
|
||||||
return sess, nil
|
return sess, nil
|
||||||
}
|
}
|
||||||
packetConn.dataToRead <- acceptClientVersionPacket(cl.srcConnID)
|
packetConn.dataToRead <- acceptClientVersionPacket(cl.srcConnID)
|
||||||
_, err := Dial(packetConn, addr, "quic.clemente.io:1337", nil, nil)
|
_, err := Dial(packetConn, addr, "quic.clemente.io:1337", nil, nil)
|
||||||
Expect(err).To(MatchError(testErr))
|
Expect(err).To(MatchError(testErr))
|
||||||
Eventually(handledPacket).Should(BeClosed())
|
|
||||||
})
|
})
|
||||||
|
|
||||||
It("closes the session when the context is canceledd", func() {
|
It("closes the session when the context is canceled", func() {
|
||||||
|
manager := NewMockPacketHandlerManager(mockCtrl)
|
||||||
|
mockMultiplexer.EXPECT().AddConn(packetConn).Return(manager)
|
||||||
|
mockMultiplexer.EXPECT().AddHandler(packetConn, gomock.Any(), gomock.Any())
|
||||||
|
|
||||||
sessionRunning := make(chan struct{})
|
sessionRunning := make(chan struct{})
|
||||||
defer close(sessionRunning)
|
defer close(sessionRunning)
|
||||||
sess := NewMockQuicSession(mockCtrl)
|
sess := NewMockQuicSession(mockCtrl)
|
||||||
|
@ -232,6 +240,37 @@ var _ = Describe("Client", func() {
|
||||||
Eventually(dialed).Should(BeClosed())
|
Eventually(dialed).Should(BeClosed())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("removes closed sessions from the multiplexer", func() {
|
||||||
|
manager := NewMockPacketHandlerManager(mockCtrl)
|
||||||
|
manager.EXPECT().Remove(connID)
|
||||||
|
mockMultiplexer.EXPECT().AddConn(packetConn).Return(manager)
|
||||||
|
mockMultiplexer.EXPECT().AddHandler(packetConn, gomock.Any(), gomock.Any())
|
||||||
|
|
||||||
|
var runner sessionRunner
|
||||||
|
sess := NewMockQuicSession(mockCtrl)
|
||||||
|
newClientSession = func(
|
||||||
|
conn connection,
|
||||||
|
runnerP sessionRunner,
|
||||||
|
_ string,
|
||||||
|
_ protocol.VersionNumber,
|
||||||
|
connID protocol.ConnectionID,
|
||||||
|
_ *tls.Config,
|
||||||
|
_ *Config,
|
||||||
|
_ protocol.VersionNumber,
|
||||||
|
_ []protocol.VersionNumber,
|
||||||
|
_ utils.Logger,
|
||||||
|
) (quicSession, error) {
|
||||||
|
runner = runnerP
|
||||||
|
return sess, nil
|
||||||
|
}
|
||||||
|
sess.EXPECT().run().Do(func() {
|
||||||
|
runner.removeConnectionID(connID)
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := DialContext(context.Background(), packetConn, addr, "quic.clemnte.io:1337", nil, nil)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
Context("quic.Config", func() {
|
Context("quic.Config", func() {
|
||||||
It("setups with the right values", func() {
|
It("setups with the right values", func() {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
|
@ -250,6 +289,9 @@ var _ = Describe("Client", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("errors when the Config contains an invalid version", func() {
|
It("errors when the Config contains an invalid version", func() {
|
||||||
|
manager := NewMockPacketHandlerManager(mockCtrl)
|
||||||
|
mockMultiplexer.EXPECT().AddConn(packetConn).Return(manager)
|
||||||
|
|
||||||
version := protocol.VersionNumber(0x1234)
|
version := protocol.VersionNumber(0x1234)
|
||||||
_, err := Dial(packetConn, nil, "localhost:1234", &tls.Config{}, &Config{Versions: []protocol.VersionNumber{version}})
|
_, err := Dial(packetConn, nil, "localhost:1234", &tls.Config{}, &Config{Versions: []protocol.VersionNumber{version}})
|
||||||
Expect(err).To(MatchError("0x1234 is not a valid QUIC version"))
|
Expect(err).To(MatchError("0x1234 is not a valid QUIC version"))
|
||||||
|
@ -286,6 +328,10 @@ var _ = Describe("Client", func() {
|
||||||
|
|
||||||
Context("gQUIC", func() {
|
Context("gQUIC", func() {
|
||||||
It("errors if it can't create a session", func() {
|
It("errors if it can't create a session", func() {
|
||||||
|
manager := NewMockPacketHandlerManager(mockCtrl)
|
||||||
|
mockMultiplexer.EXPECT().AddConn(packetConn).Return(manager)
|
||||||
|
mockMultiplexer.EXPECT().AddHandler(packetConn, gomock.Any(), gomock.Any())
|
||||||
|
|
||||||
testErr := errors.New("error creating session")
|
testErr := errors.New("error creating session")
|
||||||
newClientSession = func(
|
newClientSession = func(
|
||||||
_ connection,
|
_ connection,
|
||||||
|
@ -308,6 +354,10 @@ var _ = Describe("Client", func() {
|
||||||
|
|
||||||
Context("IETF QUIC", func() {
|
Context("IETF QUIC", func() {
|
||||||
It("creates new TLS sessions with the right parameters", func() {
|
It("creates new TLS sessions with the right parameters", func() {
|
||||||
|
manager := NewMockPacketHandlerManager(mockCtrl)
|
||||||
|
mockMultiplexer.EXPECT().AddConn(packetConn).Return(manager)
|
||||||
|
mockMultiplexer.EXPECT().AddHandler(packetConn, gomock.Any(), gomock.Any())
|
||||||
|
|
||||||
config := &Config{Versions: []protocol.VersionNumber{protocol.VersionTLS}}
|
config := &Config{Versions: []protocol.VersionNumber{protocol.VersionTLS}}
|
||||||
c := make(chan struct{})
|
c := make(chan struct{})
|
||||||
var cconn connection
|
var cconn connection
|
||||||
|
@ -360,6 +410,10 @@ var _ = Describe("Client", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("returns an error that occurs during version negotiation", func() {
|
It("returns an error that occurs during version negotiation", func() {
|
||||||
|
manager := NewMockPacketHandlerManager(mockCtrl)
|
||||||
|
mockMultiplexer.EXPECT().AddConn(packetConn).Return(manager)
|
||||||
|
mockMultiplexer.EXPECT().AddHandler(packetConn, gomock.Any(), gomock.Any())
|
||||||
|
|
||||||
testErr := errors.New("early handshake error")
|
testErr := errors.New("early handshake error")
|
||||||
newClientSession = func(
|
newClientSession = func(
|
||||||
conn connection,
|
conn connection,
|
||||||
|
@ -627,6 +681,10 @@ var _ = Describe("Client", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("creates new gQUIC sessions with the right parameters", func() {
|
It("creates new gQUIC sessions with the right parameters", func() {
|
||||||
|
manager := NewMockPacketHandlerManager(mockCtrl)
|
||||||
|
mockMultiplexer.EXPECT().AddConn(packetConn).Return(manager)
|
||||||
|
mockMultiplexer.EXPECT().AddHandler(packetConn, gomock.Any(), gomock.Any())
|
||||||
|
|
||||||
config := &Config{Versions: protocol.SupportedVersions}
|
config := &Config{Versions: protocol.SupportedVersions}
|
||||||
c := make(chan struct{})
|
c := make(chan struct{})
|
||||||
var cconn connection
|
var cconn connection
|
||||||
|
@ -664,6 +722,10 @@ var _ = Describe("Client", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("creates a new session when the server performs a retry", func() {
|
It("creates a new session when the server performs a retry", func() {
|
||||||
|
manager := NewMockPacketHandlerManager(mockCtrl)
|
||||||
|
mockMultiplexer.EXPECT().AddConn(packetConn).Return(manager)
|
||||||
|
mockMultiplexer.EXPECT().AddHandler(packetConn, gomock.Any(), gomock.Any())
|
||||||
|
|
||||||
config := &Config{Versions: []protocol.VersionNumber{protocol.VersionTLS}}
|
config := &Config{Versions: []protocol.VersionNumber{protocol.VersionTLS}}
|
||||||
cl.config = config
|
cl.config = config
|
||||||
sess1 := NewMockQuicSession(mockCtrl)
|
sess1 := NewMockQuicSession(mockCtrl)
|
||||||
|
@ -694,6 +756,10 @@ var _ = Describe("Client", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("only accepts one Retry packet", func() {
|
It("only accepts one Retry packet", func() {
|
||||||
|
manager := NewMockPacketHandlerManager(mockCtrl)
|
||||||
|
mockMultiplexer.EXPECT().AddConn(packetConn).Return(manager)
|
||||||
|
mockMultiplexer.EXPECT().AddHandler(packetConn, gomock.Any(), gomock.Any())
|
||||||
|
|
||||||
config := &Config{Versions: []protocol.VersionNumber{protocol.VersionTLS}}
|
config := &Config{Versions: []protocol.VersionNumber{protocol.VersionTLS}}
|
||||||
sess1 := NewMockQuicSession(mockCtrl)
|
sess1 := NewMockQuicSession(mockCtrl)
|
||||||
sess1.EXPECT().run().Return(handshake.ErrCloseSessionForRetry)
|
sess1.EXPECT().run().Return(handshake.ErrCloseSessionForRetry)
|
||||||
|
|
60
mock_multiplexer_test.go
Normal file
60
mock_multiplexer_test.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/lucas-clemente/quic-go (interfaces: Multiplexer)
|
||||||
|
|
||||||
|
// Package quic is a generated GoMock package.
|
||||||
|
package quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
net "net"
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
protocol "github.com/lucas-clemente/quic-go/internal/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockMultiplexer is a mock of Multiplexer interface
|
||||||
|
type MockMultiplexer struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockMultiplexerMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockMultiplexerMockRecorder is the mock recorder for MockMultiplexer
|
||||||
|
type MockMultiplexerMockRecorder struct {
|
||||||
|
mock *MockMultiplexer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockMultiplexer creates a new mock instance
|
||||||
|
func NewMockMultiplexer(ctrl *gomock.Controller) *MockMultiplexer {
|
||||||
|
mock := &MockMultiplexer{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockMultiplexerMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
|
func (m *MockMultiplexer) EXPECT() *MockMultiplexerMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddConn mocks base method
|
||||||
|
func (m *MockMultiplexer) AddConn(arg0 net.PacketConn) packetHandlerManager {
|
||||||
|
ret := m.ctrl.Call(m, "AddConn", arg0)
|
||||||
|
ret0, _ := ret[0].(packetHandlerManager)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddConn indicates an expected call of AddConn
|
||||||
|
func (mr *MockMultiplexerMockRecorder) AddConn(arg0 interface{}) *gomock.Call {
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddConn", reflect.TypeOf((*MockMultiplexer)(nil).AddConn), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddHandler mocks base method
|
||||||
|
func (m *MockMultiplexer) AddHandler(arg0 net.PacketConn, arg1 protocol.ConnectionID, arg2 packetHandler) error {
|
||||||
|
ret := m.ctrl.Call(m, "AddHandler", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddHandler indicates an expected call of AddHandler
|
||||||
|
func (mr *MockMultiplexerMockRecorder) AddHandler(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddHandler", reflect.TypeOf((*MockMultiplexer)(nil).AddHandler), arg0, arg1, arg2)
|
||||||
|
}
|
|
@ -14,5 +14,6 @@ package quic
|
||||||
//go:generate sh -c "./mockgen_private.sh quic mock_session_runner_test.go github.com/lucas-clemente/quic-go sessionRunner SessionRunner"
|
//go:generate sh -c "./mockgen_private.sh quic mock_session_runner_test.go github.com/lucas-clemente/quic-go sessionRunner SessionRunner"
|
||||||
//go:generate sh -c "./mockgen_private.sh quic mock_quic_session_test.go github.com/lucas-clemente/quic-go quicSession QuicSession"
|
//go:generate sh -c "./mockgen_private.sh quic mock_quic_session_test.go github.com/lucas-clemente/quic-go quicSession QuicSession"
|
||||||
//go:generate sh -c "./mockgen_private.sh quic mock_packet_handler_manager_test.go github.com/lucas-clemente/quic-go packetHandlerManager PacketHandlerManager"
|
//go:generate sh -c "./mockgen_private.sh quic mock_packet_handler_manager_test.go github.com/lucas-clemente/quic-go packetHandlerManager PacketHandlerManager"
|
||||||
|
//go:generate sh -c "./mockgen_private.sh quic mock_multiplexer_test.go github.com/lucas-clemente/quic-go multiplexer Multiplexer"
|
||||||
//go:generate sh -c "find . -type f -name 'mock_*_test.go' | xargs sed -i '' 's/quic_go.//g'"
|
//go:generate sh -c "find . -type f -name 'mock_*_test.go' | xargs sed -i '' 's/quic_go.//g'"
|
||||||
//go:generate sh -c "goimports -w mock*_test.go"
|
//go:generate sh -c "goimports -w mock*_test.go"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue