From ffec2af382ba06bd59f71a6e290b7167c78997eb Mon Sep 17 00:00:00 2001 From: Arseny Balobanov Date: Sat, 15 Feb 2020 22:39:40 +0300 Subject: [PATCH] Add test helpers to find free tcp ports and wait for ports to become available. --- tools/testtool/freeport.go | 57 +++++++++++++++++++++++++++++++++ tools/testtool/freeport_test.go | 27 ++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 tools/testtool/freeport.go create mode 100644 tools/testtool/freeport_test.go diff --git a/tools/testtool/freeport.go b/tools/testtool/freeport.go new file mode 100644 index 0000000..8f07e38 --- /dev/null +++ b/tools/testtool/freeport.go @@ -0,0 +1,57 @@ +package testtool + +import ( + "context" + "fmt" + "net" + "os" + "strconv" + "time" +) + +// GetFreePort returns free local tcp port. +func GetFreePort() (string, error) { + addr, err := net.ResolveTCPAddr("tcp", "localhost:0") + if err != nil { + return "", err + } + + l, err := net.ListenTCP("tcp", addr) + if err != nil { + return "", err + } + defer func() { _ = l.Close() }() + + p := l.Addr().(*net.TCPAddr).Port + + return strconv.Itoa(p), nil +} + +// WaitForPort tries to connect to given local port with constant backoff. +// +// Can be canceled via ctx. +func WaitForPort(ctx context.Context, port string) { + t := time.NewTicker(time.Millisecond * 100) + defer t.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-t.C: + if err := portIsReady(port); err != nil { + _, _ = fmt.Fprintf(os.Stderr, "waiting for port: %s\n", err) + break + } + return + } + } +} + +func portIsReady(port string) error { + conn, err := net.Dial("tcp", net.JoinHostPort("localhost", port)) + if err != nil { + return err + } + return conn.Close() +} diff --git a/tools/testtool/freeport_test.go b/tools/testtool/freeport_test.go new file mode 100644 index 0000000..2cd4177 --- /dev/null +++ b/tools/testtool/freeport_test.go @@ -0,0 +1,27 @@ +package testtool + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestGetFreePort(t *testing.T) { + p, err := GetFreePort() + require.NoError(t, err) + require.NotEmpty(t, p) +} + +func TestWaitForPort(t *testing.T) { + p, err := GetFreePort() + require.NoError(t, err) + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + WaitForPort(ctx, p) + + require.Error(t, ctx.Err()) +}