github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/prow/cmd/splice/main_test.go (about) 1 /* 2 Copyright 2016 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 "fmt" 21 "io/ioutil" 22 "net/http" 23 "net/http/httptest" 24 "reflect" 25 "testing" 26 "time" 27 28 "k8s.io/test-infra/prow/config" 29 "k8s.io/test-infra/prow/kube" 30 ) 31 32 func expectEqual(t *testing.T, msg string, have interface{}, want interface{}) { 33 if !reflect.DeepEqual(have, want) { 34 t.Errorf("bad %s: got %v, wanted %v", 35 msg, have, want) 36 } 37 } 38 39 type stringHandler string 40 41 func (h stringHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 42 fmt.Fprintf(w, "%s", h) 43 } 44 45 func TestGetQueuedPRs(t *testing.T) { 46 body := `{"E2EQueue":[ 47 {"Number":3, "Title": "blah"}, 48 {"Number":4, "BaseRef": "master"}, 49 {"Number":1}, 50 {"Number":5, "BaseRef": "release-1.5"} 51 ]}` 52 serv := httptest.NewServer(stringHandler(body)) 53 defer serv.Close() 54 q, err := getQueuedPRs(serv.URL) 55 if err != nil { 56 t.Fatal(err) 57 } 58 expectEqual(t, "queued PRs", q, []int{3, 4, 1}) 59 } 60 61 // Since the splicer object already has helpers for doing git operations, 62 // extend it to be useful for producing git repos for testing! 63 64 // branch creates a new branch off of master 65 func (s *splicer) branch(name string) error { 66 return s.gitCall("checkout", "-B", name, "master") 67 } 68 69 // commit makes a new commit with the provided files added. 70 func (s *splicer) commit(msg string, contents map[string]string) error { 71 for fname, data := range contents { 72 err := ioutil.WriteFile(s.dir+"/"+fname, []byte(data), 0644) 73 if err != nil { 74 return err 75 } 76 err = s.gitCall("add", fname) 77 if err != nil { 78 return err 79 } 80 } 81 return s.gitCall("commit", "-m", msg) 82 } 83 84 // Create a basic commit (so master can be branched off) 85 func (s *splicer) firstCommit() error { 86 return s.commit("first commit", map[string]string{"README": "hi"}) 87 } 88 89 type branchesSpec map[string]map[string]string 90 91 // addBranches does multiple branch/commit calls. 92 func (s *splicer) addBranches(b branchesSpec) error { 93 for name, contents := range b { 94 err := s.branch(name) 95 if err != nil { 96 return err 97 } 98 err = s.commit("msg", contents) 99 if err != nil { 100 return err 101 } 102 } 103 return nil 104 } 105 106 func TestGitOperations(t *testing.T) { 107 s, err := makeSplicer() 108 if err != nil { 109 t.Fatal(err) 110 } 111 defer s.cleanup() 112 err = s.firstCommit() 113 if err != nil { 114 t.Fatal(err) 115 } 116 err = s.addBranches(branchesSpec{ 117 "pr/123": {"a": "1", "b": "2"}, 118 "pr/456": {"a": "1", "b": "4", "c": "e"}, 119 }) 120 if err != nil { 121 t.Fatal(err) 122 } 123 } 124 125 func TestFindMergeable(t *testing.T) { 126 up, _ := makeSplicer() 127 defer up.cleanup() 128 up.firstCommit() 129 err := up.addBranches(branchesSpec{ 130 "pull/1/head": {"a": "1", "e": "1"}, 131 "pull/2/head": {"b": "2"}, 132 "pull/3/head": {"a": "1", "b": "2", "c": "3"}, 133 "pull/4/head": {"a": "5"}, 134 }) 135 if err != nil { 136 t.Fatal(err) 137 } 138 139 s, _ := makeSplicer() 140 defer s.cleanup() 141 mergeable, err := s.findMergeable(up.dir, []int{3, 2, 1, 4}) 142 if err != nil { 143 t.Fatal(err) 144 } 145 expectEqual(t, "mergeable PRs", mergeable, []int{3, 2, 1}) 146 147 // findMergeable should work if repeated-- the repo should be 148 // reset into a state so it can try to merge again. 149 mergeable, err = s.findMergeable(up.dir, []int{3, 2, 1, 4}) 150 if err != nil { 151 t.Fatal(err) 152 } 153 expectEqual(t, "mergeable PRs", mergeable, []int{3, 2, 1}) 154 155 // PRs that cause merge conflicts should be skipped 156 mergeable, err = s.findMergeable(up.dir, []int{1, 4, 2, 3}) 157 if err != nil { 158 t.Fatal(err) 159 } 160 expectEqual(t, "mergeable PRs", mergeable, []int{1, 2, 3}) 161 162 // doing a force push should work as well! 163 err = up.addBranches(branchesSpec{ 164 "pull/2/head": {"b": "2", "e": "2"}, // now conflicts with 1 165 }) 166 if err != nil { 167 t.Fatal(err) 168 } 169 mergeable, err = s.findMergeable(up.dir, []int{3, 2, 1, 4}) 170 if err != nil { 171 t.Fatal(err) 172 } 173 expectEqual(t, "mergeable PRs", mergeable, []int{3, 2}) 174 175 } 176 177 func fakeRefs(ref, sha string) kube.Refs { 178 return kube.Refs{ 179 BaseRef: ref, 180 BaseSHA: sha, 181 } 182 } 183 184 func fakeProwJob(context string, jobType kube.ProwJobType, completed bool, state kube.ProwJobState, refs kube.Refs) kube.ProwJob { 185 pj := kube.ProwJob{ 186 Status: kube.ProwJobStatus{ 187 State: state, 188 }, 189 Spec: kube.ProwJobSpec{ 190 Context: context, 191 Refs: refs, 192 Type: jobType, 193 }, 194 } 195 if completed { 196 pj.Status.CompletionTime = time.Now() 197 } 198 return pj 199 } 200 201 func TestCompletedJobs(t *testing.T) { 202 refs := fakeRefs("ref", "sha") 203 other := fakeRefs("otherref", "othersha") 204 tests := []struct { 205 name string 206 jobs []kube.ProwJob 207 refs kube.Refs 208 completed []string 209 }{ 210 { 211 name: "completed when passed", 212 jobs: []kube.ProwJob{ 213 fakeProwJob("passed-a", kube.BatchJob, true, kube.SuccessState, refs), 214 fakeProwJob("passed-b", kube.BatchJob, true, kube.SuccessState, refs), 215 }, 216 refs: refs, 217 completed: []string{"passed-a", "passed-b"}, 218 }, 219 { 220 name: "ignore bad ref", 221 jobs: []kube.ProwJob{ 222 fakeProwJob("passed-a", kube.BatchJob, true, kube.SuccessState, other), 223 }, 224 refs: refs, 225 }, 226 { 227 name: "only complete good refs", 228 jobs: []kube.ProwJob{ 229 fakeProwJob("passed-a", kube.BatchJob, true, kube.SuccessState, refs), 230 fakeProwJob("passed-b-bad-ref", kube.BatchJob, true, kube.SuccessState, other), 231 }, 232 refs: refs, 233 completed: []string{"passed-a"}, 234 }, 235 { 236 name: "completed when good and bad ref", 237 jobs: []kube.ProwJob{ 238 fakeProwJob("passed-a", kube.BatchJob, true, kube.SuccessState, refs), 239 fakeProwJob("passed-a", kube.BatchJob, true, kube.SuccessState, other), 240 }, 241 refs: refs, 242 completed: []string{"passed-a"}, 243 }, 244 { 245 name: "ignore incomplete", 246 jobs: []kube.ProwJob{ 247 fakeProwJob("passed-a", kube.BatchJob, true, kube.SuccessState, refs), 248 fakeProwJob("pending-b", kube.BatchJob, false, kube.PendingState, refs), 249 }, 250 refs: refs, 251 completed: []string{"passed-a"}, 252 }, 253 { 254 name: "ignore failed", 255 jobs: []kube.ProwJob{ 256 fakeProwJob("passed-a", kube.BatchJob, true, kube.SuccessState, refs), 257 fakeProwJob("failed-b", kube.BatchJob, true, kube.FailureState, refs), 258 }, 259 refs: refs, 260 completed: []string{"passed-a"}, 261 }, 262 { 263 name: "ignore non-batch", 264 jobs: []kube.ProwJob{ 265 fakeProwJob("passed-a", kube.BatchJob, true, kube.SuccessState, refs), 266 fakeProwJob("non-batch-b", kube.PresubmitJob, true, kube.SuccessState, refs), 267 }, 268 refs: refs, 269 completed: []string{"passed-a"}, 270 }, 271 } 272 273 for _, tc := range tests { 274 completed := completedJobs(tc.jobs, tc.refs) 275 var completedContexts []string 276 for _, job := range completed { 277 completedContexts = append(completedContexts, job.Spec.Context) 278 } 279 expectEqual(t, "completed contexts", completedContexts, tc.completed) 280 } 281 } 282 283 func TestRequiredPresubmits(t *testing.T) { 284 tests := []struct { 285 name string 286 possible []config.Presubmit 287 required []string 288 }{ 289 { 290 name: "basic", 291 possible: []config.Presubmit{ 292 { 293 Name: "always", 294 AlwaysRun: true, 295 }, 296 { 297 Name: "optional", 298 AlwaysRun: false, 299 }, 300 { 301 Name: "hidden", 302 AlwaysRun: true, 303 SkipReport: true, 304 }, 305 }, 306 required: []string{"always"}, 307 }, 308 } 309 310 for _, tc := range tests { 311 var names []string 312 for _, job := range requiredPresubmits(tc.possible) { 313 names = append(names, job.Name) 314 } 315 expectEqual(t, tc.name, names, tc.required) 316 } 317 } 318 319 func TestNeededPresubmits(t *testing.T) { 320 tests := []struct { 321 name string 322 possible []config.Presubmit 323 current []kube.ProwJob 324 refs kube.Refs 325 required []string 326 }{ 327 { 328 name: "basic", 329 possible: []config.Presubmit{ 330 { 331 Name: "always", 332 AlwaysRun: true, 333 }, 334 { 335 Name: "optional", 336 AlwaysRun: false, 337 }, 338 { 339 Name: "hidden", 340 AlwaysRun: true, 341 SkipReport: true, 342 }, 343 }, 344 required: []string{"always"}, 345 }, 346 { 347 name: "skip already passed", 348 possible: []config.Presubmit{ 349 { 350 Name: "new", 351 Context: "brandnew", 352 AlwaysRun: true, 353 }, 354 { 355 Name: "passed", 356 Context: "already-ran", 357 AlwaysRun: true, 358 }, 359 }, 360 current: []kube.ProwJob{ 361 fakeProwJob("already-ran", kube.BatchJob, true, kube.SuccessState, fakeRefs("ref", "sha")), 362 }, 363 refs: fakeRefs("ref", "sha"), 364 required: []string{"new"}, 365 }, 366 } 367 368 for _, tc := range tests { 369 var names []string 370 for _, job := range neededPresubmits(tc.possible, tc.current, tc.refs) { 371 names = append(names, job.Name) 372 } 373 expectEqual(t, tc.name, names, tc.required) 374 } 375 }