Skip to content

Commit

Permalink
chore(httpserver): option to set shutdown timeout for unit tests (#2066)
Browse files Browse the repository at this point in the history
* chore(httpserver): optional shutdown timeout parameter

* fix(pprof-tests): bigger shutdown timeout

* Increase timeout for `Test_Server_Run_success`

* Add test depth to `Test_New` in `httpserver`

* Add licenses
  • Loading branch information
qdm12 authored Nov 26, 2021
1 parent 42908d0 commit 844802a
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 9 deletions.
45 changes: 45 additions & 0 deletions internal/httpserver/option_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2021 ChainSafe Systems (ON)
// SPDX-License-Identifier: LGPL-3.0-only

package httpserver

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func Test_newOptionalSettings(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
options []Option
settings optionalSettings
}{
"no option": {
settings: optionalSettings{
shutdownTimeout: 3 * time.Second,
},
},
"shutdown option": {
options: []Option{
ShutdownTimeout(time.Second),
},
settings: optionalSettings{
shutdownTimeout: time.Second,
},
},
}

for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()

settings := newOptionalSettings(testCase.options)

assert.Equal(t, testCase.settings, settings)
})
}
}
34 changes: 34 additions & 0 deletions internal/httpserver/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2021 ChainSafe Systems (ON)
// SPDX-License-Identifier: LGPL-3.0-only

package httpserver

import "time"

// Option is a functional option for the HTTP server.
type Option func(s *optionalSettings)

type optionalSettings struct {
shutdownTimeout time.Duration
}

func newOptionalSettings(options []Option) (settings optionalSettings) {
for _, option := range options {
option(&settings)
}

if settings.shutdownTimeout == 0 {
const defaultShutdownTimeout = 3 * time.Second
settings.shutdownTimeout = defaultShutdownTimeout
}

return settings
}

// ShutdownTimeout sets an optional timeout for the HTTP server
// to shutdown. The default shutdown is 3 seconds.
func ShutdownTimeout(timeout time.Duration) Option {
return func(s *optionalSettings) {
s.shutdownTimeout = timeout
}
}
8 changes: 4 additions & 4 deletions internal/httpserver/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"errors"
"net"
"net/http"
"time"
)

// Run runs the HTTP server until ctx is canceled.
Expand All @@ -28,11 +27,12 @@ func (s *Server) Run(ctx context.Context, ready chan<- struct{}, done chan<- err
}

s.logger.Warn(s.name + " http server shutting down: " + ctx.Err().Error())
const shutdownGraceDuration = 3 * time.Second
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownGraceDuration)
shutdownCtx, cancel := context.WithTimeout(context.Background(),
s.optional.shutdownTimeout)
defer cancel()
if err := server.Shutdown(shutdownCtx); err != nil {
s.logger.Error(s.name + " http server failed shutting down: " + err.Error())
s.logger.Error(s.name + " http server failed shutting down within " +
s.optional.shutdownTimeout.String())
}
}()

Expand Down
4 changes: 4 additions & 0 deletions internal/httpserver/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"
"regexp"
"testing"
"time"

gomock "github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
Expand All @@ -26,6 +27,9 @@ func Test_Server_Run_success(t *testing.T) {
address: "127.0.0.1:0",
addressSet: make(chan struct{}),
logger: logger,
optional: optionalSettings{
shutdownTimeout: 10 * time.Second,
},
}

ctx, cancel := context.WithCancel(context.Background())
Expand Down
4 changes: 3 additions & 1 deletion internal/httpserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,19 @@ type Server struct {
addressSet chan struct{}
handler http.Handler
logger Logger
optional optionalSettings
}

// New creates a new HTTP server with a name, listening on
// the address specified and using the HTTP handler provided.
func New(name, address string, handler http.Handler,
logger Logger) *Server {
logger Logger, options ...Option) *Server {
return &Server{
name: name,
address: address,
addressSet: make(chan struct{}),
handler: handler,
logger: logger,
optional: newOptionalSettings(options),
}
}
7 changes: 6 additions & 1 deletion internal/httpserver/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package httpserver
import (
"net/http"
"testing"
"time"

"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
Expand All @@ -27,9 +28,13 @@ func Test_New(t *testing.T) {
address: address,
handler: handler,
logger: logger,
optional: optionalSettings{
shutdownTimeout: time.Second,
},
}

server := New(name, address, handler, logger)
server := New(name, address, handler, logger,
ShutdownTimeout(time.Second))

assert.NotNil(t, server.addressSet)
server.addressSet = nil
Expand Down
5 changes: 3 additions & 2 deletions internal/pprof/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import (

// NewServer creates a new Pprof server which will listen at
// the address specified.
func NewServer(address string, logger httpserver.Logger) *httpserver.Server {
func NewServer(address string, logger httpserver.Logger,
options ...httpserver.Option) *httpserver.Server {
handler := http.NewServeMux()
handler.HandleFunc("/debug/pprof/", pprof.Index)
handler.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
Expand All @@ -23,5 +24,5 @@ func NewServer(address string, logger httpserver.Logger) *httpserver.Server {
handler.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
handler.Handle("/debug/pprof/heap", pprof.Handler("heap"))
handler.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate"))
return httpserver.New("pprof", address, handler, logger)
return httpserver.New("pprof", address, handler, logger, options...)
}
5 changes: 4 additions & 1 deletion internal/pprof/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"testing"
"time"

"github.com/ChainSafe/gossamer/internal/httpserver"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand All @@ -27,7 +28,9 @@ func Test_Server(t *testing.T) {
logger.EXPECT().Info(newRegexMatcher("^pprof http server listening on 127.0.0.1:[1-9][0-9]{0,4}$"))
logger.EXPECT().Warn("pprof http server shutting down: context canceled")

server := NewServer(address, logger)
const httpServerShutdownTimeout = 10 * time.Second // 10s in case test worker is slow
server := NewServer(address, logger,
httpserver.ShutdownTimeout(httpServerShutdownTimeout))
require.NotNil(t, server)

ctx, cancel := context.WithCancel(context.Background())
Expand Down

0 comments on commit 844802a

Please sign in to comment.