Atlas › Test

Fatalf

Exact test identity: go.temporal.io/server/common/testing/await/TestRequire_RetriesUntilAttemptPasses/Fatalf

Package
go.temporal.io/server/common/testing/await
Suite / test hierarchy
TestRequire_RetriesUntilAttemptPasses/Fatalf
Test
Fatalf
Introduced at
Fatalf Frontier kind: Test frontier
Covered ranges
42
Covered lines
128
Covered files
4

Covered source

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

go.temporal.io/server/common/testing/await/require_ctx.go 87 covered LOC · 31 ranges

Open complete file

20 const defaultSoftDeadlockTimeout = 30 * time.Second
21
22 > func softDeadlockTimeout() time.Duration { require_ctx.go
23 > if s := os.Getenv(softDeadlockTimeoutEnvVar); s != "" {
24 if d, err := time.ParseDuration(s); err == nil {
25 return d
26 }
27 }
28 > return defaultSoftDeadlockTimeout require_ctx.go
29 }
30
54 // Pass the *await.T to require.*/assert.* — failures cause a retry, not a
55 // test failure. Use t.Context() inside the callback to honor the timeout.
56 > func Require(ctx context.Context, tb testing.TB, condition func(*T), timeout, pollInterval time.Duration) { require_ctx.go
57 > tb.Helper()
58 > run(ctx, tb, condition, legacyConfig(timeout, pollInterval, ""), "Require", requireMisuseHint, true)
59 > }
60
61 // Requiref is like [Require] but adds a formatted message to the timeout
74 misuseHint string,
75 cancellable bool,
76 > ) { require_ctx.go
77 > tb.Helper()
78 >
79 > // Skip if the test already failed — no point polling.
80 > if tb.Failed() {
81 tb.Logf("%s: skipping (test already failed)", funcName)
82 return
83 }
84 // Guard: context.WithDeadline panics on a nil parent.
85 > if parentCtx == nil { require_ctx.go
86 tb.Fatalf("%s: nil context", funcName)
87 return
88 }
89
90 > deadline := time.Now().Add(cfg.totalTimeout) require_ctx.go
91 >
92 > // Cap at the parent context's deadline if it's earlier than our timeout.
93 > if parentDeadline, hasDeadline := parentCtx.Deadline(); hasDeadline && parentDeadline.Before(deadline) {
94 deadline = parentDeadline
95 }
97 // Cap at the test's deadline if it's earlier than our deadline.
98 // Ideally, the parent context already accounts for the test's deadline - but we are being defensive.
99 > if d, ok := tb.(interface{ Deadline() (time.Time, bool) }); ok { require_ctx.go
100 > if testDeadline, hasDeadline := d.Deadline(); hasDeadline && testDeadline.Before(deadline) { require_ctx.go
101 deadline = testDeadline
102 }
103 }
104
105 > effectiveTimeout := max(0, time.Until(deadline)) require_ctx.go
106 > awaitCtx, awaitCancel := context.WithDeadline(parentCtx, deadline)
107 > defer awaitCancel()
108 >
109 > report := timeoutReport{effectiveTimeout: effectiveTimeout}
110 >
111 > for {
112 > // Parent context was canceled while we were sleeping (not our deadline).
113 > if err := awaitCtx.Err(); err != nil && !deadlineReached(deadline) {
114 report.reportAttemptErrors(tb)
115 tb.Fatalf("%s: context canceled before condition was satisfied: %v", funcName, err)
117 }
118
119 > report.nextPoll() require_ctx.go
120 >
121 > // Per-attempt context: bounded by the configured attempt timeout and
122 > // further capped by the overall awaitCtx.
123 > attemptCtx, attemptCancel := context.WithTimeout(awaitCtx, cfg.attemptTimeout)
124 > t := &T{tb: tb, ctx: attemptCtx}
125 >
126 > // Run attempt.
127 > res := runAttempt(t, condition, attemptCancel, funcName, cancellable)
128 > attemptCancel()
129 > if res.panicVal != nil {
130 panic(res.panicVal) // propagate to caller
131 }
132 > if res.deadlocked { require_ctx.go
133 report.reportAttemptErrors(tb)
134 if cancellable {
141 return
142 }
143 > report.recordErrors(t.errors) require_ctx.go
144 >
145 > // Attempt-timeout expiry: attemptCtx is done but awaitCtx is not.
146 > // Record nothing special - the attempt's recorded errors (if any)
147 > // already describe what went wrong; otherwise we just retry.
148 > attemptHitOwnTimeout := attemptCtx.Err() == context.DeadlineExceeded && awaitCtx.Err() == nil
149 > if attemptHitOwnTimeout {
150 report.recordAttemptTimeout()
151 }
152
153 // Check misuse where the real test failed instead of just the attempt.
154 > if tb.Failed() { require_ctx.go
155 tb.Fatalf("%s: the test was marked failed directly — %s", funcName, misuseHint)
156 return
158
159 // Parent context was canceled during the attempt (not our deadline).
160 > if err := awaitCtx.Err(); err != nil && !deadlineReached(deadline) { require_ctx.go
161 report.reportAttemptErrors(tb)
162 tb.Fatalf("%s: context canceled before condition was satisfied: %v", funcName, err)
165
166 // Our deadline expired.
167 > if deadlineReached(deadline) { require_ctx.go
168 report.reportTimeout(tb, funcName, cfg.timeoutMsg)
169 return
171
172 // Success: attempt completed without failures.
173 > if !res.stopped && !t.Failed() && !attemptHitOwnTimeout { require_ctx.go
174 > return require_ctx.go
175 > }
176
177 // Wait for pollInterval, or context is canceled or deadline is reached.
178 > sleep(awaitCtx, deadline, cfg.pollInterval) require_ctx.go
179 }
180 }
211 funcName string,
212 cancellable bool,
213 > ) attemptResult { require_ctx.go
214 > done := make(chan attemptResult, 1)
215 >
216 > go func() {
217 > completed := false
218 > defer func() {
219 > if r := recover(); r != nil { require_ctx.go
220 > if _, ok := r.(attemptFailed); ok { require_ctx.go
221 > done <- attemptResult{stopped: true} require_ctx.go
222 > return
223 > }
224 done <- attemptResult{panicVal: r}
225 return
227 // recover returned nil: either normal return (completed=true) or
228 // runtime.Goexit (completed=false; Goexit is not a panic).
229 > done <- attemptResult{stopped: !completed} require_ctx.go
230 }()
231 > condition(t) require_ctx.go
232 > completed = true
233 }()
234
235 > if cancellable { require_ctx.go
236 > // Soft phase: wait for the condition, our soft timer, or parent cancel. require_ctx.go
237 > softTimer := time.NewTimer(softDeadlockTimeout())
238 > defer softTimer.Stop()
239 >
240 > select {
241 > case r := <-done: require_ctx.go
242 > return r
243 case <-softTimer.C:
244 // Soft deadlock: log a warning.
266 }
267
268 > func sleep(ctx context.Context, deadline time.Time, pollInterval time.Duration) { require_ctx.go
269 > remaining := time.Until(deadline)
270 > if remaining < pollInterval {
271 pollInterval = remaining
272 }
273
274 > timer := time.NewTimer(pollInterval) require_ctx.go
275 > defer timer.Stop()
276 >
277 > select {
278 case <-ctx.Done():
279 > case <-timer.C: require_ctx.go
280 }
281 }
282
283 > func deadlineReached(deadline time.Time) bool { require_ctx.go
284 > return !time.Now().Before(deadline)
285 > }
go.temporal.io/server/common/testing/await/config.go 17 covered LOC · 3 ranges

Open complete file

17 }
18
19 > func newConfig() config { config.go
20 > return config{
21 > attemptTimeout: envDuration(attemptTimeoutEnvVar, 10*time.Second) * debug.TimeoutMultiplier,
22 > }
23 > }
24
25 > func legacyConfig(timeout, pollInterval time.Duration, timeoutMsg string) config { config.go
26 > cfg := newConfig()
27 > cfg.totalTimeout = timeout
28 > cfg.pollInterval = pollInterval
29 > cfg.timeoutMsg = timeoutMsg
30 > return cfg
31 > }
32
33 > func envDuration(name string, fallback time.Duration) time.Duration { config.go
34 > if s := os.Getenv(name); s != "" {
35 > if d, err := time.ParseDuration(s); err == nil && d > 0 {
36 > return d
37 > }
38 }
39 return fallback
go.temporal.io/server/common/testing/await/t.go 17 covered LOC · 5 ranges

Open complete file

47
48 // Fail marks the current attempt as failed without stopping it.
49 > func (t *T) Fail() { t.go
50 > t.failed = true
51 > }
52
53 // Error records an error message for reporting on timeout.
58
59 // Errorf records an error message for reporting on timeout.
60 > func (t *T) Errorf(format string, args ...any) { t.go
61 > t.Fail()
62 > t.errors = append(t.errors, fmt.Sprintf(format, args...))
63 > }
64
65 // FailNow is called by require.* on failure. It stops the current attempt.
66 // Unlike testing.TB.FailNow(), this does NOT mark the test as failed.
67 > func (t *T) FailNow() { t.go
68 > t.Fail()
69 > panic(attemptFailed{})
70 }
71
77
78 // Fatalf records an error message and stops this attempt.
79 > func (t *T) Fatalf(format string, args ...any) { t.go
80 > t.Errorf(format, args...)
81 > t.FailNow()
82 > }
83
84 // Failed reports whether this attempt has failed.
85 > func (t *T) Failed() bool { t.go
86 > return t.failed
87 > }
88
89 // Helper marks the calling function as a test helper.
go.temporal.io/server/common/testing/await/report.go 7 covered LOC · 3 ranges

Open complete file

28 }
29
30 > func (r *timeoutReport) nextPoll() { report.go
31 > r.attempts++
32 > }
33
34 > func (r *timeoutReport) recordErrors(errors []string) { report.go
35 > if len(errors) > 0 {
36 > r.failures = append(r.failures, attemptFailure{attempt: r.attempts, errors: errors}) report.go
37 > }
38 }
39