github.com/btwiuse/jiri@v0.0.0-20191125065820-53353bcfef54/project/project_test.go (about) 1 // Copyright 2015 The Vanadium 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 project_test 6 7 import ( 8 "bytes" 9 "fmt" 10 "io/ioutil" 11 "net/http" 12 "net/http/httptest" 13 "os" 14 "path/filepath" 15 "reflect" 16 "sort" 17 "strconv" 18 "strings" 19 "testing" 20 21 "github.com/btwiuse/jiri" 22 "github.com/btwiuse/jiri/cipd" 23 "github.com/btwiuse/jiri/gitutil" 24 "github.com/btwiuse/jiri/jiritest" 25 "github.com/btwiuse/jiri/jiritest/xtest" 26 "github.com/btwiuse/jiri/project" 27 ) 28 29 func dirExists(dirname string) error { 30 fileInfo, err := os.Stat(dirname) 31 if err != nil { 32 return err 33 } 34 if !fileInfo.IsDir() { 35 return os.ErrNotExist 36 } 37 return nil 38 } 39 40 func fileExists(dirname string) error { 41 fileInfo, err := os.Stat(dirname) 42 if err != nil { 43 return err 44 } 45 if fileInfo.IsDir() { 46 return os.ErrNotExist 47 } 48 return nil 49 } 50 51 func checkReadme(t *testing.T, jirix *jiri.X, p project.Project, message string) { 52 if _, err := os.Stat(p.Path); err != nil { 53 t.Fatalf("%v", err) 54 } 55 readmeFile := filepath.Join(p.Path, "README") 56 data, err := ioutil.ReadFile(readmeFile) 57 if err != nil { 58 t.Fatalf("ReadFile(%v) failed: %v", readmeFile, err) 59 } 60 if got, want := data, []byte(message); bytes.Compare(got, want) != 0 { 61 t.Fatalf("unexpected content in project %v:\ngot\n%s\nwant\n%s\n", p.Name, got, want) 62 } 63 } 64 65 func checkJiriRevFiles(t *testing.T, jirix *jiri.X, p project.Project) { 66 fake, cleanup := jiritest.NewFakeJiriRoot(t) 67 defer cleanup() 68 69 g := gitutil.New(fake.X, gitutil.RootDirOpt(p.Path)) 70 71 file := filepath.Join(p.Path, ".git", "JIRI_HEAD") 72 data, err := ioutil.ReadFile(file) 73 if err != nil { 74 t.Fatalf("ReadFile(%v) failed: %s", file, err) 75 } 76 headFileContents := string(data) 77 headFileCommit, err := g.CurrentRevisionForRef(headFileContents) 78 if err != nil { 79 t.Fatalf("CurrentRevisionForRef failed: %s", err) 80 } 81 82 projectRevision := p.Revision 83 if projectRevision == "" { 84 if p.RemoteBranch == "" { 85 projectRevision = "origin/master" 86 } else { 87 projectRevision = "origin/" + p.RemoteBranch 88 } 89 } 90 revisionCommit, err := g.CurrentRevisionForRef(projectRevision) 91 if err != nil { 92 t.Fatalf("CurrentRevisionForRef failed: %s", err) 93 } 94 95 if revisionCommit != headFileCommit { 96 t.Fatalf("JIRI_HEAD contains %s (%s) expected %s (%s)", headFileContents, headFileCommit, projectRevision, revisionCommit) 97 } 98 file = filepath.Join(p.Path, ".git", "JIRI_LAST_BASE") 99 data, err = ioutil.ReadFile(file) 100 if err != nil { 101 t.Fatalf("ReadFile(%v) failed: %s", file, err) 102 } 103 if rev, err := g.CurrentRevision(); err != nil { 104 t.Fatalf("CurrentRevision() failed: %s", err) 105 } else if rev != string(data) { 106 t.Fatalf("JIRI_LAST_BASE contains %s expected %s", string(data), rev) 107 } 108 } 109 110 func commitFile(t *testing.T, jirix *jiri.X, dir, file, msg string) { 111 cwd, err := os.Getwd() 112 if err != nil { 113 t.Fatal(err) 114 } 115 defer os.Chdir(cwd) 116 if err := os.Chdir(dir); err != nil { 117 t.Fatal(err) 118 } 119 if err := gitutil.New(jirix, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com")).CommitFile(file, msg); err != nil { 120 t.Fatal(err) 121 } 122 } 123 124 func projectName(i int) string { 125 return fmt.Sprintf("project-%d", i) 126 } 127 128 func writeUncommitedFile(t *testing.T, jirix *jiri.X, projectDir, fileName, message string) string { 129 path, perm := filepath.Join(projectDir, fileName), os.FileMode(0644) 130 if err := ioutil.WriteFile(path, []byte(message), perm); err != nil { 131 t.Fatalf("WriteFile(%v, %v) failed: %v", path, perm, err) 132 } 133 return path 134 } 135 func writeFile(t *testing.T, jirix *jiri.X, projectDir, fileName, message string) { 136 path := writeUncommitedFile(t, jirix, projectDir, fileName, message) 137 commitFile(t, jirix, projectDir, path, "creating "+fileName) 138 } 139 140 func writeReadme(t *testing.T, jirix *jiri.X, projectDir, message string) { 141 writeFile(t, jirix, projectDir, "README", message) 142 } 143 144 func checkProjectsMatchPaths(t *testing.T, gotProjects project.Projects, wantProjectPaths []string) { 145 gotProjectPaths := []string{} 146 for _, p := range gotProjects { 147 gotProjectPaths = append(gotProjectPaths, p.Path) 148 } 149 sort.Strings(gotProjectPaths) 150 sort.Strings(wantProjectPaths) 151 if !reflect.DeepEqual(gotProjectPaths, wantProjectPaths) { 152 t.Errorf("project paths got %v, want %v", gotProjectPaths, wantProjectPaths) 153 } 154 } 155 156 // TestLocalProjects tests the behavior of the LocalProjects method with 157 // different ScanModes. 158 func TestLocalProjects(t *testing.T) { 159 jirix, cleanup := xtest.NewX(t) 160 defer cleanup() 161 162 // Create some projects. 163 numProjects, projectPaths := 3, []string{} 164 for i := 0; i < numProjects; i++ { 165 name := projectName(i) 166 path := filepath.Join(jirix.Root, name) 167 if err := os.MkdirAll(path, 0755); err != nil { 168 t.Fatal(err) 169 } 170 171 // Initialize empty git repository. The commit is necessary, otherwise 172 // "git rev-parse master" fails. 173 git := gitutil.New(jirix, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(path)) 174 if err := git.Init(path); err != nil { 175 t.Fatal(err) 176 } 177 if err := git.Commit(); err != nil { 178 t.Fatal(err) 179 } 180 181 // Write project metadata. 182 p := project.Project{ 183 Path: path, 184 Name: name, 185 } 186 if err := project.InternalWriteMetadata(jirix, p, path); err != nil { 187 t.Fatalf("writeMetadata %v %v) failed: %v\n", p, path, err) 188 } 189 projectPaths = append(projectPaths, path) 190 } 191 192 // Create a latest update snapshot but only tell it about the first project. 193 manifest := project.Manifest{ 194 Version: project.ManifestVersion, 195 Projects: []project.Project{ 196 { 197 Name: projectName(0), 198 Path: projectPaths[0], 199 }, 200 }, 201 } 202 if err := os.MkdirAll(jirix.UpdateHistoryDir(), 0755); err != nil { 203 t.Fatalf("MkdirAll(%v) failed: %v", jirix.UpdateHistoryDir(), err) 204 } 205 if err := manifest.ToFile(jirix, jirix.UpdateHistoryLatestLink()); err != nil { 206 t.Fatalf("manifest.ToFile(%v) failed: %v", jirix.UpdateHistoryLatestLink(), err) 207 } 208 209 // LocalProjects with scanMode = FastScan should only find the first 210 // project. 211 foundProjects, err := project.LocalProjects(jirix, project.FastScan) 212 if err != nil { 213 t.Fatalf("LocalProjects(%v) failed: %v", project.FastScan, err) 214 } 215 checkProjectsMatchPaths(t, foundProjects, projectPaths[:1]) 216 217 // LocalProjects with scanMode = FullScan should find all projects. 218 foundProjects, err = project.LocalProjects(jirix, project.FullScan) 219 if err != nil { 220 t.Fatalf("LocalProjects(%v) failed: %v", project.FastScan, err) 221 } 222 checkProjectsMatchPaths(t, foundProjects, projectPaths[:]) 223 224 // Check that deleting a project forces LocalProjects to run a full scan, 225 // even if FastScan is specified. 226 if err := os.RemoveAll(projectPaths[0]); err != nil { 227 t.Fatalf("RemoveAll(%s) failed: %s", projectPaths[0], err) 228 } 229 foundProjects, err = project.LocalProjects(jirix, project.FastScan) 230 if err != nil { 231 t.Fatalf("LocalProjects(%v) failed: %v", project.FastScan, err) 232 } 233 checkProjectsMatchPaths(t, foundProjects, projectPaths[1:]) 234 } 235 236 // setupUniverse creates a fake jiri root with 3 remote projects. Each project 237 // has a README with text "initial readme". 238 func setupUniverse(t *testing.T) ([]project.Project, *jiritest.FakeJiriRoot, func()) { 239 fake, cleanup := jiritest.NewFakeJiriRoot(t) 240 success := false 241 defer func() { 242 if !success { 243 cleanup() 244 } 245 }() 246 247 // Create some projects and add them to the remote manifest. 248 numProjects := 7 249 localProjects := []project.Project{} 250 for i := 0; i < numProjects; i++ { 251 name := projectName(i) 252 path := fmt.Sprintf("path-%d", i) 253 if err := fake.CreateRemoteProject(name); err != nil { 254 t.Fatal(err) 255 } 256 p := project.Project{ 257 Name: name, 258 Path: filepath.Join(fake.X.Root, path), 259 Remote: fake.Projects[name], 260 } 261 localProjects = append(localProjects, p) 262 } 263 localProjects[2].HistoryDepth = 1 264 localProjects[3].Path = filepath.Join(localProjects[2].Path, "path-3") 265 localProjects[4].Path = filepath.Join(localProjects[3].Path, "path-4") 266 localProjects[5].Path = filepath.Join(localProjects[2].Path, "path-5") 267 localProjects[6].Path = filepath.Join(localProjects[0].Path, "path-6") 268 for _, p := range localProjects { 269 if err := fake.AddProject(p); err != nil { 270 t.Fatal(err) 271 } 272 } 273 274 // Create initial commit in each repo. 275 for _, remoteProjectDir := range fake.Projects { 276 writeReadme(t, fake.X, remoteProjectDir, "initial readme") 277 } 278 writeFile(t, fake.X, fake.Projects[localProjects[2].Name], ".gitignore", "path-3/\npath-5/\n") 279 writeFile(t, fake.X, fake.Projects[localProjects[0].Name], ".gitignore", "path-6/\n") 280 writeFile(t, fake.X, fake.Projects[localProjects[3].Name], ".gitignore", "path-4/\n") 281 282 success = true 283 return localProjects, fake, cleanup 284 } 285 286 // TestUpdateUniverseSimple tests that UpdateUniverse will pull remote projects 287 // locally, and that jiri metadata is ignored in the repos. 288 func TestUpdateUniverseSimple(t *testing.T) { 289 localProjects, fake, cleanup := setupUniverse(t) 290 defer cleanup() 291 292 // Check that calling UpdateUniverse() creates local copies of the remote 293 // repositories. 294 if err := fake.UpdateUniverse(false); err != nil { 295 t.Fatal(err) 296 } 297 for _, p := range localProjects { 298 if err := dirExists(p.Path); err != nil { 299 t.Fatalf("expected project to exist at path %q but none found", p.Path) 300 } 301 if branches, _, err := gitutil.New(fake.X, gitutil.RootDirOpt(p.Path)).GetBranches(); err != nil { 302 t.Fatal(err) 303 } else if len(branches) != 0 { 304 t.Fatalf("expected project %s(%s) to contain no branches but it contains %s", p.Name, p.Path, branches) 305 } 306 checkReadme(t, fake.X, p, "initial readme") 307 checkJiriRevFiles(t, fake.X, p) 308 } 309 } 310 311 func TestUpdateUniverseWhenLocalTracksLocal(t *testing.T) { 312 localProjects, fake, cleanup := setupUniverse(t) 313 defer cleanup() 314 315 // Check that calling UpdateUniverse() creates local copies of the remote 316 // repositories, and that jiri metadata is ignored by git. 317 if err := fake.UpdateUniverse(false); err != nil { 318 t.Fatal(err) 319 } 320 321 gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Path)) 322 gitLocal.CreateBranchWithUpstream("A", "origin/master") 323 gitLocal.CreateBranch("B") 324 gitLocal.SetUpstream("B", "A") 325 writeFile(t, fake.X, fake.Projects[localProjects[1].Name], "file1", "file1") 326 gitRemote := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProjects[1].Name])) 327 remoteRev, _ := gitRemote.CurrentRevision() 328 if err := project.UpdateUniverse(fake.X, false, false, false, false, true /*rebase-all*/, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout); err != nil { 329 t.Fatal(err) 330 } 331 projects, err := project.LocalProjects(fake.X, project.FastScan) 332 if err != nil { 333 t.Fatal(err) 334 } 335 states, err := project.GetProjectStates(fake.X, projects, false) 336 if err != nil { 337 t.Fatal(err) 338 } 339 state := states[localProjects[1].Key()] 340 for _, b := range state.Branches { 341 if b.Revision != remoteRev { 342 t.Fatalf("Branch %q should have rev %q, instead it has %q", b.Name, remoteRev, b.Revision) 343 } 344 } 345 } 346 347 func TestUpdateUniverseWhenLocalTracksEachOther(t *testing.T) { 348 localProjects, fake, cleanup := setupUniverse(t) 349 defer cleanup() 350 351 // Check that calling UpdateUniverse() creates local copies of the remote 352 // repositories, and that jiri metadata is ignored by git. 353 if err := fake.UpdateUniverse(false); err != nil { 354 t.Fatal(err) 355 } 356 357 gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Path)) 358 gitLocal.CreateBranch("A") 359 gitLocal.CreateBranch("B") 360 gitLocal.SetUpstream("B", "A") 361 gitLocal.SetUpstream("A", "B") 362 363 gitRemote := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProjects[1].Name])) 364 oldRemoteRev, _ := gitRemote.CurrentRevision() 365 writeFile(t, fake.X, fake.Projects[localProjects[1].Name], "file1", "file1") 366 remoteRev, _ := gitRemote.CurrentRevision() 367 368 if err := project.UpdateUniverse(fake.X, false, false, false, false, true /*rebase-all*/, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout); err != nil { 369 t.Fatal(err) 370 } 371 projects, err := project.LocalProjects(fake.X, project.FastScan) 372 if err != nil { 373 t.Fatal(err) 374 } 375 states, err := project.GetProjectStates(fake.X, projects, false) 376 if err != nil { 377 t.Fatal(err) 378 } 379 state := states[localProjects[1].Key()] 380 for _, b := range state.Branches { 381 expectedRev := oldRemoteRev 382 if b.Name == "" { 383 expectedRev = remoteRev 384 } 385 if b.Revision != expectedRev { 386 t.Fatalf("Branch %q should have rev %q, instead it has %q", b.Name, expectedRev, b.Revision) 387 } 388 } 389 } 390 391 // TestOldMetaDirIsMovedOnUpdate tests that old metadir os moved to new 392 // location on update and projects are updated properly 393 func TestOldMetaDirIsMovedOnUpdate(t *testing.T) { 394 localProjects, fake, cleanup := setupUniverse(t) 395 defer cleanup() 396 397 if err := fake.UpdateUniverse(false); err != nil { 398 t.Fatal(err) 399 } 400 for i, p := range localProjects { 401 oldPath := filepath.Join(p.Path, jiri.OldProjectMetaDir) 402 newPath := filepath.Join(p.Path, jiri.ProjectMetaDir) 403 404 // move new path to old path to replicate old structure 405 if err := os.Rename(newPath, oldPath); err != nil { 406 t.Fatal(err) 407 } 408 if i != 1 { 409 writeReadme(t, fake.X, fake.Projects[p.Name], "new readme") 410 } 411 } 412 if err := fake.UpdateUniverse(false); err != nil { 413 t.Fatal(err) 414 } 415 for i, p := range localProjects { 416 newPath := filepath.Join(p.Path, jiri.ProjectMetaDir) 417 if err := dirExists(newPath); err != nil { 418 t.Fatalf("expected metadata to exist at path %q but none found", newPath) 419 } 420 // Check all projects are at latest 421 if i != 1 { 422 checkReadme(t, fake.X, p, "new readme") 423 } else { 424 checkReadme(t, fake.X, p, "initial readme") 425 } 426 } 427 } 428 429 // TestUpdateUniverseWithCache checks that UpdateUniverse can clone and pull 430 // from a cache. 431 func TestUpdateUniverseWithCache(t *testing.T) { 432 localProjects, fake, cleanup := setupUniverse(t) 433 defer cleanup() 434 435 // Create cache directory 436 cacheDir, err := ioutil.TempDir("", "cache") 437 if err != nil { 438 t.Fatalf("TempDir() failed: %v", err) 439 } 440 if err := os.MkdirAll(cacheDir, os.FileMode(0700)); err != nil { 441 t.Fatal(err) 442 } 443 defer func() { 444 if err := os.RemoveAll(cacheDir); err != nil { 445 t.Fatalf("RemoveAll(%q) failed: %v", cacheDir, err) 446 } 447 }() 448 fake.X.Cache = cacheDir 449 450 if err := fake.UpdateUniverse(false); err != nil { 451 t.Fatal(err) 452 } 453 for _, p := range localProjects { 454 // Check that local clone was referenced from cache 455 err := fileExists(p.Path + "/.git/objects/info/alternates") 456 if p.HistoryDepth == 0 { 457 if err != nil { 458 t.Fatalf("expected %v to exist, but not found", p.Path+"/.git/objects/info/alternates") 459 } 460 } else if err == nil { 461 t.Fatalf("expected %v to not exist, but found", p.Path+"/.git/objects/info/alternates") 462 } 463 checkReadme(t, fake.X, p, "initial readme") 464 checkJiriRevFiles(t, fake.X, p) 465 } 466 467 // Commit to master branch of a project 1. 468 writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "master commit") 469 470 gitRemote := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Remote)) 471 remoteRev, err := gitRemote.CurrentRevision() 472 if err != nil { 473 t.Fatal(err) 474 } 475 476 // Update the manifest with our new HEAD position. 477 fake.AddProjectOverride(localProjects[1].Name, localProjects[1].Remote, remoteRev) 478 if err := fake.UpdateUniverse(false); err != nil { 479 t.Fatal(err) 480 } 481 482 checkReadme(t, fake.X, localProjects[1], "master commit") 483 checkJiriRevFiles(t, fake.X, localProjects[1]) 484 485 // Check that cache was updated 486 cacheDirPath, err := localProjects[1].CacheDirPath(fake.X) 487 if err != nil { 488 t.Fatal(err) 489 } 490 gCache := gitutil.New(fake.X, gitutil.RootDirOpt(cacheDirPath)) 491 cacheRev, err := gCache.CurrentRevision() 492 if err != nil { 493 t.Fatal(err) 494 } 495 if cacheRev != remoteRev { 496 t.Fatalf("Cache revision(%v) not equal to local revision(%v)", cacheRev, remoteRev) 497 } 498 } 499 500 func TestProjectUpdateWhenNoUpdate(t *testing.T) { 501 localProjects, fake, cleanup := setupUniverse(t) 502 defer cleanup() 503 if err := fake.UpdateUniverse(false); err != nil { 504 t.Fatal(err) 505 } 506 507 lc := project.LocalConfig{NoUpdate: true} 508 project.WriteLocalConfig(fake.X, localProjects[1], lc) 509 // Commit to master branch of a project 1. 510 writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "master commit") 511 gitRemote := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProjects[1].Name])) 512 remoteRev, _ := gitRemote.CurrentRevision() 513 if err := fake.UpdateUniverse(false); err != nil { 514 t.Fatal(err) 515 } 516 517 gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Path)) 518 localRev, _ := gitLocal.CurrentRevision() 519 520 if remoteRev == localRev { 521 t.Fatal("local project should not be updated") 522 } 523 } 524 525 func TestRecursiveImport(t *testing.T) { 526 localProjects, fake, cleanup := setupUniverse(t) 527 defer cleanup() 528 529 manifest, err := fake.ReadRemoteManifest() 530 if err != nil { 531 t.Fatal(err) 532 } 533 534 // Remove last project from manifest 535 lastProject := manifest.Projects[len(manifest.Projects)-1] 536 manifest.Projects = manifest.Projects[:len(manifest.Projects)-1] 537 remoteManifestStr := "remotemanifest" 538 if err := fake.CreateRemoteProject(remoteManifestStr); err != nil { 539 t.Fatal(err) 540 } 541 // Fix last projet rev 542 lastPRev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[lastProject.Name])).CurrentRevision() 543 lastProject.Revision = lastPRev 544 remoteManifest := &project.Manifest{ 545 Projects: []project.Project{lastProject, project.Project{ 546 Name: remoteManifestStr, 547 Path: remoteManifestStr, 548 Remote: fake.Projects[remoteManifestStr], 549 }}, 550 } 551 remoteManifestFile := filepath.Join(fake.Projects[remoteManifestStr], "manifest") 552 if err := remoteManifest.ToFile(fake.X, remoteManifestFile); err != nil { 553 t.Fatal(err) 554 } 555 commitFile(t, fake.X, fake.Projects[remoteManifestStr], "manifest", "1") 556 rev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[remoteManifestStr])).CurrentRevision() 557 558 // unpin last project in next commit 559 remoteManifest.Projects[0].Revision = "" 560 if err := remoteManifest.ToFile(fake.X, remoteManifestFile); err != nil { 561 t.Fatal(err) 562 } 563 commitFile(t, fake.X, fake.Projects[remoteManifestStr], "manifest", "2") 564 writeFile(t, fake.X, fake.Projects[lastProject.Name], "file1", "file1") 565 manifest.Imports = []project.Import{project.Import{ 566 Name: remoteManifestStr, 567 Remote: fake.Projects[remoteManifestStr], 568 Manifest: "manifest", 569 Revision: rev, 570 }} 571 fake.WriteRemoteManifest(manifest) 572 if err := fake.UpdateUniverse(false); err != nil { 573 t.Fatal(err) 574 } 575 576 // check all local projects 577 for _, p := range localProjects { 578 if err := dirExists(p.Path); err != nil { 579 t.Fatalf("expected project to exist at path %q but none found", p.Path) 580 } 581 checkReadme(t, fake.X, p, "initial readme") 582 } 583 584 // check that remotemanifest is at correct revision 585 remoteManifestPath := filepath.Join(fake.X.Root, remoteManifestStr) 586 if err := dirExists(remoteManifestPath); err != nil { 587 t.Fatalf("expected project to exist at path %q but none found", remoteManifestPath) 588 } 589 currentRev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(remoteManifestPath)).CurrentRevision() 590 if currentRev != rev { 591 t.Fatalf("For project remotemanifest expected rev to be %q got %q", rev, currentRev) 592 } 593 // check last project revision 594 currentRev, _ = gitutil.New(fake.X, gitutil.RootDirOpt(filepath.Join(fake.X.Root, lastProject.Path))).CurrentRevision() 595 if currentRev != lastPRev { 596 t.Fatalf("For project %q expected rev to be %q got %q", lastProject.Name, lastPRev, currentRev) 597 } 598 599 //unpin import 600 manifest.Imports[0].Revision = "" 601 fake.WriteRemoteManifest(manifest) 602 if err := fake.UpdateUniverse(false); err != nil { 603 t.Fatal(err) 604 } 605 606 //check that projects advances 607 currentRev, _ = gitutil.New(fake.X, gitutil.RootDirOpt(remoteManifestPath)).CurrentRevision() 608 if currentRev == rev { 609 t.Fatalf("For project remotemanifest expected rev to NOT be %q", rev) 610 } 611 // check last project revision 612 currentRev, _ = gitutil.New(fake.X, gitutil.RootDirOpt(filepath.Join(fake.X.Root, lastProject.Path))).CurrentRevision() 613 if currentRev == lastPRev { 614 t.Fatalf("For project %q expected rev to NOT be %q", lastProject.Name, lastPRev) 615 } 616 } 617 618 func TestLoadManifestFileRecursiveImport(t *testing.T) { 619 _, fake, cleanup := setupUniverse(t) 620 defer cleanup() 621 622 manifest, err := fake.ReadRemoteManifest() 623 if err != nil { 624 t.Fatal(err) 625 } 626 627 // Remove last project from manifest 628 lastProject := manifest.Projects[len(manifest.Projects)-1] 629 manifest.Projects = manifest.Projects[:len(manifest.Projects)-1] 630 remoteManifestStr := "remotemanifest" 631 if err := fake.CreateRemoteProject(remoteManifestStr); err != nil { 632 t.Fatal(err) 633 } 634 635 remoteManifest := &project.Manifest{ 636 Projects: []project.Project{lastProject, project.Project{ 637 Name: remoteManifestStr, 638 Path: remoteManifestStr, 639 Remote: fake.Projects[remoteManifestStr], 640 }}, 641 } 642 remoteManifestFile := filepath.Join(fake.Projects[remoteManifestStr], "manifest") 643 if err := remoteManifest.ToFile(fake.X, remoteManifestFile); err != nil { 644 t.Fatal(err) 645 } 646 commitFile(t, fake.X, fake.Projects[remoteManifestStr], "manifest", "1") 647 rev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[remoteManifestStr])).CurrentRevision() 648 649 manifest.Imports = []project.Import{project.Import{ 650 Name: remoteManifestStr, 651 Remote: fake.Projects[remoteManifestStr], 652 Manifest: "manifest", 653 Revision: rev, 654 }} 655 fake.WriteRemoteManifest(manifest) 656 if err := fake.UpdateUniverse(false); err != nil { 657 t.Fatal(err) 658 } 659 660 // Write arbitrary revision 661 manifest.Imports[0].Revision = "AB" 662 fake.WriteRemoteManifest(manifest) 663 664 // local fetch on manifest project 665 gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(filepath.Join(fake.X.Root, jiritest.ManifestProjectPath))) 666 if err := gitLocal.Fetch("origin"); err != nil { 667 t.Fatal(err) 668 } 669 localProjects, err := project.LocalProjects(fake.X, project.FastScan) 670 if err != nil { 671 t.Fatal(err) 672 } 673 if _, _, _, err := project.LoadManifestFile(fake.X, fake.X.JiriManifestFile(), localProjects, false); err != nil { 674 t.Fatal(err) 675 } 676 } 677 678 func TestRecursiveImportWithLocalImport(t *testing.T) { 679 _, fake, cleanup := setupUniverse(t) 680 defer cleanup() 681 682 manifest, err := fake.ReadRemoteManifest() 683 if err != nil { 684 t.Fatal(err) 685 } 686 687 // Remove last project from manifest 688 lastProject := manifest.Projects[len(manifest.Projects)-1] 689 manifest.Projects = manifest.Projects[:len(manifest.Projects)-1] 690 remoteManifestStr := "remotemanifest" 691 if err := fake.CreateRemoteProject(remoteManifestStr); err != nil { 692 t.Fatal(err) 693 } 694 // Fix last project rev 695 lastPRev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[lastProject.Name])).CurrentRevision() 696 lastProject.Revision = lastPRev 697 remoteManifest := &project.Manifest{ 698 Projects: []project.Project{lastProject, project.Project{ 699 Name: remoteManifestStr, 700 Path: remoteManifestStr, 701 Remote: fake.Projects[remoteManifestStr], 702 }}, 703 } 704 remoteManifestFile := filepath.Join(fake.Projects[remoteManifestStr], "manifest") 705 if err := remoteManifest.ToFile(fake.X, remoteManifestFile); err != nil { 706 t.Fatal(err) 707 } 708 commitFile(t, fake.X, fake.Projects[remoteManifestStr], "manifest", "1") 709 rev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[remoteManifestStr])).CurrentRevision() 710 manifest.Imports = []project.Import{project.Import{ 711 Name: remoteManifestStr, 712 Remote: fake.Projects[remoteManifestStr], 713 Manifest: "manifest", 714 Revision: rev, 715 }} 716 717 // unpin last project in next commit 718 remoteManifest.Projects[0].Revision = "" 719 if err := remoteManifest.ToFile(fake.X, remoteManifestFile); err != nil { 720 t.Fatal(err) 721 } 722 commitFile(t, fake.X, fake.Projects[remoteManifestStr], "manifest", "2") 723 // get latest revision 724 rev, _ = gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[remoteManifestStr])).CurrentRevision() 725 writeFile(t, fake.X, fake.Projects[lastProject.Name], "file1", "file1") 726 // Get latest last project revision 727 lastPRev, _ = gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[lastProject.Name])).CurrentRevision() 728 fake.WriteRemoteManifest(manifest) 729 if err := fake.UpdateUniverse(false); err != nil { 730 t.Fatal(err) 731 } 732 733 // make local change in top level manifest and unpin remote manifest 734 manifest.Imports[0].Revision = "" 735 if err := manifest.ToFile(fake.X, filepath.Join(fake.X.Root, jiritest.ManifestProjectPath, jiritest.ManifestFileName)); err != nil { 736 t.Fatal(err) 737 } 738 if err := project.UpdateUniverse(fake.X, false, true /* localManifest */, false, false, false, false, false, project.DefaultHookTimeout, project.DefaultPackageTimeout); err != nil { 739 t.Fatal(err) 740 } 741 742 remoteManifestPath := filepath.Join(fake.X.Root, remoteManifestStr) 743 currentRev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(remoteManifestPath)).CurrentRevision() 744 if currentRev != rev { 745 t.Fatalf("For project remotemanifest expected rev to be %q got %q", rev, currentRev) 746 } 747 // check last project revision 748 currentRev, _ = gitutil.New(fake.X, gitutil.RootDirOpt(filepath.Join(fake.X.Root, lastProject.Path))).CurrentRevision() 749 if currentRev != lastPRev { 750 t.Fatalf("For project %q expected rev to be %q got %q", lastProject.Name, lastPRev, currentRev) 751 } 752 } 753 754 func TestRecursiveImportWhenOriginalManifestIsImportedAgain(t *testing.T) { 755 _, fake, cleanup := setupUniverse(t) 756 defer cleanup() 757 758 manifest, err := fake.ReadRemoteManifest() 759 if err != nil { 760 t.Fatal(err) 761 } 762 763 // Remove last project from manifest and add it to local import 764 lastProject := manifest.Projects[len(manifest.Projects)-1] 765 manifest.Projects = manifest.Projects[:len(manifest.Projects)-1] 766 manifest.LocalImports = []project.LocalImport{project.LocalImport{ 767 File: "localmanifest", 768 }} 769 localManifest := project.Manifest{ 770 Projects: []project.Project{lastProject}, 771 } 772 localManifestFile := filepath.Join(fake.Projects[jiritest.ManifestProjectName], "localmanifest") 773 if err := localManifest.ToFile(fake.X, localManifestFile); err != nil { 774 t.Fatal(err) 775 } 776 commitFile(t, fake.X, fake.Projects[jiritest.ManifestProjectName], "localmanifest", "1") 777 778 remoteManifestStr := "remotemanifest" 779 if err := fake.CreateRemoteProject(remoteManifestStr); err != nil { 780 t.Fatal(err) 781 } 782 manifest.Imports = []project.Import{project.Import{ 783 Name: remoteManifestStr, 784 Remote: fake.Projects[remoteManifestStr], 785 Manifest: "manifest", 786 }} 787 fake.WriteRemoteManifest(manifest) 788 789 // Fix last project rev 790 remoteManifest := &project.Manifest{ 791 Projects: []project.Project{project.Project{ 792 Name: remoteManifestStr, 793 Path: remoteManifestStr, 794 Remote: fake.Projects[remoteManifestStr], 795 }}, 796 Imports: []project.Import{project.Import{ 797 Name: jiritest.ManifestProjectName, 798 Remote: fake.Projects[jiritest.ManifestProjectName], 799 Manifest: "localmanifest", 800 }}, 801 } 802 remoteManifestFile := filepath.Join(fake.Projects[remoteManifestStr], "manifest") 803 if err := remoteManifest.ToFile(fake.X, remoteManifestFile); err != nil { 804 t.Fatal(err) 805 } 806 commitFile(t, fake.X, fake.Projects[remoteManifestStr], "manifest", "1") 807 808 if err := fake.UpdateUniverse(false); err != nil { 809 t.Fatal(err) 810 } 811 //pin last project and don't commit 812 lastPRev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[lastProject.Name])).CurrentRevision() 813 localManifest.Projects[0].Revision = lastPRev 814 if err := localManifest.ToFile(fake.X, filepath.Join(fake.X.Root, jiritest.ManifestProjectPath, "localmanifest")); err != nil { 815 t.Fatal(err) 816 } 817 818 // Add new commit to last project 819 writeFile(t, fake.X, fake.Projects[lastProject.Name], "file1", "file1") 820 if err := project.UpdateUniverse(fake.X, false, true /* localManifest */, false, false, false, false, false, project.DefaultHookTimeout, project.DefaultPackageTimeout); err != nil { 821 t.Fatal(err) 822 } 823 // check last project revision 824 currentRev, _ := gitutil.New(fake.X, gitutil.RootDirOpt(filepath.Join(fake.X.Root, lastProject.Path))).CurrentRevision() 825 if currentRev != lastPRev { 826 t.Fatalf("For project %q expected rev to be %q got %q", lastProject.Name, lastPRev, currentRev) 827 } 828 } 829 830 func TestProjectUpdateWhenIgnore(t *testing.T) { 831 localProjects, fake, cleanup := setupUniverse(t) 832 defer cleanup() 833 if err := fake.UpdateUniverse(false); err != nil { 834 t.Fatal(err) 835 } 836 837 lc := project.LocalConfig{Ignore: true} 838 project.WriteLocalConfig(fake.X, localProjects[1], lc) 839 // Commit to master branch of a project 1. 840 writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "master commit") 841 gitRemote := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProjects[1].Name])) 842 remoteRev, _ := gitRemote.CurrentRevision() 843 if err := fake.UpdateUniverse(false); err != nil { 844 t.Fatal(err) 845 } 846 847 gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Path)) 848 localRev, _ := gitLocal.CurrentRevision() 849 850 if remoteRev == localRev { 851 t.Fatal("local project should not be updated") 852 } 853 } 854 855 func TestLocalProjectWithConfig(t *testing.T) { 856 localProjects, fake, cleanup := setupUniverse(t) 857 defer cleanup() 858 if err := fake.UpdateUniverse(false); err != nil { 859 t.Fatal(err) 860 } 861 project.WriteUpdateHistorySnapshot(fake.X, "", nil, nil, false) 862 863 lc := project.LocalConfig{Ignore: true} 864 project.WriteLocalConfig(fake.X, localProjects[1], lc) 865 scanModes := []project.ScanMode{project.FullScan, project.FastScan} 866 for _, scanMode := range scanModes { 867 newLocalProjects, err := project.LocalProjects(fake.X, scanMode) 868 if err != nil { 869 t.Fatal(err) 870 } 871 for k, p := range newLocalProjects { 872 expectedIgnore := k == localProjects[1].Key() 873 if p.LocalConfig.Ignore != expectedIgnore { 874 t.Errorf("local config ignore: got %t, want %t", p.LocalConfig.Ignore, expectedIgnore) 875 } 876 877 if p.LocalConfig.NoUpdate != false { 878 t.Errorf("local config no-update: got %t, want %t", p.LocalConfig.NoUpdate, false) 879 } 880 881 if p.LocalConfig.NoRebase != false { 882 t.Errorf("local config no-rebase: got %t, want %t", p.LocalConfig.NoUpdate, false) 883 } 884 } 885 } 886 } 887 888 func TestProjectUpdateWhenNoRebase(t *testing.T) { 889 localProjects, fake, cleanup := setupUniverse(t) 890 defer cleanup() 891 if err := fake.UpdateUniverse(false); err != nil { 892 t.Fatal(err) 893 } 894 895 lc := project.LocalConfig{NoRebase: true} 896 project.WriteLocalConfig(fake.X, localProjects[1], lc) 897 // Commit to master branch of a project 1. 898 writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "master commit") 899 gitRemote := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProjects[1].Name])) 900 remoteRev, _ := gitRemote.CurrentRevision() 901 if err := fake.UpdateUniverse(false); err != nil { 902 t.Fatal(err) 903 } 904 905 gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Path)) 906 localRev, _ := gitLocal.CurrentRevision() 907 908 if remoteRev != localRev { 909 t.Fatal("local project should be updated") 910 } 911 } 912 913 func TestBranchUpdateWhenNoRebase(t *testing.T) { 914 localProjects, fake, cleanup := setupUniverse(t) 915 defer cleanup() 916 917 if err := fake.UpdateUniverse(false); err != nil { 918 t.Fatal(err) 919 } 920 gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Path)) 921 gitLocal.CheckoutBranch("master") 922 923 lc := project.LocalConfig{NoRebase: true} 924 project.WriteLocalConfig(fake.X, localProjects[1], lc) 925 // Commit to master branch of a project 1. 926 writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "master commit") 927 gitRemote := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProjects[1].Name])) 928 remoteRev, _ := gitRemote.CurrentRevision() 929 if err := fake.UpdateUniverse(false); err != nil { 930 t.Fatal(err) 931 } 932 933 localRev, _ := gitLocal.CurrentRevision() 934 935 if remoteRev == localRev { 936 t.Fatal("local branch master should not be updated") 937 } 938 } 939 940 // TestHookLoadSimple tests that manifest is loaded correctly 941 // with correct project path in hook 942 func TestHookLoadSimple(t *testing.T) { 943 p, fake, cleanup := setupUniverse(t) 944 defer cleanup() 945 err := fake.AddHook(project.Hook{Name: "hook1", 946 Action: "action.sh", 947 ProjectName: p[0].Name}) 948 949 if err != nil { 950 t.Fatal(err) 951 } 952 err = fake.UpdateUniverse(false) 953 if err == nil { 954 t.Fatal("run hook should throw error as there is no action.sh script") 955 } 956 } 957 958 // TestRunHookFlag tests that hook is not executed when flag is false 959 func TestRunHookFlag(t *testing.T) { 960 p, fake, cleanup := setupUniverse(t) 961 defer cleanup() 962 err := fake.AddHook(project.Hook{Name: "hook1", 963 Action: "action.sh", 964 ProjectName: p[0].Name}) 965 966 if err != nil { 967 t.Fatal(err) 968 } 969 if err := project.UpdateUniverse(fake.X, false, false, true /*rebaseTracked*/, false, false, false /*run-hooks*/, false /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout); err != nil { 970 t.Fatal(err) 971 } 972 } 973 974 // TestHookLoadError tests that manifest load 975 // throws error for invalid hook 976 func TestHookLoadError(t *testing.T) { 977 _, fake, cleanup := setupUniverse(t) 978 defer cleanup() 979 err := fake.AddHook(project.Hook{Name: "hook1", 980 Action: "action", 981 ProjectName: "non-existant"}) 982 983 if err != nil { 984 t.Fatal(err) 985 } 986 err = fake.UpdateUniverse(false) 987 if err == nil { 988 t.Fatal("Update universe should throw error for the hook") 989 } 990 if !strings.Contains(err.Error(), "invalid hook") { 991 t.Fatal(err) 992 } 993 } 994 995 // TestUpdateUniverseWithRevision checks that UpdateUniverse will pull remote 996 // projects at the specified revision. 997 func TestUpdateUniverseWithRevision(t *testing.T) { 998 localProjects, fake, cleanup := setupUniverse(t) 999 defer cleanup() 1000 1001 // Set project 1's revision in the manifest to the current revision. 1002 g := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProjects[1].Name])) 1003 rev, err := g.CurrentRevision() 1004 if err != nil { 1005 t.Fatal(err) 1006 } 1007 m, err := fake.ReadRemoteManifest() 1008 if err != nil { 1009 t.Fatal(err) 1010 } 1011 projects := []project.Project{} 1012 for _, p := range m.Projects { 1013 if p.Name == localProjects[1].Name { 1014 p.Revision = rev 1015 } 1016 projects = append(projects, p) 1017 } 1018 m.Projects = projects 1019 if err := fake.WriteRemoteManifest(m); err != nil { 1020 t.Fatal(err) 1021 } 1022 // Update README in all projects. 1023 for _, remoteProjectDir := range fake.Projects { 1024 writeReadme(t, fake.X, remoteProjectDir, "new revision") 1025 } 1026 // Check that calling UpdateUniverse() updates all projects except for 1027 // project 1. 1028 if err := fake.UpdateUniverse(false); err != nil { 1029 t.Fatal(err) 1030 } 1031 for i, p := range localProjects { 1032 if i == 1 { 1033 checkReadme(t, fake.X, p, "initial readme") 1034 } else { 1035 checkReadme(t, fake.X, p, "new revision") 1036 } 1037 } 1038 } 1039 1040 // TestUpdateUniverseWithBadRevision checks that UpdateUniverse 1041 // will not leave bad state behind. 1042 //func TestUpdateUniverseWithBadRevision(t *testing.T) { 1043 // localProjects, fake, cleanup := setupUniverse(t) 1044 // defer cleanup() 1045 // 1046 // m, err := fake.ReadRemoteManifest() 1047 // if err != nil { 1048 // t.Fatal(err) 1049 // } 1050 // projects := []project.Project{} 1051 // for _, p := range m.Projects { 1052 // if p.Name == localProjects[1].Name { 1053 // p.Revision = "badrev" 1054 // } 1055 // projects = append(projects, p) 1056 // } 1057 // m.Projects = projects 1058 // if err := fake.WriteRemoteManifest(m); err != nil { 1059 // t.Fatal(err) 1060 // } 1061 // 1062 // if err := fake.UpdateUniverse(false); err == nil { 1063 // t.Fatal("should have thrown error") 1064 // } 1065 // 1066 // if err := dirExists(localProjects[1].Path); err == nil { 1067 // t.Fatalf("expected project %q at path %q not to exist but it did", localProjects[1].Name, localProjects[1].Path) 1068 // } 1069 // 1070 //} 1071 1072 func commitChanges(t *testing.T, jirix *jiri.X, dir string) { 1073 scm := gitutil.New(jirix, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(dir)) 1074 if err := scm.AddUpdatedFiles(); err != nil { 1075 t.Fatal(err) 1076 } 1077 if err := scm.Commit(); err != nil { 1078 t.Fatal(err) 1079 } 1080 } 1081 1082 // TestSubDirToNestedProj checks that UpdateUniverse will correctly update when 1083 // nested folder is converted to nested project 1084 func TestSubDirToNestedProj(t *testing.T) { 1085 localProjects, fake, cleanup := setupUniverse(t) 1086 defer cleanup() 1087 1088 folderName := "nested_folder" 1089 nestedFolderPath := filepath.Join(fake.Projects[localProjects[1].Name], folderName) 1090 os.MkdirAll(nestedFolderPath, os.FileMode(0755)) 1091 writeReadme(t, fake.X, nestedFolderPath, "nested folder") 1092 1093 if err := fake.UpdateUniverse(false); err != nil { 1094 t.Fatal(err) 1095 } 1096 os.RemoveAll(nestedFolderPath) 1097 commitChanges(t, fake.X, fake.Projects[localProjects[1].Name]) 1098 1099 // Create nested project 1100 if err := fake.CreateRemoteProject(folderName); err != nil { 1101 t.Fatal(err) 1102 } 1103 writeReadme(t, fake.X, fake.Projects[folderName], "nested folder") 1104 p := project.Project{ 1105 Name: folderName, 1106 Path: filepath.Join(localProjects[1].Path, folderName), 1107 Remote: fake.Projects[folderName], 1108 } 1109 if err := fake.AddProject(p); err != nil { 1110 t.Fatal(err) 1111 } 1112 1113 if err := fake.UpdateUniverse(false); err != nil { 1114 t.Fatal(err) 1115 } 1116 checkReadme(t, fake.X, p, "nested folder") 1117 } 1118 1119 // TestMoveNestedProjects checks that UpdateUniverse will correctly move nested projects 1120 func TestMoveNestedProjects(t *testing.T) { 1121 localProjects, fake, cleanup := setupUniverse(t) 1122 defer cleanup() 1123 1124 folderName := "nested_proj" 1125 // Create nested project 1126 if err := fake.CreateRemoteProject(folderName); err != nil { 1127 t.Fatal(err) 1128 } 1129 writeReadme(t, fake.X, fake.Projects[folderName], "nested folder") 1130 p := project.Project{ 1131 Name: folderName, 1132 Path: filepath.Join(localProjects[1].Path, folderName), 1133 Remote: fake.Projects[folderName], 1134 } 1135 if err := fake.AddProject(p); err != nil { 1136 t.Fatal(err) 1137 } 1138 1139 if err := fake.UpdateUniverse(false); err != nil { 1140 t.Fatal(err) 1141 } 1142 1143 oldProjectPath := localProjects[1].Path 1144 localProjects[1].Path = filepath.Join(fake.X.Root, "new-project-path") 1145 p.Path = filepath.Join(localProjects[1].Path, folderName) 1146 m, err := fake.ReadRemoteManifest() 1147 if err != nil { 1148 t.Fatal(err) 1149 } 1150 projects := []project.Project{} 1151 for _, proj := range m.Projects { 1152 if proj.Name == localProjects[1].Name { 1153 proj.Path = localProjects[1].Path 1154 } 1155 if proj.Name == p.Name { 1156 proj.Path = p.Path 1157 } 1158 projects = append(projects, proj) 1159 } 1160 m.Projects = projects 1161 if err := fake.WriteRemoteManifest(m); err != nil { 1162 t.Fatal(err) 1163 } 1164 1165 if err := fake.UpdateUniverse(false); err != nil { 1166 t.Fatal(err) 1167 } 1168 checkReadme(t, fake.X, localProjects[1], "initial readme") 1169 checkReadme(t, fake.X, p, "nested folder") 1170 if err := dirExists(oldProjectPath); err == nil { 1171 t.Fatalf("expected project %q at path %q not to exist but it did", localProjects[1].Name, oldProjectPath) 1172 } 1173 } 1174 1175 // TestUpdateUniverseWithUncommitted checks that uncommitted files are not droped 1176 // by UpdateUniverse(). This ensures that the "git reset --hard" mechanism used 1177 // for pointing the master branch to a fixed revision does not lose work in 1178 // progress. 1179 func TestUpdateUniverseWithUncommitted(t *testing.T) { 1180 localProjects, fake, cleanup := setupUniverse(t) 1181 defer cleanup() 1182 if err := fake.UpdateUniverse(false); err != nil { 1183 t.Fatal(err) 1184 } 1185 1186 // Create an uncommitted file in project 1. 1187 file, perm, want := filepath.Join(localProjects[1].Path, "uncommitted_file"), os.FileMode(0644), []byte("uncommitted work") 1188 if err := ioutil.WriteFile(file, want, perm); err != nil { 1189 t.Fatalf("WriteFile(%v, %v) failed: %v", file, err, perm) 1190 } 1191 if err := fake.UpdateUniverse(false); err != nil { 1192 t.Fatal(err) 1193 } 1194 got, err := ioutil.ReadFile(file) 1195 if err != nil { 1196 t.Fatalf("%v", err) 1197 } 1198 if bytes.Compare(got, want) != 0 { 1199 t.Fatalf("unexpected content %v:\ngot\n%s\nwant\n%s\n", localProjects[1], got, want) 1200 } 1201 } 1202 1203 // TestUpdateUniverseMovedProject checks that UpdateUniverse can move a 1204 // project. 1205 func TestUpdateUniverseMovedProject(t *testing.T) { 1206 localProjects, fake, cleanup := setupUniverse(t) 1207 defer cleanup() 1208 if err := fake.UpdateUniverse(false); err != nil { 1209 t.Fatal(err) 1210 } 1211 1212 // Update the local path at which project 1 is located. 1213 m, err := fake.ReadRemoteManifest() 1214 if err != nil { 1215 t.Fatal(err) 1216 } 1217 oldProjectPath := localProjects[1].Path 1218 localProjects[1].Path = filepath.Join(fake.X.Root, "new-project-path") 1219 projects := []project.Project{} 1220 for _, p := range m.Projects { 1221 if p.Name == localProjects[1].Name { 1222 p.Path = localProjects[1].Path 1223 } 1224 projects = append(projects, p) 1225 } 1226 m.Projects = projects 1227 if err := fake.WriteRemoteManifest(m); err != nil { 1228 t.Fatal(err) 1229 } 1230 // Check that UpdateUniverse() moves the local copy of the project 1. 1231 if err := fake.UpdateUniverse(false); err != nil { 1232 t.Fatal(err) 1233 } 1234 if err := dirExists(oldProjectPath); err == nil { 1235 t.Fatalf("expected project %q at path %q not to exist but it did", localProjects[1].Name, oldProjectPath) 1236 } 1237 if err := dirExists(localProjects[2].Path); err != nil { 1238 t.Fatalf("expected project %q at path %q to exist but it did not", localProjects[1].Name, localProjects[1].Path) 1239 } 1240 checkReadme(t, fake.X, localProjects[1], "initial readme") 1241 } 1242 1243 // TestUpdateUniverseChangeRemote checks that UpdateUniverse can change remote 1244 // of a project. 1245 func TestUpdateUniverseChangeRemote(t *testing.T) { 1246 localProjects, fake, cleanup := setupUniverse(t) 1247 defer cleanup() 1248 if err := fake.UpdateUniverse(false); err != nil { 1249 t.Fatal(err) 1250 } 1251 1252 changedRemoteDir := fake.Projects[localProjects[1].Name] + "-remote-changed" 1253 if err := os.Rename(fake.Projects[localProjects[1].Name], changedRemoteDir); err != nil { 1254 t.Fatal(err) 1255 } 1256 1257 writeReadme(t, fake.X, changedRemoteDir, "new commit") 1258 1259 // Update the local path at which project 1 is located. 1260 m, err := fake.ReadRemoteManifest() 1261 if err != nil { 1262 t.Fatal(err) 1263 } 1264 projects := []project.Project{} 1265 for _, p := range m.Projects { 1266 if p.Name == localProjects[1].Name { 1267 p.Remote = changedRemoteDir 1268 } 1269 projects = append(projects, p) 1270 } 1271 m.Projects = projects 1272 if err := fake.WriteRemoteManifest(m); err != nil { 1273 t.Fatal(err) 1274 } 1275 // Check that UpdateUniverse() moves the local copy of the project 1. 1276 if err := fake.UpdateUniverse(false); err != nil { 1277 t.Fatal(err) 1278 } 1279 checkReadme(t, fake.X, localProjects[1], "new commit") 1280 } 1281 1282 func TestIgnoredProjectsNotMoved(t *testing.T) { 1283 localProjects, fake, cleanup := setupUniverse(t) 1284 defer cleanup() 1285 if err := fake.UpdateUniverse(false); err != nil { 1286 t.Fatal(err) 1287 } 1288 1289 // Update the local path at which project 1 is located. 1290 m, err := fake.ReadRemoteManifest() 1291 if err != nil { 1292 t.Fatal(err) 1293 } 1294 lc := project.LocalConfig{Ignore: true} 1295 project.WriteLocalConfig(fake.X, localProjects[1], lc) 1296 oldProjectPath := localProjects[1].Path 1297 localProjects[1].Path = filepath.Join(fake.X.Root, "new-project-path") 1298 projects := []project.Project{} 1299 for _, p := range m.Projects { 1300 if p.Name == localProjects[1].Name { 1301 p.Path = localProjects[1].Path 1302 } 1303 projects = append(projects, p) 1304 } 1305 m.Projects = projects 1306 if err := fake.WriteRemoteManifest(m); err != nil { 1307 t.Fatal(err) 1308 } 1309 1310 // Check that UpdateUniverse() does not move the local copy of the project 1. 1311 if err := fake.UpdateUniverse(false); err != nil { 1312 t.Fatal(err) 1313 } 1314 if err := dirExists(oldProjectPath); err != nil { 1315 t.Fatalf("expected project %q at path %q to exist but it did not: %s", localProjects[1].Name, oldProjectPath, err) 1316 } 1317 if err := dirExists(localProjects[2].Path); err != nil { 1318 t.Fatalf("expected project %q at path %q to not exist but it did", localProjects[1].Name, localProjects[1].Path) 1319 } 1320 } 1321 1322 // TestUpdateUniverseRenamedProject checks that UpdateUniverse can update 1323 // renamed project. 1324 func TestUpdateUniverseRenamedProject(t *testing.T) { 1325 localProjects, fake, cleanup := setupUniverse(t) 1326 defer cleanup() 1327 if err := fake.UpdateUniverse(false); err != nil { 1328 t.Fatal(err) 1329 } 1330 1331 m, err := fake.ReadRemoteManifest() 1332 if err != nil { 1333 t.Fatal(err) 1334 } 1335 oldProjectName := localProjects[1].Name 1336 localProjects[1].Name = localProjects[1].Name + "new" 1337 projects := []project.Project{} 1338 for _, p := range m.Projects { 1339 if p.Name == oldProjectName { 1340 p.Name = localProjects[1].Name 1341 } 1342 projects = append(projects, p) 1343 } 1344 m.Projects = projects 1345 if err := fake.WriteRemoteManifest(m); err != nil { 1346 t.Fatal(err) 1347 } 1348 1349 if err := fake.UpdateUniverse(false); err != nil { 1350 t.Fatal(err) 1351 } 1352 newLocalProjects, err := project.LocalProjects(fake.X, project.FullScan) 1353 if err != nil { 1354 t.Fatal(err) 1355 } 1356 projectFound := false 1357 for _, p := range newLocalProjects { 1358 if p.Name == localProjects[1].Name { 1359 projectFound = true 1360 } 1361 } 1362 if !projectFound { 1363 t.Fatalf("Project with updated name(%v) not found", localProjects[1].Name) 1364 } 1365 } 1366 1367 // testUpdateUniverseDeletedProject checks that UpdateUniverse will delete a 1368 // project if gc=true. 1369 func testUpdateUniverseDeletedProject(t *testing.T, testDirtyProjectDelete, testProjectWithBranch bool) { 1370 localProjects, fake, cleanup := setupUniverse(t) 1371 defer cleanup() 1372 if err := fake.UpdateUniverse(false); err != nil { 1373 t.Fatal(err) 1374 } 1375 1376 // Delete project 1. 1377 m, err := fake.ReadRemoteManifest() 1378 if err != nil { 1379 t.Fatal(err) 1380 } 1381 projects := []project.Project{} 1382 if testDirtyProjectDelete { 1383 writeUncommitedFile(t, fake.X, localProjects[4].Path, "extra", "") 1384 } else if testProjectWithBranch { 1385 // Create and checkout master. 1386 git := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[4].Path)) 1387 if err := git.CreateAndCheckoutBranch("master"); err != nil { 1388 t.Fatal(err) 1389 } 1390 } 1391 for _, p := range m.Projects { 1392 skip := false 1393 for i := 1; i <= 5; i++ { 1394 if p.Name == localProjects[i].Name { 1395 skip = true 1396 } 1397 } 1398 if skip { 1399 continue 1400 } 1401 projects = append(projects, p) 1402 } 1403 m.Projects = projects 1404 if err := fake.WriteRemoteManifest(m); err != nil { 1405 t.Fatal(err) 1406 } 1407 // Check that UpdateUniverse() with gc=false does not delete the local copy 1408 // of the project. 1409 if err := fake.UpdateUniverse(false); err != nil { 1410 t.Fatal(err) 1411 } 1412 for i := 1; i <= 5; i++ { 1413 if err := dirExists(localProjects[i].Path); err != nil { 1414 t.Fatalf("expected project %q at path %q to exist but it did not", localProjects[i].Name, localProjects[i].Path) 1415 } 1416 checkReadme(t, fake.X, localProjects[i], "initial readme") 1417 } 1418 // Check that UpdateUniverse() with gc=true does delete the local copy of 1419 // the project. 1420 if err := fake.UpdateUniverse(true); err != nil { 1421 t.Fatal(err) 1422 } 1423 for i := 1; i <= 5; i++ { 1424 err := dirExists(localProjects[i].Path) 1425 if (testProjectWithBranch || testDirtyProjectDelete) && i >= 2 && i <= 4 { 1426 if err != nil { 1427 t.Fatalf("expected project %q at path %q to exist but it did not", localProjects[i].Name, localProjects[i].Path) 1428 } 1429 } else if err == nil { 1430 t.Fatalf("expected project %q at path %q not to exist but it did", localProjects[i].Name, localProjects[i].Path) 1431 } 1432 } 1433 } 1434 1435 func TestUpdateUniverseDeletedProject(t *testing.T) { 1436 testUpdateUniverseDeletedProject(t, false, false) 1437 testUpdateUniverseDeletedProject(t, true, false) 1438 testUpdateUniverseDeletedProject(t, false, true) 1439 } 1440 1441 func TestIgnoredProjectsNotDeleted(t *testing.T) { 1442 localProjects, fake, cleanup := setupUniverse(t) 1443 defer cleanup() 1444 if err := fake.UpdateUniverse(false); err != nil { 1445 t.Fatal(err) 1446 } 1447 1448 // Delete project 1. 1449 m, err := fake.ReadRemoteManifest() 1450 if err != nil { 1451 t.Fatal(err) 1452 } 1453 projects := []project.Project{} 1454 for _, p := range m.Projects { 1455 if p.Name == localProjects[1].Name { 1456 continue 1457 } 1458 projects = append(projects, p) 1459 } 1460 m.Projects = projects 1461 if err := fake.WriteRemoteManifest(m); err != nil { 1462 t.Fatal(err) 1463 } 1464 lc := project.LocalConfig{Ignore: true} 1465 project.WriteLocalConfig(fake.X, localProjects[1], lc) 1466 if err := fake.UpdateUniverse(true); err != nil { 1467 t.Fatal(err) 1468 } 1469 if err := dirExists(localProjects[1].Path); err != nil { 1470 t.Fatalf("expected project %q at path %q to exist but it did not: %s", localProjects[1].Name, localProjects[1].Path, err) 1471 } 1472 } 1473 1474 // TestUpdateUniverseNewProjectSamePath checks that UpdateUniverse can handle a 1475 // new project with the same path as a deleted project, but a different path. 1476 func TestUpdateUniverseNewProjectSamePath(t *testing.T) { 1477 localProjects, fake, cleanup := setupUniverse(t) 1478 defer cleanup() 1479 if err := fake.UpdateUniverse(false); err != nil { 1480 t.Fatal(err) 1481 } 1482 1483 // Delete a project 1 and create a new one with a different name but the 1484 // same path. 1485 m, err := fake.ReadRemoteManifest() 1486 if err != nil { 1487 t.Fatal(err) 1488 } 1489 newProjectName := "new-project-name" 1490 projects := []project.Project{} 1491 for _, p := range m.Projects { 1492 if p.Path == localProjects[1].Path { 1493 p.Name = newProjectName 1494 } 1495 projects = append(projects, p) 1496 } 1497 localProjects[1].Name = newProjectName 1498 m.Projects = projects 1499 if err := fake.WriteRemoteManifest(m); err != nil { 1500 t.Fatal(err) 1501 } 1502 // Check that UpdateUniverse() does not fail. 1503 if err := fake.UpdateUniverse(true); err != nil { 1504 t.Fatal(err) 1505 } 1506 } 1507 1508 // TestUpdateUniverseRemoteBranch checks that UpdateUniverse can pull from a 1509 // non-master remote branch. 1510 func TestUpdateUniverseRemoteBranch(t *testing.T) { 1511 localProjects, fake, cleanup := setupUniverse(t) 1512 defer cleanup() 1513 if err := fake.UpdateUniverse(false); err != nil { 1514 t.Fatal(err) 1515 } 1516 1517 // Commit to master branch of a project 1. 1518 writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "master commit") 1519 // Create and checkout a new branch of project 1 and make a new commit. 1520 git := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProjects[1].Name])) 1521 if err := git.CreateAndCheckoutBranch("non-master"); err != nil { 1522 t.Fatal(err) 1523 } 1524 writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "non-master commit") 1525 // Point the manifest to the new non-master branch. 1526 m, err := fake.ReadRemoteManifest() 1527 if err != nil { 1528 t.Fatal(err) 1529 } 1530 projects := []project.Project{} 1531 for _, p := range m.Projects { 1532 if p.Name == localProjects[1].Name { 1533 p.RemoteBranch = "non-master" 1534 } 1535 projects = append(projects, p) 1536 } 1537 m.Projects = projects 1538 if err := fake.WriteRemoteManifest(m); err != nil { 1539 t.Fatal(err) 1540 } 1541 // Check that UpdateUniverse pulls the commit from the non-master branch. 1542 if err := fake.UpdateUniverse(false); err != nil { 1543 t.Fatal(err) 1544 } 1545 checkReadme(t, fake.X, localProjects[1], "non-master commit") 1546 } 1547 1548 // TestUpdateWhenRemoteChangesRebased checks that UpdateUniverse can pull from a 1549 // non-master remote branch if the local changes were rebased somewhere else(gerrit) 1550 // before being pushed to remote 1551 func TestUpdateWhenRemoteChangesRebased(t *testing.T) { 1552 localProjects, fake, cleanup := setupUniverse(t) 1553 defer cleanup() 1554 if err := fake.UpdateUniverse(false); err != nil { 1555 t.Fatal(err) 1556 } 1557 1558 gitRemote := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(fake.Projects[localProjects[1].Name])) 1559 if err := gitRemote.CreateAndCheckoutBranch("non-master"); err != nil { 1560 t.Fatal(err) 1561 } 1562 1563 gitLocal := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(localProjects[1].Path)) 1564 if err := gitLocal.Fetch("origin", gitutil.PruneOpt(true)); err != nil { 1565 t.Fatal(err) 1566 } 1567 1568 // checkout branch in local repo 1569 if err := gitLocal.CheckoutBranch("non-master"); err != nil { 1570 t.Fatal(err) 1571 } 1572 1573 // Create commits in remote repo 1574 writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "non-master commit") 1575 writeFile(t, fake.X, fake.Projects[localProjects[1].Name], "file1", "file1") 1576 file1CommitRev, _ := gitRemote.CurrentRevision() 1577 writeFile(t, fake.X, fake.Projects[localProjects[1].Name], "file2", "file2") 1578 file2CommitRev, _ := gitRemote.CurrentRevision() 1579 1580 if err := gitLocal.Fetch("origin", gitutil.PruneOpt(true)); err != nil { 1581 t.Fatal(err) 1582 } 1583 1584 // Cherry pick creation of file1, so that it acts like been rebased on remote repo 1585 // As there is a commit creating README on remote not in local repo 1586 if err := gitLocal.CherryPick(file1CommitRev); err != nil { 1587 t.Fatal(err) 1588 } 1589 1590 if err := project.UpdateUniverse(fake.X, false, false, true /*rebaseTracked*/, false, false, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout); err != nil { 1591 t.Fatal(err) 1592 } 1593 1594 // It rebased properly and pulled latest changes 1595 localRev, _ := gitLocal.CurrentRevision() 1596 if file2CommitRev != localRev { 1597 t.Fatalf("Current commit is %v, it should be %v\n", localRev, file2CommitRev) 1598 } 1599 } 1600 1601 func TestUpdateWhenConflictMerge(t *testing.T) { 1602 localProjects, fake, cleanup := setupUniverse(t) 1603 defer cleanup() 1604 if err := fake.UpdateUniverse(false); err != nil { 1605 t.Fatal(err) 1606 } 1607 1608 gitRemote := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(fake.Projects[localProjects[1].Name])) 1609 if err := gitRemote.CreateAndCheckoutBranch("non-master"); err != nil { 1610 t.Fatal(err) 1611 } 1612 1613 gitLocal := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(localProjects[1].Path)) 1614 if err := gitLocal.Fetch("origin", gitutil.PruneOpt(true)); err != nil { 1615 t.Fatal(err) 1616 } 1617 1618 // checkout branch in local repo 1619 if err := gitLocal.CheckoutBranch("non-master"); err != nil { 1620 t.Fatal(err) 1621 } 1622 1623 // Create commits in remote repo 1624 writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "non-master commit") 1625 writeFile(t, fake.X, fake.Projects[localProjects[1].Name], "file1", "file1") 1626 file1CommitRev, _ := gitRemote.CurrentRevision() 1627 writeFile(t, fake.X, fake.Projects[localProjects[1].Name], "file2", "file2") 1628 1629 if err := gitLocal.Fetch("origin", gitutil.PruneOpt(true)); err != nil { 1630 t.Fatal(err) 1631 } 1632 1633 // Cherry pick creation of file1, so that it acts like been rebased on remote repo 1634 // This would act like conflicting merge 1635 if err := gitLocal.CherryPick(file1CommitRev); err != nil { 1636 t.Fatal(err) 1637 } 1638 rev, _ := gitLocal.CurrentRevision() 1639 1640 if err := fake.UpdateUniverse(false); err != nil { 1641 t.Fatal(err) 1642 } 1643 1644 localRev, _ := gitLocal.CurrentRevision() 1645 if rev != localRev { 1646 t.Fatalf("Current commit is %v, it should be %v\n", localRev, rev) 1647 } 1648 checkJiriRevFiles(t, fake.X, localProjects[1]) 1649 } 1650 1651 func TestTagNotContainedInBranch(t *testing.T) { 1652 localProjects, fake, cleanup := setupUniverse(t) 1653 defer cleanup() 1654 if err := fake.UpdateUniverse(false); err != nil { 1655 t.Fatal(err) 1656 } 1657 1658 gitRemote := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(fake.Projects[localProjects[1].Name])) 1659 if err := gitRemote.CreateAndCheckoutBranch("non-master"); err != nil { 1660 t.Fatal(err) 1661 } 1662 1663 // Create commits in remote repo 1664 writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "non-master commit") 1665 writeFile(t, fake.X, fake.Projects[localProjects[1].Name], "file1", "file1") 1666 file1CommitRev, _ := gitRemote.CurrentRevision() 1667 if err := gitRemote.CreateLightweightTag("testtag"); err != nil { 1668 t.Fatalf("Creating tag: %s", err) 1669 1670 } 1671 if err := gitRemote.CheckoutBranch("master"); err != nil { 1672 t.Fatal(err) 1673 } 1674 if err := gitRemote.DeleteBranch("non-master", gitutil.ForceOpt(true)); err != nil { 1675 t.Fatal(err) 1676 } 1677 1678 m, err := fake.ReadRemoteManifest() 1679 if err != nil { 1680 t.Fatal(err) 1681 } 1682 projects := []project.Project{} 1683 for _, p := range m.Projects { 1684 if p.Name == localProjects[1].Name { 1685 p.Revision = "testtag" 1686 } 1687 projects = append(projects, p) 1688 } 1689 m.Projects = projects 1690 if err := fake.WriteRemoteManifest(m); err != nil { 1691 t.Fatal(err) 1692 } 1693 1694 if err := fake.UpdateUniverse(false); err != nil { 1695 t.Fatal(err) 1696 } 1697 1698 gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[1].Path)) 1699 // It rebased properly and pulled latest changes 1700 localRev, _ := gitLocal.CurrentRevision() 1701 if file1CommitRev != localRev { 1702 t.Fatalf("Current commit is %v, it should be %v\n", localRev, file1CommitRev) 1703 } 1704 } 1705 1706 // TestCheckoutSnapshotUrl tests checking out snapshot functionality from a url 1707 func TestCheckoutSnapshotUrl(t *testing.T) { 1708 testCheckoutSnapshot(t, true) 1709 } 1710 1711 // TestCheckoutSnapshotFileSystem tests checking out snapshot functionality from filesystem 1712 func TestCheckoutSnapshotFileSystem(t *testing.T) { 1713 testCheckoutSnapshot(t, false) 1714 } 1715 1716 func testCheckoutSnapshot(t *testing.T, testURL bool) { 1717 localProjects, fake, cleanup := setupUniverse(t) 1718 defer cleanup() 1719 var oldCommitRevs []string 1720 var latestCommitRevs []string 1721 1722 for i, localProject := range localProjects { 1723 gitRemote := gitutil.New(fake.X, gitutil.RootDirOpt(fake.Projects[localProject.Name])) 1724 writeFile(t, fake.X, fake.Projects[localProject.Name], "file1"+strconv.Itoa(i), "file1"+strconv.Itoa(i)) 1725 file1CommitRev, _ := gitRemote.CurrentRevision() 1726 oldCommitRevs = append(oldCommitRevs, file1CommitRev) 1727 writeFile(t, fake.X, fake.Projects[localProject.Name], "file2"+strconv.Itoa(i), "file2"+strconv.Itoa(i)) 1728 file2CommitRev, _ := gitRemote.CurrentRevision() 1729 latestCommitRevs = append(latestCommitRevs, file2CommitRev) 1730 } 1731 1732 if err := fake.UpdateUniverse(false); err != nil { 1733 t.Fatal(err) 1734 } 1735 1736 for i, localProject := range localProjects { 1737 gitLocal := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(localProject.Path)) 1738 rev, _ := gitLocal.CurrentRevision() 1739 if rev != latestCommitRevs[i] { 1740 t.Fatalf("Current commit for project %q is %v, it should be %v\n", localProject.Name, rev, latestCommitRevs[i]) 1741 } 1742 1743 // Test case when local repo in on a branch 1744 if i == 1 { 1745 if err := gitLocal.CheckoutBranch("master"); err != nil { 1746 t.Fatal(err) 1747 } 1748 } 1749 } 1750 dir, err := ioutil.TempDir("", "snap") 1751 if err != nil { 1752 t.Fatal(err) 1753 } 1754 defer os.RemoveAll(dir) 1755 manifest := &project.Manifest{Version: project.ManifestVersion} 1756 for _, localProject := range localProjects { 1757 manifest.Projects = append(manifest.Projects, localProject) 1758 } 1759 manifest.Projects[0].Revision = oldCommitRevs[0] 1760 manifest.Projects[1].Revision = oldCommitRevs[1] 1761 1762 // Test case when snapshot specifies latest revision 1763 manifest.Projects[2].Revision = latestCommitRevs[2] 1764 1765 manifest.Projects[3].Revision = oldCommitRevs[3] 1766 manifest.Projects[4].Revision = latestCommitRevs[4] 1767 manifest.Projects[5].Revision = oldCommitRevs[5] 1768 manifest.Projects[6].Revision = latestCommitRevs[6] 1769 snapshotFile := filepath.Join(dir, "snapshot") 1770 manifest.ToFile(fake.X, snapshotFile) 1771 if testURL { 1772 snapBytes, err := ioutil.ReadFile(snapshotFile) 1773 if err != nil { 1774 t.Fatal(err) 1775 } 1776 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1777 w.Header().Set("Content-Type", "html/text") 1778 fmt.Fprintln(w, string(snapBytes[:])) 1779 })) 1780 defer server.Close() 1781 1782 project.CheckoutSnapshot(fake.X, server.URL, false, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout) 1783 } else { 1784 project.CheckoutSnapshot(fake.X, snapshotFile, false, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout) 1785 } 1786 sort.Sort(project.ProjectsByPath(localProjects)) 1787 for i, localProject := range localProjects { 1788 gitLocal := gitutil.New(fake.X, gitutil.RootDirOpt(localProject.Path)) 1789 rev, _ := gitLocal.CurrentRevision() 1790 expectedRev := manifest.Projects[i].Revision 1791 if rev != expectedRev { 1792 t.Fatalf("Current commit for project %q is %v, it should be %v\n", localProject.Name, rev, expectedRev) 1793 } 1794 } 1795 } 1796 1797 func testLocalBranchesAreUpdated(t *testing.T, shouldLocalBeOnABranch, rebaseAll bool) { 1798 localProjects, fake, cleanup := setupUniverse(t) 1799 defer cleanup() 1800 if err := fake.UpdateUniverse(false); err != nil { 1801 t.Fatal(err) 1802 } 1803 1804 gitRemote := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(fake.Projects[localProjects[1].Name])) 1805 if err := gitRemote.CreateAndCheckoutBranch("non-master"); err != nil { 1806 t.Fatal(err) 1807 } 1808 1809 // This will fetch non-master to local 1810 if err := fake.UpdateUniverse(false); err != nil { 1811 t.Fatal(err) 1812 } 1813 1814 writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "non-master commit") 1815 1816 if err := gitRemote.CheckoutBranch("master"); err != nil { 1817 t.Fatal(err) 1818 } 1819 writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "master commit") 1820 1821 gitLocal := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(localProjects[1].Path)) 1822 1823 // This will create a local branch non-master 1824 if err := gitLocal.CheckoutBranch("non-master"); err != nil { 1825 t.Fatal(err) 1826 } 1827 1828 // Go back to detached HEAD 1829 if !shouldLocalBeOnABranch { 1830 if err := gitLocal.CheckoutBranch("HEAD", gitutil.DetachOpt(true)); err != nil { 1831 t.Fatal(err) 1832 } 1833 } 1834 1835 if err := project.UpdateUniverse(fake.X, false, false, false, false, rebaseAll, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout); err != nil { 1836 t.Fatal(err) 1837 } 1838 1839 if shouldLocalBeOnABranch && gitLocal.IsOnBranch() == false { 1840 t.Fatalf("local repo should be on the branch after update") 1841 } else if !shouldLocalBeOnABranch && gitLocal.IsOnBranch() == true { 1842 t.Fatalf("local repo should be on detached head after update") 1843 } 1844 1845 projects, err := project.LocalProjects(fake.X, project.FastScan) 1846 if err != nil { 1847 t.Fatal(err) 1848 } 1849 1850 states, err := project.GetProjectStates(fake.X, projects, false) 1851 if err != nil { 1852 t.Fatal(err) 1853 } 1854 1855 state := states[localProjects[1].Key()] 1856 if shouldLocalBeOnABranch && state.CurrentBranch.Name != "non-master" { 1857 t.Fatalf("local repo should be on branch(non-master) it is on %q", state.CurrentBranch.Name) 1858 } 1859 1860 if rebaseAll { 1861 for _, branch := range state.Branches { 1862 if branch.Tracking != nil { 1863 if branch.Revision != branch.Tracking.Revision { 1864 t.Fatalf("branch %q has different revision then remote branch %q", branch.Name, branch.Tracking.Name) 1865 } 1866 } 1867 } 1868 } else { 1869 for _, branch := range state.Branches { 1870 if branch.Tracking != nil { 1871 if branch.Name == state.CurrentBranch.Name { 1872 if branch.Revision != branch.Tracking.Revision { 1873 t.Fatalf("branch %q has different revision then remote branch %q", branch.Name, branch.Tracking.Name) 1874 } 1875 } else if branch.Revision == branch.Tracking.Revision { 1876 t.Fatalf("branch %q should have different revision then remote branch %q", branch.Name, branch.Tracking.Name) 1877 } 1878 } 1879 } 1880 } 1881 } 1882 1883 // TestLocalBranchesAreUpdatedWhenOnHead test that all the local branches are 1884 // updated on jiri update when local repo is on detached head 1885 func TestLocalBranchesAreUpdatedWhenOnHead(t *testing.T) { 1886 testLocalBranchesAreUpdated(t, false, true) 1887 } 1888 1889 // TestLocalBranchesAreUpdatedWhenOnBranch test that all the local branches are 1890 // updated on jiri update when local repo is on a branch 1891 func TestLocalBranchesAreUpdatedWhenOnBranch(t *testing.T) { 1892 testLocalBranchesAreUpdated(t, true, true) 1893 } 1894 1895 // TestLocalBranchesNotUpdatedWhenOnHead test that all the local branches are not 1896 // updated on jiri update when local repo is on detached head and rebaseAll is false 1897 func TestLocalBranchesNotUpdatedWhenOnHead(t *testing.T) { 1898 testLocalBranchesAreUpdated(t, false, false) 1899 } 1900 1901 // TestLocalBranchesAreUpdatedWhenOnBranch test that all the local branches are not 1902 // updated on jiri update when local repo is on a branch and rebaseAll is false 1903 func TestLocalBranchesNotUpdatedWhenOnBranch(t *testing.T) { 1904 testLocalBranchesAreUpdated(t, true, false) 1905 } 1906 1907 func TestFileImportCycle(t *testing.T) { 1908 jirix, cleanup := xtest.NewX(t) 1909 defer cleanup() 1910 1911 // Set up the cycle .jiri_manifest -> A -> B -> A 1912 jiriManifest := project.Manifest{ 1913 LocalImports: []project.LocalImport{ 1914 {File: "A"}, 1915 }, 1916 } 1917 manifestA := project.Manifest{ 1918 LocalImports: []project.LocalImport{ 1919 {File: "B"}, 1920 }, 1921 } 1922 manifestB := project.Manifest{ 1923 LocalImports: []project.LocalImport{ 1924 {File: "A"}, 1925 }, 1926 } 1927 if err := jiriManifest.ToFile(jirix, jirix.JiriManifestFile()); err != nil { 1928 t.Fatal(err) 1929 } 1930 if err := manifestA.ToFile(jirix, filepath.Join(jirix.Root, "A")); err != nil { 1931 t.Fatal(err) 1932 } 1933 if err := manifestB.ToFile(jirix, filepath.Join(jirix.Root, "B")); err != nil { 1934 t.Fatal(err) 1935 } 1936 1937 // The update should complain about the cycle. 1938 err := project.UpdateUniverse(jirix, false, false, false, false, false, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout) 1939 if got, want := fmt.Sprint(err), "import cycle detected in local manifest files"; !strings.Contains(got, want) { 1940 t.Errorf("got error %v, want substr %v", got, want) 1941 } 1942 } 1943 1944 func TestRemoteImportCycle(t *testing.T) { 1945 fake, cleanup := jiritest.NewFakeJiriRoot(t) 1946 defer cleanup() 1947 1948 // Set up two remote manifest projects, remote1 and remote1. 1949 if err := fake.CreateRemoteProject("remote1"); err != nil { 1950 t.Fatal(err) 1951 } 1952 if err := fake.CreateRemoteProject("remote2"); err != nil { 1953 t.Fatal(err) 1954 } 1955 remote1 := fake.Projects["remote1"] 1956 remote2 := fake.Projects["remote2"] 1957 1958 fileA, fileB := filepath.Join(remote1, "A"), filepath.Join(remote2, "B") 1959 1960 // Set up the cycle .jiri_manifest -> remote1+A -> remote2+B -> remote1+A 1961 jiriManifest := project.Manifest{ 1962 Imports: []project.Import{ 1963 {Manifest: "A", Name: "n1", Remote: remote1}, 1964 }, 1965 } 1966 manifestA := project.Manifest{ 1967 Imports: []project.Import{ 1968 {Manifest: "B", Name: "n2", Remote: remote2}, 1969 }, 1970 } 1971 manifestB := project.Manifest{ 1972 Imports: []project.Import{ 1973 {Manifest: "A", Name: "n3", Remote: remote1}, 1974 }, 1975 } 1976 if err := jiriManifest.ToFile(fake.X, fake.X.JiriManifestFile()); err != nil { 1977 t.Fatal(err) 1978 } 1979 if err := manifestA.ToFile(fake.X, fileA); err != nil { 1980 t.Fatal(err) 1981 } 1982 if err := manifestB.ToFile(fake.X, fileB); err != nil { 1983 t.Fatal(err) 1984 } 1985 commitFile(t, fake.X, remote1, fileA, "commit A") 1986 commitFile(t, fake.X, remote2, fileB, "commit B") 1987 1988 // The update should complain about the cycle. 1989 err := project.UpdateUniverse(fake.X, false, false, false, false, false, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout) 1990 if got, want := fmt.Sprint(err), "import cycle detected in remote manifest imports"; !strings.Contains(got, want) { 1991 t.Errorf("got error %v, want substr %v", got, want) 1992 } 1993 } 1994 1995 func TestFileAndRemoteImportCycle(t *testing.T) { 1996 fake, cleanup := jiritest.NewFakeJiriRoot(t) 1997 defer cleanup() 1998 1999 // Set up two remote manifest projects, remote1 and remote2. 2000 // Set up two remote manifest projects, remote1 and remote1. 2001 if err := fake.CreateRemoteProject("remote1"); err != nil { 2002 t.Fatal(err) 2003 } 2004 if err := fake.CreateRemoteProject("remote2"); err != nil { 2005 t.Fatal(err) 2006 } 2007 remote1 := fake.Projects["remote1"] 2008 remote2 := fake.Projects["remote2"] 2009 fileA, fileD := filepath.Join(remote1, "A"), filepath.Join(remote1, "D") 2010 fileB, fileC := filepath.Join(remote2, "B"), filepath.Join(remote2, "C") 2011 2012 // Set up the cycle .jiri_manifest -> remote1+A -> remote2+B -> C -> remote1+D -> A 2013 jiriManifest := project.Manifest{ 2014 Imports: []project.Import{ 2015 {Manifest: "A", Root: "r1", Name: "n1", Remote: remote1}, 2016 }, 2017 } 2018 manifestA := project.Manifest{ 2019 Imports: []project.Import{ 2020 {Manifest: "B", Root: "r2", Name: "n2", Remote: remote2}, 2021 }, 2022 } 2023 manifestB := project.Manifest{ 2024 LocalImports: []project.LocalImport{ 2025 {File: "C"}, 2026 }, 2027 } 2028 manifestC := project.Manifest{ 2029 Imports: []project.Import{ 2030 {Manifest: "D", Root: "r3", Name: "n3", Remote: remote1}, 2031 }, 2032 } 2033 manifestD := project.Manifest{ 2034 LocalImports: []project.LocalImport{ 2035 {File: "A"}, 2036 }, 2037 } 2038 if err := jiriManifest.ToFile(fake.X, fake.X.JiriManifestFile()); err != nil { 2039 t.Fatal(err) 2040 } 2041 if err := manifestA.ToFile(fake.X, fileA); err != nil { 2042 t.Fatal(err) 2043 } 2044 if err := manifestB.ToFile(fake.X, fileB); err != nil { 2045 t.Fatal(err) 2046 } 2047 if err := manifestC.ToFile(fake.X, fileC); err != nil { 2048 t.Fatal(err) 2049 } 2050 if err := manifestD.ToFile(fake.X, fileD); err != nil { 2051 t.Fatal(err) 2052 } 2053 commitFile(t, fake.X, remote1, fileA, "commit A") 2054 commitFile(t, fake.X, remote2, fileB, "commit B") 2055 commitFile(t, fake.X, remote2, fileC, "commit C") 2056 commitFile(t, fake.X, remote1, fileD, "commit D") 2057 2058 // The update should complain about the cycle. 2059 err := project.UpdateUniverse(fake.X, false, false, false, false, false, true /*run-hooks*/, true /*run-packages*/, project.DefaultHookTimeout, project.DefaultPackageTimeout) 2060 if got, want := fmt.Sprint(err), "import cycle detected"; !strings.Contains(got, want) { 2061 t.Errorf("got error %v, want substr %v", got, want) 2062 } 2063 } 2064 2065 func TestManifestToFromBytes(t *testing.T) { 2066 tests := []struct { 2067 Manifest project.Manifest 2068 XML string 2069 }{ 2070 { 2071 project.Manifest{}, 2072 `<manifest> 2073 </manifest> 2074 `, 2075 }, 2076 { 2077 project.Manifest{ 2078 Imports: []project.Import{ 2079 { 2080 Manifest: "manifest1", 2081 Name: "remoteimport1", 2082 Remote: "remote1", 2083 Revision: "HEAD", 2084 RemoteBranch: "master", 2085 }, 2086 { 2087 Manifest: "manifest2", 2088 Name: "remoteimport2", 2089 Remote: "remote2", 2090 Revision: "HEAD", 2091 RemoteBranch: "branch2", 2092 }, 2093 { 2094 Manifest: "manifest3", 2095 Name: "remoteimport3", 2096 Remote: "remote3", 2097 Revision: "rev3", 2098 RemoteBranch: "branch3", 2099 }, 2100 }, 2101 LocalImports: []project.LocalImport{ 2102 {File: "fileimport"}, 2103 }, 2104 Projects: []project.Project{ 2105 { 2106 Name: "project1", 2107 Path: "path1", 2108 Remote: "remote1", 2109 RemoteBranch: "master", 2110 Revision: "HEAD", 2111 GerritHost: "https://test-review.googlesource.com", 2112 GitHooks: "path/to/githooks", 2113 }, 2114 { 2115 Name: "project2", 2116 Path: "path2", 2117 Remote: "remote2", 2118 RemoteBranch: "branch2", 2119 Revision: "rev2", 2120 }, 2121 }, 2122 Hooks: []project.Hook{ 2123 { 2124 Name: "testhook", 2125 ProjectName: "project1", 2126 Action: "action.sh", 2127 }, 2128 }, 2129 }, 2130 `<manifest> 2131 <imports> 2132 <import manifest="manifest1" name="remoteimport1" remote="remote1"/> 2133 <import manifest="manifest2" name="remoteimport2" remote="remote2" remotebranch="branch2"/> 2134 <import manifest="manifest3" name="remoteimport3" remote="remote3" revision="rev3" remotebranch="branch3"/> 2135 <localimport file="fileimport"/> 2136 </imports> 2137 <projects> 2138 <project name="project1" path="path1" remote="remote1" gerrithost="https://test-review.googlesource.com" githooks="path/to/githooks"/> 2139 <project name="project2" path="path2" remote="remote2" remotebranch="branch2" revision="rev2"/> 2140 </projects> 2141 <hooks> 2142 <hook name="testhook" action="action.sh" project="project1"/> 2143 </hooks> 2144 </manifest> 2145 `, 2146 }, 2147 } 2148 for _, test := range tests { 2149 gotBytes, err := test.Manifest.ToBytes() 2150 if err != nil { 2151 t.Errorf("%+v ToBytes failed: %v", test.Manifest, err) 2152 } 2153 if got, want := string(gotBytes), test.XML; got != want { 2154 t.Errorf("%+v ToBytes GOT\n%v\nWANT\n%v", test.Manifest, got, want) 2155 } 2156 manifest, err := project.ManifestFromBytes([]byte(test.XML)) 2157 if err != nil { 2158 t.Errorf("%+v FromBytes failed: %v", test.Manifest, err) 2159 } 2160 if got, want := manifest, &test.Manifest; !reflect.DeepEqual(got, want) { 2161 t.Errorf("%+v FromBytes GOT\n%#v\nWANT\n%#v", test.Manifest, got, want) 2162 } 2163 } 2164 } 2165 2166 func TestProjectToFromFile(t *testing.T) { 2167 jirix, cleanup := xtest.NewX(t) 2168 defer cleanup() 2169 2170 tests := []struct { 2171 Project project.Project 2172 XML string 2173 }{ 2174 { 2175 // Default fields are dropped when marshaled, and added when unmarshaled. 2176 project.Project{ 2177 Name: "project1", 2178 Path: filepath.Join(jirix.Root, "path1"), 2179 Remote: "remote1", 2180 RemoteBranch: "master", 2181 Revision: "HEAD", 2182 }, 2183 `<project name="project1" path="path1" remote="remote1"/> 2184 `, 2185 }, 2186 { 2187 project.Project{ 2188 Name: "project2", 2189 Path: filepath.Join(jirix.Root, "path2"), 2190 GitHooks: filepath.Join(jirix.Root, "git-hooks"), 2191 Remote: "remote2", 2192 RemoteBranch: "branch2", 2193 Revision: "rev2", 2194 }, 2195 `<project name="project2" path="path2" remote="remote2" remotebranch="branch2" revision="rev2" githooks="git-hooks"/> 2196 `, 2197 }, 2198 } 2199 for index, test := range tests { 2200 filename := filepath.Join(jirix.Root, fmt.Sprintf("test-%d", index)) 2201 if err := test.Project.ToFile(jirix, filename); err != nil { 2202 t.Errorf("%+v ToFile failed: %v", test.Project, err) 2203 } 2204 gotBytes, err := ioutil.ReadFile(filename) 2205 if err != nil { 2206 t.Errorf("%+v ReadFile failed: %v", test.Project, err) 2207 } 2208 if got, want := string(gotBytes), test.XML; got != want { 2209 t.Errorf("%+v ToFile GOT\n%v\nWANT\n%v", test.Project, got, want) 2210 } 2211 project, err := project.ProjectFromFile(jirix, filename) 2212 if err != nil { 2213 t.Errorf("%+v FromFile failed: %v", test.Project, err) 2214 } 2215 if got, want := project, &test.Project; !reflect.DeepEqual(got, want) { 2216 t.Errorf("%+v FromFile got %#v, want %#v", test.Project, got, want) 2217 } 2218 } 2219 } 2220 2221 func TestMarshalAndUnmarshalLockEntries(t *testing.T) { 2222 2223 projectLock0 := project.ProjectLock{"https://dart.googlesource.com/web_socket_channel.git", "dart", "1.0.9"} 2224 pkgLock0 := project.PackageLock{ 2225 PackageName: "fuchsia/go/mac-amd64", 2226 VersionTag: "git_revision:b8bd7d94a2ae6c80ab8b6ed5900d3eeba8a777c3", 2227 InstanceID: "3c33b55c1a75b900536c91181805bb8668857341", 2228 } 2229 2230 testProjectLocks0 := project.ProjectLocks{ 2231 projectLock0.Key(): projectLock0, 2232 } 2233 testPkgLocks0 := project.PackageLocks{ 2234 pkgLock0.Key(): pkgLock0, 2235 } 2236 2237 jsonData, err := project.MarshalLockEntries(testProjectLocks0, testPkgLocks0) 2238 2239 if err != nil { 2240 t.Errorf("marshalling lockfile failed due to error: %v", err) 2241 } 2242 2243 projectLocks, pkgLocks, err := project.UnmarshalLockEntries(jsonData) 2244 if err != nil { 2245 t.Errorf("unmarshalling lockfile failed due to error: %v", err) 2246 } 2247 2248 if !reflect.DeepEqual(projectLocks, testProjectLocks0) { 2249 t.Errorf("unmarshalled project locks do not match test data, expecting %v, got %v", testProjectLocks0, projectLocks) 2250 } 2251 2252 if !reflect.DeepEqual(pkgLocks, testPkgLocks0) { 2253 t.Errorf("unmarshalled locks do not match test data, expecting %v, got %v", testPkgLocks0, pkgLocks) 2254 } 2255 2256 jsonData = []byte(` 2257 [ 2258 { 2259 "repository_url": "https://dart.googlesource.com/web_socket_channel.git", 2260 "name": "dart", 2261 "revision": "1.0.9" 2262 }, 2263 { 2264 "repository_url": "https://dart.googlesource.com/web_socket_channel.git", 2265 "name": "dart", 2266 "revision": "1.1.0" 2267 } 2268 ]`) 2269 2270 if _, _, err := project.UnmarshalLockEntries(jsonData); err == nil { 2271 t.Errorf("unmarshalling lockfile with conflicting data should fail but it did not happen") 2272 } else { 2273 if !strings.Contains(err.Error(), "has more than 1") { 2274 t.Errorf("unmarshalling lockfile with conflicting data failed due to unrelated error: %v", err) 2275 } 2276 } 2277 2278 } 2279 2280 func TestGetPath(t *testing.T) { 2281 testPkgs := []project.Package{ 2282 project.Package{Name: "test0", Version: "version", Path: "A/test0"}, 2283 project.Package{Name: "test1/${platform}", Version: "version", Path: ""}, 2284 project.Package{Name: "test2/${os}-${arch}", Version: "version", Path: ""}, 2285 project.Package{Name: "test3/${platform=linux-armv6l}", Version: "version", Path: ""}, 2286 } 2287 testResults := []string{ 2288 "A/test0", 2289 "prebuilt/test1/", 2290 "prebuilt/test2/", 2291 "prebuilt", 2292 } 2293 2294 for i, v := range testPkgs { 2295 defaultPath, err := v.GetPath() 2296 if err != nil { 2297 t.Errorf("TestGetPath failed due to error: %v", err) 2298 } 2299 if strings.HasSuffix(testResults[i], "/") { 2300 testResults[i] += cipd.CipdPlatform.String() 2301 } 2302 if testResults[i] != defaultPath { 2303 t.Errorf("expecting %q, got %q", testResults[i], defaultPath) 2304 } 2305 } 2306 } 2307 2308 func TestWritePackageFlags(t *testing.T) { 2309 jirix, cleanup := xtest.NewX(t) 2310 defer cleanup() 2311 2312 testPkgsWAS := []project.Package{ 2313 project.Package{Name: "test0", Version: "version", Flag: "flagfile0|internal = true|internal = false"}, 2314 project.Package{Name: "test1", Version: "version", Flag: "flagfile1|{\"internal\" = true}|{\"internal\" = false}"}, 2315 } 2316 testPkgsWoAS := []project.Package{ 2317 project.Package{Name: "test2", Version: "version", Flag: "flagfile2|internal = true|internal = false"}, 2318 project.Package{Name: "test3", Version: "version", Flag: "flagfile3|{\"internal\" = true}|{\"internal\" = false}"}, 2319 } 2320 expected := map[string]string{ 2321 "flagfile0": "internal = true", 2322 "flagfile1": "{\"internal\" = true}", 2323 "flagfile2": "internal = false", 2324 "flagfile3": "{\"internal\" = false}", 2325 } 2326 2327 testPkgs := make(project.Packages) 2328 testPkgsWA := make(project.Packages) 2329 for _, v := range testPkgsWAS { 2330 testPkgs[v.Key()] = v 2331 testPkgsWA[v.Key()] = v 2332 } 2333 for _, v := range testPkgsWoAS { 2334 testPkgs[v.Key()] = v 2335 } 2336 2337 if err := project.WritePackageFlags(jirix, testPkgs, testPkgsWA); err != nil { 2338 t.Errorf("WritePackageFlags failed due to error: %v", err) 2339 } 2340 2341 for k, v := range expected { 2342 data, err := ioutil.ReadFile(filepath.Join(jirix.Root, k)) 2343 if err != nil { 2344 t.Errorf("read flag file %q failed due error: %v", k, err) 2345 } 2346 if string(data) != v { 2347 t.Errorf("expecting flag %q from file %q, got %q", v, k, string(data)) 2348 } 2349 } 2350 } 2351 2352 func TestWriteProjectFlags(t *testing.T) { 2353 jirix, cleanup := xtest.NewX(t) 2354 defer cleanup() 2355 2356 testProjsList := []project.Project{ 2357 project.Project{Name: "test0", Revision: "version", Flag: "flagfile0|internal = true|internal = false"}, 2358 project.Project{Name: "test1", Revision: "version", Flag: ""}, 2359 } 2360 expected := map[string]string{ 2361 "flagfile0": "internal = true", 2362 } 2363 2364 testProjs := make(project.Projects) 2365 for _, v := range testProjsList { 2366 testProjs[v.Key()] = v 2367 } 2368 2369 if err := project.WriteProjectFlags(jirix, testProjs); err != nil { 2370 t.Errorf("WritePackageFlags failed due to error: %v", err) 2371 } 2372 2373 for k, v := range expected { 2374 data, err := ioutil.ReadFile(filepath.Join(jirix.Root, k)) 2375 if err != nil { 2376 t.Errorf("read flag file %q failed due error: %v", k, err) 2377 } 2378 if string(data) != v { 2379 t.Errorf("expecting flag %q from file %q, got %q", v, k, string(data)) 2380 } 2381 } 2382 } 2383 2384 func TestPackageVersionTemplate(t *testing.T) { 2385 jirix, cleanup := xtest.NewX(t) 2386 defer cleanup() 2387 2388 testProjs := []project.Project{ 2389 project.Project{ 2390 Name: "testTP0", 2391 Path: "testTP0", 2392 Remote: "https://example.com/testTP0", 2393 Revision: "acf8ae59e121b3922bc8ac979323a531da418cc5", 2394 GerritHost: "https://example.com", 2395 }, 2396 project.Project{ 2397 Name: "testTP1", 2398 Path: "testTP1", 2399 Remote: "https://example.com/testTP1", 2400 Revision: "1e4aee79b472a6939ac26811c5dee8a132c29fef", 2401 GerritHost: "https://example.com", 2402 }, 2403 } 2404 2405 testPkgs := []project.Package{ 2406 project.Package{ 2407 Name: "test0", 2408 Version: "git_revision:{{(index .Projects \"testTP0\").Revision}}", 2409 Path: "test0", 2410 }, 2411 project.Package{ 2412 Name: "test1", 2413 Version: "git_revision:{{(index .Projects \"testTP1\").Revision}}", 2414 Path: "test0", 2415 }, 2416 } 2417 2418 projects := make(project.Projects) 2419 pkgs := make(project.Packages) 2420 verificationMap := make(map[string]project.Project) 2421 for i, v := range testPkgs { 2422 projects[testProjs[i].Key()] = testProjs[i] 2423 pkgs[v.Key()] = v 2424 verificationMap[v.Name] = testProjs[i] 2425 } 2426 2427 pkgs, err := project.ResolveImplicitPackageVersions(jirix, projects, pkgs) 2428 if err != nil { 2429 t.Errorf("ResolveImplicitPackageVersions failed due to error: %v", err) 2430 } 2431 for _, v := range pkgs { 2432 if proj, ok := verificationMap[v.Name]; ok { 2433 if proj.Revision != v.Version[len("git_revision:"):] { 2434 t.Errorf("expecting \"git_revision:%s\", got %q", proj.Revision, v.Version) 2435 } 2436 } else { 2437 t.Errorf("unexpected package %+v", v) 2438 } 2439 } 2440 } 2441 2442 func TestOptionalProjectsAndPackages(t *testing.T) { 2443 fake, cleanup := jiritest.NewFakeJiriRoot(t) 2444 defer cleanup() 2445 2446 // Set up projects and packages with explict attributes 2447 numProjects := 3 2448 numOptionalProjects := 2 2449 localProjects := []project.Project{} 2450 2451 createRemoteProj := func(i int, attributes string) { 2452 name := projectName(i) 2453 path := fmt.Sprintf("path-%d", i) 2454 if err := fake.CreateRemoteProject(name); err != nil { 2455 t.Errorf("failed to create remote project due to error: %v", err) 2456 } 2457 p := project.Project{ 2458 Name: name, 2459 Path: filepath.Join(fake.X.Root, path), 2460 Remote: fake.Projects[name], 2461 Attributes: attributes, 2462 } 2463 localProjects = append(localProjects, p) 2464 if err := fake.AddProject(p); err != nil { 2465 t.Errorf("failed to add a project to manifest due to error: %v", err) 2466 } 2467 } 2468 2469 for i := 0; i < numProjects; i++ { 2470 createRemoteProj(i, "") 2471 } 2472 2473 for i := numProjects; i < numProjects+numOptionalProjects; i++ { 2474 createRemoteProj(i, "optional,debug") 2475 } 2476 2477 // Create initial commit in each repo. 2478 for _, remoteProjectDir := range fake.Projects { 2479 writeReadme(t, fake.X, remoteProjectDir, "initial readme") 2480 } 2481 pkg0 := project.Package{ 2482 Name: "gn/gn/${platform}", 2483 Path: "path-pkg0", 2484 Version: "git_revision:bdb0fd02324b120cacde634a9235405061c8ea06", 2485 Attributes: "debug,testing", 2486 } 2487 pkg1 := project.Package{ 2488 Name: "fuchsia/tools/jiri/${platform}", 2489 Path: "path-pkg1", 2490 Version: "git_revision:05715c8fbbdb952ab38e50533a1b653445e74b40", 2491 Attributes: "", 2492 } 2493 2494 fake.AddPackage(pkg0) 2495 fake.AddPackage(pkg1) 2496 2497 pathExists := func(projPath string) bool { 2498 if _, err := os.Stat(projPath); err != nil { 2499 if os.IsNotExist(err) { 2500 return false 2501 } 2502 t.Errorf("failed to access path due to error: %v", err) 2503 } 2504 return true 2505 } 2506 assertExist := func(localPath string) { 2507 if !pathExists(localPath) { 2508 t.Errorf("expecting path %q exists, but it does not", localPath) 2509 } 2510 } 2511 assertNotExist := func(localPath string) { 2512 if pathExists(localPath) { 2513 t.Errorf("expecting path %q does not exist, but it does", localPath) 2514 } 2515 } 2516 2517 // Try default mode 2518 fake.X.FetchingAttrs = "" 2519 fake.UpdateUniverse(true) 2520 // The optional projects should not be fetched 2521 for i := 0; i < numProjects; i++ { 2522 assertExist(localProjects[i].Path) 2523 } 2524 for i := numProjects; i < numOptionalProjects+numProjects; i++ { 2525 assertNotExist(localProjects[i].Path) 2526 } 2527 assertNotExist(filepath.Join(fake.X.Root, pkg0.Path)) 2528 assertExist(filepath.Join(fake.X.Root, pkg1.Path)) 2529 2530 // Try setting attributes to "optional, testing" 2531 fake.X.FetchingAttrs = "optional, testing" 2532 fake.UpdateUniverse(true) 2533 for i := 0; i < numProjects; i++ { 2534 assertExist(localProjects[i].Path) 2535 } 2536 for i := numProjects; i < numOptionalProjects+numProjects; i++ { 2537 assertExist(localProjects[i].Path) 2538 } 2539 assertExist(filepath.Join(fake.X.Root, pkg0.Path)) 2540 assertExist(filepath.Join(fake.X.Root, pkg1.Path)) 2541 2542 // Reset optional attributes 2543 fake.X.FetchingAttrs = "nonexist" 2544 fake.UpdateUniverse(true) 2545 for i := 0; i < numProjects; i++ { 2546 assertExist(localProjects[i].Path) 2547 } 2548 for i := numProjects; i < numOptionalProjects+numProjects; i++ { 2549 assertNotExist(localProjects[i].Path) 2550 } 2551 assertNotExist(filepath.Join(fake.X.Root, pkg0.Path)) 2552 assertExist(filepath.Join(fake.X.Root, pkg1.Path)) 2553 } 2554 2555 func TestMultiplePackageVersions(t *testing.T) { 2556 fake, cleanup := jiritest.NewFakeJiriRoot(t) 2557 defer cleanup() 2558 2559 pkg0 := project.Package{ 2560 Name: "fuchsia/tools/jiri/${platform}", 2561 Path: "path-pkg0", 2562 Version: "git_revision:9904764ed228c7fb87bfb252762952b502d1e360", 2563 } 2564 pkg1 := project.Package{ 2565 Name: "fuchsia/tools/jiri/${platform}", 2566 Path: "path-pkg1", 2567 Version: "git_revision:05715c8fbbdb952ab38e50533a1b653445e74b40", 2568 } 2569 2570 fake.AddPackage(pkg0) 2571 fake.AddPackage(pkg1) 2572 2573 pathExists := func(projPath string) bool { 2574 if _, err := os.Stat(projPath); err != nil { 2575 if os.IsNotExist(err) { 2576 return false 2577 } 2578 t.Errorf("failed to access path due to error: %v", err) 2579 } 2580 return true 2581 } 2582 assertExist := func(localPath string) { 2583 if !pathExists(localPath) { 2584 t.Errorf("expecting path %q exists, but it does not", localPath) 2585 } 2586 } 2587 2588 fake.UpdateUniverse(true) 2589 2590 assertExist(filepath.Join(fake.X.Root, pkg0.Path)) 2591 assertExist(filepath.Join(fake.X.Root, pkg1.Path)) 2592 } 2593 2594 func TestOverrideImport(t *testing.T) { 2595 _, fake, cleanup := setupUniverse(t) 2596 defer cleanup() 2597 2598 // override the import 2599 hashes, ok := fake.ProjectHashes[jiritest.ManifestProjectName] 2600 if !ok || len(hashes) < 2 { 2601 t.Errorf("failed to retrieve git hashes of manifest project") 2602 } 2603 testHash := hashes[len(hashes)-2] 2604 fake.AddImportOverride(jiritest.ManifestProjectName, fake.Projects[jiritest.ManifestProjectName], testHash, jiritest.ManifestFileName) 2605 if err := fake.UpdateUniverse(false); err != nil { 2606 t.Errorf("Update universe failed due to error: %v", err) 2607 } 2608 scm := gitutil.New(fake.X, gitutil.RootDirOpt(filepath.Join(fake.X.Root, jiritest.ManifestProjectPath))) 2609 currentHash, err := scm.CurrentRevision() 2610 if err != nil { 2611 t.Errorf("failed to get current hash due to error: %v", err) 2612 } 2613 if currentHash != testHash { 2614 t.Errorf("expected git hash %q, got %q", testHash, currentHash) 2615 } 2616 } 2617 2618 func TestOverrideProject(t *testing.T) { 2619 localProjects, fake, cleanup := setupUniverse(t) 2620 defer cleanup() 2621 2622 // override localProjects 1 2623 hashes, ok := fake.ProjectHashes[localProjects[0].Name] 2624 if !ok || len(hashes) == 0 { 2625 t.Errorf("fail to retrieve git hashes of test project 0") 2626 } 2627 testHash := hashes[0] 2628 fake.AddProjectOverride(localProjects[0].Name, localProjects[0].Remote, testHash) 2629 2630 fake.UpdateUniverse(true) 2631 scm := gitutil.New(fake.X, gitutil.RootDirOpt(localProjects[0].Path)) 2632 currentHash, err := scm.CurrentRevision() 2633 if err != nil { 2634 t.Errorf("failed to get current hash due to error: %v", err) 2635 } 2636 if currentHash != testHash { 2637 t.Errorf("expected git hash %q, got %q", testHash, currentHash) 2638 } 2639 } 2640 2641 func TestHostnameAllowed(t *testing.T) { 2642 tests := map[string]bool{ 2643 "*.google.com,fuchsia.google.com": true, 2644 "*.google.com,fuchsia.dev.google.com": true, 2645 "google.com,google.com": true, 2646 "*google.com,fuchsiagoogle.com": true, 2647 "google.com,fuchsiagoogle.com": false, 2648 "google.com,oogle.com": false, 2649 "fuchsia-internal,fuchsia-internal": true, 2650 "fuchsia-internal,fuchsia": false, 2651 ",": true, 2652 "*google*.com,github.com/btwiuse": false, 2653 } 2654 for k, v := range tests { 2655 test := strings.Split(k, ",") 2656 if len(test) != 2 { 2657 t.Errorf("expecting a single ',' in %q", k) 2658 } 2659 ret := project.HostnameAllowed(test[0], test[1]) 2660 if v != ret { 2661 t.Errorf("expecting %v, got %v from test %q", v, ret, k) 2662 } 2663 } 2664 } 2665 2666 func TestCheckProjectsHostnames(t *testing.T) { 2667 allowList := []string{ 2668 "*.googlesource.com", 2669 "fuchsia-internal", 2670 } 2671 allowListIllegal := []string{ 2672 "*.google*.com", 2673 "fuchsia-internal", 2674 } 2675 testProjectListsTrue := []project.Project{ 2676 { 2677 Name: "project1", 2678 Remote: "https://github.com/btwiuse/project1", 2679 }, 2680 { 2681 Name: "project2", 2682 Remote: "https://chromium.googlesource.com/project2", 2683 }, 2684 { 2685 Name: "project4", 2686 Remote: "sso://fuchsia-internal/project4", 2687 }, 2688 } 2689 testProjectListsFalse := []project.Project{ 2690 { 2691 Name: "project1", 2692 Remote: "https://github.com/btwiuse/project1", 2693 }, 2694 { 2695 Name: "project2", 2696 Remote: "https://chromium.googlesource.com/project2", 2697 }, 2698 { 2699 Name: "project3", 2700 Remote: "https://test.github.com/project3", 2701 }, 2702 { 2703 Name: "project4", 2704 Remote: "sso://fuchsia-internal/project4", 2705 }, 2706 } 2707 2708 mapTrue := make(project.Projects) 2709 for _, item := range testProjectListsTrue { 2710 mapTrue[item.Key()] = item 2711 } 2712 if err := project.CheckProjectsHostnames(mapTrue, allowList); err != nil { 2713 t.Errorf("expecting nil from CheckProjectsHostnames, but got: %v", err) 2714 } 2715 2716 mapFalse := make(project.Projects) 2717 for _, item := range testProjectListsFalse { 2718 mapFalse[item.Key()] = item 2719 } 2720 if err := project.CheckProjectsHostnames(mapFalse, allowList); err == nil { 2721 t.Errorf("expecting error from CheckProjectsHostnames, but got nil") 2722 } 2723 2724 if err := project.CheckProjectsHostnames(mapTrue, allowListIllegal); err == nil { 2725 t.Errorf("expecting error from CheckProjectsHostnames, but got nil") 2726 } 2727 }