@@ -4,6 +4,8 @@ package terraform_test
44
55import (
66 "context"
7+ "encoding/json"
8+ "fmt"
79 "os"
810 "path/filepath"
911 "strings"
@@ -36,63 +38,65 @@ func TestTimingsFromProvision(t *testing.T) {
3638 binaryPath : fakeBin ,
3739 })
3840 sess := configure (ctx , t , api , & proto.Config {})
39- _ = initDo (t , sess , testutil .CreateTar (t , nil ))
4041
4142 ctx , cancel := context .WithTimeout (ctx , testutil .WaitLong )
4243 t .Cleanup (cancel )
4344
44- // When: a plan is executed in the provisioner, our fake terraform will be executed and will produce a
45- // state file and some log content.
46- err = sendPlan (sess , proto .WorkspaceTransition_START )
47- require .NoError (t , err )
48-
4945 var timings []* proto.Timing
5046
51- for {
52- select {
53- case <- ctx .Done ():
54- t .Fatal (ctx .Err ())
55- default :
56- }
57-
58- msg , err := sess .Recv ()
59- require .NoError (t , err )
60-
61- if log := msg .GetLog (); log != nil {
62- t .Logf ("%s: %s: %s" , "plan" , log .Level .String (), log .Output )
63- }
64- if c := msg .GetPlan (); c != nil {
65- require .Empty (t , c .Error )
66- // Capture the timing information returned by the plan process.
67- timings = append (timings , c .GetTimings ()... )
47+ handleResponse := func (t * testing.T , stage string ) {
48+ t .Helper ()
49+ for {
50+ select {
51+ case <- ctx .Done ():
52+ t .Fatal (ctx .Err ())
53+ default :
54+ }
55+
56+ msg , err := sess .Recv ()
57+ require .NoError (t , err )
58+
59+ if log := msg .GetLog (); log != nil {
60+ t .Logf ("%s: %s: %s" , stage , log .Level .String (), log .Output )
61+ continue
62+ }
63+ switch {
64+ case msg .GetInit () != nil :
65+ timings = append (timings , msg .GetInit ().GetTimings ()... )
66+ case msg .GetPlan () != nil :
67+ timings = append (timings , msg .GetPlan ().GetTimings ()... )
68+ case msg .GetApply () != nil :
69+ timings = append (timings , msg .GetApply ().GetTimings ()... )
70+ case msg .GetGraph () != nil :
71+ timings = append (timings , msg .GetGraph ().GetTimings ()... )
72+ }
6873 break
6974 }
7075 }
7176
77+ // When: configured, our fake terraform will fake an init setup
78+ err = sendInit (sess , testutil .CreateTar (t , nil ))
79+ require .NoError (t , err )
80+ handleResponse (t , "init" )
81+
82+ // When: a plan is executed in the provisioner, our fake terraform will be executed and will produce a
83+ // state file and some log content.
84+ err = sendPlan (sess , proto .WorkspaceTransition_START )
85+ require .NoError (t , err )
86+
87+ handleResponse (t , "plan" )
88+
7289 // When: the plan has completed, let's trigger an apply.
7390 err = sendApply (sess , proto .WorkspaceTransition_START )
7491 require .NoError (t , err )
7592
76- for {
77- select {
78- case <- ctx .Done ():
79- t .Fatal (ctx .Err ())
80- default :
81- }
93+ handleResponse (t , "apply" )
8294
83- msg , err := sess .Recv ()
84- require .NoError (t , err )
95+ // When: the apply has completed, graph the results
96+ err = sendGraph (sess , proto .GraphSource_SOURCE_STATE )
97+ require .NoError (t , err )
8598
86- if log := msg .GetLog (); log != nil {
87- t .Logf ("%s: %s: %s" , "apply" , log .Level .String (), log .Output )
88- }
89- if c := msg .GetApply (); c != nil {
90- require .Empty (t , c .Error )
91- // Capture the timing information returned by the apply process.
92- timings = append (timings , c .GetTimings ()... )
93- break
94- }
95- }
99+ handleResponse (t , "graph" )
96100
97101 // Sort the timings stably to keep reduce flakiness.
98102 terraform_internal .StableSortTimings (t , timings )
@@ -107,7 +111,8 @@ func TestTimingsFromProvision(t *testing.T) {
107111 // NOTE: These timings have been encoded to JSON format to make the tests more readable.
108112 initTimings := terraform_internal .ParseTimingLines (t , []byte (`{"start":"2025-10-22T17:48:29Z","end":"2025-10-22T17:48:31Z","action":"load","resource":"modules","stage":"init","state":"COMPLETED"}
109113{"start":"2025-10-22T17:48:29Z","end":"2025-10-22T17:48:29Z","action":"load","resource":"backend","stage":"init","state":"COMPLETED"}
110- {"start":"2025-10-22T17:48:31Z","end":"2025-10-22T17:48:34Z","action":"load","resource":"provider plugins","stage":"init","state":"COMPLETED"}` ))
114+ {"start":"2025-10-22T17:48:31Z","end":"2025-10-22T17:48:34Z","action":"load","resource":"provider plugins","stage":"init","state":"COMPLETED"}
115+ {}` ))
111116 planTimings := terraform_internal .ParseTimingLines (t , []byte (`{"start":"2024-08-15T08:26:39.194726Z", "end":"2024-08-15T08:26:39.195836Z", "action":"read", "source":"coder", "resource":"data.coder_parameter.memory_size", "stage":"plan", "state":"COMPLETED"}
112117{"start":"2024-08-15T08:26:39.194726Z", "end":"2024-08-15T08:26:39.195712Z", "action":"read", "source":"coder", "resource":"data.coder_provisioner.me", "stage":"plan", "state":"COMPLETED"}
113118{"start":"2024-08-15T08:26:39.194726Z", "end":"2024-08-15T08:26:39.195820Z", "action":"read", "source":"coder", "resource":"data.coder_workspace.me", "stage":"plan", "state":"COMPLETED"}` ))
@@ -118,6 +123,10 @@ func TestTimingsFromProvision(t *testing.T) {
118123 graphTimings := terraform_internal .ParseTimingLines (t , []byte (`{"start":"2000-01-01T01:01:01.123456Z", "end":"2000-01-01T01:01:01.123456Z", "action":"building terraform dependency graph", "source":"terraform", "resource":"state file", "stage":"graph", "state":"COMPLETED"}` ))
119124 graphTiming := graphTimings [0 ]
120125
126+ for _ , ti := range timings {
127+ msg , _ := json .Marshal (ti )
128+ fmt .Println (string (msg ))
129+ }
121130 require .Len (t , timings , len (initTimings )+ len (planTimings )+ len (applyTimings )+ len (graphTimings ))
122131
123132 // init/graph timings are computed dynamically during provisioning whereas plan/apply come from the logs (fixtures) in
0 commit comments