@@ -282,7 +282,7 @@ func (runner *TestFileRunner) Test(file *moduletest.File) {
282
282
}
283
283
284
284
// walk and execute the graph
285
- diags = runner .walkGraph (graph )
285
+ diags = runner .walkGraph (graph , file )
286
286
287
287
// If the graph walk was terminated, we don't want to add the diagnostics.
288
288
// The error the user receives will just be:
@@ -298,8 +298,9 @@ func (runner *TestFileRunner) Test(file *moduletest.File) {
298
298
}
299
299
300
300
// walkGraph goes through the graph and execute each run it finds.
301
- func (runner * TestFileRunner ) walkGraph (g * terraform.Graph ) tfdiags.Diagnostics {
301
+ func (runner * TestFileRunner ) walkGraph (g * terraform.Graph , file * moduletest. File ) tfdiags.Diagnostics {
302
302
sem := runner .Suite .semaphore
303
+ collectRunStatus , updateFileStatus := runner .trackRunStatuses (file )
303
304
304
305
// Walk the graph.
305
306
walkFn := func (v dag.Vertex ) (diags tfdiags.Diagnostics ) {
@@ -376,9 +377,13 @@ func (runner *TestFileRunner) walkGraph(g *terraform.Graph) tfdiags.Diagnostics
376
377
}
377
378
378
379
startTime := time .Now ().UTC ()
379
- runner .run (run , file , startTime )
380
+ deferFileStatus := runner .run (run , file , startTime )
380
381
runner .Suite .View .Run (run , file , moduletest .Complete , 0 )
381
- file .UpdateStatus (run .Status )
382
+ // If the run block is done, but it was due to an expected failure, we
383
+ // don't want to update the file status immediately. We'll collect the
384
+ // status of this run block and update the file status at the end of the
385
+ // file execution.
386
+ collectRunStatus (run , deferFileStatus )
382
387
case graph.GraphNodeExecutable :
383
388
diags = v .Execute (runner .EvalContext )
384
389
return diags
@@ -389,10 +394,12 @@ func (runner *TestFileRunner) walkGraph(g *terraform.Graph) tfdiags.Diagnostics
389
394
return
390
395
}
391
396
392
- return g .AcyclicGraph .Walk (walkFn )
397
+ diags := g .AcyclicGraph .Walk (walkFn )
398
+ updateFileStatus ()
399
+ return diags
393
400
}
394
401
395
- func (runner * TestFileRunner ) run (run * moduletest.Run , file * moduletest.File , startTime time.Time ) {
402
+ func (runner * TestFileRunner ) run (run * moduletest.Run , file * moduletest.File , startTime time.Time ) ( deferFileStatus bool ) {
396
403
log .Printf ("[TRACE] TestFileRunner: executing run block %s/%s" , file .Name , run .Name )
397
404
defer func () {
398
405
// If we got far enough to actually execute the run then we'll give
@@ -401,6 +408,7 @@ func (runner *TestFileRunner) run(run *moduletest.Run, file *moduletest.File, st
401
408
Start : startTime ,
402
409
Duration : time .Since (startTime ),
403
410
}
411
+
404
412
}()
405
413
406
414
key := run .GetStateKey ()
@@ -487,9 +495,9 @@ func (runner *TestFileRunner) run(run *moduletest.Run, file *moduletest.File, st
487
495
488
496
planScope , plan , planDiags := runner .plan (tfCtx , config , state , run , file , setVariables , references , start )
489
497
if run .Config .Command == configs .PlanTestCommand {
490
- // Then we want to assess our conditions and diagnostics differently.
491
498
planDiags = run .ValidateExpectedFailures (planDiags )
492
499
run .Diagnostics = run .Diagnostics .Append (planDiags )
500
+ // Then we want to assess our conditions and diagnostics differently.
493
501
if planDiags .HasErrors () {
494
502
run .Status = moduletest .Error
495
503
return
@@ -536,12 +544,21 @@ func (runner *TestFileRunner) run(run *moduletest.Run, file *moduletest.File, st
536
544
return
537
545
}
538
546
539
- // Otherwise any error during the planning prevents our apply from
547
+ // Otherwise any error (expected or unexpected) during the planning prevents our apply from
540
548
// continuing which is an error.
541
549
planDiags = run .ExplainExpectedFailures (planDiags )
542
550
run .Diagnostics = run .Diagnostics .Append (planDiags )
543
551
if planDiags .HasErrors () {
544
552
run .Status = moduletest .Error
553
+ // If the plan failed, but all the failures were expected, then we don't
554
+ // want to mark the overall file as a failure, so that subsequent runs can
555
+ // still be executed.
556
+ // We will collect the status of this run instead of updating the file status.
557
+ // At the end of the file execution, we will update the file status based on the
558
+ // statuses of all the runs.
559
+ if ! run .ValidateExpectedFailures (planDiags ).HasErrors () {
560
+ deferFileStatus = true
561
+ }
545
562
return
546
563
}
547
564
@@ -1255,3 +1272,27 @@ func (runner *TestFileRunner) AddVariablesToConfig(run *moduletest.Run, variable
1255
1272
}
1256
1273
1257
1274
}
1275
+
1276
+ // trackRunStatuses is a helper function that returns two functions. The first
1277
+ // function is used to collect the statuses of the runs, and the second function
1278
+ // is used to update the overall file status based on the statuses of the runs.
1279
+ func (runner * TestFileRunner ) trackRunStatuses (file * moduletest.File ) (func (* moduletest.Run , bool ), func ()) {
1280
+ statuses := make ([]moduletest.Status , len (file .Runs ))
1281
+ collector := func (run * moduletest.Run , deferred bool ) {
1282
+ if deferred {
1283
+ statuses [run .Index ] = run .Status
1284
+ } else {
1285
+ file .UpdateStatus (run .Status )
1286
+ }
1287
+ }
1288
+
1289
+ updater := func () {
1290
+ for _ , status := range statuses {
1291
+ file .UpdateStatus (status )
1292
+ }
1293
+ }
1294
+
1295
+ // We'll return two functions, one to collect the statuses of the runs, and
1296
+ // one to update the overall file status.
1297
+ return collector , updater
1298
+ }
0 commit comments