// Copyright 2022 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 vcstest_test import ( "cmd/go/internal/vcweb" "errors" "flag" "fmt" "io" "io/fs" "log" "net" "net/http" "net/http/httptest" "os" "os/exec" "path/filepath" "strings" "testing" "time" ) var ( dir = flag.String("dir", "../../../testdata/vcstest", "directory containing scripts to serve") host = flag.String("host", "localhost", "hostname on which to serve HTTP") port = flag.Int("port", -1, "port on which to serve HTTP; if nonnegative, skips running tests") ) func TestMain(m *testing.M) { flag.Parse() if *port >= 0 { err := serveStandalone(*host, *port) if err != nil { log.Fatal(err) } os.Exit(0) } m.Run() } // serveStandalone serves the vcweb testdata in a standalone HTTP server. func serveStandalone(host string, port int) (err error) { scriptDir, err := filepath.Abs(*dir) if err != nil { return err } work, err := os.MkdirTemp("", "vcweb") if err != nil { return err } defer func() { if rmErr := os.RemoveAll(work); err == nil { err = rmErr } }() log.Printf("running scripts in %s", work) v, err := vcweb.NewServer(scriptDir, work, log.Default()) if err != nil { return err } l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", host, port)) if err != nil { return err } log.Printf("serving on http://%s:%d/", host, l.Addr().(*net.TCPAddr).Port) return http.Serve(l, v) } // TestScripts verifies that the VCS setup scripts in cmd/go/testdata/vcstest // run successfully. func TestScripts(t *testing.T) { scriptDir, err := filepath.Abs(*dir) if err != nil { t.Fatal(err) } s, err := vcweb.NewServer(scriptDir, t.TempDir(), log.Default()) if err != nil { t.Fatal(err) } srv := httptest.NewServer(s) // To check for data races in the handler, run the root handler to produce an // overview of the script status at an arbitrary point during the test. // (We ignore the output because the expected failure mode is a friendly stack // dump from the race detector.) t.Run("overview", func(t *testing.T) { t.Parallel() time.Sleep(1 * time.Millisecond) // Give the other handlers time to race. resp, err := http.Get(srv.URL) if err == nil { io.Copy(io.Discard, resp.Body) resp.Body.Close() } else { t.Error(err) } }) t.Cleanup(func() { // The subtests spawned by WalkDir run in parallel. When they complete, this // Cleanup callback will run. At that point we fetch the root URL (which // contains a status page), both to test that the root handler runs without // crashing and to display a nice summary of the server's view of the test // coverage. resp, err := http.Get(srv.URL) if err == nil { var body []byte body, err = io.ReadAll(resp.Body) if err == nil && testing.Verbose() { t.Logf("GET %s:\n%s", srv.URL, body) } resp.Body.Close() } if err != nil { t.Error(err) } srv.Close() }) err = filepath.WalkDir(scriptDir, func(path string, d fs.DirEntry, err error) error { if err != nil || d.IsDir() { return err } rel, err := filepath.Rel(scriptDir, path) if err != nil { return err } if rel == "README" { return nil } t.Run(filepath.ToSlash(rel), func(t *testing.T) { t.Parallel() buf := new(strings.Builder) logger := log.New(buf, "", log.LstdFlags) // Load the script but don't try to serve the results: // different VCS tools have different handler protocols, // and the tests that actually use these repos will ensure // that they are served correctly as a side effect anyway. err := s.HandleScript(rel, logger, func(http.Handler) {}) if buf.Len() > 0 { t.Log(buf) } if err != nil { if notInstalled := (vcweb.ServerNotInstalledError{}); errors.As(err, ¬Installed) || errors.Is(err, exec.ErrNotFound) { t.Skip(err) } t.Error(err) } }) return nil }) if err != nil { t.Error(err) } }