Skip to content

Commit ba73694

Browse files
committed
Add total execution timeout flag
Add --timeout flag (default 30s) to limit total test execution time across all test suites, complementing per-test-case --handler-timeout.
1 parent 655dcfc commit ba73694

File tree

2 files changed

+34
-7
lines changed

2 files changed

+34
-7
lines changed

cmd/runner/main.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"context"
45
"flag"
56
"fmt"
67
"io/fs"
@@ -15,6 +16,7 @@ import (
1516
func main() {
1617
handlerPath := flag.String("handler", "", "Path to handler binary")
1718
handlerTimeout := flag.Duration("handler-timeout", 10*time.Second, "Max time to wait for handler to respond to each test case (e.g., 10s, 500ms)")
19+
timeout := flag.Duration("timeout", 30*time.Second, "Total timeout for executing all test suites (e.g., 30s, 1m)")
1820
flag.Parse()
1921

2022
if *handlerPath == "" {
@@ -36,13 +38,17 @@ func main() {
3638
}
3739

3840
// Create test runner
39-
testRunner, err := runner.NewTestRunner(*handlerPath, *handlerTimeout)
41+
testRunner, err := runner.NewTestRunner(*handlerPath, *handlerTimeout, *timeout)
4042
if err != nil {
4143
fmt.Fprintf(os.Stderr, "Error creating test runner: %v\n", err)
4244
os.Exit(1)
4345
}
4446
defer testRunner.CloseHandler()
4547

48+
// Create context with total execution timeout
49+
ctx, cancel := context.WithTimeout(context.Background(), *timeout)
50+
defer cancel()
51+
4652
// Run tests
4753
totalPassed := 0
4854
totalFailed := 0
@@ -59,7 +65,7 @@ func main() {
5965
}
6066

6167
// Run suite
62-
result := testRunner.RunTestSuite(*suite)
68+
result := testRunner.RunTestSuite(ctx, *suite)
6369
printResults(suite, result)
6470

6571
totalPassed += result.PassedTests

runner/runner.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package runner
22

33
import (
4+
"context"
45
"embed"
56
"encoding/json"
67
"fmt"
@@ -15,12 +16,15 @@ import (
1516
type TestRunner struct {
1617
handler *Handler
1718
handlerConfig *HandlerConfig
19+
timeout time.Duration
1820
}
1921

2022
// NewTestRunner creates a new test runner for executing test suites against a handler binary.
2123
// The handlerTimeout parameter specifies the maximum duration to wait for the handler to
2224
// respond to each test case. If zero, defaults to 10 seconds.
23-
func NewTestRunner(handlerPath string, handlerTimeout time.Duration) (*TestRunner, error) {
25+
// The timeout parameter specifies the total duration allowed for running all tests
26+
// across all test suites. If zero, defaults to 30 seconds.
27+
func NewTestRunner(handlerPath string, handlerTimeout time.Duration, timeout time.Duration) (*TestRunner, error) {
2428
if _, err := os.Stat(handlerPath); os.IsNotExist(err) {
2529
return nil, fmt.Errorf("handler binary not found: %s", handlerPath)
2630
}
@@ -33,12 +37,17 @@ func NewTestRunner(handlerPath string, handlerTimeout time.Duration) (*TestRunne
3337
return nil, err
3438
}
3539

40+
if timeout == 0 {
41+
timeout = 30 * time.Second
42+
}
43+
3644
return &TestRunner{
3745
handler: handler,
3846
handlerConfig: &HandlerConfig{
3947
Path: handlerPath,
4048
Timeout: handlerTimeout,
4149
},
50+
timeout: timeout,
4251
}, nil
4352
}
4453

@@ -89,15 +98,16 @@ func (tr *TestRunner) CloseHandler() {
8998
tr.handler = nil
9099
}
91100

92-
// RunTestSuite executes a test suite
93-
func (tr *TestRunner) RunTestSuite(suite TestSuite) TestResult {
101+
// RunTestSuite executes a test suite. The context can be used to enforce a total
102+
// execution timeout across all test suites.
103+
func (tr *TestRunner) RunTestSuite(ctx context.Context, suite TestSuite) TestResult {
94104
result := TestResult{
95105
SuiteName: suite.Name,
96106
TotalTests: len(suite.Tests),
97107
}
98108

99109
for _, test := range suite.Tests {
100-
testResult := tr.runTest(test)
110+
testResult := tr.runTest(ctx, test)
101111
result.TestResults = append(result.TestResults, testResult)
102112
if testResult.Passed {
103113
result.PassedTests++
@@ -111,7 +121,18 @@ func (tr *TestRunner) RunTestSuite(suite TestSuite) TestResult {
111121

112122
// runTest executes a single test case by sending a request, reading the response,
113123
// and validating the result matches expected output
114-
func (tr *TestRunner) runTest(test TestCase) SingleTestResult {
124+
func (tr *TestRunner) runTest(ctx context.Context, test TestCase) SingleTestResult {
125+
// Check if context is already cancelled
126+
select {
127+
case <-ctx.Done():
128+
return SingleTestResult{
129+
TestID: test.ID,
130+
Passed: false,
131+
Message: fmt.Sprintf("Total execution timeout exceeded (%v)", tr.timeout),
132+
}
133+
default:
134+
}
135+
115136
req := Request{
116137
ID: test.ID,
117138
Method: test.Method,

0 commit comments

Comments
 (0)