| // Copyright 2019 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| package repo |
| |
| import ( |
| "context" |
| "encoding/base64" |
| "fmt" |
| "io/ioutil" |
| "net/http" |
| "os" |
| "path/filepath" |
| "reflect" |
| "testing" |
| |
| "github.com/golang/mock/gomock" |
| "go.chromium.org/chromiumos/infra/go/internal/gerrit" |
| "go.chromium.org/chromiumos/infra/go/internal/util" |
| bbproto "go.chromium.org/luci/buildbucket/proto" |
| gitilespb "go.chromium.org/luci/common/proto/gitiles" |
| "gotest.tools/assert" |
| ) |
| |
| func TestFetchFilesFromGitiles_success(t *testing.T) { |
| ctl := gomock.NewController(t) |
| defer ctl.Finish() |
| |
| // This is a base64-encoded .tar.gz file containing a snapshot of external full.xml and |
| // _remotes.xml. I apologize to your eyes. This is a good representation of what comes back from |
| // the Gitiles Archive call though. |
| base64Enc := `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` |
| // Override because this is the name of the file in the above base64 gibberish. |
| rootXml = "full.xml" |
| encodedZip, err := base64.StdEncoding.DecodeString(base64Enc) |
| if err != nil { |
| t.Error(err) |
| } |
| |
| gitilesMock := gitilespb.NewMockGitilesClient(ctl) |
| gitilesMock.EXPECT().Archive(gomock.Any(), gomock.Any()).Times(2).Return( |
| &gitilespb.ArchiveResponse{ |
| Contents: encodedZip, |
| }, |
| nil, |
| ) |
| gerrit.MockGitiles = gitilesMock |
| gitilesCommit := &bbproto.GitilesCommit{ |
| Host: "chrome-internal.googlesource.com", |
| Project: "chromeos/manifest-internal", |
| Id: "snapshot", |
| } |
| |
| m, err := GetRepoToRemoteBranchToSourceRootFromManifests(http.DefaultClient, context.Background(), gitilesCommit) |
| if err != nil { |
| t.Error(err) |
| } |
| if len(m) != 176 { |
| t.Errorf("expected %d project mappings, found %d", 176, len(m)) |
| } |
| // Make sure that a sample project is present. |
| if m["chromiumos/platform/mosys"]["refs/heads/master"] != "src/platform/mosys" { |
| t.Errorf("expected to find a mapping for mosys repo. Got mappings: %v", m) |
| } |
| } |
| |
| func TestGetRepoToRemoteBranchToSourceRootFromManifestFile_success(t *testing.T) { |
| m, err := GetRepoToRemoteBranchToSourceRootFromManifestFile("test_data/foo.xml") |
| if err != nil { |
| t.Error(err) |
| } |
| if len(m) != 4 { |
| t.Errorf("expected %d project mappings, found %d", 4, len(m)) |
| } |
| // Make sure that a sample project is present. |
| if m["baz"]["refs/heads/master"] != "baz/" { |
| t.Errorf("expected to find a mapping for baz. Got mappings: %v", m) |
| } |
| } |
| |
| func TestGetRepoToRemoteBranchToSourceRootFromManifestFile_duplicate(t *testing.T) { |
| m, err := GetRepoToRemoteBranchToSourceRootFromManifestFile("test_data/duplicate.xml") |
| if err != nil { |
| t.Error(err) |
| } |
| if len(m) != 1 { |
| t.Errorf("expected %d project mappings, found %d", 1, len(m)) |
| } |
| // The last mapping for a given name and branch should take precedent. |
| if m["foo"]["refs/heads/master"] != "buz/" { |
| t.Errorf("expected to find a mapping for buz. Got mappings: %v", m) |
| } |
| } |
| |
| func ManifestEq(a, b *Manifest) bool { |
| if len(a.Projects) != len(b.Projects) { |
| return false |
| } |
| for i := range a.Projects { |
| if !reflect.DeepEqual(&a.Projects[i], &b.Projects[i]) { |
| return false |
| } |
| } |
| if len(a.Includes) != len(b.Includes) { |
| return false |
| } |
| for i := range a.Includes { |
| if a.Includes[i] != b.Includes[i] { |
| return false |
| } |
| } |
| return true |
| } |
| |
| func ManifestMapEq(expected, actual map[string]*Manifest) error { |
| for file := range expected { |
| if _, ok := actual[file]; !ok { |
| return fmt.Errorf("missing manifest %s", file) |
| } |
| if !ManifestEq(expected[file], actual[file]) { |
| return fmt.Errorf("expected %v, found %v", expected[file], actual[file]) |
| } |
| } |
| return nil |
| } |
| |
| func TestLoadManifestTree_success(t *testing.T) { |
| expected_results := make(map[string]*Manifest) |
| expected_results["foo.xml"] = &Manifest{ |
| Projects: []Project{ |
| {Path: "baz/", Name: "baz", Revision: "123", RemoteName: "chromium"}, |
| {Path: "fiz/", Name: "fiz", Revision: "124", RemoteName: "chromeos"}, |
| {Path: "buz", Name: "buz", Revision: "125", RemoteName: "google", |
| Annotations: []Annotation{ |
| {Name: "branch-mode", Value: "pin"}, |
| }, |
| }, |
| }, |
| Includes: []Include{ |
| {"sub/bar.xml"}, |
| }, |
| } |
| expected_results["sub/bar.xml"] = &Manifest{ |
| Projects: []Project{ |
| {Path: "baz/", Name: "baz"}, |
| {Path: "project/", Name: "project"}, |
| }, |
| } |
| |
| res, err := LoadManifestTree("test_data/foo.xml") |
| assert.NilError(t, err) |
| if err = ManifestMapEq(expected_results, res); err != nil { |
| t.Errorf(err.Error()) |
| } |
| } |
| |
| func TestLoadManifestTree_bad_include(t *testing.T) { |
| _, err := LoadManifestTree("test_data/bogus.xml") |
| assert.ErrorContains(t, err, "bad-include.xml") |
| } |
| |
| func TestLoadManifestTree_bad_xml(t *testing.T) { |
| _, err := LoadManifestTree("test_data/invalid.xml") |
| assert.ErrorContains(t, err, "unmarshal") |
| } |
| |
| func TestGetUniqueProject(t *testing.T) { |
| manifest := &Manifest{ |
| Projects: []Project{ |
| {Path: "foo-a/", Name: "foo"}, |
| {Path: "foo-b/", Name: "foo"}, |
| {Path: "bar/", Name: "bar"}, |
| }, |
| } |
| |
| _, err := manifest.GetUniqueProject("foo") |
| assert.ErrorContains(t, err, "multiple projects") |
| |
| project, err := manifest.GetUniqueProject("bar") |
| assert.NilError(t, err) |
| assert.Assert(t, reflect.DeepEqual(&project, &manifest.Projects[2])) |
| } |
| |
| func TestWrite(t *testing.T) { |
| tmpDir := "repotest_tmp_dir" |
| tmpDir, err := ioutil.TempDir("", tmpDir) |
| defer os.RemoveAll(tmpDir) |
| assert.NilError(t, err) |
| tmpPath := filepath.Join(tmpDir, "foo.xml") |
| |
| manifest := &Manifest{ |
| Projects: []Project{ |
| {Path: "foo-a/", Name: "foo"}, |
| {Path: "foo-b/", Name: "foo"}, |
| {Path: "bar/", Name: "bar"}, |
| }, |
| } |
| manifest.Write(tmpPath) |
| // Make sure file was written successfully. |
| _, err = os.Stat(tmpPath) |
| assert.NilError(t, err) |
| // Make sure manifest was marshalled and written correctly. |
| manifestMap, err := LoadManifestTree(tmpPath) |
| assert.NilError(t, err) |
| assert.Assert(t, reflect.DeepEqual(manifest, manifestMap["foo.xml"])) |
| } |
| |
| func TestGitName(t *testing.T) { |
| remote := Remote{ |
| Alias: "batman", |
| Name: "bruce wayne", |
| } |
| assert.Equal(t, remote.GitName(), "batman") |
| remote = Remote{ |
| Name: "robin", |
| } |
| assert.Equal(t, remote.GitName(), "robin") |
| } |
| |
| func TestGetProjectByName(t *testing.T) { |
| m := Manifest{ |
| Projects: []Project{ |
| {Path: "a/", Name: "a"}, |
| {Path: "b/", Name: "b"}, |
| {Path: "c/", Name: "c"}, |
| }, |
| } |
| |
| project, err := m.GetProjectByName("b") |
| assert.NilError(t, err) |
| assert.Assert(t, reflect.DeepEqual(*project, m.Projects[1])) |
| project, err = m.GetProjectByName("d") |
| assert.Assert(t, err != nil) |
| } |
| func TestGetProjectByPath(t *testing.T) { |
| m := Manifest{ |
| Projects: []Project{ |
| {Path: "a/", Name: "a"}, |
| {Path: "b/", Name: "b"}, |
| {Path: "c/", Name: "c"}, |
| }, |
| } |
| |
| project, err := m.GetProjectByPath("b/") |
| assert.NilError(t, err) |
| assert.Assert(t, reflect.DeepEqual(*project, m.Projects[1])) |
| project, err = m.GetProjectByPath("d/") |
| assert.Assert(t, err != nil) |
| } |
| |
| func deref(projects []*Project) []Project { |
| res := []Project{} |
| for _, project := range projects { |
| res = append(res, *project) |
| } |
| return res |
| } |
| |
| func TestGetProjects(t *testing.T) { |
| m := Manifest{ |
| Projects: []Project{ |
| {Path: "a1/", Name: "chromiumos/a"}, |
| {Path: "a2/", Name: "chromiumos/a", Annotations: []Annotation{{Name: "branch-mode", Value: "pin"}}}, |
| {Path: "b/", Name: "b", Annotations: []Annotation{{Name: "branch-mode", Value: "pin"}}}, |
| {Path: "c/", Name: "c", Annotations: []Annotation{{Name: "branch-mode", Value: "tot"}}}, |
| {Path: "d/", Name: "chromiumos/d"}, |
| {Path: "e/", Name: "chromiumos/e"}, |
| }, |
| Remotes: []Remote{ |
| {Name: "cros"}, |
| }, |
| Default: Default{ |
| RemoteName: "cros", |
| }, |
| } |
| m = *m.ResolveImplicitLinks() |
| singleProjects := deref(m.GetSingleCheckoutProjects()) |
| assert.Assert(t, reflect.DeepEqual(singleProjects, m.Projects[4:6])) |
| multiProjects := deref(m.GetMultiCheckoutProjects()) |
| assert.Assert(t, reflect.DeepEqual(multiProjects, m.Projects[:2])) |
| pinnedProjects := deref(m.GetPinnedProjects()) |
| assert.Assert(t, reflect.DeepEqual(pinnedProjects, m.Projects[1:3])) |
| totProjects := deref(m.GetTotProjects()) |
| assert.Assert(t, reflect.DeepEqual(totProjects, m.Projects[3:4])) |
| } |
| |
| var canBranchTestManifestAnnotation = Manifest{ |
| Projects: []Project{ |
| // Projects with annotations labeling branch mode. |
| {Path: "foo1/", Name: "foo1", |
| Annotations: []Annotation{ |
| {Name: "branch-mode", Value: "create"}, |
| }, |
| }, |
| {Path: "foo2/", Name: "foo2", |
| Annotations: []Annotation{ |
| {Name: "branch-mode", Value: "pin"}, |
| }, |
| }, |
| {Path: "foo3/", Name: "foo3", |
| Annotations: []Annotation{ |
| {Name: "branch-mode", Value: "tot"}, |
| }, |
| }, |
| {Path: "foo4/", Name: "foo4", |
| Annotations: []Annotation{ |
| {Name: "branch-mode", Value: "bogus"}, |
| }, |
| }, |
| }, |
| } |
| var canBranchTestManifestRemote = Manifest{ |
| Projects: []Project{ |
| // Remote has name but no alias. Project is branchable. |
| {Path: "bar/", Name: "chromiumos/bar", RemoteName: "cros"}, |
| // Remote has alias. Project is branchable. |
| {Path: "baz1/", Name: "aosp/baz", RemoteName: "cros1"}, |
| // Remote has alias. Remote is not a cros remote. |
| {Path: "baz2/", Name: "aosp/baz", RemoteName: "cros2"}, |
| // Remote has alias. Remote is a cros remote, but not a branchable one. |
| {Path: "fizz/", Name: "fizz", RemoteName: "cros"}, |
| // Remote has name but no alias. Remote is a branchable remote, but specific |
| // project is not branchable. |
| {Path: "buzz/", Name: "buzz", RemoteName: "weave"}, |
| }, |
| Remotes: []Remote{ |
| {Name: "cros"}, |
| {Name: "cros1", Alias: "cros"}, |
| {Name: "cros2", Alias: "github"}, |
| {Name: "weave"}, |
| }, |
| } |
| |
| func TestProjectBranchMode_annotation(t *testing.T) { |
| manifest := canBranchTestManifestAnnotation |
| assert.Equal(t, manifest.ProjectBranchMode(manifest.Projects[0]), Create) |
| assert.Equal(t, manifest.ProjectBranchMode(manifest.Projects[1]), Pinned) |
| assert.Equal(t, manifest.ProjectBranchMode(manifest.Projects[2]), Tot) |
| assert.Equal(t, manifest.ProjectBranchMode(manifest.Projects[3]), UnspecifiedMode) |
| } |
| |
| func TestProjectBranchMode_remote(t *testing.T) { |
| manifest := canBranchTestManifestRemote |
| // Remote has name but no alias. Project is branchable. |
| assert.Equal(t, manifest.ProjectBranchMode(manifest.Projects[0]), Create) |
| // Remote has alias. Project is branchable. |
| assert.Equal(t, manifest.ProjectBranchMode(manifest.Projects[1]), Create) |
| // Remote has alias. Remote is not a cros remote. |
| assert.Equal(t, manifest.ProjectBranchMode(manifest.Projects[2]), Pinned) |
| // Remote has alias. Remote is a cros remote, but not a branchable one. |
| assert.Equal(t, manifest.ProjectBranchMode(manifest.Projects[4]), Pinned) |
| // Remote has name but no alias. Remote is a branchable remote, but specific |
| // project is not branchable. |
| assert.Equal(t, manifest.ProjectBranchMode(manifest.Projects[3]), Pinned) |
| } |
| |
| func TestMergeManifests(t *testing.T) { |
| // Manifest inheritance is as follows: |
| // a --> b --> c |
| // \ |
| // \--> d |
| a := Manifest{ |
| Default: Default{ |
| RemoteName: "cros", |
| Revision: "refs/heads/master", |
| }, |
| Remotes: []Remote{ |
| {Name: "cros"}, |
| {Name: "cros-internal"}, |
| }, |
| Projects: []Project{ |
| {Path: "project1/", Name: "project1"}, |
| {Path: "project2/", Name: "project2"}, |
| {Path: "project3/", Name: "project3", RemoteName: "cros-internal"}, |
| }, |
| Includes: []Include{ |
| {Name: "b.xml"}, |
| {Name: "d.xml"}, |
| }, |
| } |
| b := Manifest{ |
| Default: Default{ |
| RemoteName: "cros-internal", |
| Revision: "refs/heads/internal", |
| }, |
| Remotes: []Remote{ |
| {Name: "cros"}, |
| {Name: "cros-internal"}, |
| }, |
| Projects: []Project{ |
| {Path: "project3-v2/", Name: "project3"}, |
| {Path: "project4/", Name: "project4"}, |
| }, |
| Includes: []Include{ |
| {Name: "c.xml"}, |
| }, |
| } |
| c := Manifest{ |
| Default: Default{ |
| RemoteName: "cros-special", |
| Revision: "refs/heads/special", |
| }, |
| Remotes: []Remote{ |
| {Name: "cros-special", Revision: "refs/heads/unique"}, |
| }, |
| Projects: []Project{ |
| {Path: "project5/", Name: "project5"}, |
| }, |
| } |
| d := Manifest{ |
| Default: Default{ |
| RemoteName: "cros", |
| Revision: "refs/heads/develop", |
| }, |
| Remotes: []Remote{ |
| {Name: "cros"}, |
| }, |
| Projects: []Project{ |
| {Path: "project6/", Name: "project6"}, |
| {Path: "project7/", Name: "project7"}, |
| }, |
| } |
| manifestDict := map[string]*Manifest{ |
| "a.xml": &a, |
| "b.xml": &b, |
| "c.xml": &c, |
| "d.xml": &d, |
| } |
| expected := Manifest{ |
| Default: Default{ |
| RemoteName: "cros", |
| Revision: "refs/heads/master", |
| }, |
| Remotes: []Remote{ |
| {Name: "cros"}, |
| {Name: "cros-internal"}, |
| {Name: "cros-special", Revision: "refs/heads/unique"}, |
| }, |
| Projects: []Project{ |
| {Path: "project1/", Name: "project1", RemoteName: "cros", Revision: "refs/heads/master"}, |
| {Path: "project2/", Name: "project2", RemoteName: "cros", Revision: "refs/heads/master"}, |
| {Path: "project3/", Name: "project3", RemoteName: "cros-internal", Revision: "refs/heads/master"}, |
| {Path: "project3-v2/", Name: "project3", RemoteName: "cros-internal", Revision: "refs/heads/internal"}, |
| {Path: "project4/", Name: "project4", RemoteName: "cros-internal", Revision: "refs/heads/internal"}, |
| {Path: "project5/", Name: "project5", RemoteName: "cros-special", Revision: "refs/heads/unique"}, |
| {Path: "project6/", Name: "project6", RemoteName: "cros", Revision: "refs/heads/develop"}, |
| {Path: "project7/", Name: "project7", RemoteName: "cros", Revision: "refs/heads/develop"}, |
| }, |
| Includes: []Include{}, |
| } |
| mergedManifest, err := MergeManifests("a.xml", &manifestDict) |
| assert.NilError(t, err) |
| assert.Assert(t, reflect.DeepEqual(expected, *mergedManifest)) |
| } |
| |
| func TestLoadManifestFromFileWithIncludes(t *testing.T) { |
| expectedProjectNames := []string{"baz", "fiz", "buz", "project"} |
| |
| res, err := LoadManifestFromFileWithIncludes("test_data/foo.xml") |
| assert.NilError(t, err) |
| |
| projectNames := make([]string, len(res.Projects)) |
| for i, project := range res.Projects { |
| projectNames[i] = project.Name |
| } |
| assert.Assert(t, util.UnorderedEqual(expectedProjectNames, projectNames)) |
| } |