Skip to content

Commit 73bac3f

Browse files
committed
Moar tests
Signed-off-by: Danny Kopping <danny@coder.com>
1 parent 275bfca commit 73bac3f

File tree

6 files changed

+426
-127
lines changed

6 files changed

+426
-127
lines changed

provisioner/terraform/testdata/timings-aggregation/fake-terraform.sh

Lines changed: 148 additions & 0 deletions
Large diffs are not rendered by default.

provisioner/terraform/testutil.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package terraform
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"slices"
7+
"testing"
8+
9+
"github.com/cespare/xxhash"
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
"google.golang.org/protobuf/encoding/protojson"
13+
protobuf "google.golang.org/protobuf/proto"
14+
15+
"github.com/coder/coder/v2/provisionersdk/proto"
16+
)
17+
18+
func ParseTimingLines(t *testing.T, input []byte) []*proto.Timing {
19+
t.Helper()
20+
21+
// Parse the input into *proto.Timing structs.
22+
var expected []*proto.Timing
23+
scanner := bufio.NewScanner(bytes.NewBuffer(input))
24+
for scanner.Scan() {
25+
line := scanner.Bytes()
26+
27+
var msg proto.Timing
28+
require.NoError(t, protojson.Unmarshal(line, &msg))
29+
30+
expected = append(expected, &msg)
31+
}
32+
require.NoError(t, scanner.Err())
33+
StableSortTimings(t, expected) // To reduce flakiness.
34+
35+
return expected
36+
}
37+
38+
func TimingsAreEqual(t *testing.T, expected []*proto.Timing, actual []*proto.Timing) bool {
39+
t.Helper()
40+
41+
// Shortcut check.
42+
if len(expected)+len(actual) == 0 {
43+
t.Logf("both timings are empty")
44+
return true
45+
}
46+
47+
// Shortcut check.
48+
if len(expected) != len(actual) {
49+
t.Logf("timings lengths are not equal: %d != %d", len(expected), len(actual))
50+
return false
51+
}
52+
53+
// Compare each element; both are expected to be sorted in a stable manner.
54+
for i := 0; i < len(expected); i++ {
55+
ex := expected[i]
56+
ac := actual[i]
57+
if !protobuf.Equal(ex, ac) {
58+
t.Logf("timings are not equivalent: %q != %q", ex.String(), ac.String())
59+
return false
60+
}
61+
}
62+
63+
return true
64+
}
65+
66+
func IngestAllSpans(t *testing.T, input []byte, aggregator *timingAggregator) {
67+
t.Helper()
68+
69+
scanner := bufio.NewScanner(bytes.NewBuffer(input))
70+
for scanner.Scan() {
71+
line := scanner.Bytes()
72+
log := parseTerraformLogLine(line)
73+
if log == nil {
74+
continue
75+
}
76+
77+
ts, span, err := extractTimingSpan(log)
78+
if err != nil {
79+
// t.Logf("%s: failed span extraction on line: %q", err, line)
80+
continue
81+
}
82+
83+
require.NotZerof(t, ts, "failed on line: %q", line)
84+
require.NotNilf(t, span, "failed on line: %q", line)
85+
86+
aggregator.ingest(ts, span)
87+
}
88+
89+
require.NoError(t, scanner.Err())
90+
}
91+
92+
func PrintTiming(t *testing.T, timing *proto.Timing) {
93+
t.Helper()
94+
95+
marshaler := protojson.MarshalOptions{
96+
Multiline: false, // Ensure it's set to false for single-line JSON
97+
Indent: "", // No indentation
98+
}
99+
100+
out, err := marshaler.Marshal(timing)
101+
assert.NoError(t, err)
102+
t.Logf("%s", out)
103+
}
104+
105+
func StableSortTimings(t *testing.T, timings []*proto.Timing) {
106+
t.Helper()
107+
108+
slices.SortStableFunc(timings, func(a, b *proto.Timing) int {
109+
if a == nil || b == nil || a.Start == nil || b.Start == nil {
110+
return 0
111+
}
112+
113+
if a.Start.AsTime().Equal(b.Start.AsTime()) {
114+
// Special case: when start times are equal, we need to keep the ordering stable, so we hash both entries
115+
// and sort based on that (since end times could be equal too, in principle).
116+
ah := xxhash.Sum64String(a.String())
117+
bh := xxhash.Sum64String(b.String())
118+
119+
if ah == bh {
120+
// WTF.
121+
t.Logf("identical timings detected!")
122+
PrintTiming(t, a)
123+
PrintTiming(t, b)
124+
return 0
125+
}
126+
127+
if ah < bh {
128+
return -1
129+
}
130+
131+
return 1
132+
}
133+
134+
if a.Start.AsTime().Before(b.Start.AsTime()) {
135+
return -1
136+
}
137+
138+
return 1
139+
})
140+
}
Lines changed: 7 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
11
package terraform
22

33
import (
4-
"bufio"
5-
"bytes"
64
_ "embed"
7-
"slices"
85
"testing"
96

10-
"github.com/cespare/xxhash"
117
"github.com/stretchr/testify/assert"
128
"github.com/stretchr/testify/require"
139
"golang.org/x/tools/txtar"
14-
"google.golang.org/protobuf/encoding/protojson"
15-
protobuf "google.golang.org/protobuf/proto"
1610

1711
"github.com/coder/coder/v2/coderd/database"
1812
"github.com/coder/coder/v2/provisionersdk/proto"
@@ -79,138 +73,24 @@ func TestAggregation(t *testing.T) {
7973
file.Name, database.AllProvisionerJobTimingStageValues())
8074

8175
agg := newTimingAggregator(stage)
82-
extractAllSpans(t, file.Data, agg)
76+
IngestAllSpans(t, file.Data, agg)
8377
actualTimings = append(actualTimings, agg.aggregate()...)
8478
}
8579

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+
}
8885
})
8986
}
9087
}
9188

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-
16189
func printExpectation(t *testing.T, actual []*proto.Timing) {
16290
t.Helper()
16391

16492
t.Log("expected:")
16593
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)
17695
}
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-
})
21696
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy