github.com/yrj2011/jx-test-infra@v0.0.0-20190529031832-7a2065ee98eb/prow/config/jobs_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 config
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"os"
    24  	"regexp"
    25  	"strings"
    26  	"testing"
    27  
    28  	"flag"
    29  	"k8s.io/test-infra/prow/kube"
    30  )
    31  
    32  var c *Config
    33  var cj configJSON
    34  var configPath = flag.String("config", "../config.yaml", "Path to prow config")
    35  var jobConfigPath = flag.String("job-config", "", "Path to prow job config")
    36  var configJSONPath = flag.String("config-json", "../../jobs/config.json", "Path to prow job config")
    37  var podRe = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`)
    38  
    39  type configJSON map[string]map[string]interface{}
    40  
    41  const (
    42  	testThis   = "/test all"
    43  	retestBody = "/test all [submit-queue is verifying that this PR is safe to merge]"
    44  )
    45  
    46  type JSONJob struct {
    47  	Scenario string   `json:"scenario"`
    48  	Args     []string `json:"args"`
    49  }
    50  
    51  // Consistent but meaningless order.
    52  func flattenJobs(jobs []Presubmit) []Presubmit {
    53  	ret := jobs
    54  	for _, job := range jobs {
    55  		if len(job.RunAfterSuccess) > 0 {
    56  			ret = append(ret, flattenJobs(job.RunAfterSuccess)...)
    57  		}
    58  	}
    59  	return ret
    60  }
    61  
    62  // Returns if two brancher has overlapping branches
    63  func checkOverlapBrancher(b1, b2 Brancher) bool {
    64  	if b1.RunsAgainstAllBranch() || b2.RunsAgainstAllBranch() {
    65  		return true
    66  	}
    67  
    68  	for _, run1 := range b1.Branches {
    69  		if b2.RunsAgainstBranch(run1) {
    70  			return true
    71  		}
    72  	}
    73  
    74  	for _, run2 := range b2.Branches {
    75  		if b1.RunsAgainstBranch(run2) {
    76  			return true
    77  		}
    78  	}
    79  
    80  	return false
    81  }
    82  
    83  func readConfigJSON(path string) (config configJSON, err error) {
    84  	raw, err := ioutil.ReadFile(path)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	config = configJSON{}
    89  	err = json.Unmarshal(raw, &config)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	return config, nil
    94  }
    95  
    96  func TestMain(m *testing.M) {
    97  	flag.Parse()
    98  	if *configPath == "" {
    99  		fmt.Println("--config must set")
   100  		os.Exit(1)
   101  	}
   102  
   103  	conf, err := Load(*configPath, *jobConfigPath)
   104  	if err != nil {
   105  		fmt.Printf("Could not load config: %v", err)
   106  		os.Exit(1)
   107  	}
   108  	c = conf
   109  
   110  	if *configJSONPath != "" {
   111  		cj, err = readConfigJSON(*configJSONPath)
   112  		if err != nil {
   113  			fmt.Printf("Could not load jobs config: %v", err)
   114  			os.Exit(1)
   115  		}
   116  	}
   117  
   118  	os.Exit(m.Run())
   119  }
   120  
   121  // TODO(spxtr): Some of this is generic prowjob stuff and some of this is k8s-
   122  // specific. Figure out which is which and split this up.
   123  func TestPresubmits(t *testing.T) {
   124  	if len(c.Presubmits) == 0 {
   125  		t.Fatalf("No jobs found in presubmit.yaml.")
   126  	}
   127  	b, err := ioutil.ReadFile("../../jobs/config.json")
   128  	if err != nil {
   129  		t.Fatalf("Could not load jobs/config.json: %v", err)
   130  	}
   131  	var bootstrapConfig map[string]JSONJob
   132  	json.Unmarshal(b, &bootstrapConfig)
   133  	for _, rootJobs := range c.Presubmits {
   134  		jobs := flattenJobs(rootJobs)
   135  		for i, job := range jobs {
   136  			if job.Name == "" {
   137  				t.Errorf("Job %v needs a name.", job)
   138  				continue
   139  			}
   140  			if !job.SkipReport && job.Context == "" {
   141  				t.Errorf("Job %s needs a context.", job.Name)
   142  			}
   143  			if job.RerunCommand == "" || job.Trigger == "" {
   144  				t.Errorf("Job %s needs a trigger and a rerun command.", job.Name)
   145  				continue
   146  			}
   147  			// Check that the merge bot will run AlwaysRun jobs, otherwise it
   148  			// will attempt to rerun forever.
   149  			if job.AlwaysRun && !job.re.MatchString(testThis) {
   150  				t.Errorf("AlwaysRun job %s: \"%s\" does not match regex \"%v\".", job.Name, testThis, job.Trigger)
   151  			}
   152  			if job.AlwaysRun && !job.re.MatchString(retestBody) {
   153  				t.Errorf("AlwaysRun job %s: \"%s\" does not match regex \"%v\".", job.Name, retestBody, job.Trigger)
   154  			}
   155  			// Check that the merge bot will not run Non-AlwaysRun jobs
   156  			if !job.AlwaysRun && job.re.MatchString(testThis) {
   157  				t.Errorf("Non-AlwaysRun job %s: \"%s\" matches regex \"%v\".", job.Name, testThis, job.Trigger)
   158  			}
   159  			if !job.AlwaysRun && job.re.MatchString(retestBody) {
   160  				t.Errorf("Non-AlwaysRun job %s: \"%s\" matches regex \"%v\".", job.Name, retestBody, job.Trigger)
   161  			}
   162  
   163  			if len(job.Brancher.Branches) > 0 && len(job.Brancher.SkipBranches) > 0 {
   164  				t.Errorf("Job %s : Cannot have both branches and skip_branches set", job.Name)
   165  			}
   166  			// Next check that the rerun command doesn't run any other jobs.
   167  			for j, job2 := range jobs[i+1:] {
   168  				if job.Name == job2.Name {
   169  					// Make sure max_concurrency are the same
   170  					if job.MaxConcurrency != job2.MaxConcurrency {
   171  						t.Errorf("Jobs %s share same name but has different max_concurrency", job.Name)
   172  					}
   173  					// Make sure branches are not overlapping
   174  					if checkOverlapBrancher(job.Brancher, job2.Brancher) {
   175  						t.Errorf("Two jobs have the same name: %s, and have conflicting branches", job.Name)
   176  					}
   177  				} else {
   178  					if job.Context == job2.Context {
   179  						t.Errorf("Jobs %s and %s have the same context: %s", job.Name, job2.Name, job.Context)
   180  					}
   181  					if job2.re.MatchString(job.RerunCommand) {
   182  						t.Errorf("%d, %d, RerunCommand \"%s\" from job %s matches \"%v\" from job %s but shouldn't.", i, j, job.RerunCommand, job.Name, job2.Trigger, job2.Name)
   183  					}
   184  				}
   185  			}
   186  			var scenario string
   187  			job.Name = strings.Replace(job.Name, "pull-security-kubernetes", "pull-kubernetes", 1)
   188  			if j, present := bootstrapConfig[job.Name]; present {
   189  				scenario = fmt.Sprintf("scenarios/%s.py", j.Scenario)
   190  			}
   191  
   192  			// Ensure that jobs have a shell script of the same name.
   193  			if s, err := os.Stat(fmt.Sprintf("../../%s", scenario)); err != nil {
   194  				t.Errorf("Cannot find test-infra/%s for %s", scenario, job.Name)
   195  			} else {
   196  				if s.Mode()&0111 == 0 {
   197  					t.Errorf("Not executable: test-infra/%s (%o)", scenario, s.Mode()&0777)
   198  				}
   199  				if s.Mode()&0444 == 0 {
   200  					t.Errorf("Not readable: test-infra/%s (%o)", scenario, s.Mode()&0777)
   201  				}
   202  			}
   203  		}
   204  	}
   205  }
   206  
   207  func TestCommentBodyMatches(t *testing.T) {
   208  	var testcases = []struct {
   209  		repo         string
   210  		body         string
   211  		expectedJobs []string
   212  	}{
   213  		{
   214  			"org/repo",
   215  			"this is a random comment",
   216  			[]string{},
   217  		},
   218  		{
   219  			"org/repo",
   220  			"/ok-to-test",
   221  			[]string{"gce", "unit"},
   222  		},
   223  		{
   224  			"org/repo",
   225  			"/test all",
   226  			[]string{"gce", "unit", "gke"},
   227  		},
   228  		{
   229  			"org/repo",
   230  			"/test unit",
   231  			[]string{"unit"},
   232  		},
   233  		{
   234  			"org/repo",
   235  			"/test federation",
   236  			[]string{"federation"},
   237  		},
   238  		{
   239  			"org/repo2",
   240  			"/test all",
   241  			[]string{"cadveapster", "after-cadveapster", "after-after-cadveapster"},
   242  		},
   243  		{
   244  			"org/repo2",
   245  			"/test really",
   246  			[]string{"after-cadveapster"},
   247  		},
   248  		{
   249  			"org/repo2",
   250  			"/test again really",
   251  			[]string{"after-after-cadveapster"},
   252  		},
   253  		{
   254  			"org/repo3",
   255  			"/test all",
   256  			[]string{},
   257  		},
   258  	}
   259  	c := &Config{
   260  		JobConfig: JobConfig{
   261  			Presubmits: map[string][]Presubmit{
   262  				"org/repo": {
   263  					{
   264  						Name:      "gce",
   265  						re:        regexp.MustCompile(`/test (gce|all)`),
   266  						AlwaysRun: true,
   267  					},
   268  					{
   269  						Name:      "unit",
   270  						re:        regexp.MustCompile(`/test (unit|all)`),
   271  						AlwaysRun: true,
   272  					},
   273  					{
   274  						Name:      "gke",
   275  						re:        regexp.MustCompile(`/test (gke|all)`),
   276  						AlwaysRun: false,
   277  					},
   278  					{
   279  						Name:      "federation",
   280  						re:        regexp.MustCompile(`/test federation`),
   281  						AlwaysRun: false,
   282  					},
   283  				},
   284  				"org/repo2": {
   285  					{
   286  						Name:      "cadveapster",
   287  						re:        regexp.MustCompile(`/test all`),
   288  						AlwaysRun: true,
   289  						RunAfterSuccess: []Presubmit{
   290  							{
   291  								Name:      "after-cadveapster",
   292  								re:        regexp.MustCompile(`/test (really|all)`),
   293  								AlwaysRun: true,
   294  								RunAfterSuccess: []Presubmit{
   295  									{
   296  										Name:      "after-after-cadveapster",
   297  										re:        regexp.MustCompile(`/test (again really|all)`),
   298  										AlwaysRun: true,
   299  									},
   300  								},
   301  							},
   302  							{
   303  								Name:      "another-after-cadveapster",
   304  								re:        regexp.MustCompile(`@k8s-bot dont test this`),
   305  								AlwaysRun: true,
   306  							},
   307  						},
   308  					},
   309  				},
   310  			},
   311  		},
   312  	}
   313  	for _, tc := range testcases {
   314  		actualJobs := c.MatchingPresubmits(tc.repo, tc.body, regexp.MustCompile(`/ok-to-test`).MatchString(tc.body))
   315  		match := true
   316  		if len(actualJobs) != len(tc.expectedJobs) {
   317  			match = false
   318  		} else {
   319  			for _, actualJob := range actualJobs {
   320  				found := false
   321  				for _, expectedJob := range tc.expectedJobs {
   322  					if expectedJob == actualJob.Name {
   323  						found = true
   324  						break
   325  					}
   326  				}
   327  				if !found {
   328  					match = false
   329  					break
   330  				}
   331  			}
   332  		}
   333  		if !match {
   334  			t.Errorf("Wrong jobs for body %s. Got %v, expected %v.", tc.body, actualJobs, tc.expectedJobs)
   335  		}
   336  	}
   337  }
   338  
   339  func TestRetestPresubmits(t *testing.T) {
   340  	var testcases = []struct {
   341  		skipContexts     map[string]bool
   342  		runContexts      map[string]bool
   343  		expectedContexts []string
   344  	}{
   345  		{
   346  			map[string]bool{},
   347  			map[string]bool{},
   348  			[]string{"gce", "unit"},
   349  		},
   350  		{
   351  			map[string]bool{"gce": true},
   352  			map[string]bool{},
   353  			[]string{"unit"},
   354  		},
   355  		{
   356  			map[string]bool{},
   357  			map[string]bool{"federation": true, "nonexistent": true},
   358  			[]string{"gce", "unit", "federation"},
   359  		},
   360  		{
   361  			map[string]bool{},
   362  			map[string]bool{"gke": true},
   363  			[]string{"gce", "unit", "gke"},
   364  		},
   365  		{
   366  			map[string]bool{"gce": true},
   367  			map[string]bool{"gce": true}, // should never happen
   368  			[]string{"unit"},
   369  		},
   370  	}
   371  	c := &Config{
   372  		JobConfig: JobConfig{
   373  			Presubmits: map[string][]Presubmit{
   374  				"org/repo": {
   375  					{
   376  						Context:   "gce",
   377  						AlwaysRun: true,
   378  					},
   379  					{
   380  						Context:   "unit",
   381  						AlwaysRun: true,
   382  					},
   383  					{
   384  						Context:   "gke",
   385  						AlwaysRun: false,
   386  					},
   387  					{
   388  						Context:   "federation",
   389  						AlwaysRun: false,
   390  					},
   391  				},
   392  				"org/repo2": {
   393  					{
   394  						Context:   "shouldneverrun",
   395  						AlwaysRun: true,
   396  					},
   397  				},
   398  			},
   399  		},
   400  	}
   401  	for _, tc := range testcases {
   402  		actualContexts := c.RetestPresubmits("org/repo", tc.skipContexts, tc.runContexts)
   403  		match := true
   404  		if len(actualContexts) != len(tc.expectedContexts) {
   405  			match = false
   406  		} else {
   407  			for _, actualJob := range actualContexts {
   408  				found := false
   409  				for _, expectedContext := range tc.expectedContexts {
   410  					if expectedContext == actualJob.Context {
   411  						found = true
   412  						break
   413  					}
   414  				}
   415  				if !found {
   416  					match = false
   417  					break
   418  				}
   419  			}
   420  		}
   421  		if !match {
   422  			t.Errorf("Wrong contexts for skip %v run %v. Got %v, expected %v.", tc.runContexts, tc.skipContexts, actualContexts, tc.expectedContexts)
   423  		}
   424  	}
   425  
   426  }
   427  
   428  func TestConditionalPresubmits(t *testing.T) {
   429  	presubmits := []Presubmit{
   430  		{
   431  			Name:         "cross build",
   432  			RunIfChanged: `(Makefile|\.sh|_(windows|linux|osx|unknown)(_test)?\.go)$`,
   433  		},
   434  	}
   435  	SetPresubmitRegexes(presubmits)
   436  	ps := presubmits[0]
   437  	var testcases = []struct {
   438  		changes  []string
   439  		expected bool
   440  	}{
   441  		{[]string{"some random file"}, false},
   442  		{[]string{"./pkg/util/rlimit/rlimit_linux.go"}, true},
   443  		{[]string{"./pkg/util/rlimit/rlimit_unknown_test.go"}, true},
   444  		{[]string{"build.sh"}, true},
   445  		{[]string{"build.shoo"}, false},
   446  		{[]string{"Makefile"}, true},
   447  	}
   448  	for _, tc := range testcases {
   449  		actual := ps.RunsAgainstChanges(tc.changes)
   450  		if actual != tc.expected {
   451  			t.Errorf("wrong RunsAgainstChanges(%#v) result. Got %v, expected %v", tc.changes, actual, tc.expected)
   452  		}
   453  	}
   454  }
   455  
   456  func TestListPresubmit(t *testing.T) {
   457  	c := &Config{
   458  		JobConfig: JobConfig{
   459  			Presubmits: map[string][]Presubmit{
   460  				"r1": {
   461  					{
   462  						Name: "a",
   463  						RunAfterSuccess: []Presubmit{
   464  							{Name: "aa"},
   465  							{Name: "ab"},
   466  						},
   467  					},
   468  					{Name: "b"},
   469  				},
   470  				"r2": {
   471  					{
   472  						Name: "c",
   473  						RunAfterSuccess: []Presubmit{
   474  							{Name: "ca"},
   475  							{Name: "cb"},
   476  						},
   477  					},
   478  					{Name: "d"},
   479  				},
   480  			},
   481  			Postsubmits: map[string][]Postsubmit{
   482  				"r1": {{Name: "e"}},
   483  			},
   484  			Periodics: []Periodic{
   485  				{Name: "f"},
   486  			},
   487  		},
   488  	}
   489  
   490  	var testcases = []struct {
   491  		name     string
   492  		expected []string
   493  		repos    []string
   494  	}{
   495  		{
   496  			"all presubmits",
   497  			[]string{"a", "aa", "ab", "b", "c", "ca", "cb", "d"},
   498  			[]string{},
   499  		},
   500  		{
   501  			"r2 presubmits",
   502  			[]string{"c", "ca", "cb", "d"},
   503  			[]string{"r2"},
   504  		},
   505  	}
   506  
   507  	for _, tc := range testcases {
   508  		actual := c.AllPresubmits(tc.repos)
   509  		if len(actual) != len(tc.expected) {
   510  			t.Fatalf("test %s - Wrong number of jobs. Got %v, expected %v", tc.name, actual, tc.expected)
   511  		}
   512  		for _, j1 := range tc.expected {
   513  			found := false
   514  			for _, j2 := range actual {
   515  				if j1 == j2.Name {
   516  					found = true
   517  					break
   518  				}
   519  			}
   520  			if !found {
   521  				t.Errorf("test %s - Did not find job %s in output", tc.name, j1)
   522  			}
   523  		}
   524  	}
   525  }
   526  
   527  func TestListPostsubmit(t *testing.T) {
   528  	c := &Config{
   529  		JobConfig: JobConfig{
   530  			Presubmits: map[string][]Presubmit{
   531  				"r1": {{Name: "a"}},
   532  			},
   533  			Postsubmits: map[string][]Postsubmit{
   534  				"r1": {
   535  					{
   536  						Name: "c",
   537  						RunAfterSuccess: []Postsubmit{
   538  							{Name: "ca"},
   539  							{Name: "cb"},
   540  						},
   541  					},
   542  					{Name: "d"},
   543  				},
   544  				"r2": {{Name: "e"}},
   545  			},
   546  			Periodics: []Periodic{
   547  				{Name: "f"},
   548  			},
   549  		},
   550  	}
   551  
   552  	var testcases = []struct {
   553  		name     string
   554  		expected []string
   555  		repos    []string
   556  	}{
   557  		{
   558  			"all postsubmits",
   559  			[]string{"c", "ca", "cb", "d", "e"},
   560  			[]string{},
   561  		},
   562  		{
   563  			"r2 presubmits",
   564  			[]string{"e"},
   565  			[]string{"r2"},
   566  		},
   567  	}
   568  
   569  	for _, tc := range testcases {
   570  		actual := c.AllPostsubmits(tc.repos)
   571  		if len(actual) != len(tc.expected) {
   572  			t.Fatalf("%s - Wrong number of jobs. Got %v, expected %v", tc.name, actual, tc.expected)
   573  		}
   574  		for _, j1 := range tc.expected {
   575  			found := false
   576  			for _, j2 := range actual {
   577  				if j1 == j2.Name {
   578  					found = true
   579  					break
   580  				}
   581  			}
   582  			if !found {
   583  				t.Errorf("Did not find job %s in output", j1)
   584  			}
   585  		}
   586  	}
   587  }
   588  
   589  func TestListPeriodic(t *testing.T) {
   590  	c := &Config{
   591  		JobConfig: JobConfig{
   592  			Presubmits: map[string][]Presubmit{
   593  				"r1": {{Name: "a"}},
   594  			},
   595  			Postsubmits: map[string][]Postsubmit{
   596  				"r1": {{Name: "b"}},
   597  			},
   598  			Periodics: []Periodic{
   599  				{
   600  					Name: "c",
   601  					RunAfterSuccess: []Periodic{
   602  						{Name: "ca"},
   603  						{Name: "cb"},
   604  					},
   605  				},
   606  				{Name: "d"},
   607  			},
   608  		},
   609  	}
   610  
   611  	expected := []string{"c", "ca", "cb", "d"}
   612  	actual := c.AllPeriodics()
   613  	if len(actual) != len(expected) {
   614  		t.Fatalf("Wrong number of jobs. Got %v, expected %v", actual, expected)
   615  	}
   616  	for _, j1 := range expected {
   617  		found := false
   618  		for _, j2 := range actual {
   619  			if j1 == j2.Name {
   620  				found = true
   621  				break
   622  			}
   623  		}
   624  		if !found {
   625  			t.Errorf("Did not find job %s in output", j1)
   626  		}
   627  	}
   628  }
   629  
   630  func TestRunAgainstBranch(t *testing.T) {
   631  	jobs := []Presubmit{
   632  		{
   633  			Name:     "a",
   634  			Brancher: Brancher{SkipBranches: []string{"s"}},
   635  		},
   636  		{
   637  			Name:     "b",
   638  			Brancher: Brancher{Branches: []string{"r"}},
   639  		},
   640  		{
   641  			Name: "c",
   642  			Brancher: Brancher{
   643  				SkipBranches: []string{"s"},
   644  				Branches:     []string{"r"},
   645  			},
   646  		},
   647  		{
   648  			Name: "d",
   649  			Brancher: Brancher{
   650  				SkipBranches: []string{"s"},
   651  				Branches:     []string{"s", "r"},
   652  			},
   653  		},
   654  		{
   655  			Name: "default",
   656  		},
   657  	}
   658  
   659  	if err := SetPresubmitRegexes(jobs); err != nil {
   660  		t.Fatalf("could not set regexes: %v", err)
   661  	}
   662  
   663  	for _, job := range jobs {
   664  		if job.Name == "default" {
   665  			if !job.RunsAgainstBranch("s") {
   666  				t.Errorf("Job %s should run branch s", job.Name)
   667  			}
   668  		} else if job.RunsAgainstBranch("s") {
   669  			t.Errorf("Job %s should not run branch s", job.Name)
   670  		}
   671  
   672  		if !job.RunsAgainstBranch("r") {
   673  			t.Errorf("Job %s should run branch r", job.Name)
   674  		}
   675  	}
   676  }
   677  
   678  func TestValidPodNames(t *testing.T) {
   679  	for _, j := range c.AllPresubmits([]string{}) {
   680  		if !podRe.MatchString(j.Name) {
   681  			t.Errorf("Job \"%s\" must match regex \"%s\".", j.Name, podRe.String())
   682  		}
   683  	}
   684  	for _, j := range c.AllPostsubmits([]string{}) {
   685  		if !podRe.MatchString(j.Name) {
   686  			t.Errorf("Job \"%s\" must match regex \"%s\".", j.Name, podRe.String())
   687  		}
   688  	}
   689  	for _, j := range c.AllPeriodics() {
   690  		if !podRe.MatchString(j.Name) {
   691  			t.Errorf("Job \"%s\" must match regex \"%s\".", j.Name, podRe.String())
   692  		}
   693  	}
   694  }
   695  
   696  func TestNoDuplicateJobs(t *testing.T) {
   697  	// Presubmit test is covered under TestPresubmits() above
   698  
   699  	allJobs := make(map[string]bool)
   700  	for _, j := range c.AllPostsubmits([]string{}) {
   701  		if allJobs[j.Name] {
   702  			t.Errorf("Found duplicate job in postsubmit: %s.", j.Name)
   703  		}
   704  		allJobs[j.Name] = true
   705  	}
   706  
   707  	allJobs = make(map[string]bool)
   708  	for _, j := range c.AllPeriodics() {
   709  		if allJobs[j.Name] {
   710  			t.Errorf("Found duplicate job in periodic %s.", j.Name)
   711  		}
   712  		allJobs[j.Name] = true
   713  	}
   714  }
   715  
   716  func TestMergePreset(t *testing.T) {
   717  	tcs := []struct {
   718  		name      string
   719  		jobLabels map[string]string
   720  		pod       *kube.PodSpec
   721  		presets   []Preset
   722  
   723  		shouldError  bool
   724  		numEnv       int
   725  		numVol       int
   726  		numVolMounts int
   727  	}{
   728  		{
   729  			name:      "one volume",
   730  			jobLabels: map[string]string{"foo": "bar"},
   731  			pod:       &kube.PodSpec{},
   732  			presets: []Preset{
   733  				{
   734  					Labels:  map[string]string{"foo": "bar"},
   735  					Volumes: []kube.Volume{{Name: "baz"}},
   736  				},
   737  			},
   738  			numVol: 1,
   739  		},
   740  		{
   741  			name:      "wrong label",
   742  			jobLabels: map[string]string{"foo": "nope"},
   743  			pod:       &kube.PodSpec{},
   744  			presets: []Preset{
   745  				{
   746  					Labels:  map[string]string{"foo": "bar"},
   747  					Volumes: []kube.Volume{{Name: "baz"}},
   748  				},
   749  			},
   750  		},
   751  		{
   752  			name:      "conflicting volume name",
   753  			jobLabels: map[string]string{"foo": "bar"},
   754  			pod:       &kube.PodSpec{Volumes: []kube.Volume{{Name: "baz"}}},
   755  			presets: []Preset{
   756  				{
   757  					Labels:  map[string]string{"foo": "bar"},
   758  					Volumes: []kube.Volume{{Name: "baz"}},
   759  				},
   760  			},
   761  			shouldError: true,
   762  		},
   763  		{
   764  			name:      "non conflicting volume name",
   765  			jobLabels: map[string]string{"foo": "bar"},
   766  			pod:       &kube.PodSpec{Volumes: []kube.Volume{{Name: "baz"}}},
   767  			presets: []Preset{
   768  				{
   769  					Labels:  map[string]string{"foo": "bar"},
   770  					Volumes: []kube.Volume{{Name: "qux"}},
   771  				},
   772  			},
   773  			numVol: 2,
   774  		},
   775  		{
   776  			name:      "one env",
   777  			jobLabels: map[string]string{"foo": "bar"},
   778  			pod:       &kube.PodSpec{Containers: []kube.Container{{}}},
   779  			presets: []Preset{
   780  				{
   781  					Labels: map[string]string{"foo": "bar"},
   782  					Env:    []kube.EnvVar{{Name: "baz"}},
   783  				},
   784  			},
   785  			numEnv: 1,
   786  		},
   787  		{
   788  			name:      "one vm",
   789  			jobLabels: map[string]string{"foo": "bar"},
   790  			pod:       &kube.PodSpec{Containers: []kube.Container{{}}},
   791  			presets: []Preset{
   792  				{
   793  					Labels:       map[string]string{"foo": "bar"},
   794  					VolumeMounts: []kube.VolumeMount{{Name: "baz"}},
   795  				},
   796  			},
   797  			numVolMounts: 1,
   798  		},
   799  		{
   800  			name:      "one of each",
   801  			jobLabels: map[string]string{"foo": "bar"},
   802  			pod:       &kube.PodSpec{Containers: []kube.Container{{}}},
   803  			presets: []Preset{
   804  				{
   805  					Labels:       map[string]string{"foo": "bar"},
   806  					Env:          []kube.EnvVar{{Name: "baz"}},
   807  					VolumeMounts: []kube.VolumeMount{{Name: "baz"}},
   808  					Volumes:      []kube.Volume{{Name: "qux"}},
   809  				},
   810  			},
   811  			numEnv:       1,
   812  			numVol:       1,
   813  			numVolMounts: 1,
   814  		},
   815  		{
   816  			name:      "two vm",
   817  			jobLabels: map[string]string{"foo": "bar"},
   818  			pod:       &kube.PodSpec{Containers: []kube.Container{{}}},
   819  			presets: []Preset{
   820  				{
   821  					Labels:       map[string]string{"foo": "bar"},
   822  					VolumeMounts: []kube.VolumeMount{{Name: "baz"}, {Name: "foo"}},
   823  				},
   824  			},
   825  			numVolMounts: 2,
   826  		},
   827  	}
   828  	for _, tc := range tcs {
   829  		if err := resolvePresets("foo", tc.jobLabels, tc.pod, tc.presets); err == nil && tc.shouldError {
   830  			t.Errorf("For test \"%s\": expected error but got none.", tc.name)
   831  		} else if err != nil && !tc.shouldError {
   832  			t.Errorf("For test \"%s\": expected no error but got %v.", tc.name, err)
   833  		}
   834  		if tc.shouldError {
   835  			continue
   836  		}
   837  		if len(tc.pod.Volumes) != tc.numVol {
   838  			t.Errorf("For test \"%s\": wrong number of volumes. Got %d, expected %d.", tc.name, len(tc.pod.Volumes), tc.numVol)
   839  		}
   840  		for _, c := range tc.pod.Containers {
   841  			if len(c.VolumeMounts) != tc.numVolMounts {
   842  				t.Errorf("For test \"%s\": wrong number of volume mounts. Got %d, expected %d.", tc.name, len(c.VolumeMounts), tc.numVolMounts)
   843  			}
   844  			if len(c.Env) != tc.numEnv {
   845  				t.Errorf("For test \"%s\": wrong number of env vars. Got %d, expected %d.", tc.name, len(c.Env), tc.numEnv)
   846  			}
   847  		}
   848  	}
   849  }