| // 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) |
| } |