github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/testgrid/cmd/updater/main_test.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "reflect" 21 "testing" 22 23 "github.com/golang/protobuf/proto" 24 "k8s.io/test-infra/testgrid/state" 25 ) 26 27 func Test_ValidateName(t *testing.T) { 28 cases := []struct { 29 name string 30 input string 31 context string 32 timestamp string 33 thread string 34 empty bool 35 }{ 36 37 { 38 name: "not junit", 39 input: "./started.json", 40 empty: true, 41 }, 42 { 43 name: "forgot suffix", 44 input: "./junit", 45 empty: true, 46 }, 47 { 48 name: "basic", 49 input: "./junit.xml", 50 }, 51 { 52 name: "context", 53 input: "./junit_hello world isn't-this exciting!.xml", 54 context: "hello world isn't-this exciting!", 55 }, 56 { 57 name: "numeric context", 58 input: "./junit_12345.xml", 59 context: "12345", 60 }, 61 { 62 name: "context and thread", 63 input: "./junit_context_12345.xml", 64 context: "context", 65 thread: "12345", 66 }, 67 { 68 name: "context and timestamp", 69 input: "./junit_context_20180102-1234.xml", 70 context: "context", 71 timestamp: "20180102-1234", 72 }, 73 { 74 name: "context thread timestamp", 75 input: "./junit_context_20180102-1234_5555.xml", 76 context: "context", 77 timestamp: "20180102-1234", 78 thread: "5555", 79 }, 80 } 81 82 for _, tc := range cases { 83 actual := ValidateName(tc.input) 84 switch { 85 case actual == nil && !tc.empty: 86 t.Errorf("%s: unexpected nil map", tc.name) 87 case actual != nil && tc.empty: 88 t.Errorf("%s: should not have returned a map: %v", tc.name, actual) 89 case actual != nil: 90 for k, expected := range map[string]string{ 91 "Context": tc.context, 92 "Thread": tc.thread, 93 "Timestamp": tc.timestamp, 94 } { 95 if a, ok := actual[k]; !ok { 96 t.Errorf("%s: missing key %s", tc.name, k) 97 } else if a != expected { 98 t.Errorf("%s: %s actual %s != expected %s", tc.name, k, a, expected) 99 } 100 } 101 } 102 } 103 104 } 105 106 func Test_ExtractRows(t *testing.T) { 107 cases := []struct { 108 name string 109 content string 110 metadata map[string]string 111 rows map[string][]Row 112 err bool 113 }{ 114 { 115 name: "not xml", 116 content: `{"super": 123}`, 117 err: true, 118 }, 119 { 120 name: "not junit", 121 content: `<amazing><content/></amazing>`, 122 err: true, 123 }, 124 { 125 name: "basic testsuite", 126 content: ` 127 <testsuite> 128 <testcase name="good"/> 129 <testcase name="bad"><failure/></testcase> 130 <testcase name="skip"><skipped/></testcase> 131 </testsuite>`, 132 rows: map[string][]Row{ 133 "good": { 134 { 135 Result: state.Row_PASS, 136 Metadata: map[string]string{ 137 "Tests name": "good", 138 }, 139 }, 140 }, 141 "bad": { 142 { 143 Result: state.Row_FAIL, 144 Metadata: map[string]string{ 145 "Tests name": "bad", 146 }, 147 }, 148 }, 149 }, 150 }, 151 { 152 name: "basic testsuites", 153 content: ` 154 <testsuites> 155 <testsuite> 156 <testcase name="good"/> 157 </testsuite> 158 <testsuite> 159 <testcase name="bad"><failure/></testcase> 160 <testcase name="skip"><skipped/></testcase> 161 </testsuite> 162 </testsuites>`, 163 rows: map[string][]Row{ 164 "good": { 165 { 166 Result: state.Row_PASS, 167 Metadata: map[string]string{ 168 "Tests name": "good", 169 }, 170 }, 171 }, 172 "bad": { 173 { 174 Result: state.Row_FAIL, 175 Metadata: map[string]string{ 176 "Tests name": "bad", 177 }, 178 }, 179 }, 180 }, 181 }, 182 { 183 name: "suite name", 184 content: ` 185 <testsuite name="hello"> 186 <testcase name="world" /> 187 </testsuite>`, 188 rows: map[string][]Row{ 189 "hello.world": { 190 { 191 Result: state.Row_PASS, 192 Metadata: map[string]string{ 193 "Tests name": "hello.world", 194 }, 195 }, 196 }, 197 }, 198 }, 199 { 200 name: "duplicate target names", 201 content: ` 202 <testsuite> 203 <testcase name="multi"> 204 <failure>doh</failure> 205 </testcase> 206 <testcase name="multi" /> 207 </testsuite>`, 208 rows: map[string][]Row{ 209 "multi": { 210 { 211 Result: state.Row_FAIL, 212 Metadata: map[string]string{ 213 "Tests name": "multi", 214 }, 215 }, 216 { 217 Result: state.Row_PASS, 218 Metadata: map[string]string{ 219 "Tests name": "multi", 220 }, 221 }, 222 }, 223 }, 224 }, 225 { 226 name: "basic timing", 227 content: ` 228 <testsuite> 229 <testcase name="slow" time="100.1" /> 230 <testcase name="slow-failure" time="123456789"> 231 <failure>terrible</failure> 232 </testcase> 233 <testcase name="fast" time="0.0001" /> 234 <testcase name="nothing-elapsed" time="0" /> 235 </testsuite>`, 236 rows: map[string][]Row{ 237 "slow": { 238 { 239 Result: state.Row_PASS, 240 Metrics: map[string]float64{elapsedKey: 100.1}, 241 Metadata: map[string]string{ 242 "Tests name": "slow", 243 }, 244 }, 245 }, 246 "slow-failure": { 247 { 248 Result: state.Row_FAIL, 249 Metrics: map[string]float64{elapsedKey: 123456789}, 250 Metadata: map[string]string{ 251 "Tests name": "slow-failure", 252 }, 253 }, 254 }, 255 "fast": { 256 { 257 Result: state.Row_PASS, 258 Metrics: map[string]float64{elapsedKey: 0.0001}, 259 Metadata: map[string]string{ 260 "Tests name": "fast", 261 }, 262 }, 263 }, 264 "nothing-elapsed": { 265 { 266 Result: state.Row_PASS, 267 Metadata: map[string]string{ 268 "Tests name": "nothing-elapsed", 269 }, 270 }, 271 }, 272 }, 273 }, 274 { 275 name: "add metadata", 276 content: ` 277 <testsuite> 278 <testcase name="fancy" /> 279 <testcase name="ketchup" /> 280 </testsuite>`, 281 metadata: map[string]string{ 282 "Context": "debian", 283 "Timestamp": "1234", 284 "Thread": "7", 285 }, 286 rows: map[string][]Row{ 287 "fancy": { 288 { 289 Result: state.Row_PASS, 290 Metadata: map[string]string{ 291 "Tests name": "fancy", 292 "Context": "debian", 293 "Timestamp": "1234", 294 "Thread": "7", 295 }, 296 }, 297 }, 298 "ketchup": { 299 { 300 Result: state.Row_PASS, 301 Metadata: map[string]string{ 302 "Tests name": "ketchup", 303 "Context": "debian", 304 "Timestamp": "1234", 305 "Thread": "7", 306 }, 307 }, 308 }, 309 }, 310 }, 311 } 312 313 for _, tc := range cases { 314 rows := map[string][]Row{} 315 316 rows, err := extractRows([]byte(tc.content), tc.metadata) 317 switch { 318 case err == nil && tc.err: 319 t.Errorf("%s: failed to raise an error", tc.name) 320 case err != nil && !tc.err: 321 t.Errorf("%s: unexpected err: %v", tc.name, err) 322 case len(rows) > len(tc.rows): 323 t.Errorf("%s: extra rows: actual %v != expected %v", tc.name, rows, tc.rows) 324 default: 325 for target, expectedRows := range tc.rows { 326 actualRows, ok := rows[target] 327 if !ok { 328 t.Errorf("%s: missing row %s", tc.name, target) 329 continue 330 } else if len(actualRows) != len(expectedRows) { 331 t.Errorf("%s: bad results for %s: actual %v != expected %v", tc.name, target, actualRows, expectedRows) 332 continue 333 } 334 for i, er := range expectedRows { 335 ar := actualRows[i] 336 if er.Result != ar.Result { 337 t.Errorf("%s: %s %d actual %v != expected %v", tc.name, target, i, ar.Result, er.Result) 338 } 339 340 if len(ar.Metrics) > len(er.Metrics) { 341 t.Errorf("%s: extra %s %d metrics: actual %v != expected %v", tc.name, target, i, ar.Metrics, er.Metrics) 342 } else { 343 for m, ev := range er.Metrics { 344 if av, ok := ar.Metrics[m]; !ok { 345 t.Errorf("%s: %s %d missing %s metric", tc.name, target, i, m) 346 } else if ev != av { 347 t.Errorf("%s: %s %d bad %s metric: actual %f != expected %f", tc.name, target, i, m, av, ev) 348 } 349 } 350 } 351 352 if len(ar.Metadata) > len(er.Metadata) { 353 t.Errorf("%s: extra %s %d metadata: actual %v != expected %v", tc.name, target, i, ar.Metadata, er.Metadata) 354 } else { 355 for m, ev := range er.Metadata { 356 if av, ok := ar.Metadata[m]; !ok { 357 t.Errorf("%s: %s %d missing %s metadata", tc.name, target, i, m) 358 } else if ev != av { 359 t.Errorf("%s: %s %d bad %s metadata: actual %s != expected %s", tc.name, target, i, m, av, ev) 360 } 361 } 362 } 363 } 364 } 365 } 366 } 367 } 368 369 func Test_MarshalGrid(t *testing.T) { 370 g1 := state.Grid{ 371 Columns: []*state.Column{ 372 {Build: "alpha"}, 373 {Build: "second"}, 374 }, 375 } 376 g2 := state.Grid{ 377 Columns: []*state.Column{ 378 {Build: "first"}, 379 {Build: "second"}, 380 }, 381 } 382 383 b1, e1 := marshalGrid(g1) 384 b2, e2 := marshalGrid(g2) 385 uncompressed, e1a := proto.Marshal(&g1) 386 387 switch { 388 case e1 != nil, e2 != nil: 389 t.Errorf("unexpected error %v %v %v", e1, e2, e1a) 390 } 391 392 if reflect.DeepEqual(b1, b2) { 393 t.Errorf("unexpected equality %v == %v", b1, b2) 394 } 395 396 if reflect.DeepEqual(b1, uncompressed) { 397 t.Errorf("should be compressed but is not: %v", b1) 398 } 399 }