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