Open mdlayher opened 5 years ago
Spoke with @acln0 and we came up with a possible alternative API that would have similar semantics, but would enable future extensibility as well. Something along the lines of:
type LocalPipe struct {
// Same semantics, cannot be nil.
Listen func() (net.Listener, error)
// Same semantics: if nil, use net.Dial.
Dial func(addr net.Addr) (net.Conn, error)
// Room for future extensibility, like perhaps:
//
// If not nil, called to clean up any artifacts produced by the net.Listener, e.g.
// UNIX socket files.
// Cleanup func(l net.Listener) error
}
// MakePipe produces a MakePipe function using the configuration defined on LocalPipe.
func (lp *LocalPipe) MakePipe() nettest.MakePipe {}
However, we aren't sure that the extensibility would ever be needed.
I think it makes sense to keep the UNIX socket cleanup logic from the original test if net.Listener.Network()
is "unix" or "unixgram". I think it also makes sense to require the caller to deal with cleanup of any other socket artifacts from unknown families on their own.
I am in favor of this proposal. Having implemented this particular scaffolding logic in a number of different places already, it would be pleasant to have a canonical home for it.
With the alternative struct-based construction @mdlayher mentioned, perhaps a direct method is nicer:
func (lp LocalPipe) MakePipe() (c1, c2 net.Conn, close func(), err error)
Then, caller code makes use of the method value like so:
package vsock_test
func TestConn(t *testing.T) {
vsocklp := netttest.LocalPipe{
Listen: func() (net.Listener, error) {
return vsock.Listen(0)
},
Dial: func(addr net.Addr) (net.Conn, error) {
a := addr.(*vsock.Addr)
return vsock.Dial(a.ContextID, a.Port)
},
}
nettest.TestConn(t, vsocklp.MakePipe)
}
Thoughts, @mikioh?
Once @mikioh and @mdlayher agree here, this can be accepted.
Ping, @mikioh and @mdlayher. Note that the crypto/tls package tests had what amounts to a copy of the nettest/conntest_test.go TestTestConn body, and we had some reliability problems with it on some systems. I submitted a few CLs to deflake tests (https://golang.org/cl/184157), by not assuming that every dial would succeed. I don't know if that's necessary in general, but if it is then that may influence the API design.
I'm happy to submit the code I have been using for this. I haven't heard anything from @mikioh in a while.
I can't say I've run into tests which sometimes fail to dial, but assuming both of my proposed hook functions continue to return errors, I wouldn't be opposed to adding a retry mechanism.
I'm currently implementing my own
net.Conn
type and am working on tests usingnettest.TestConn
: https://godoc.org/golang.org/x/net/nettest#TestConn.The
nettest.MakePipe
function provides adequate functionality for my needs, but a fair amount of boilerplate is required to create anettest.MakePipe
that can:net.Listener
net.Conn
at the listenerAs it turns out, there's a pretty useful function to make a local pipe in one of the tests: https://go.googlesource.com/net/+/refs/heads/master/nettest/conntest_test.go
I propose we generalize and export this function for use with
net.Listener
andnet.Conn
implementations of any type. I'm happy to accept name suggestions, but my proposed signature and WIP documentation is as follows:Usage is as follows:
Producing a local pipe with a TCP listener and connection, using net.Dial as a default dial function.
Producing a local pipe with a custom listener and connection implemented outside of the stdlib (https://github.com/mdlayher/vsock)
I don't mind putting this code in my own repository or similar if deemed unnecessary, but it seems like this would be an appropriate addition to
nettest
, since it enables easy setup of scaffolding for use withnettest.TestConn
./cc @dsnet @mikioh