go.fuchsia.dev/jiri@v0.0.0-20240502161911-b66513b29486/cmd/jiri/branch_test.go (about) 1 // Copyright 2017 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "fmt" 9 "math/rand" 10 "net/http" 11 "net/http/httptest" 12 "os" 13 "path/filepath" 14 "sort" 15 "strconv" 16 "strings" 17 "testing" 18 "time" 19 20 "go.fuchsia.dev/jiri/gitutil" 21 "go.fuchsia.dev/jiri/jiritest" 22 "go.fuchsia.dev/jiri/project" 23 ) 24 25 func setDefaultBranchFlags() { 26 branchFlags.deleteFlag = false 27 branchFlags.deleteMergedClsFlag = false 28 branchFlags.deleteMergedFlag = false 29 branchFlags.forceDeleteFlag = false 30 branchFlags.listFlag = false 31 branchFlags.overrideProjectConfigFlag = false 32 } 33 34 func createBranchCommits(t *testing.T, fake *jiritest.FakeJiriRoot, localProjects []project.Project) { 35 for i, localProject := range localProjects { 36 writeFile(t, fake.X, fake.Projects[localProject.Name], "file1"+strconv.Itoa(i), "file1"+strconv.Itoa(i)) 37 } 38 } 39 40 func createBranchProjects(t *testing.T, fake *jiritest.FakeJiriRoot, numProjects int) []project.Project { 41 localProjects := []project.Project{} 42 for i := 0; i < numProjects; i++ { 43 name := fmt.Sprintf("project-%d", i) 44 path := fmt.Sprintf("path-%d", i) 45 if err := fake.CreateRemoteProject(name); err != nil { 46 t.Fatal(err) 47 } 48 p := project.Project{ 49 Name: name, 50 Path: filepath.Join(fake.X.Root, path), 51 Remote: fake.Projects[name], 52 } 53 localProjects = append(localProjects, p) 54 if err := fake.AddProject(p); err != nil { 55 t.Fatal(err) 56 } 57 } 58 createBranchCommits(t, fake, localProjects) 59 return localProjects 60 } 61 62 func TestBranch(t *testing.T) { 63 setDefaultBranchFlags() 64 fake, cleanup := jiritest.NewFakeJiriRoot(t) 65 defer cleanup() 66 67 // Add projects 68 numProjects := 8 69 localProjects := createBranchProjects(t, fake, numProjects) 70 if err := fake.UpdateUniverse(false); err != nil { 71 t.Fatal(err) 72 } 73 74 gitLocals := make([]*gitutil.Git, numProjects) 75 for i, localProject := range localProjects { 76 gitLocal := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(localProject.Path)) 77 gitLocals[i] = gitLocal 78 } 79 80 testBranch := "testBranch" 81 testBranch2 := "testBranch2" 82 83 defaultWant := "" 84 branchWant := "" 85 listWant := "" 86 cDir, err := os.Getwd() 87 if err != nil { 88 t.Fatal(err) 89 } 90 relativePath := make([]string, numProjects) 91 for i, p := range localProjects { 92 relativePath[i], err = filepath.Rel(cDir, p.Path) 93 if err != nil { 94 t.Fatal(err) 95 } 96 } 97 // current branch is not testBranch 98 i := 0 99 gitLocals[i].CreateBranch(testBranch) 100 gitLocals[i].CheckoutBranch("main", localProjects[i].GitSubmodules, false) 101 branchWant = fmt.Sprintf("%s%s(%s)\n", branchWant, localProjects[i].Name, relativePath[i]) 102 defaultWant = fmt.Sprintf("%sProject: %s(%s)\n", defaultWant, localProjects[i].Name, relativePath[i]) 103 defaultWant = fmt.Sprintf("%sBranch(es): %s, *main\n\n", defaultWant, testBranch) 104 105 i = 2 106 gitLocals[i].CreateBranch(testBranch) 107 gitLocals[i].CreateBranch(testBranch2) 108 branchWant = fmt.Sprintf("%s%s(%s)\n", branchWant, localProjects[i].Name, relativePath[i]) 109 defaultWant = fmt.Sprintf("%sProject: %s(%s)\n", defaultWant, localProjects[i].Name, relativePath[i]) 110 defaultWant = fmt.Sprintf("%sBranch(es): %s, %s\n\n", defaultWant, testBranch, testBranch2) 111 112 i = 3 113 gitLocals[i].CreateBranch(testBranch) 114 branchWant = fmt.Sprintf("%s%s(%s)\n", branchWant, localProjects[i].Name, relativePath[i]) 115 defaultWant = fmt.Sprintf("%sProject: %s(%s)\n", defaultWant, localProjects[i].Name, relativePath[i]) 116 defaultWant = fmt.Sprintf("%sBranch(es): %s\n\n", defaultWant, testBranch) 117 118 // current branch is test branch 119 i = 1 120 gitLocals[i].CreateBranch(testBranch) 121 gitLocals[i].CheckoutBranch(testBranch, localProjects[i].GitSubmodules, false) 122 gitLocals[i].CreateBranch(testBranch2) 123 listWant = fmt.Sprintf("%s%s(%s)\n", listWant, localProjects[i].Name, relativePath[i]) 124 defaultWant = fmt.Sprintf("%sProject: %s(%s)\n", defaultWant, localProjects[i].Name, relativePath[i]) 125 defaultWant = fmt.Sprintf("%sBranch(es): *%s, %s\n\n", defaultWant, testBranch, testBranch2) 126 127 i = 6 128 gitLocals[i].CreateBranch(testBranch) 129 gitLocals[i].CreateBranch("main") 130 gitLocals[i].CheckoutBranch(testBranch, localProjects[i].GitSubmodules, false) 131 listWant = fmt.Sprintf("%s%s(%s)\n", listWant, localProjects[i].Name, relativePath[i]) 132 defaultWant = fmt.Sprintf("%sProject: %s(%s)\n", defaultWant, localProjects[i].Name, relativePath[i]) 133 defaultWant = fmt.Sprintf("%sBranch(es): *%s, main\n\n", defaultWant, testBranch) 134 135 i = 4 136 gitLocals[i].CreateBranch(testBranch) 137 gitLocals[i].CheckoutBranch(testBranch, localProjects[i].GitSubmodules, false) 138 gitLocals[i].CreateBranch(testBranch2) 139 listWant = fmt.Sprintf("%s%s(%s)\n", listWant, localProjects[i].Name, relativePath[i]) 140 branchWant = fmt.Sprintf("%s%s", branchWant, listWant) 141 defaultWant = fmt.Sprintf("%sProject: %s(%s)\n", defaultWant, localProjects[i].Name, relativePath[i]) 142 defaultWant = fmt.Sprintf("%sBranch(es): *%s, %s\n\n", defaultWant, testBranch, testBranch2) 143 144 // Run default 145 if got := executeBranch(t, fake); !equalDefaultBranchOut(got, defaultWant) { 146 t.Errorf("got %s, want %s", got, defaultWant) 147 } 148 // Run with branch 149 if got := executeBranch(t, fake, testBranch); !equalBranchOut(got, branchWant) { 150 t.Errorf("got %s, want %s", got, branchWant) 151 } 152 153 // Run with listFlag 154 branchFlags.listFlag = true 155 if got := executeBranch(t, fake, testBranch); !equalBranchOut(got, listWant) { 156 t.Errorf("got %s, want %s", got, listWant) 157 } 158 } 159 160 func TestDeleteBranchWithProjectConfig(t *testing.T) { 161 testDeleteBranchWithProjectConfig(t, false) 162 testDeleteBranchWithProjectConfig(t, true) 163 } 164 165 func testDeleteBranchWithProjectConfig(t *testing.T, override_pc bool) { 166 setDefaultBranchFlags() 167 fake, cleanup := jiritest.NewFakeJiriRoot(t) 168 defer cleanup() 169 170 // Add projects 171 numProjects := 4 172 localProjects := createBranchProjects(t, fake, numProjects) 173 if err := fake.UpdateUniverse(false); err != nil { 174 t.Fatal(err) 175 } 176 177 gitLocals := make([]*gitutil.Git, numProjects) 178 for i, localProject := range localProjects { 179 gitLocal := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(localProject.Path)) 180 gitLocals[i] = gitLocal 181 } 182 183 testBranch := "testBranch" 184 185 // Test case when new test branch is on HEAD 186 i := 0 187 gitLocals[i].CreateBranch(testBranch) 188 lc := project.LocalConfig{NoUpdate: true} 189 project.WriteLocalConfig(fake.X, localProjects[i], lc) 190 191 // Test when git branch -d fails 192 i = 1 193 gitLocals[i].CreateBranch(testBranch) 194 gitLocals[i].CheckoutBranch(testBranch, localProjects[i].GitSubmodules, false) 195 writeFile(t, fake.X, localProjects[i].Path, "extrafile", "extrafile") 196 gitLocals[i].CheckoutBranch("main", localProjects[i].GitSubmodules, false) 197 198 // Test when current branch is test branch 199 i = 2 200 gitLocals[i].CreateBranch(testBranch) 201 gitLocals[i].CheckoutBranch(testBranch, localProjects[i].GitSubmodules, false) 202 203 // project-3 has no test branch 204 205 projects := make(project.Projects) 206 for _, localProject := range localProjects { 207 projects[localProject.Key()] = localProject 208 } 209 210 setDefaultBranchFlags() 211 branchFlags.deleteFlag = true 212 branchFlags.overrideProjectConfigFlag = override_pc 213 executeBranch(t, fake, testBranch) 214 215 states, err := project.GetProjectStates(fake.X, projects, false) 216 if err != nil { 217 t.Error(err) 218 } 219 220 // test project states 221 for i = 0; i < numProjects; i++ { 222 localProject := localProjects[i] 223 state, _ := states[localProject.Key()] 224 branchFound := false 225 for _, branch := range state.Branches { 226 if branch.Name == testBranch { 227 branchFound = true 228 break 229 } 230 } 231 if (!override_pc && i == 0) || i == 1 || i == 2 { 232 if !branchFound { 233 t.Errorf("project %q should contain branch %q", localProject.Name, testBranch) 234 } 235 } else { 236 if branchFound { 237 t.Errorf("project %q should not contain branch %q", localProject.Name, testBranch) 238 } 239 240 } 241 } 242 } 243 244 func TestDeleteBranch(t *testing.T) { 245 setDefaultBranchFlags() 246 fake, cleanup := jiritest.NewFakeJiriRoot(t) 247 defer cleanup() 248 249 // Add projects 250 numProjects := 4 251 localProjects := createBranchProjects(t, fake, numProjects) 252 if err := fake.UpdateUniverse(false); err != nil { 253 t.Fatal(err) 254 } 255 256 gitLocals := make([]*gitutil.Git, numProjects) 257 for i, localProject := range localProjects { 258 gitLocal := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(localProject.Path)) 259 gitLocals[i] = gitLocal 260 } 261 262 testBranch := "testBranch" 263 264 // Test case when new test branch is on HEAD 265 i := 0 266 gitLocals[i].CreateBranch(testBranch) 267 268 // Test when git branch -d fails 269 i = 1 270 gitLocals[i].CreateBranch(testBranch) 271 gitLocals[i].CheckoutBranch(testBranch, localProjects[i].GitSubmodules, false) 272 writeFile(t, fake.X, localProjects[i].Path, "extrafile", "extrafile") 273 gitLocals[i].CheckoutBranch("main", localProjects[i].GitSubmodules, false) 274 275 // Test when current branch is test branch 276 i = 2 277 gitLocals[i].CreateBranch(testBranch) 278 gitLocals[i].CheckoutBranch(testBranch, localProjects[i].GitSubmodules, false) 279 280 // project-3 has no test branch 281 282 projects := make(project.Projects) 283 for _, localProject := range localProjects { 284 projects[localProject.Key()] = localProject 285 } 286 287 // Run on default, should not delete any branch 288 executeBranch(t, fake, testBranch) 289 290 states, err := project.GetProjectStates(fake.X, projects, false) 291 if err != nil { 292 t.Error(err) 293 } 294 295 // test project states 296 for i = 0; i < numProjects; i++ { 297 localProject := localProjects[i] 298 state, _ := states[localProject.Key()] 299 branchFound := false 300 for _, branch := range state.Branches { 301 if branch.Name == testBranch { 302 branchFound = true 303 break 304 } 305 } 306 if i == 0 || i == 1 || i == 2 { 307 if !branchFound { 308 t.Errorf("project %q should contain branch %q", localProject.Name, testBranch) 309 } 310 } 311 } 312 313 setDefaultBranchFlags() 314 branchFlags.deleteFlag = true 315 executeBranch(t, fake, testBranch) 316 317 states, err = project.GetProjectStates(fake.X, projects, false) 318 if err != nil { 319 t.Error(err) 320 } 321 322 // test project states 323 for i = 0; i < numProjects; i++ { 324 localProject := localProjects[i] 325 state, _ := states[localProject.Key()] 326 branchFound := false 327 for _, branch := range state.Branches { 328 if branch.Name == testBranch { 329 branchFound = true 330 break 331 } 332 } 333 if i == 1 || i == 2 { 334 if !branchFound { 335 t.Errorf("project %q should contain branch %q", localProject.Name, testBranch) 336 } 337 } else { 338 if branchFound { 339 340 t.Errorf("project %q should not contain branch %q", localProject.Name, testBranch) 341 } 342 343 } 344 } 345 346 setDefaultBranchFlags() 347 branchFlags.forceDeleteFlag = true 348 executeBranch(t, fake, testBranch) 349 350 states, err = project.GetProjectStates(fake.X, projects, false) 351 if err != nil { 352 t.Error(err) 353 } 354 355 // test project states 356 for i = 0; i < numProjects; i++ { 357 localProject := localProjects[i] 358 state, _ := states[localProject.Key()] 359 branchFound := false 360 for _, branch := range state.Branches { 361 if branch.Name == testBranch { 362 branchFound = true 363 break 364 } 365 } 366 if i == 2 { 367 if !branchFound { 368 t.Errorf("project %q should contain branch %q", localProject.Name, testBranch) 369 } 370 } else { 371 if branchFound { 372 373 t.Errorf("project %q should not contain branch %q", localProject.Name, testBranch) 374 } 375 376 } 377 } 378 } 379 380 var r *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) 381 382 func randomString(strlen int) string { 383 const chars = "abcde0123456789ABCDE" 384 result := make([]byte, strlen) 385 for i := range result { 386 result[i] = chars[r.Intn(len(chars))] 387 } 388 return string(result) 389 } 390 391 func generateChangeIds(n int) []string { 392 ids := make([]string, n) 393 for i := range ids { 394 ids[i] = "I" + randomString(40) 395 } 396 return ids 397 } 398 399 func TestDeleteMergedClsBranch(t *testing.T) { 400 mergedIds := generateChangeIds(2) 401 unmergedIds := generateChangeIds(1) 402 localIds := generateChangeIds(1) 403 serverMux := http.NewServeMux() 404 serverMux.HandleFunc("/changes/", func(rw http.ResponseWriter, r *http.Request) { 405 r.ParseForm() 406 if id, ok := r.Form["q"]; ok { 407 for _, m := range mergedIds { 408 if m == id[0] { 409 rw.Write([]byte(")]}'\n[{\"submitted\":\"time\"}]")) 410 return 411 } 412 } 413 for _, u := range unmergedIds { 414 if u == id[0] { 415 rw.Write([]byte(fmt.Sprintf(")]}'\n[{\"change-id\":\"%s\"}]", id[0]))) 416 return 417 } 418 } 419 } 420 rw.Write([]byte(")]}'\n[]")) 421 }) 422 serverMux.HandleFunc("/tools/hooks/commit-msg", func(rw http.ResponseWriter, r *http.Request) { 423 rw.Write([]byte("#!/bin/sh")) 424 }) 425 server := httptest.NewServer(serverMux) 426 defer server.Close() 427 428 fake, cleanup := jiritest.NewFakeJiriRoot(t) 429 defer cleanup() 430 431 // Add projects 432 numProjects := 6 433 localProjects := createBranchProjects(t, fake, numProjects) 434 435 m, err := fake.ReadRemoteManifest() 436 if err != nil { 437 t.Fatal(err) 438 } 439 ps := []project.Project{} 440 for _, p := range m.Projects { 441 p.GerritHost = server.URL 442 ps = append(ps, p) 443 } 444 m.Projects = ps 445 if err := fake.WriteRemoteManifest(m); err != nil { 446 t.Fatal(err) 447 } 448 449 if err := fake.UpdateUniverse(false); err != nil { 450 t.Fatal(err) 451 } 452 453 gitLocals := make([]*gitutil.Git, numProjects) 454 for i, localProject := range localProjects { 455 gitLocal := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(localProject.Path)) 456 gitLocals[i] = gitLocal 457 } 458 459 branchToDelete1 := "branchToDelete1" 460 branchToDelete2 := "branchToDelete2" 461 branchNotToDelete := "branchNotToDelete" 462 463 changeIdPrefix := "Change-Id: " 464 465 i := 0 466 gitLocals[i].CreateBranch(branchToDelete1) 467 gitLocals[i].CheckoutBranch(branchToDelete1, localProjects[i].GitSubmodules, false) 468 for j := 0; j < 2; j++ { 469 writeFile(t, fake.X, localProjects[i].Path, "extrafile", "extrafile\n"+changeIdPrefix+mergedIds[j]) 470 } 471 gitLocals[i].CreateBranchWithUpstream(branchToDelete2, "origin/main") 472 gitLocals[i].CheckoutBranch(branchToDelete2, localProjects[i].GitSubmodules, false) 473 for j := 0; j < 2; j++ { 474 writeFile(t, fake.X, localProjects[i].Path, "extrafile", "extrafile\n"+changeIdPrefix+mergedIds[j]) 475 } 476 477 i = 1 478 gitLocals[i].CreateBranchWithUpstream(branchToDelete1, "origin/main") 479 gitLocals[i].CheckoutBranch(branchToDelete1, localProjects[i].GitSubmodules, false) 480 for j := 0; j < 2; j++ { 481 writeFile(t, fake.X, localProjects[i].Path, "extrafile", "extrafile\n"+changeIdPrefix+mergedIds[j]) 482 } 483 gitLocals[i].CreateAndCheckoutBranch(branchNotToDelete) 484 writeFile(t, fake.X, localProjects[i].Path, "extrafile", "extrafile"+changeIdPrefix+localIds[0]) 485 486 i = 2 487 gitLocals[i].CreateBranchWithUpstream(branchToDelete1, "origin/main") 488 gitLocals[i].CreateBranchWithUpstream(branchNotToDelete, "origin/main") 489 gitLocals[i].CheckoutBranch(branchNotToDelete, localProjects[i].GitSubmodules, false) 490 writeFile(t, fake.X, localProjects[i].Path, "extrafile", "extrafile\n"+changeIdPrefix+unmergedIds[0]) 491 492 // project-3 has no branch 493 494 // Don't delete current branch with changes 495 i = 4 496 gitLocals[i].CreateBranchWithUpstream(branchToDelete1, "origin/main") 497 gitLocals[i].CreateBranchWithUpstream(branchNotToDelete, "origin/main") 498 gitLocals[i].CheckoutBranch(branchNotToDelete, localProjects[i].GitSubmodules, false) 499 writeFile(t, fake.X, localProjects[i].Path, "extrafile", "extrafile\n"+changeIdPrefix+mergedIds[0]) 500 newfile(t, localProjects[i].Path, "uncommitted.go") 501 502 // Don't delete branch when it has local commits 503 i = 5 504 gitLocals[i].CreateBranchWithUpstream(branchNotToDelete, "origin/main") 505 gitLocals[i].CheckoutBranch(branchNotToDelete, localProjects[i].GitSubmodules, false) 506 writeFile(t, fake.X, localProjects[i].Path, "extrafile", "extrafile\n"+changeIdPrefix+localIds[0]) 507 writeFile(t, fake.X, localProjects[i].Path, "extrafile", "extrafile\n"+changeIdPrefix+mergedIds[0]) 508 509 projects := make(project.Projects) 510 for _, localProject := range localProjects { 511 projects[localProject.Key()] = localProject 512 } 513 514 oldstates, err := project.GetProjectStates(fake.X, projects, false) 515 if err != nil { 516 t.Error(err) 517 } 518 519 setDefaultBranchFlags() 520 branchFlags.deleteMergedClsFlag = true 521 got := executeBranch(t, fake) 522 fmt.Println(got) 523 524 newstates, err := project.GetProjectStates(fake.X, projects, false) 525 if err != nil { 526 t.Error(err) 527 } 528 529 // test project states 530 for i = 0; i < numProjects; i++ { 531 localProject := localProjects[i] 532 oldstate, _ := oldstates[localProject.Key()] 533 newstate, _ := newstates[localProject.Key()] 534 newBranchMap := make(map[string]bool) 535 for _, newb := range newstate.Branches { 536 newBranchMap[newb.Name] = true 537 } 538 for _, oldb := range oldstate.Branches { 539 if oldb.Name == branchNotToDelete && !newBranchMap[oldb.Name] { 540 t.Errorf("project %q should contain branch %q", localProject.Name, oldb.Name) 541 } else if strings.HasPrefix(oldb.Name, "branchToDelete") && newBranchMap[oldb.Name] { 542 t.Errorf("project %q should not contain branch %q", localProject.Name, oldb.Name) 543 } 544 } 545 } 546 } 547 548 func TestDeleteMergedBranch(t *testing.T) { 549 testDeleteMergedBranch(t, false) 550 testDeleteMergedBranch(t, true) 551 } 552 553 func testDeleteMergedBranch(t *testing.T, override_pc bool) { 554 setDefaultBranchFlags() 555 fake, cleanup := jiritest.NewFakeJiriRoot(t) 556 defer cleanup() 557 558 // Add projects 559 numProjects := 7 560 localProjects := createBranchProjects(t, fake, numProjects) 561 if err := fake.UpdateUniverse(false); err != nil { 562 t.Fatal(err) 563 } 564 565 gitLocals := make([]*gitutil.Git, numProjects) 566 for i, localProject := range localProjects { 567 gitLocal := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(localProject.Path)) 568 gitLocals[i] = gitLocal 569 } 570 571 branchToDelete1 := "branchToDelete1" 572 branchToDelete2 := "branchToDelete2" 573 branchNotToDelete := "branchNotToDelete" 574 575 i := 0 576 gitLocals[i].CreateBranch(branchToDelete1) 577 gitLocals[i].CreateBranchWithUpstream(branchToDelete2, "origin/main") 578 579 i = 1 580 gitLocals[i].CreateBranchWithUpstream(branchToDelete1, "origin/main") 581 gitLocals[i].CheckoutBranch(branchNotToDelete, localProjects[i].GitSubmodules, false) 582 writeFile(t, fake.X, localProjects[i].Path, "extrafile", "extrafile") 583 584 i = 2 585 gitLocals[i].CreateBranchWithUpstream(branchToDelete1, "origin/main") 586 gitLocals[i].CreateBranchWithUpstream(branchNotToDelete, "origin/main") 587 gitLocals[i].CheckoutBranch(branchNotToDelete, localProjects[i].GitSubmodules, false) 588 writeFile(t, fake.X, localProjects[i].Path, "extrafile", "extrafile") 589 590 // project-3 has no branch 591 592 i = 4 593 gitLocals[i].CreateBranchWithUpstream(branchToDelete1, "origin/main") 594 gitLocals[i].CreateBranch(branchToDelete2) 595 gitLocals[i].CheckoutBranch(branchNotToDelete, localProjects[i].GitSubmodules, false) 596 writeFile(t, fake.X, localProjects[i].Path, "extrafile", "extrafile") 597 lc := project.LocalConfig{NoUpdate: true} 598 project.WriteLocalConfig(fake.X, localProjects[i], lc) 599 localProjects[i].LocalConfig = lc 600 601 // Don't delete current branch with changes 602 i = 5 603 gitLocals[i].CreateBranchWithUpstream(branchToDelete1, "origin/main") 604 gitLocals[i].CreateBranchWithUpstream(branchNotToDelete, "origin/main") 605 gitLocals[i].CheckoutBranch(branchNotToDelete, localProjects[i].GitSubmodules, false) 606 newfile(t, localProjects[i].Path, "uncommitted.go") 607 608 // Don't delete current branch with changes 609 i = 6 610 gitLocals[i].CreateBranch(branchToDelete1) 611 gitLocals[i].CreateBranch(branchNotToDelete) 612 gitLocals[i].CheckoutBranch(branchNotToDelete, localProjects[i].GitSubmodules, false) 613 newfile(t, localProjects[i].Path, "uncommitted.go") 614 615 projects := make(project.Projects) 616 for _, localProject := range localProjects { 617 projects[localProject.Key()] = localProject 618 } 619 620 oldstates, err := project.GetProjectStates(fake.X, projects, false) 621 if err != nil { 622 t.Error(err) 623 } 624 625 setDefaultBranchFlags() 626 branchFlags.deleteMergedFlag = true 627 branchFlags.overrideProjectConfigFlag = override_pc 628 executeBranch(t, fake) 629 630 newstates, err := project.GetProjectStates(fake.X, projects, false) 631 if err != nil { 632 t.Error(err) 633 } 634 635 // test project states 636 for i = 0; i < numProjects; i++ { 637 localProject := localProjects[i] 638 dontdelete := localProject.LocalConfig.NoUpdate && !override_pc 639 oldstate, _ := oldstates[localProject.Key()] 640 newstate, _ := newstates[localProject.Key()] 641 newBranchMap := make(map[string]bool) 642 for _, newb := range newstate.Branches { 643 newBranchMap[newb.Name] = true 644 } 645 for _, oldb := range oldstate.Branches { 646 if (dontdelete || oldb.Name == branchNotToDelete) && !newBranchMap[oldb.Name] { 647 t.Errorf("project %q should contain branch %q", localProject.Name, oldb.Name) 648 } else if !dontdelete && strings.HasPrefix(oldb.Name, "branchToDelete") && newBranchMap[oldb.Name] { 649 t.Errorf("project %q should not contain branch %q", localProject.Name, oldb.Name) 650 } 651 } 652 } 653 654 // Test that if <branch> is passed only that branch is deleted 655 i = 0 656 gitLocals[i].CreateBranch(branchToDelete1) 657 gitLocals[i].DeleteBranch(branchNotToDelete, gitutil.ForceOpt(true)) 658 gitLocals[i].CreateBranchWithUpstream(branchNotToDelete, "origin/main") 659 660 i = 1 661 gitLocals[i].CreateBranchWithUpstream(branchToDelete1, "origin/main") 662 gitLocals[i].DeleteBranch(branchNotToDelete, gitutil.ForceOpt(true)) 663 gitLocals[i].CreateBranchWithUpstream(branchNotToDelete, "origin/main") 664 665 i = 2 666 gitLocals[i].CreateBranchWithUpstream(branchToDelete1, "origin/main") 667 gitLocals[i].DeleteBranch(branchNotToDelete, gitutil.ForceOpt(true)) 668 gitLocals[i].CreateBranch(branchNotToDelete) 669 670 i = 3 671 gitLocals[i].CreateBranch(branchToDelete1) 672 gitLocals[i].DeleteBranch(branchNotToDelete, gitutil.ForceOpt(true)) 673 gitLocals[i].CreateBranch(branchNotToDelete) 674 675 i = 4 676 gitLocals[i].CreateBranchWithUpstream(branchToDelete1, "origin/main") 677 gitLocals[i].DeleteBranch(branchNotToDelete, gitutil.ForceOpt(true)) 678 gitLocals[i].CreateBranch(branchNotToDelete) 679 lc = project.LocalConfig{NoUpdate: true} 680 project.WriteLocalConfig(fake.X, localProjects[i], lc) 681 localProjects[i].LocalConfig = lc 682 683 setDefaultBranchFlags() 684 branchFlags.deleteMergedFlag = true 685 branchFlags.overrideProjectConfigFlag = override_pc 686 executeBranch(t, fake, branchToDelete1) 687 688 newstates, err = project.GetProjectStates(fake.X, projects, false) 689 if err != nil { 690 t.Error(err) 691 } 692 693 // test project states 694 for i = 0; i <= 4; i++ { 695 localProject := localProjects[i] 696 dontdelete := localProject.LocalConfig.NoUpdate && !override_pc 697 newstate, _ := newstates[localProject.Key()] 698 newBranchMap := make(map[string]bool) 699 for _, newb := range newstate.Branches { 700 newBranchMap[newb.Name] = true 701 } 702 703 if !newBranchMap[branchNotToDelete] { 704 t.Errorf("project %q should contain branch %q", localProject.Name, branchNotToDelete) 705 } 706 707 if dontdelete && !newBranchMap[branchToDelete1] { 708 t.Errorf("project %q should contain branch %q", localProject.Name, branchToDelete1) 709 } 710 711 if !dontdelete && newBranchMap[branchToDelete1] { 712 t.Errorf("project %q should not contain branch %q", localProject.Name, branchToDelete1) 713 } 714 } 715 716 } 717 718 func equalBranchOut(first, second string) bool { 719 second = strings.TrimSpace(second) 720 firstStrings := strings.Split(first, "\n") 721 secondStrings := strings.Split(second, "\n") 722 if len(firstStrings) != len(secondStrings) { 723 return false 724 } 725 sort.Strings(firstStrings) 726 sort.Strings(secondStrings) 727 for i, first := range firstStrings { 728 if first != secondStrings[i] { 729 return false 730 } 731 } 732 return true 733 } 734 735 func equalDefaultBranchOut(first, second string) bool { 736 second = strings.TrimSpace(second) 737 firstStrings := strings.Split(first, "\n\n") 738 secondStrings := strings.Split(second, "\n\n") 739 if len(firstStrings) != len(secondStrings) { 740 return false 741 } 742 sort.Strings(firstStrings) 743 sort.Strings(secondStrings) 744 for i, first := range firstStrings { 745 if first != secondStrings[i] { 746 return false 747 } 748 } 749 return true 750 } 751 752 func executeBranch(t *testing.T, fake *jiritest.FakeJiriRoot, args ...string) string { 753 stderr := "" 754 runCmd := func() { 755 if err := runBranch(fake.X, args); err != nil { 756 stderr = err.Error() 757 } 758 } 759 stdout, _, err := runfunc(runCmd) 760 if err != nil { 761 t.Fatal(err) 762 } 763 return strings.TrimSpace(strings.Join([]string{stdout, stderr}, " ")) 764 }