Atlas › Test

TestContextHasDeadline

Exact test identity: go.temporal.io/server/common/testing/parallelsuite/TestRun_AcceptsSuite/context/TestContextHasDeadline

Package
go.temporal.io/server/common/testing/parallelsuite
Suite / test hierarchy
TestRun_AcceptsSuite/context/TestContextHasDeadline
Test
TestContextHasDeadline
Introduced at
TestContextHasDeadline Frontier kind: Test frontier
Covered ranges
45
Covered lines
174
Covered files
5

Covered source

Expand a file to inspect source; the > gutter marks covered lines.

go.temporal.io/server/common/testing/parallelsuite/suite.go 93 covered LOC · 26 ranges

Open complete file

74 //
75 //nolint:revive // ctx is last so callers can pass nil to mean "no override"; SA1012 forbids passing nil as the first ctx arg.
76 > func (s *Suite[T]) copySuite(t *testing.T, parallel bool, assertT require.TestingT, ctx context.Context) testingSuite { suite.go
77 > cp := reflect.New(reflect.TypeFor[T]().Elem()).Interface().(T)
78 > cp.initSuite(t, parallel, assertT, ctx)
79 > return cp
80 > }
81
82 //nolint:revive // see copySuite above.
83 > func (s *Suite[T]) initSuite(t *testing.T, parallel bool, assertT require.TestingT, ctx context.Context) { suite.go
84 > g := &s.guardT
85 > g.name = t.Name()
86 > g.T = t
87 > g.hasSubtests.Store(false)
88 > s.runParallel = parallel
89 > s.ctx = ctx
90 > s.ctxOnce = sync.Once{}
91 > if s.runParallel {
92 > t.Parallel() //nolint:testifylint // parallelsuite intentionally supports parallel tests
93 > }
94 > if assertT == nil {
95 > assertT = g
96 > }
97 > s.assertT = assertT
98 > s.Assertions = require.New(assertT)
99 > s.ProtoAssertions = protorequire.New(assertT)
100 > s.HistoryRequire = historyrequire.New(assertT)
101 }
102
103 // T returns the *testing.T, panicking if the guard has been sealed.
104 > func (s *Suite[T]) T() *testing.T { suite.go
105 > if s.guardT.hasSubtests.Load() {
106 panic("parallelsuite: do not call T() after Run(); use the subtest callback's parameter instead")
107 }
108 > return s.guardT.T suite.go
109 }
110
126 // Context returns the test-scoped context (created from [testcontext]).
127 // Inside an [Await] callback, it returns the await-scoped context.
128 > func (s *Suite[T]) Context() context.Context { suite.go
129 > s.ctxOnce.Do(func() {
130 > if s.ctx == nil {
131 > s.ctx = testcontext.For(s.T())
132 > }
133 })
134 > return s.ctx suite.go
135 }
136
177 //
178 // The suite must embed [Suite] and have no other fields.
179 > func Run[T testingSuite](t *testing.T, s T, args ...any) { suite.go
180 > run(t, s, true, args...)
181 > }
182
183 // RunLegacySequential behaves like [Run] but does not mark test methods as parallel.
189 }
190
191 > func run[T testingSuite](t *testing.T, s T, methodsParallel bool, args ...any) { suite.go
192 > t.Helper()
193 >
194 > typ := reflect.TypeFor[T]()
195 > if typ.Kind() != reflect.Pointer || typ.Elem().Kind() != reflect.Struct {
196 panic(fmt.Sprintf("parallelsuite.Run: suite must be a pointer to a struct, got %v", typ))
197 }
198 > structType := typ.Elem() suite.go
199 >
200 > validateSuiteStruct(structType)
201 >
202 > methods := discoverTestMethods(typ, structType, args)
203 > if len(methods) == 0 {
204 panic(fmt.Sprintf("parallelsuite.Run: suite %s has no Test* methods", structType.Name()))
205 }
206
207 > methods = applyTestifyMFilter(methods) suite.go
208 > if len(methods) == 0 {
209 return // all methods filtered by -testify.m; nothing to run
210 }
211
212 > argVals := make([]reflect.Value, len(args)) suite.go
213 > for i, a := range args {
214 argVals[i] = reflect.ValueOf(a)
215 }
216
217 > s.initSuite(t, true, nil, nil) suite.go
218 >
219 > for _, method := range methods {
220 > t.Run(method.Name, func(t *testing.T) {
221 > cpS := s.copySuite(t, methodsParallel, nil, nil)
222 > callArgs := append([]reflect.Value{reflect.ValueOf(cpS)}, argVals...)
223 > method.Func.Call(callArgs)
224 > })
225 }
226 }
228 var inheritedMethods map[string]bool
229
230 > func init() { suite.go
231 > type ds struct{ Suite[*ds] }
232 > ptrType := reflect.TypeFor[*ds]()
233 > inheritedMethods = make(map[string]bool, ptrType.NumMethod())
234 > for method := range ptrType.Methods() {
235 > inheritedMethods[method.Name] = true
236 > }
237 }
238
239 > func validateSuiteStruct(structType reflect.Type) { suite.go
240 > if !strings.HasSuffix(structType.Name(), "Suite") {
241 panic(fmt.Sprintf("parallelsuite.Run: struct name %q must end with \"Suite\"", structType.Name()))
242 }
243
244 > if structType.NumField() != 1 { suite.go
245 panic(fmt.Sprintf(
246 "parallelsuite.Run: suite %s must have no fields besides the embedded parallelsuite.Suite; "+
249 ))
250 }
251 > f := structType.Field(0) suite.go
252 > if !f.Anonymous {
253 panic(fmt.Sprintf(
254 "parallelsuite.Run: suite %s must embed parallelsuite.Suite, found named field %q",
263 // The flag is registered by testify's suite package (imported above); we share
264 // that registration via flag.Lookup rather than registering it a second time.
265 > func applyTestifyMFilter(methods []reflect.Method) []reflect.Method { suite.go
266 > f := flag.Lookup("testify.m")
267 > if f == nil {
268 return methods
269 }
270 > pattern := f.Value.String() suite.go
271 > if pattern == "" {
272 > return methods suite.go
273 > }
274 re, err := regexp.Compile(pattern)
275 if err != nil {
285 }
286
287 > func discoverTestMethods(ptrType, structType reflect.Type, args []any) []reflect.Method { suite.go
288 > expectedNumIn := 1 + len(args)
289 >
290 > for method := range ptrType.Methods() {
291 > name := method.Name
292 > if !strings.HasPrefix(name, "Test") && !inheritedMethods[name] {
293 panic(fmt.Sprintf(
294 "parallelsuite.Run: suite %s has exported method %s that does not start with Test; "+
299 }
300
301 > var methods []reflect.Method suite.go
302 > for method := range ptrType.Methods() {
303 > if !strings.HasPrefix(method.Name, "Test") {
304 > continue
305 }
306
307 > mt := method.Type suite.go
308 > if mt.NumOut() != 0 {
309 panic(fmt.Sprintf(
310 "parallelsuite.Run: method %s.%s must not have return values, got %v",
312 ))
313 }
314 > if mt.NumIn() != expectedNumIn { suite.go
315 panic(fmt.Sprintf(
316 "parallelsuite.Run: method %s.%s has wrong number of parameters: expected %d, got %d (%v)",
319 }
320
321 > for j, a := range args { suite.go
322 paramType := mt.In(1 + j)
323 argType := reflect.TypeOf(a)
330 }
331
332 > methods = append(methods, method) suite.go
333 }
334 > return methods suite.go
335 }
go.temporal.io/server/common/testing/testcontext/context.go 70 covered LOC · 15 ranges

Open complete file

39
40 // DefaultTimeout returns the effective default timeout for test-scoped contexts.
41 > func DefaultTimeout() time.Duration { context.go
42 > return effectiveTimeout(0)
43 > }
44
45 // For returns the test-scoped context for tb. The context is canceled
49 // return the same context, but an explicit different timeout fails instead of
50 // being silently ignored.
51 > func For(tb testing.TB, opts ...Option) context.Context { context.go
52 > tb.Helper()
53 >
54 > cfg := config{timeout: DefaultTimeout()}
55 > for _, opt := range opts {
56 opt(&cfg)
57 }
58
59 > st := getContextState(tb, cfg.timeout) context.go
60 > st.configure(tb, cfg)
61 > return st.context()
62 }
63
98 }
99
100 > func getContextState(tb testing.TB, timeout time.Duration) *contextState { context.go
101 > tb.Helper()
102 >
103 > testContexts.Lock()
104 > defer testContexts.Unlock()
105 >
106 > if st, ok := testContexts.byTest[tb]; ok {
107 return st
108 }
109
110 > ctx, cancel := context.WithTimeout(tb.Context(), timeout) context.go
111 >
112 > // Annotate gRPC requests with the test name for OTEL tracing.
113 > ctx = metadata.AppendToOutgoingContext(ctx, testNameMetadataKey, tb.Name())
114 >
115 > st := &contextState{
116 > ctx: ctx,
117 > cancel: cancel,
118 > timeout: timeout,
119 > decorators: make(map[any]struct{}),
120 > }
121 > testContexts.byTest[tb] = st
122 >
123 > tb.Cleanup(func() {
124 > err := st.err()
125 > st.cancel()
126 > testContexts.Lock()
127 > delete(testContexts.byTest, tb)
128 > testContexts.Unlock()
129 > if err == context.DeadlineExceeded {
130 tb.Errorf("test exceeded timeout of %v", st.timeout)
131 }
132 > st.release() context.go
133 })
134 > return st context.go
135 }
136
137 > func (s *contextState) configure(tb testing.TB, cfg config) { context.go
138 > tb.Helper()
139 >
140 > s.mu.Lock()
141 > defer s.mu.Unlock()
142 >
143 > if cfg.timeoutSet && cfg.timeout != s.timeout {
144 tb.Fatalf("testcontext: test context already exists with timeout %v; cannot change it to %v", s.timeout, cfg.timeout)
145 }
168 }
169
170 > func (s *contextState) context() context.Context { context.go
171 > s.mu.Lock()
172 > defer s.mu.Unlock()
173 > return s.ctx
174 > }
175
176 > func (s *contextState) err() error { context.go
177 > s.mu.Lock()
178 > defer s.mu.Unlock()
179 > return s.ctx.Err()
180 > }
181
182 > func (s *contextState) release() { context.go
183 > s.mu.Lock()
184 > defer s.mu.Unlock()
185 > s.ctx = nil
186 > }
187
188 > func effectiveTimeout(customTimeout time.Duration) (timeout time.Duration) { context.go
189 > defer func() {
190 > // Build flag TEMPORAL_DEBUG applies a timeout multiplier to all test timeouts.
191 > timeout *= debug.TimeoutMultiplier
192 > }()
193
194 // 1. Custom timeout (via WithTimeout option).
195 > if customTimeout > 0 { context.go
196 return customTimeout
197 }
198
199 // 2. TEMPORAL_TEST_TIMEOUT environment variable.
200 > if envTimeout := os.Getenv("TEMPORAL_TEST_TIMEOUT"); envTimeout != "" { context.go
201 if dur, err := time.ParseDuration(envTimeout); err == nil && dur > 0 {
202 return dur
205
206 // 3. Default timeout.
207 > return defaultTimeout context.go
208 }
go.temporal.io/server/common/testing/historyrequire/history_require.go 5 covered LOC · 1 range

Open complete file

36 )
37
38 > func New(t require.TestingT) HistoryRequire { history_require.go
39 > return HistoryRequire{
40 > t: t,
41 > }
42 > }
43
44 // TODO (maybe):
go.temporal.io/server/common/testing/parallelsuite/guard.go 3 covered LOC · 2 ranges

Open complete file

16 }
17
18 > func (g *guardT) Helper() { guard.go
19 > if g.hasSubtests.Load() {
20 panic(fmt.Sprintf(
21 "parallelsuite: assertion called on %q after Run() was called; "+
24 ))
25 }
26 > g.T.Helper() guard.go
27 }
28
go.temporal.io/server/common/testing/protorequire/require.go 3 covered LOC · 1 range

Open complete file

38 }
39
40 > func New(t require.TestingT) ProtoAssertions { require.go
41 > return ProtoAssertions{t}
42 > }
43
44 // ProtoEqual compares two proto messages for equality using proto semantics. Options can be passed to customize