Close out the repository audit
This commit is contained in:
@@ -19,7 +19,13 @@ func main() {
|
||||
}
|
||||
|
||||
func runCommand(args []string, stdout, stderr io.Writer, runner func(context.Context, []string, io.Writer, io.Writer) error) error {
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
return runCommandWithSignalContext(args, stdout, stderr, runner, signal.NotifyContext)
|
||||
}
|
||||
|
||||
type signalContextFunc func(context.Context, ...os.Signal) (context.Context, context.CancelFunc)
|
||||
|
||||
func runCommandWithSignalContext(args []string, stdout, stderr io.Writer, runner func(context.Context, []string, io.Writer, io.Writer) error, signalContext signalContextFunc) error {
|
||||
ctx, stop := signalContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
return runner(ctx, args, stdout, stderr)
|
||||
}
|
||||
|
||||
@@ -7,50 +7,30 @@ import (
|
||||
"os"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRunCommandCancelsActionContextOnSignal(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
signal os.Signal
|
||||
}{
|
||||
{name: "Interrupt", signal: os.Interrupt},
|
||||
{name: "Terminate", signal: syscall.SIGTERM},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
started := make(chan struct{})
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
done <- runCommand(nil, io.Discard, io.Discard, func(ctx context.Context, _ []string, _, _ io.Writer) error {
|
||||
close(started)
|
||||
<-ctx.Done()
|
||||
return ctx.Err()
|
||||
})
|
||||
}()
|
||||
func TestRunCommandBuildsCancelableSignalContext(t *testing.T) {
|
||||
var signals []os.Signal
|
||||
stopped := false
|
||||
signalContext := func(parent context.Context, requested ...os.Signal) (context.Context, context.CancelFunc) {
|
||||
signals = append([]os.Signal(nil), requested...)
|
||||
ctx, cancel := context.WithCancel(parent)
|
||||
cancel()
|
||||
return ctx, func() {
|
||||
stopped = true
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-started:
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("runner did not receive an action context")
|
||||
}
|
||||
|
||||
process, err := os.FindProcess(os.Getpid())
|
||||
if err != nil {
|
||||
t.Fatalf("FindProcess() error = %v", err)
|
||||
}
|
||||
if err := process.Signal(tt.signal); err != nil {
|
||||
t.Fatalf("Signal(%v) error = %v", tt.signal, err)
|
||||
}
|
||||
|
||||
select {
|
||||
case err := <-done:
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("runCommand() error = %v, want context cancellation", err)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("interrupt did not cancel the action context")
|
||||
}
|
||||
})
|
||||
err := runCommandWithSignalContext(nil, io.Discard, io.Discard, func(ctx context.Context, _ []string, _, _ io.Writer) error {
|
||||
return ctx.Err()
|
||||
}, signalContext)
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("runCommandWithSignalContext() error = %v, want context cancellation", err)
|
||||
}
|
||||
if len(signals) != 2 || signals[0] != os.Interrupt || signals[1] != syscall.SIGTERM {
|
||||
t.Fatalf("requested signals = %#v, want Interrupt and SIGTERM", signals)
|
||||
}
|
||||
if !stopped {
|
||||
t.Fatal("signal context stop function was not called")
|
||||
}
|
||||
}
|
||||
|
||||
58
cmd/weatherreporter/main_unix_test.go
Normal file
58
cmd/weatherreporter/main_unix_test.go
Normal file
@@ -0,0 +1,58 @@
|
||||
//go:build unix
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRunCommandCancelsActionContextOnSignal(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
signal os.Signal
|
||||
}{
|
||||
{name: "Interrupt", signal: os.Interrupt},
|
||||
{name: "Terminate", signal: syscall.SIGTERM},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
started := make(chan struct{})
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
done <- runCommand(nil, io.Discard, io.Discard, func(ctx context.Context, _ []string, _, _ io.Writer) error {
|
||||
close(started)
|
||||
<-ctx.Done()
|
||||
return ctx.Err()
|
||||
})
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-started:
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("runner did not receive an action context")
|
||||
}
|
||||
|
||||
process, err := os.FindProcess(os.Getpid())
|
||||
if err != nil {
|
||||
t.Fatalf("FindProcess() error = %v", err)
|
||||
}
|
||||
if err := process.Signal(tt.signal); err != nil {
|
||||
t.Fatalf("Signal(%v) error = %v", tt.signal, err)
|
||||
}
|
||||
|
||||
select {
|
||||
case err := <-done:
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("runCommand() error = %v, want context cancellation", err)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("interrupt did not cancel the action context")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user