|
1 | 1 | package terraform
|
2 | 2 |
|
3 | 3 | import (
|
4 |
| - "bufio" |
5 |
| - "bytes" |
6 | 4 | _ "embed"
|
7 |
| - "slices" |
8 | 5 | "testing"
|
9 | 6 |
|
10 |
| - "github.com/cespare/xxhash" |
11 | 7 | "github.com/stretchr/testify/assert"
|
12 | 8 | "github.com/stretchr/testify/require"
|
13 | 9 | "golang.org/x/tools/txtar"
|
14 |
| - "google.golang.org/protobuf/encoding/protojson" |
15 |
| - protobuf "google.golang.org/protobuf/proto" |
16 | 10 |
|
17 | 11 | "github.com/coder/coder/v2/coderd/database"
|
18 | 12 | "github.com/coder/coder/v2/provisionersdk/proto"
|
@@ -79,138 +73,24 @@ func TestAggregation(t *testing.T) {
|
79 | 73 | file.Name, database.AllProvisionerJobTimingStageValues())
|
80 | 74 |
|
81 | 75 | agg := newTimingAggregator(stage)
|
82 |
| - extractAllSpans(t, file.Data, agg) |
| 76 | + IngestAllSpans(t, file.Data, agg) |
83 | 77 | actualTimings = append(actualTimings, agg.aggregate()...)
|
84 | 78 | }
|
85 | 79 |
|
86 |
| - stableSortTimings(t, actualTimings) // To reduce flakiness. |
87 |
| - require.True(t, timingsAreEqual(t, expectedTimings.Data, actualTimings)) |
| 80 | + expected := ParseTimingLines(t, expectedTimings.Data) |
| 81 | + StableSortTimings(t, actualTimings) // To reduce flakiness. |
| 82 | + if !assert.True(t, TimingsAreEqual(t, expected, actualTimings)) { |
| 83 | + printExpectation(t, expected) |
| 84 | + } |
88 | 85 | })
|
89 | 86 | }
|
90 | 87 | }
|
91 | 88 |
|
92 |
| -func timingsAreEqual(t *testing.T, input []byte, actual []*proto.Timing) bool { |
93 |
| - t.Helper() |
94 |
| - |
95 |
| - // Parse the input into *proto.Timing structs. |
96 |
| - var expected []*proto.Timing |
97 |
| - scanner := bufio.NewScanner(bytes.NewBuffer(input)) |
98 |
| - for scanner.Scan() { |
99 |
| - line := scanner.Bytes() |
100 |
| - |
101 |
| - var msg proto.Timing |
102 |
| - require.NoError(t, protojson.Unmarshal(line, &msg)) |
103 |
| - |
104 |
| - expected = append(expected, &msg) |
105 |
| - } |
106 |
| - require.NoError(t, scanner.Err()) |
107 |
| - |
108 |
| - // Shortcut check. |
109 |
| - if len(expected)+len(actual) == 0 { |
110 |
| - t.Logf("both timings are empty") |
111 |
| - return true |
112 |
| - } |
113 |
| - |
114 |
| - // Shortcut check. |
115 |
| - if len(expected) != len(actual) { |
116 |
| - t.Logf("timings lengths are not equal: %d != %d", len(expected), len(actual)) |
117 |
| - printExpectation(t, actual) |
118 |
| - return false |
119 |
| - } |
120 |
| - |
121 |
| - // Compare each element; both are expected to be sorted in a stable manner. |
122 |
| - for i := 0; i < len(expected); i++ { |
123 |
| - ex := expected[i] |
124 |
| - ac := actual[i] |
125 |
| - if !protobuf.Equal(ex, ac) { |
126 |
| - t.Logf("timings are not equivalent: %q != %q", ex.String(), ac.String()) |
127 |
| - printExpectation(t, actual) |
128 |
| - return false |
129 |
| - } |
130 |
| - } |
131 |
| - |
132 |
| - return true |
133 |
| -} |
134 |
| - |
135 |
| -func extractAllSpans(t *testing.T, input []byte, aggregator *timingAggregator) { |
136 |
| - t.Helper() |
137 |
| - |
138 |
| - scanner := bufio.NewScanner(bytes.NewBuffer(input)) |
139 |
| - for scanner.Scan() { |
140 |
| - line := scanner.Bytes() |
141 |
| - log := parseTerraformLogLine(line) |
142 |
| - if log == nil { |
143 |
| - continue |
144 |
| - } |
145 |
| - |
146 |
| - ts, span, err := extractTimingSpan(log) |
147 |
| - if err != nil { |
148 |
| - // t.Logf("%s: failed span extraction on line: %q", err, line) |
149 |
| - continue |
150 |
| - } |
151 |
| - |
152 |
| - require.NotZerof(t, ts, "failed on line: %q", line) |
153 |
| - require.NotNilf(t, span, "failed on line: %q", line) |
154 |
| - |
155 |
| - aggregator.ingest(ts, span) |
156 |
| - } |
157 |
| - |
158 |
| - require.NoError(t, scanner.Err()) |
159 |
| -} |
160 |
| - |
161 | 89 | func printExpectation(t *testing.T, actual []*proto.Timing) {
|
162 | 90 | t.Helper()
|
163 | 91 |
|
164 | 92 | t.Log("expected:")
|
165 | 93 | for _, a := range actual {
|
166 |
| - printTiming(t, a) |
167 |
| - } |
168 |
| -} |
169 |
| - |
170 |
| -func printTiming(t *testing.T, timing *proto.Timing) { |
171 |
| - t.Helper() |
172 |
| - |
173 |
| - marshaler := protojson.MarshalOptions{ |
174 |
| - Multiline: false, // Ensure it's set to false for single-line JSON |
175 |
| - Indent: "", // No indentation |
| 94 | + PrintTiming(t, a) |
176 | 95 | }
|
177 |
| - |
178 |
| - out, err := marshaler.Marshal(timing) |
179 |
| - assert.NoError(t, err) |
180 |
| - t.Logf("%s", out) |
181 |
| -} |
182 |
| - |
183 |
| -func stableSortTimings(t *testing.T, timings []*proto.Timing) { |
184 |
| - slices.SortStableFunc(timings, func(a, b *proto.Timing) int { |
185 |
| - if a == nil || b == nil || a.Start == nil || b.Start == nil { |
186 |
| - return 0 |
187 |
| - } |
188 |
| - |
189 |
| - if a.Start.AsTime().Equal(b.Start.AsTime()) { |
190 |
| - // Special case: when start times are equal, we need to keep the ordering stable, so we hash both entries |
191 |
| - // and sort based on that (since end times could be equal too, in principle). |
192 |
| - ah := xxhash.Sum64String(a.String()) |
193 |
| - bh := xxhash.Sum64String(b.String()) |
194 |
| - |
195 |
| - if ah == bh { |
196 |
| - // WTF. |
197 |
| - t.Logf("identical timings detected!") |
198 |
| - printTiming(t, a) |
199 |
| - printTiming(t, b) |
200 |
| - return 0 |
201 |
| - } |
202 |
| - |
203 |
| - if ah < bh { |
204 |
| - return -1 |
205 |
| - } |
206 |
| - |
207 |
| - return 1 |
208 |
| - } |
209 |
| - |
210 |
| - if a.Start.AsTime().Before(b.Start.AsTime()) { |
211 |
| - return -1 |
212 |
| - } |
213 |
| - |
214 |
| - return 1 |
215 |
| - }) |
216 | 96 | }
|
0 commit comments