blob: 9df6865840a5a25d43dbe0ca8c517a973ee02ea1 [file] [log] [blame]
// 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])
}
}
}