blob: de105cadc628290d13bc32a14141627dba4d7c30 [file] [log] [blame]
// Copyright 2017 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 main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"golang.org/x/build/internal/envutil"
repospkg "golang.org/x/build/repos"
)
func TestHomepage(t *testing.T) {
tm := newTestMirror(t)
if body := tm.get("/"); !strings.Contains(body, "build") {
t.Errorf("expected body to contain \"build\", didn't: %q", body)
}
}
func TestDebugWatcher(t *testing.T) {
tm := newTestMirror(t)
tm.commit("hello world")
tm.loopOnce()
body := tm.get("/debug/watcher/build")
if substr := `watcher status for repo: "build"`; !strings.Contains(body, substr) {
t.Fatalf("GET /debug/watcher/build: want %q in body, got %s", substr, body)
}
if substr := "waiting"; !strings.Contains(body, substr) {
t.Fatalf("GET /debug/watcher/build: want %q in body, got %s", substr, body)
}
}
func TestArchive(t *testing.T) {
tm := newTestMirror(t)
// Start with a revision we know about.
tm.commit("hello world")
initialRev := strings.TrimSpace(tm.git(tm.gerrit, "rev-parse", "HEAD"))
tm.loopOnce() // fetch the commit.
tm.get("/build.tar.gz?rev=" + initialRev)
// Now test one we don't see yet. It will be fetched automatically.
tm.commit("round two")
secondRev := strings.TrimSpace(tm.git(tm.gerrit, "rev-parse", "HEAD"))
// As of writing, the git version installed on the builders has some kind
// of bug that prevents the "git fetch" this triggers from working. Skip.
if strings.HasPrefix(tm.git(tm.gerrit, "version"), "git version 2.11") {
t.Skip("known-buggy git version")
}
tm.get("/build.tar.gz?rev=" + secondRev)
// Pick it up normally and re-fetch it to make sure we don't get confused.
tm.loopOnce()
tm.get("/build.tar.gz?rev=" + secondRev)
}
func TestMirror(t *testing.T) {
tm := newTestMirror(t)
for i := 0; i < 2; i++ {
tm.commit(fmt.Sprintf("revision %v", i))
rev := tm.git(tm.gerrit, "rev-parse", "HEAD")
tm.loopOnce()
if githubRev := tm.git(tm.github, "rev-parse", "HEAD"); rev != githubRev {
t.Errorf("github HEAD is %v, want %v", githubRev, rev)
}
if csrRev := tm.git(tm.csr, "rev-parse", "HEAD"); rev != csrRev {
t.Errorf("csr HEAD is %v, want %v", csrRev, rev)
}
}
}
// Tests that mirroring an initially empty repository works. See golang/go#39597.
// The repository still has to exist.
func TestMirrorInitiallyEmpty(t *testing.T) {
tm := newTestMirror(t)
if err := tm.m.repos["build"].loopOnce(); err == nil {
t.Error("expected error mirroring empty repository, got none")
}
tm.commit("first commit")
tm.loopOnce()
rev := tm.git(tm.gerrit, "rev-parse", "HEAD")
if githubRev := tm.git(tm.github, "rev-parse", "HEAD"); rev != githubRev {
t.Errorf("github HEAD is %v, want %v", githubRev, rev)
}
}
type testMirror struct {
// Local paths to the copies of the build repo.
gerrit, github, csr string
m *gitMirror
server *httptest.Server
buildRepo *repo
t *testing.T
}
// newTestMirror returns a mirror configured to watch the "build" repository
// and mirror it to GitHub and CSR. All repositories are faked out with local
// versions created hermetically. The mirror is idle and must be pumped with
// loopOnce.
func newTestMirror(t *testing.T) *testMirror {
if _, err := exec.LookPath("git"); err != nil {
t.Skip("skipping; git not in PATH")
}
goBase := t.TempDir()
gerrit := filepath.Join(goBase, "build")
if err := os.Mkdir(gerrit, 0755); err != nil {
t.Fatalf("error creating gerrit build directory: %v", err)
}
tm := &testMirror{
gerrit: gerrit,
github: t.TempDir(),
csr: t.TempDir(),
m: &gitMirror{
mux: http.NewServeMux(),
cacheDir: t.TempDir(),
homeDir: t.TempDir(),
// gitMirror generally expects goBase to be a URL, not
// a path, but git handles local paths just fine. As a
// result, gitMirror uses standard string concatenation
// rather than path.Join. Ensure the path ends in / to
// make sure concatenation is OK.
goBase: goBase + "/",
repos: map[string]*repo{},
mirrorGitHub: true,
mirrorCSR: true,
timeoutScale: 0,
},
t: t,
}
tm.m.mux.HandleFunc("/", tm.m.handleRoot)
tm.server = httptest.NewServer(tm.m.mux)
t.Cleanup(tm.server.Close)
// The origin is non-bare so we can commit to it; the destinations are
// bare so we can push to them.
initRepo := func(dir string, bare bool) {
initArgs := []string{"init"}
if bare {
initArgs = append(initArgs, "--bare")
}
for _, args := range [][]string{
initArgs,
{"config", "user.name", "Gopher"},
{"config", "user.email", "gopher@golang.org"},
} {
cmd := exec.Command("git", args...)
envutil.SetDir(cmd, dir)
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, out)
}
}
}
initRepo(tm.gerrit, false)
initRepo(tm.github, true)
initRepo(tm.csr, true)
tm.buildRepo = tm.m.addRepo(&repospkg.Repo{
GoGerritProject: "build",
ImportPath: "golang.org/x/build",
MirrorToGitHub: true,
GitHubRepo: "golang/build",
MirrorToCSRProject: "golang-org",
})
if err := tm.buildRepo.init(); err != nil {
t.Fatal(err)
}
// Manually add mirror repos. We can't use tm.m.addMirrors, as they
// hard-codes the real remotes, but we need to use local test
// directories.
tm.buildRepo.addRemote("github", tm.github, "")
tm.buildRepo.addRemote("csr", tm.csr, "")
return tm
}
func (tm *testMirror) loopOnce() {
tm.t.Helper()
if err := tm.buildRepo.loopOnce(); err != nil {
tm.t.Fatal(err)
}
}
func (tm *testMirror) commit(content string) {
tm.t.Helper()
if err := os.WriteFile(filepath.Join(tm.gerrit, "README"), []byte(content), 0777); err != nil {
tm.t.Fatal(err)
}
tm.git(tm.gerrit, "add", ".")
tm.git(tm.gerrit, "commit", "-m", content)
}
func (tm *testMirror) git(dir string, args ...string) string {
tm.t.Helper()
cmd := exec.Command("git", args...)
envutil.SetDir(cmd, dir)
out, err := cmd.CombinedOutput()
if err != nil {
tm.t.Fatalf("git: %v, %s", err, out)
}
return string(out)
}
func (tm *testMirror) get(path string) string {
tm.t.Helper()
resp, err := http.Get(tm.server.URL + path)
if err != nil {
tm.t.Fatal(err)
}
body, err := io.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
tm.t.Fatal(err)
}
if resp.StatusCode != http.StatusOK {
tm.t.Fatalf("request for %q failed", path)
}
return string(body)
}