gotest.tools/gotestsum@v1.11.0/cmd/tool/matrix/matrix_test.go (about) 1 package matrix 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "io" 7 "os" 8 "strconv" 9 "strings" 10 "testing" 11 "time" 12 13 "gotest.tools/gotestsum/testjson" 14 "gotest.tools/v3/assert" 15 "gotest.tools/v3/fs" 16 ) 17 18 func TestPackagePercentile(t *testing.T) { 19 ms := time.Millisecond 20 timing := map[string][]time.Duration{ 21 "none": {}, 22 "one": {time.Second}, 23 "two": {4 * ms, 2 * ms}, 24 "three": {2 * ms, 3 * ms, 5 * ms}, 25 "four": {4 * ms, 3 * ms, ms, 2 * ms}, 26 "five": {6 * ms, 2 * ms, 3 * ms, 4 * ms, 9 * ms}, 27 "nine": {6 * ms, 2 * ms, 3 * ms, 4 * ms, 9 * ms, 1 * ms, 5 * ms, 7 * ms, 8 * ms}, 28 "ten": {6 * ms, 2 * ms, 3 * ms, 4 * ms, 9 * ms, 5 * ms, 7 * ms, 8 * ms, ms, ms}, 29 "twenty": { 30 6 * ms, 2 * ms, 3 * ms, 4 * ms, 9 * ms, 5 * ms, 7 * ms, 8 * ms, ms, ms, 31 100, 200, 600, 700, 800, 900, 200, 300, 400, 500, 32 }, 33 } 34 35 out := packagePercentile(timing) 36 expected := map[string]time.Duration{ 37 "none": 0, 38 "one": time.Second, 39 "two": 4 * ms, 40 "three": 5 * ms, 41 "four": 4 * ms, 42 "five": 9 * ms, 43 "nine": 8 * ms, 44 "ten": 8 * ms, 45 "twenty": 6 * ms, 46 } 47 assert.DeepEqual(t, out, expected) 48 } 49 50 func TestBucketPackages(t *testing.T) { 51 ms := time.Millisecond 52 timing := map[string]time.Duration{ 53 "one": 190 * ms, 54 "two": 200 * ms, 55 "three": 3800 * ms, 56 "four": 4000 * ms, 57 "five": 50 * ms, 58 "six": 606 * ms, 59 "rm1": time.Second, 60 "rm2": time.Second, 61 } 62 packages := []string{"new1", "new2", "one", "two", "three", "four", "five", "six"} 63 64 type testCase struct { 65 n uint 66 expected []bucket 67 } 68 69 run := func(t *testing.T, tc testCase) { 70 buckets := bucketPackages(timing, packages, tc.n) 71 assert.DeepEqual(t, buckets, tc.expected) 72 } 73 74 testCases := []testCase{ 75 { 76 n: 2, 77 expected: []bucket{ 78 0: {Total: 4440 * ms, Packages: []string{"four", "two", "one", "five"}}, 79 1: {Total: 4406 * ms, Packages: []string{"three", "six", "new2", "new1"}}, 80 }, 81 }, 82 { 83 n: 3, 84 expected: []bucket{ 85 0: {Total: 4000 * ms, Packages: []string{"four"}}, 86 1: {Total: 3800 * ms, Packages: []string{"three"}}, 87 2: {Total: 1046 * ms, Packages: []string{"six", "two", "one", "five", "new1", "new2"}}, 88 }, 89 }, 90 { 91 n: 4, 92 expected: []bucket{ 93 0: {Total: 4000 * ms, Packages: []string{"four"}}, 94 1: {Total: 3800 * ms, Packages: []string{"three"}}, 95 2: {Total: 606 * ms, Packages: []string{"six"}}, 96 3: {Total: 440 * ms, Packages: []string{"two", "one", "five", "new2", "new1"}}, 97 }, 98 }, 99 { 100 n: 8, 101 expected: []bucket{ 102 0: {Total: 4000 * ms, Packages: []string{"four"}}, 103 1: {Total: 3800 * ms, Packages: []string{"three"}}, 104 2: {Total: 606 * ms, Packages: []string{"six"}}, 105 3: {Total: 200 * ms, Packages: []string{"two"}}, 106 4: {Total: 190 * ms, Packages: []string{"one"}}, 107 5: {Total: 50 * ms, Packages: []string{"five"}}, 108 6: {Packages: []string{"new1"}}, 109 7: {Packages: []string{"new2"}}, 110 }, 111 }, 112 } 113 114 for _, tc := range testCases { 115 t.Run(strconv.FormatUint(uint64(tc.n), 10), func(t *testing.T) { 116 run(t, tc) 117 }) 118 } 119 } 120 121 func TestReadTimingReports(t *testing.T) { 122 events := func(t *testing.T, start time.Time) string { 123 t.Helper() 124 var buf bytes.Buffer 125 encoder := json.NewEncoder(&buf) 126 for _, i := range []int{0, 1, 2} { 127 assert.NilError(t, encoder.Encode(testjson.TestEvent{ 128 Time: start.Add(time.Duration(i) * time.Second), 129 Action: testjson.ActionRun, 130 Package: "pkg" + strconv.Itoa(i), 131 })) 132 } 133 return buf.String() 134 } 135 136 now := time.Now() 137 dir := fs.NewDir(t, "timing-files", 138 fs.WithFile("report1.log", events(t, now.Add(-time.Hour))), 139 fs.WithFile("report2.log", events(t, now.Add(-47*time.Hour))), 140 fs.WithFile("report3.log", events(t, now.Add(-49*time.Hour))), 141 fs.WithFile("report4.log", events(t, now.Add(-101*time.Hour)))) 142 143 t.Run("match files", func(t *testing.T) { 144 opts := options{ 145 timingFilesPattern: dir.Join("*.log"), 146 } 147 148 files, err := readTimingReports(opts) 149 assert.NilError(t, err) 150 defer closeFiles(files) 151 assert.Equal(t, len(files), 4) 152 153 for _, fh := range files { 154 // check the files are properly seeked to 0 155 event, err := parseEvent(fh) 156 assert.NilError(t, err) 157 assert.Equal(t, event.Package, "pkg0") 158 } 159 160 actual, err := os.ReadDir(dir.Path()) 161 assert.NilError(t, err) 162 assert.Equal(t, len(actual), 4) 163 }) 164 165 t.Run("no glob match, func", func(t *testing.T) { 166 opts := options{ 167 timingFilesPattern: dir.Join("*.json"), 168 } 169 170 files, err := readTimingReports(opts) 171 assert.NilError(t, err) 172 assert.Equal(t, len(files), 0) 173 }) 174 } 175 176 func TestRun(t *testing.T) { 177 events := func(t *testing.T) string { 178 t.Helper() 179 var buf bytes.Buffer 180 encoder := json.NewEncoder(&buf) 181 for _, i := range []int{0, 1, 2} { 182 elapsed := time.Duration(i+1) * 2 * time.Second 183 end := time.Now().Add(-5 * time.Second) 184 start := end.Add(-elapsed) 185 186 assert.NilError(t, encoder.Encode(testjson.TestEvent{ 187 Time: start, 188 Action: testjson.ActionRun, 189 Package: "pkg" + strconv.Itoa(i), 190 })) 191 assert.NilError(t, encoder.Encode(testjson.TestEvent{ 192 Time: end, 193 Action: testjson.ActionPass, 194 Package: "pkg" + strconv.Itoa(i), 195 Elapsed: elapsed.Seconds(), 196 })) 197 } 198 return buf.String() 199 } 200 201 dir := fs.NewDir(t, "timing-files", 202 fs.WithFile("report1.log", events(t)), 203 fs.WithFile("report2.log", events(t)), 204 fs.WithFile("report3.log", events(t)), 205 fs.WithFile("report4.log", events(t)), 206 fs.WithFile("report5.log", events(t))) 207 208 stdout := new(bytes.Buffer) 209 opts := options{ 210 numPartitions: 3, 211 timingFilesPattern: dir.Join("*.log"), 212 debug: true, 213 stdout: stdout, 214 stdin: strings.NewReader("pkg0\npkg1\npkg2\nother"), 215 } 216 err := run(opts) 217 assert.NilError(t, err) 218 assert.Equal(t, strings.Count(stdout.String(), "\n"), 1, 219 "the output should be a single line") 220 221 assert.Equal(t, formatJSON(t, stdout), expectedMatrix) 222 } 223 224 // expectedMatrix can be automatically updated by running tests with -update 225 // nolint:lll 226 var expectedMatrix = `{ 227 "include": [ 228 { 229 "description": "0 - pkg2", 230 "estimatedRuntime": "6s", 231 "id": 0, 232 "packages": "pkg2" 233 }, 234 { 235 "description": "1 - pkg1", 236 "estimatedRuntime": "4s", 237 "id": 1, 238 "packages": "pkg1" 239 }, 240 { 241 "description": "2 - pkg0 and 1 others", 242 "estimatedRuntime": "2s", 243 "id": 2, 244 "packages": "pkg0 other" 245 } 246 ] 247 }` 248 249 func formatJSON(t *testing.T, v io.Reader) string { 250 t.Helper() 251 raw := map[string]interface{}{} 252 err := json.NewDecoder(v).Decode(&raw) 253 assert.NilError(t, err) 254 255 formatted, err := json.MarshalIndent(raw, "", " ") 256 assert.NilError(t, err) 257 return string(formatted) 258 }