github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/kubetest/util_test.go (about) 1 /* 2 Copyright 2017 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 "bytes" 21 "errors" 22 "fmt" 23 "io/ioutil" 24 "log" 25 "os" 26 "os/exec" 27 "strconv" 28 "strings" 29 "testing" 30 "time" 31 ) 32 33 func TestPushEnv(t *testing.T) { 34 env := "fake-env" 35 empty := "" 36 filled := "initial" 37 cases := []struct { 38 name string 39 initial *string 40 pushed string 41 }{ 42 { 43 name: "initial-missing-popped-missing", 44 pushed: "hello", 45 }, 46 { 47 name: "initial-empty-popped-empty", 48 initial: &empty, 49 pushed: "hello", 50 }, 51 { 52 name: "initial-set-popped-set", 53 initial: &filled, 54 pushed: "hello", 55 }, 56 } 57 for _, tc := range cases { 58 if tc.initial == nil { 59 if err := os.Unsetenv(env); err != nil { 60 t.Fatalf("%s: could not unset %s: %v", tc.name, env, err) 61 } 62 } else { 63 if err := os.Setenv(env, *tc.initial); err != nil { 64 t.Fatalf("%s: could not set %s: %v", tc.name, env, err) 65 } 66 } 67 f, err := pushEnv(env, tc.pushed) 68 if err != nil { 69 t.Errorf("%s: push error: %v", tc.name, err) 70 continue 71 } 72 actual, present := os.LookupEnv(env) 73 if !present { 74 t.Errorf("%s: failed to push %s", tc.name, tc.pushed) 75 continue 76 } 77 if actual != tc.pushed { 78 t.Errorf("%s: actual %s != expected %s", tc.name, actual, tc.pushed) 79 continue 80 } 81 if err = f(); err != nil { 82 t.Errorf("%s: pop error: %v", tc.name, err) 83 } 84 actual, present = os.LookupEnv(env) 85 if tc.initial == nil && present { 86 t.Errorf("%s: env present after popping", tc.name) 87 continue 88 } else if tc.initial != nil && *tc.initial != actual { 89 t.Errorf("%s: popped env is %s not initial %s", tc.name, actual, *tc.initial) 90 } 91 } 92 93 } 94 95 func TestXmlWrap(t *testing.T) { 96 cases := []struct { 97 name string 98 interrupted bool 99 shouldInterrupt bool 100 err string 101 expectSkipped bool 102 expectError bool 103 }{ 104 { 105 name: "xmlWrap can pass", 106 }, 107 { 108 name: "xmlWrap can error", 109 err: "hello there", 110 expectError: true, 111 }, 112 { 113 name: "xmlWrap always errors on interrupt", 114 err: "", 115 shouldInterrupt: true, 116 expectError: true, 117 }, 118 { 119 name: "xmlWrap errors on interrupt", 120 shouldInterrupt: true, 121 err: "the step failed", 122 expectError: true, 123 }, 124 { 125 name: "xmlWrap skips errors when already interrupted", 126 interrupted: true, 127 err: "this failed because we interrupted the previous step", 128 expectSkipped: true, 129 }, 130 { 131 name: "xmlWrap can pass when interrupted", 132 interrupted: true, 133 err: "", 134 }, 135 } 136 137 for _, tc := range cases { 138 interrupted = tc.interrupted 139 suite.Cases = suite.Cases[:0] 140 suite.Failures = 6 141 suite.Tests = 9 142 err := xmlWrap(tc.name, func() error { 143 if tc.shouldInterrupt { 144 interrupted = true 145 } 146 if tc.err != "" { 147 return errors.New(tc.err) 148 } 149 return nil 150 }) 151 if tc.shouldInterrupt && tc.expectError { 152 if err == nil { 153 t.Fatalf("Case %s did not error", tc.name) 154 } 155 if tc.err == "" { 156 tc.err = err.Error() 157 } 158 } 159 if (tc.err == "") != (err == nil) { 160 t.Errorf("Case %s expected err: %s != actual: %v", tc.name, tc.err, err) 161 } 162 if tc.shouldInterrupt && !interrupted { 163 t.Errorf("Case %s did not interrupt", tc.name) 164 } 165 if len(suite.Cases) != 1 { 166 t.Fatalf("Case %s did not result in a single suite testcase: %v", tc.name, suite.Cases) 167 } 168 sc := suite.Cases[0] 169 if sc.Name != tc.name { 170 t.Errorf("Case %s resulted in wrong test case name %s", tc.name, sc.Name) 171 } 172 if tc.expectError { 173 if sc.Failure != tc.err { 174 t.Errorf("Case %s expected error %s but got %s", tc.name, tc.err, sc.Failure) 175 } 176 if suite.Failures != 7 { 177 t.Errorf("Case %s failed and should increase suite failures from 6 to 7, found: %d", tc.name, suite.Failures) 178 } 179 } else if tc.expectSkipped { 180 if sc.Skipped != tc.err { 181 t.Errorf("Case %s expected skipped %s but got %s", tc.name, tc.err, sc.Skipped) 182 } 183 if suite.Failures != 7 { 184 t.Errorf("Case %s interrupted and increase suite failures from 6 to 7, found: %d", tc.name, suite.Failures) 185 } 186 } else { 187 if suite.Failures != 6 { 188 t.Errorf("Case %s passed so suite failures should remain at 6, found: %d", tc.name, suite.Failures) 189 } 190 } 191 192 } 193 } 194 195 func TestOutput(t *testing.T) { 196 cases := []struct { 197 name string 198 terminated bool 199 interrupted bool 200 causeTermination bool 201 causeInterruption bool 202 pass bool 203 sleep int 204 output bool 205 shouldError bool 206 shouldInterrupt bool 207 shouldTerminate bool 208 }{ 209 { 210 name: "finishRunning can pass", 211 pass: true, 212 }, 213 { 214 name: "output can pass", 215 output: true, 216 pass: true, 217 }, 218 { 219 name: "finishRuning can fail", 220 pass: false, 221 shouldError: true, 222 }, 223 { 224 name: "output can fail", 225 pass: false, 226 output: true, 227 shouldError: true, 228 }, 229 { 230 name: "finishRunning should error when terminated", 231 terminated: true, 232 pass: true, 233 shouldError: true, 234 }, 235 { 236 name: "output should error when terminated", 237 terminated: true, 238 pass: true, 239 output: true, 240 shouldError: true, 241 }, 242 { 243 name: "finishRunning should interrupt when interrupted", 244 pass: true, 245 sleep: 60, 246 causeInterruption: true, 247 shouldError: true, 248 }, 249 { 250 name: "output should interrupt when interrupted", 251 pass: true, 252 sleep: 60, 253 output: true, 254 causeInterruption: true, 255 shouldError: true, 256 }, 257 { 258 name: "output should terminate when terminated", 259 pass: true, 260 sleep: 60, 261 output: true, 262 causeTermination: true, 263 shouldError: true, 264 }, 265 { 266 name: "finishRunning should terminate when terminated", 267 pass: true, 268 sleep: 60, 269 causeTermination: true, 270 shouldError: true, 271 }, 272 } 273 274 clearTimers := func() { 275 if !terminate.Stop() { 276 <-terminate.C 277 } 278 if !interrupt.Stop() { 279 <-interrupt.C 280 } 281 } 282 283 for _, tc := range cases { 284 log.Println(tc.name) 285 terminated = tc.terminated 286 interrupted = tc.interrupted 287 interrupt = time.NewTimer(time.Duration(0)) 288 terminate = time.NewTimer(time.Duration(0)) 289 clearTimers() 290 if tc.causeInterruption { 291 interrupt.Reset(0) 292 } 293 if tc.causeTermination { 294 terminate.Reset(0) 295 } 296 var cmd *exec.Cmd 297 if !tc.pass { 298 cmd = exec.Command("false") 299 } else if tc.sleep == 0 { 300 cmd = exec.Command("true") 301 } else { 302 cmd = exec.Command("sleep", strconv.Itoa(tc.sleep)) 303 } 304 var err error 305 if tc.output { 306 _, err = output(cmd) 307 } else { 308 err = finishRunning(cmd) 309 } 310 if err == nil == tc.shouldError { 311 t.Errorf("Step %s shouldError=%v error: %v", tc.name, tc.shouldError, err) 312 } 313 if tc.causeInterruption && !interrupted { 314 t.Errorf("Step %s did not interrupt, err: %v", tc.name, err) 315 } else if tc.causeInterruption && !terminate.Reset(0) { 316 t.Errorf("Step %s did not reset the terminate timer: %v", tc.name, err) 317 } 318 if tc.causeTermination && !terminated { 319 t.Errorf("Step %s did not terminate, err: %v", tc.name, err) 320 } 321 } 322 terminated = false 323 interrupted = false 324 if !terminate.Stop() { 325 <-terminate.C 326 } 327 } 328 329 func TestFinishRunningParallel(t *testing.T) { 330 cases := []struct { 331 name string 332 terminated bool 333 interrupted bool 334 causeTermination bool 335 causeInterruption bool 336 cmds []*exec.Cmd 337 shouldError bool 338 shouldInterrupt bool 339 shouldTerminate bool 340 }{ 341 { 342 name: "finishRunningParallel with single command can pass", 343 cmds: []*exec.Cmd{exec.Command("true")}, 344 }, 345 { 346 name: "finishRunningParallel with multiple commands can pass", 347 cmds: []*exec.Cmd{exec.Command("true"), exec.Command("true")}, 348 }, 349 { 350 name: "finishRunningParallel with single command can fail", 351 cmds: []*exec.Cmd{exec.Command("false")}, 352 shouldError: true, 353 }, 354 { 355 name: "finishRunningParallel with multiple commands can fail", 356 cmds: []*exec.Cmd{exec.Command("true"), exec.Command("false")}, 357 shouldError: true, 358 }, 359 { 360 name: "finishRunningParallel should error when terminated", 361 cmds: []*exec.Cmd{exec.Command("true"), exec.Command("true")}, 362 terminated: true, 363 shouldError: true, 364 }, 365 { 366 name: "finishRunningParallel should interrupt when interrupted", 367 cmds: []*exec.Cmd{exec.Command("true"), exec.Command("sleep", "60"), exec.Command("sleep", "30")}, 368 causeInterruption: true, 369 shouldError: true, 370 }, 371 { 372 name: "finishRunningParallel should terminate when terminated", 373 cmds: []*exec.Cmd{exec.Command("true"), exec.Command("sleep", "60"), exec.Command("sleep", "30")}, 374 causeTermination: true, 375 shouldError: true, 376 }, 377 } 378 379 clearTimers := func() { 380 if !terminate.Stop() { 381 <-terminate.C 382 } 383 if !interrupt.Stop() { 384 <-interrupt.C 385 } 386 } 387 388 for _, tc := range cases { 389 log.Println(tc.name) 390 terminated = tc.terminated 391 interrupted = tc.interrupted 392 interrupt = time.NewTimer(time.Duration(0)) 393 terminate = time.NewTimer(time.Duration(0)) 394 clearTimers() 395 if tc.causeInterruption { 396 interrupt.Reset(1 * time.Second) 397 } 398 if tc.causeTermination { 399 terminate.Reset(1 * time.Second) 400 } 401 402 err := finishRunningParallel(tc.cmds...) 403 if err == nil == tc.shouldError { 404 t.Errorf("TC %q shouldError=%v error: %v", tc.name, tc.shouldError, err) 405 } 406 if tc.causeInterruption && !interrupted { 407 t.Errorf("TC %q did not interrupt, err: %v", tc.name, err) 408 } else if tc.causeInterruption && !terminate.Reset(0) { 409 t.Errorf("TC %q did not reset the terminate timer: %v", tc.name, err) 410 } 411 if tc.causeTermination && !terminated { 412 t.Errorf("TC %q did not terminate, err: %v", tc.name, err) 413 } 414 } 415 terminated = false 416 interrupted = false 417 if !terminate.Stop() { 418 <-terminate.C 419 } 420 } 421 422 func TestOutputOutputs(t *testing.T) { 423 b, err := output(exec.Command("echo", "hello world")) 424 txt := string(b) 425 if err != nil { 426 t.Fatalf("failed to echo: %v", err) 427 } 428 if !strings.Contains(txt, "hello world") { 429 t.Errorf("output() did not echo hello world: %v", txt) 430 } 431 } 432 433 func TestHttpFileScheme(t *testing.T) { 434 expected := "some testdata" 435 tmpfile, err := ioutil.TempFile("", "test_http_file_scheme") 436 if err != nil { 437 t.Errorf("Error creating temporary file: %v", err) 438 } 439 defer os.Remove(tmpfile.Name()) 440 if _, err := tmpfile.WriteString(expected); err != nil { 441 t.Errorf("Error writing to temporary file: %v", err) 442 } 443 if err := tmpfile.Close(); err != nil { 444 t.Errorf("Error closing temporary file: %v", err) 445 } 446 447 fileURL := fmt.Sprintf("file://%s", tmpfile.Name()) 448 buf := new(bytes.Buffer) 449 if err := httpRead(fileURL, buf); err != nil { 450 t.Errorf("Error reading temporary file through httpRead: %v", err) 451 } 452 453 if buf.String() != expected { 454 t.Errorf("httpRead(%s): expected %v, got %v", fileURL, expected, buf) 455 } 456 } 457 458 func TestMigrateOptions(t *testing.T) { 459 ov := "option-value" 460 ev := "env-value" 461 462 cases := []struct { 463 name string 464 setEnv bool 465 setOption bool 466 push bool 467 expectedEnv *string 468 expectedOption string 469 }{ 470 { 471 name: "no flag or env results in no change", 472 }, 473 { 474 name: "flag and env, no push results in no change", 475 setEnv: true, 476 setOption: true, 477 expectedEnv: &ev, 478 expectedOption: ov, 479 }, 480 { 481 name: "flag and env, push overwrites env", 482 setEnv: true, 483 setOption: true, 484 push: true, 485 expectedEnv: &ov, 486 expectedOption: ov, 487 }, 488 { 489 name: "flag and no env, no push results in no change", 490 setOption: true, 491 expectedOption: ov, 492 }, 493 { 494 name: "flag and no env, push overwites env", 495 setOption: true, 496 push: true, 497 expectedEnv: &ov, 498 expectedOption: ov, 499 }, 500 { 501 name: "no flag and env overwrites option", 502 setEnv: true, 503 expectedEnv: &ev, 504 expectedOption: ev, 505 }, 506 } 507 508 env := "random-env" 509 510 for _, tc := range cases { 511 if tc.setEnv { 512 if err := os.Setenv(env, ev); err != nil { 513 t.Fatalf("%s: %v", tc.name, err) 514 } 515 } else if err := os.Unsetenv(env); err != nil { 516 t.Fatalf("%s: %v", tc.name, err) 517 } 518 519 opt := "" 520 if tc.setOption { 521 opt = ov 522 } 523 if err := migrateOptions([]migratedOption{ 524 { 525 env: env, 526 option: &opt, 527 name: "--random-flag", 528 skipPush: !tc.push, 529 }, 530 }); err != nil { 531 t.Fatalf("%s: %v", tc.name, err) 532 } 533 534 val, present := os.LookupEnv(env) 535 if present && tc.expectedEnv == nil { 536 t.Errorf("%s: env should not be set", tc.name) 537 } else if tc.expectedEnv != nil && !present { 538 t.Errorf("%s: env should be set", tc.name) 539 } else if tc.expectedEnv != nil && val != *tc.expectedEnv { 540 t.Errorf("%s: env actual %s != expected %s", tc.name, val, *tc.expectedEnv) 541 } 542 543 if tc.expectedOption != opt { 544 t.Errorf("%s: option actual %s != expected %s", tc.name, opt, tc.expectedOption) 545 } 546 } 547 } 548 549 func TestAppendField(t *testing.T) { 550 flag := "--target" 551 add := "hello" 552 cases := []struct { 553 name string 554 start string 555 expected string 556 }{ 557 { 558 name: "missing", 559 start: "--a=1 --b=2", 560 expected: "--a=1 --b=2 --target=hello", 561 }, 562 { 563 name: "empty", 564 start: "--target= --b=2", 565 expected: "--b=2 --target=hello", 566 }, 567 { 568 name: "set", 569 start: "--target=first --b=2", 570 expected: "--b=2 --target=first-hello", 571 }, 572 } 573 574 for _, tc := range cases { 575 actual := strings.Join(appendField(strings.Fields(tc.start), flag, add), " ") 576 if actual != tc.expected { 577 t.Errorf("%s: actual %s != expected %s", tc.name, actual, tc.expected) 578 } 579 } 580 } 581 582 func TestSetFieldDefault(t *testing.T) { 583 flag := "--target" 584 def := "default-value" 585 cases := []struct { 586 name string 587 start string 588 expected string 589 }{ 590 { 591 name: "missing", 592 start: "--a 1 --b 2", 593 expected: "--a 1 --b 2 --target=default-value", 594 }, 595 { 596 name: "empty", 597 start: "--target= --b=2", 598 expected: "--b=2 --target=", 599 }, 600 { 601 name: "set", 602 start: "--target=1 --b=2", 603 expected: "--b=2 --target=1", 604 }, 605 } 606 607 for _, tc := range cases { 608 actual := strings.Join(setFieldDefault(strings.Fields(tc.start), flag, def), " ") 609 if actual != tc.expected { 610 t.Errorf("%s: actual %s != expected %s", tc.name, actual, tc.expected) 611 } 612 } 613 } 614 615 func TestExtractField(t *testing.T) { 616 cases := []struct { 617 name string 618 start string 619 target string 620 out string 621 extracted string 622 found bool 623 }{ 624 { 625 name: "not present", 626 start: "--a=1 --b=2 --c=3", 627 target: "--missing", 628 out: "--a=1 --b=2 --c=3", 629 extracted: "", 630 found: false, 631 }, 632 { 633 name: "found filled", 634 start: "--a=1 --b=2 --c=3", 635 target: "--b", 636 out: "--a=1 --c=3", 637 extracted: "2", 638 found: true, 639 }, 640 { 641 name: "found empty", 642 start: "--a=1 --b= --c=3", 643 target: "--b", 644 out: "--a=1 --c=3", 645 extracted: "", 646 found: true, 647 }, 648 { 649 name: "found space instead of =", 650 start: "--a 1 --b 2 --c=3", 651 target: "--b", 652 out: "--a 1 --c=3", 653 extracted: "2", 654 found: true, 655 }, 656 } 657 for _, tc := range cases { 658 f, extracted, found := extractField(strings.Fields(tc.start), tc.target) 659 out := strings.Join(f, " ") 660 if out != tc.out { 661 t.Errorf("%s: actual fields %s != expected %s", tc.name, out, tc.out) 662 } 663 if extracted != tc.extracted { 664 t.Errorf("%s: actual extracted %s != expected %s", tc.name, extracted, tc.extracted) 665 } 666 if found != tc.found { 667 t.Errorf("%s: actual found %t != expected %t", tc.name, found, tc.found) 668 } 669 } 670 }