| // Copyright 2024 The Go 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 task |
| |
| import ( |
| "context" |
| "crypto/rand" |
| "errors" |
| "fmt" |
| "slices" |
| "strings" |
| "testing" |
| "time" |
| |
| "golang.org/x/build/gerrit" |
| wf "golang.org/x/build/internal/workflow" |
| ) |
| |
| type fakeCoalesceGerrit struct { |
| *FakeGerrit |
| |
| changes map[string]*gerrit.ChangeInfo |
| commitMessages map[string]string |
| cherryPicks map[string][]cherryPickedCommit |
| } |
| |
| type cherryPickedCommit struct { |
| changeID string |
| message string |
| } |
| |
| func (g *fakeCoalesceGerrit) GetChange(_ context.Context, changeID string, _ ...gerrit.QueryChangesOpt) (*gerrit.ChangeInfo, error) { |
| ci, ok := g.changes[changeID] |
| if !ok { |
| return nil, errors.New("GetChange: not found") |
| } |
| return ci, nil |
| } |
| |
| func (g *fakeCoalesceGerrit) GetRevisionActions(_ context.Context, changeID string, revision string) (map[string]*gerrit.ActionInfo, error) { |
| if _, ok := g.changes[changeID]; !ok { |
| return nil, nil |
| } |
| action := &gerrit.ActionInfo{Enabled: true} |
| return map[string]*gerrit.ActionInfo{"submit": action}, nil |
| } |
| |
| func (g *fakeCoalesceGerrit) MoveChange(ctx context.Context, changeID string, branch string) (gerrit.ChangeInfo, error) { |
| ci, ok := g.changes[changeID] |
| if !ok { |
| return gerrit.ChangeInfo{}, errors.New("MoveChange: not found") |
| } |
| ci.Branch = branch |
| return *ci, nil |
| } |
| |
| func (g *fakeCoalesceGerrit) SubmitChange(ctx context.Context, changeID string) (gerrit.ChangeInfo, error) { |
| ci, ok := g.changes[changeID] |
| if !ok { |
| return gerrit.ChangeInfo{}, errors.New("SubmitChange: not found") |
| } |
| |
| r := make([]byte, 4) |
| rand.Read(r) |
| g.repos["go"].CommitOnBranch(ci.Branch, map[string]string{"patch": fmt.Sprintf("%x", r)}) |
| |
| ci.Status = gerrit.ChangeStatusMerged |
| |
| return *ci, nil |
| } |
| |
| func (g *fakeCoalesceGerrit) RebaseChange(ctx context.Context, changeID string, baseRev string) (gerrit.ChangeInfo, error) { |
| return *g.changes[changeID], nil |
| } |
| |
| func (g *fakeCoalesceGerrit) CreateCherryPick(ctx context.Context, changeID string, branch string, message string) (gerrit.ChangeInfo, bool, error) { |
| ci, ok := g.changes[changeID] |
| if !ok { |
| return gerrit.ChangeInfo{}, false, errors.New("CreateCherryPick: not found") |
| } |
| |
| g.cherryPicks[branch] = append(g.cherryPicks[branch], cherryPickedCommit{ci.ChangeID, message}) |
| return *ci, false, nil |
| } |
| |
| func (g *fakeCoalesceGerrit) GetCommitMessage(ctx context.Context, changeID string) (string, error) { |
| return g.commitMessages[changeID], nil |
| } |
| |
| type securityVersionClient struct { |
| GerritClient |
| tags []string |
| } |
| |
| func (c *securityVersionClient) ListTags(ctx context.Context, project string) ([]string, error) { |
| return c.tags, nil |
| } |
| |
| func (c *securityVersionClient) GetTag(ctx context.Context, project, tag string) (gerrit.TagInfo, error) { |
| for _, t := range c.tags { |
| if tag == t { |
| return gerrit.TagInfo{Created: gerrit.TimeStamp(time.Now())}, nil |
| } |
| } |
| return gerrit.TagInfo{}, errors.New("not found") |
| } |
| |
| func TestSecurityReleaseCoalesceTask(t *testing.T) { |
| privRepo := NewFakeRepo(t, "go") |
| privGerrit := &fakeCoalesceGerrit{FakeGerrit: NewFakeGerrit(t, privRepo), cherryPicks: map[string][]cherryPickedCommit{}, commitMessages: map[string]string{}} |
| task := &SecurityReleaseCoalesceTask{ |
| PrivateGerrit: privGerrit, |
| Version: &VersionTasks{ |
| Gerrit: &securityVersionClient{ |
| tags: []string{"go1.3", "go1.3.1", "go1.4", "go1.4.1"}, |
| }, |
| GoProject: "go", |
| }, |
| } |
| |
| privGerrit.changes = map[string]*gerrit.ChangeInfo{ |
| "1234": { |
| ID: "1234", |
| ChangeID: "1234", |
| ChangeNumber: 1234, |
| Branch: "public", |
| Submittable: true, |
| Mergeable: true, |
| }, |
| "5678": { |
| ID: "5678", |
| ChangeID: "5678", |
| ChangeNumber: 5678, |
| Branch: "public", |
| Submittable: true, |
| Mergeable: true, |
| }, |
| } |
| |
| privGerrit.commitMessages = map[string]string{ |
| "1234": `subject: 1234 |
| |
| body`, |
| "5678": `subject: 5678 |
| |
| other body`, |
| } |
| |
| head := privRepo.History()[0] |
| privRepo.Branch("public", head) |
| privRepo.Branch("release-branch.go1.3", head) |
| privRepo.Branch("release-branch.go1.4", head) |
| |
| wd := task.NewDefinition() |
| w, err := wf.Start(wd, map[string]interface{}{ |
| "Security Patch CL Numbers": []string{"1234", "5678"}, |
| }) |
| if err != nil { |
| t.Fatal(err) |
| } |
| ctx, cancel := context.WithCancel(context.Background()) |
| t.Cleanup(cancel) |
| _, err = w.Run(&wf.TaskContext{Context: ctx, Logger: &testLogger{t: t}}, &verboseListener{t: t}) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| // Check checkpoint branch has the expected number of submitted changes |
| commits := len(strings.Split(string(privRepo.runGit("log", "go1.4.2-and-go1.3.2-checkpoint", "--format=%H")), "\n")) - 1 |
| if commits != 3 { |
| t.Errorf("unexpected number of commits on checkpoint branch: got %d, want 3", commits) |
| } |
| |
| // Check each internal release branch has the expected cherry-picks |
| expected := map[string][]cherryPickedCommit{ |
| "internal-release-branch.go1.4.2": []cherryPickedCommit{ |
| { |
| changeID: "1234", |
| message: `[release-branch.go1.4] subject: 1234 |
| |
| body`, |
| }, |
| { |
| changeID: "5678", |
| message: `[release-branch.go1.4] subject: 5678 |
| |
| other body`, |
| }, |
| }, |
| "internal-release-branch.go1.3.2": []cherryPickedCommit{ |
| { |
| changeID: "1234", |
| message: `[release-branch.go1.3] subject: 1234 |
| |
| body`, |
| }, |
| { |
| changeID: "5678", |
| message: `[release-branch.go1.3] subject: 5678 |
| |
| other body`, |
| }, |
| }, |
| } |
| |
| for branch, commits := range privGerrit.cherryPicks { |
| if !slices.Equal(commits, expected[branch]) { |
| t.Errorf("unexpected cherry-picks on %s: got %s, want %s", branch, commits, expected[branch]) |
| } |
| } |
| } |