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  }