Skip to content

Commit 5415620

Browse files
Merge pull request #37 from deads2k/thread-context
add context down through testing
2 parents fb21123 + fc8ce57 commit 5415620

File tree

7 files changed

+82
-16
lines changed

7 files changed

+82
-16
lines changed

pkg/cmd/cmdrun/runsuite.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package cmdrun
22

33
import (
4+
"context"
45
"fmt"
56
"os"
7+
"os/signal"
8+
"syscall"
9+
"time"
610

711
"github.com/pkg/errors"
812
"github.com/spf13/cobra"
@@ -31,6 +35,32 @@ func NewRunSuiteCommand(registry *extension.Registry) *cobra.Command {
3135
"development use. Orchestration parameters, scheduling, isolation, etc are not obeyed, and Ginkgo tests are executed serially.",
3236
SilenceUsage: true,
3337
RunE: func(cmd *cobra.Command, args []string) error {
38+
ctx, cancelCause := context.WithCancelCause(context.Background())
39+
defer cancelCause(errors.New("exiting"))
40+
41+
abortCh := make(chan os.Signal, 2)
42+
go func() {
43+
<-abortCh
44+
fmt.Fprintf(os.Stderr, "Interrupted, terminating tests")
45+
cancelCause(errors.New("interrupt received"))
46+
47+
select {
48+
case sig := <-abortCh:
49+
fmt.Fprintf(os.Stderr, "Interrupted twice, exiting (%s)", sig)
50+
switch sig {
51+
case syscall.SIGINT:
52+
os.Exit(130)
53+
default:
54+
os.Exit(130) // if we were interrupted, never return zero.
55+
}
56+
57+
case <-time.After(30 * time.Minute): // allow time for cleanup. If we finish before this, we'll exit
58+
fmt.Fprintf(os.Stderr, "Timed out during cleanup, exiting")
59+
os.Exit(130) // if we were interrupted, never return zero.
60+
}
61+
}()
62+
signal.Notify(abortCh, syscall.SIGINT, syscall.SIGTERM)
63+
3464
ext := registry.Get(opts.componentFlags.Component)
3565
if ext == nil {
3666
return fmt.Errorf("component not found: %s", opts.componentFlags.Component)
@@ -72,7 +102,7 @@ func NewRunSuiteCommand(registry *extension.Registry) *cobra.Command {
72102
return errors.Wrap(err, "couldn't filter specs")
73103
}
74104

75-
return specs.Run(compositeWriter, opts.concurrencyFlags.MaxConcurency)
105+
return specs.Run(ctx, compositeWriter, opts.concurrencyFlags.MaxConcurency)
76106
},
77107
}
78108
opts.componentFlags.BindFlags(cmd.Flags())

pkg/cmd/cmdrun/runtest.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@ package cmdrun
22

33
import (
44
"bufio"
5+
"context"
56
"fmt"
67
"os"
8+
"errors"
9+
"os/signal"
10+
"syscall"
11+
"time"
712

813
"github.com/spf13/cobra"
914

@@ -30,6 +35,32 @@ func NewRunTestCommand(registry *extension.Registry) *cobra.Command {
3035
Short: "Runs tests by name",
3136
SilenceUsage: true,
3237
RunE: func(cmd *cobra.Command, args []string) error {
38+
ctx, cancelCause := context.WithCancelCause(context.Background())
39+
defer cancelCause(errors.New("exiting"))
40+
41+
abortCh := make(chan os.Signal, 2)
42+
go func() {
43+
<-abortCh
44+
fmt.Fprintf(os.Stderr, "Interrupted, terminating tests")
45+
cancelCause(errors.New("interrupt received"))
46+
47+
select {
48+
case sig := <-abortCh:
49+
fmt.Fprintf(os.Stderr, "Interrupted twice, exiting (%s)", sig)
50+
switch sig {
51+
case syscall.SIGINT:
52+
os.Exit(130)
53+
default:
54+
os.Exit(130) // if we were interrupted, never return zero.
55+
}
56+
57+
case <-time.After(30 * time.Minute): // allow time for cleanup. If we finish before this, we'll exit
58+
fmt.Fprintf(os.Stderr, "Timed out during cleanup, exiting")
59+
os.Exit(130) // if we were interrupted, never return zero.
60+
}
61+
}()
62+
signal.Notify(abortCh, syscall.SIGINT, syscall.SIGTERM)
63+
3364
ext := registry.Get(opts.componentFlags.Component)
3465
if ext == nil {
3566
return fmt.Errorf("component not found: %s", opts.componentFlags.Component)
@@ -69,7 +100,7 @@ func NewRunTestCommand(registry *extension.Registry) *cobra.Command {
69100
}
70101
defer w.Flush()
71102

72-
return specs.Run(w, opts.concurrencyFlags.MaxConcurency)
103+
return specs.Run(ctx, w, opts.concurrencyFlags.MaxConcurency)
73104
},
74105
}
75106
opts.componentFlags.BindFlags(cmd.Flags())

pkg/cypress/util.go

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

33
import (
4+
"context"
45
"encoding/json"
56
"encoding/xml"
67
"fmt"
@@ -54,10 +55,10 @@ func BuildExtensionTestSpecsFromCypressMetadata(metadata []byte) (ext.ExtensionT
5455
Name: tc.Name,
5556
Labels: sets.New(tc.Tags...),
5657
CodeLocations: []string{tc.FilePath},
57-
Run: func() *ext.ExtensionTestResult {
58+
Run: func(ctx context.Context) *ext.ExtensionTestResult {
5859
return runCypressTest(tc.ID, tc.Name, tc.FilePath)
5960
},
60-
RunParallel: func() *ext.ExtensionTestResult {
61+
RunParallel: func(ctx context.Context) *ext.ExtensionTestResult {
6162
// this is equivalent to before, but potentially could be improved.
6263
return runCypressTest(tc.ID, tc.Name, tc.FilePath)
6364
},

pkg/extension/extensiontests/spec.go

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

33
import (
4+
"context"
45
"fmt"
56
"strings"
67
"sync"
@@ -178,7 +179,7 @@ func (specs ExtensionTestSpecs) Names() []string {
178179
// are written to the given ResultWriter after each spec has completed execution. BeforeEach,
179180
// BeforeAll, AfterEach, AfterAll hooks are executed when specified. "Each" hooks must be thread
180181
// safe. Returns an error if any test spec failed, indicating the quantity of failures.
181-
func (specs ExtensionTestSpecs) Run(w ResultWriter, maxConcurrent int) error {
182+
func (specs ExtensionTestSpecs) Run(ctx context.Context, w ResultWriter, maxConcurrent int) error {
182183
queue := make(chan *ExtensionTestSpec)
183184
failures := atomic.Int64{}
184185

@@ -214,7 +215,7 @@ func (specs ExtensionTestSpecs) Run(w ResultWriter, maxConcurrent int) error {
214215
beforeEachTask.Run(*spec)
215216
}
216217

217-
res := runSpec(spec, runSingleSpec)
218+
res := runSpec(ctx, spec, runSingleSpec)
218219
if res.Result == ResultFailed {
219220
failures.Add(1)
220221
}
@@ -546,13 +547,13 @@ func (spec *ExtensionTestSpec) Exclude(excludeCEL string) *ExtensionTestSpec {
546547
return spec
547548
}
548549

549-
func runSpec(spec *ExtensionTestSpec, runSingleSpec bool) *ExtensionTestResult {
550+
func runSpec(ctx context.Context, spec *ExtensionTestSpec, runSingleSpec bool) *ExtensionTestResult {
550551
startTime := time.Now().UTC()
551552
var res *ExtensionTestResult
552553
if runSingleSpec || spec.RunParallel == nil {
553-
res = spec.Run()
554+
res = spec.Run(ctx)
554555
} else {
555-
res = spec.RunParallel()
556+
res = spec.RunParallel(ctx)
556557
}
557558
duration := time.Since(startTime)
558559
endTime := startTime.Add(duration).UTC()

pkg/extension/extensiontests/spec_test.go

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

33
import (
4+
"context"
45
"fmt"
56
"reflect"
67
"sync/atomic"
@@ -209,7 +210,7 @@ func TestExtensionTestSpecs_HookExecution(t *testing.T) {
209210
for i := 0; i < tc.numSpecs; i++ {
210211
specs = append(specs, &ExtensionTestSpec{
211212
Name: fmt.Sprintf("test spec %d", i+1),
212-
Run: func() *ExtensionTestResult {
213+
Run: func(ctx context.Context) *ExtensionTestResult {
213214
return produceTestResult(fmt.Sprintf("test result %d", i+1), 20*time.Second)
214215
},
215216
})
@@ -241,7 +242,7 @@ func TestExtensionTestSpecs_HookExecution(t *testing.T) {
241242
}
242243

243244
// Run the test specs
244-
err := specs.Run(NullResultWriter{}, 10)
245+
err := specs.Run(context.TODO(), NullResultWriter{}, 10)
245246
if err != nil {
246247
t.Fatalf("Unexpected error: %v", err)
247248
}

pkg/extension/extensiontests/types.go

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

33
import (
4+
"context"
5+
46
"github.com/openshift-eng/openshift-tests-extension/pkg/dbtime"
57
"github.com/openshift-eng/openshift-tests-extension/pkg/util/sets"
68
)
@@ -45,11 +47,11 @@ type ExtensionTestSpec struct {
4547

4648
// Run invokes a test in-process. It must not call back into `ote-binary run-test` because that will usually
4749
// cause an infinite recursion.
48-
Run func() *ExtensionTestResult `json:"-"`
50+
Run func(ctx context.Context) *ExtensionTestResult `json:"-"`
4951

5052
// RunParallel invokes a test in parallel with other tests. This is usually done by exec-ing out
5153
// to the `ote-binary run-test "test name"` commmand and interpretting the result.
52-
RunParallel func() *ExtensionTestResult `json:"-"`
54+
RunParallel func(ctx context.Context) *ExtensionTestResult `json:"-"`
5355

5456
// Hook functions
5557
afterAll []*OneTimeTask

pkg/ginkgo/util.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func BuildExtensionTestSpecsFromOpenShiftGinkgoSuite(selectFns ...ext.SelectFunc
7070
Labels: sets.New[string](spec.Labels()...),
7171
CodeLocations: codeLocations,
7272
Lifecycle: GetLifecycle(spec.Labels()),
73-
Run: func() *ext.ExtensionTestResult {
73+
Run: func(ctx context.Context) *ext.ExtensionTestResult {
7474
enforceSerialExecutionForGinkgo.Lock()
7575
defer enforceSerialExecutionForGinkgo.Unlock()
7676

@@ -131,9 +131,9 @@ func BuildExtensionTestSpecsFromOpenShiftGinkgoSuite(selectFns ...ext.SelectFunc
131131

132132
return result
133133
},
134-
RunParallel: func() *ext.ExtensionTestResult {
134+
RunParallel: func(ctx context.Context) *ext.ExtensionTestResult {
135135
// TODO pass through timeout and determine Lifecycle
136-
return SpawnProcessToRunTest(context.TODO(), name, 90*time.Minute)
136+
return SpawnProcessToRunTest(ctx, name, 90*time.Minute)
137137
},
138138
}
139139
specs = append(specs, testCase)

0 commit comments

Comments
 (0)