blob: e19083baea673777885e05b42d535831702ebf1c [file] [log] [blame]
package task
import (
"context"
"flag"
"fmt"
"net/http"
"net/http/httptest"
"reflect"
"runtime"
"strings"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/uuid"
"golang.org/x/build/gerrit"
"golang.org/x/build/internal/workflow"
)
var flagRunTagXTest = flag.Bool("run-tagx-test", false, "run tag x/ repo test, which is read-only and safe. Must have a Gerrit cookie in gitcookies.")
func TestSelectReposLive(t *testing.T) {
if !*flagRunTagXTest {
t.Skip("Not enabled by flags")
}
tasks := &TagXReposTasks{
Gerrit: &RealGerritClient{
Client: gerrit.NewClient("https://go-review.googlesource.com", gerrit.GitCookiesAuth()),
},
}
ctx := &workflow.TaskContext{
Context: context.Background(),
Logger: &testLogger{t, ""},
}
repos, err := tasks.SelectRepos(ctx)
if err != nil {
t.Fatal(err)
}
for _, r := range repos {
t.Logf("%#v", r)
}
}
func TestCycles(t *testing.T) {
tests := []struct {
repos []TagRepo
want []string
}{
{
repos: []TagRepo{
{Name: "text", Deps: []string{"tools"}},
{Name: "tools", Deps: []string{"text"}},
{Name: "sys"},
{Name: "net", Deps: []string{"sys"}},
},
want: []string{
"tools,text,tools",
"text,tools,text",
},
},
{
repos: []TagRepo{
{Name: "text", Deps: []string{"tools"}},
{Name: "tools", Deps: []string{"fake"}},
{Name: "fake", Deps: []string{"text"}},
},
want: []string{
"tools,fake,text,tools",
"text,tools,fake,text",
"fake,text,tools,fake",
},
},
{
repos: []TagRepo{
{Name: "text", Deps: []string{"tools"}},
{Name: "tools", Deps: []string{"fake", "text"}},
{Name: "fake", Deps: []string{"tools"}},
},
want: []string{
"tools,text,tools",
"text,tools,text",
"tools,fake,tools",
"fake,tools,fake",
},
},
{
repos: []TagRepo{
{Name: "text", Deps: []string{"tools"}},
{Name: "tools", Deps: []string{"fake", "text"}},
{Name: "fake1", Deps: []string{"fake2"}},
{Name: "fake2", Deps: []string{"tools"}},
},
want: []string{
"tools,text,tools",
"text,tools,text",
},
},
}
for _, tt := range tests {
var repos []TagRepo
for _, r := range tt.repos {
repos = append(repos, TagRepo{
Name: r.Name,
ModPath: r.Name,
Deps: r.Deps,
})
}
cycles := checkCycles(repos)
got := map[string]bool{}
for _, cycle := range cycles {
got[strings.Join(cycle, ",")] = true
}
want := map[string]bool{}
for _, cycle := range tt.want {
want[cycle] = true
}
if diff := cmp.Diff(got, want); diff != "" {
t.Errorf("%v result unexpected: %v", tt.repos, diff)
}
}
}
const fakeGo = `#!/bin/bash -eu
case "$1" in
"get")
ls go.mod go.sum >/dev/null
for i in "${@:2}"; do
echo -e "// pretend we've upgraded to $i" >> go.mod
echo "$i h1:asdasd" | tr '@' ' ' >> go.sum
done
;;
"mod")
ls go.mod go.sum >/dev/null
echo "tidied!" >> go.mod
;;
*)
echo unexpected command $@
exit 1
;;
esac
`
func TestTagXRepos(t *testing.T) {
if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
t.Skip("Requires bash shell scripting support.")
}
goServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ServeTarball("dl/go1.19.linux-amd64.tar.gz", map[string]string{
"go/bin/go": fakeGo,
}, w, r)
}))
t.Cleanup(goServer.Close)
fakeBuildlets := NewFakeBuildlets(t, "")
sys := NewFakeRepo(t, "sys")
sys1 := sys.Commit(map[string]string{
"go.mod": "module golang.org/x/sys\n",
"go.sum": "\n",
})
sys.Tag("v0.1.0", sys1)
sys2 := sys.Commit(map[string]string{
"main.go": "package main",
})
mod := NewFakeRepo(t, "mod")
mod1 := mod.Commit(map[string]string{
"go.mod": "module golang.org/x/mod\n",
"go.sum": "\n",
})
mod.Tag("v1.0.0", mod1)
tools := NewFakeRepo(t, "tools")
tools1 := tools.Commit(map[string]string{
"go.mod": "module golang.org/x/tools\nrequire golang.org/x/mod v1.0.0\nrequire golang.org/x/sys v0.1.0\n",
"go.sum": "\n",
})
tools.Tag("v1.1.5", tools1)
fakeGerrit := NewFakeGerrit(t, sys, mod, tools)
tasks := &TagXReposTasks{
Gerrit: fakeGerrit,
GerritURL: fakeGerrit.GerritURL(),
CreateBuildlet: fakeBuildlets.CreateBuildlet,
LatestGoBinaries: func(context.Context) (string, error) {
return goServer.URL + "/dl/go1.19.linux-amd64.tar.gz", nil
},
}
wd := tasks.NewDefinition()
w, err := workflow.Start(wd, nil)
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
_, err = w.Run(ctx, &verboseListener{t: t})
if err != nil {
t.Fatal(err)
}
tag, err := fakeGerrit.GetTag(ctx, "sys", "v0.2.0")
if err != nil {
t.Fatalf("sys should have been tagged with v0.2.0: %v", err)
}
if tag.Revision != sys2 {
t.Errorf("sys v0.2.0 = %v, want %v", tag.Revision, sys2)
}
tags, err := fakeGerrit.ListTags(ctx, "mod")
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(tags, []string{"v1.0.0"}) {
t.Errorf("mod has tags %v, wanted only v1.0.0", tags)
}
tag, err = fakeGerrit.GetTag(ctx, "tools", "v1.2.0")
if err != nil {
t.Fatalf("tools should have been tagged with v1.2.0: %v", err)
}
goMod, err := fakeGerrit.ReadFile(ctx, "tools", tag.Revision, "go.mod")
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(goMod), "sys@v0.2.0") || !strings.Contains(string(goMod), "mod@v1.0.0") {
t.Errorf("tools should use sys v0.2.0 and mod v1.0.0. go.mod: %v", string(goMod))
}
if !strings.Contains(string(goMod), "tidied!") {
t.Error("tools go.mod should be tidied")
}
}
type verboseListener struct {
t *testing.T
outputListener func(string, interface{})
}
func (l *verboseListener) TaskStateChanged(_ uuid.UUID, _ string, st *workflow.TaskState) error {
switch {
case !st.Finished:
l.t.Logf("task %-10v: started", st.Name)
case st.Error != "":
l.t.Logf("task %-10v: error: %v", st.Name, st.Error)
default:
l.t.Logf("task %-10v: done: %v", st.Name, st.Result)
if l.outputListener != nil {
l.outputListener(st.Name, st.Result)
}
}
return nil
}
func (l *verboseListener) Logger(_ uuid.UUID, task string) workflow.Logger {
return &testLogger{t: l.t, task: task}
}
type testLogger struct {
t *testing.T
task string // Optional.
}
func (l *testLogger) Printf(format string, v ...interface{}) {
l.t.Logf("task %-10v: LOG: %s", l.task, fmt.Sprintf(format, v...))
}