Source file src/cmd/go/internal/vcweb/vcstest/vcstest_test.go

     1  // Copyright 2022 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package vcstest_test
     6  
     7  import (
     8  	"cmd/go/internal/vcweb"
     9  	"errors"
    10  	"flag"
    11  	"fmt"
    12  	"io"
    13  	"io/fs"
    14  	"log"
    15  	"net"
    16  	"net/http"
    17  	"net/http/httptest"
    18  	"os"
    19  	"os/exec"
    20  	"path/filepath"
    21  	"strings"
    22  	"testing"
    23  	"time"
    24  )
    25  
    26  var (
    27  	dir  = flag.String("dir", "../../../testdata/vcstest", "directory containing scripts to serve")
    28  	host = flag.String("host", "localhost", "hostname on which to serve HTTP")
    29  	port = flag.Int("port", -1, "port on which to serve HTTP; if nonnegative, skips running tests")
    30  )
    31  
    32  func TestMain(m *testing.M) {
    33  	flag.Parse()
    34  
    35  	if *port >= 0 {
    36  		err := serveStandalone(*host, *port)
    37  		if err != nil {
    38  			log.Fatal(err)
    39  		}
    40  		os.Exit(0)
    41  	}
    42  
    43  	m.Run()
    44  }
    45  
    46  // serveStandalone serves the vcweb testdata in a standalone HTTP server.
    47  func serveStandalone(host string, port int) (err error) {
    48  	scriptDir, err := filepath.Abs(*dir)
    49  	if err != nil {
    50  		return err
    51  	}
    52  	work, err := os.MkdirTemp("", "vcweb")
    53  	if err != nil {
    54  		return err
    55  	}
    56  	defer func() {
    57  		if rmErr := os.RemoveAll(work); err == nil {
    58  			err = rmErr
    59  		}
    60  	}()
    61  
    62  	log.Printf("running scripts in %s", work)
    63  
    64  	v, err := vcweb.NewServer(scriptDir, work, log.Default())
    65  	if err != nil {
    66  		return err
    67  	}
    68  
    69  	l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", host, port))
    70  	if err != nil {
    71  		return err
    72  	}
    73  	log.Printf("serving on http://%s:%d/", host, l.Addr().(*net.TCPAddr).Port)
    74  
    75  	return http.Serve(l, v)
    76  }
    77  
    78  // TestScripts verifies that the VCS setup scripts in cmd/go/testdata/vcstest
    79  // run successfully.
    80  func TestScripts(t *testing.T) {
    81  	scriptDir, err := filepath.Abs(*dir)
    82  	if err != nil {
    83  		t.Fatal(err)
    84  	}
    85  	s, err := vcweb.NewServer(scriptDir, t.TempDir(), log.Default())
    86  	if err != nil {
    87  		t.Fatal(err)
    88  	}
    89  	srv := httptest.NewServer(s)
    90  
    91  	// To check for data races in the handler, run the root handler to produce an
    92  	// overview of the script status at an arbitrary point during the test.
    93  	// (We ignore the output because the expected failure mode is a friendly stack
    94  	// dump from the race detector.)
    95  	t.Run("overview", func(t *testing.T) {
    96  		t.Parallel()
    97  
    98  		time.Sleep(1 * time.Millisecond) // Give the other handlers time to race.
    99  
   100  		resp, err := http.Get(srv.URL)
   101  		if err == nil {
   102  			io.Copy(io.Discard, resp.Body)
   103  			resp.Body.Close()
   104  		} else {
   105  			t.Error(err)
   106  		}
   107  	})
   108  
   109  	t.Cleanup(func() {
   110  		// The subtests spawned by WalkDir run in parallel. When they complete, this
   111  		// Cleanup callback will run. At that point we fetch the root URL (which
   112  		// contains a status page), both to test that the root handler runs without
   113  		// crashing and to display a nice summary of the server's view of the test
   114  		// coverage.
   115  		resp, err := http.Get(srv.URL)
   116  		if err == nil {
   117  			var body []byte
   118  			body, err = io.ReadAll(resp.Body)
   119  			if err == nil && testing.Verbose() {
   120  				t.Logf("GET %s:\n%s", srv.URL, body)
   121  			}
   122  			resp.Body.Close()
   123  		}
   124  		if err != nil {
   125  			t.Error(err)
   126  		}
   127  
   128  		srv.Close()
   129  	})
   130  
   131  	err = filepath.WalkDir(scriptDir, func(path string, d fs.DirEntry, err error) error {
   132  		if err != nil || d.IsDir() {
   133  			return err
   134  		}
   135  
   136  		rel, err := filepath.Rel(scriptDir, path)
   137  		if err != nil {
   138  			return err
   139  		}
   140  		if rel == "README" {
   141  			return nil
   142  		}
   143  
   144  		t.Run(filepath.ToSlash(rel), func(t *testing.T) {
   145  			t.Parallel()
   146  
   147  			buf := new(strings.Builder)
   148  			logger := log.New(buf, "", log.LstdFlags)
   149  			// Load the script but don't try to serve the results:
   150  			// different VCS tools have different handler protocols,
   151  			// and the tests that actually use these repos will ensure
   152  			// that they are served correctly as a side effect anyway.
   153  			err := s.HandleScript(rel, logger, func(http.Handler) {})
   154  			if buf.Len() > 0 {
   155  				t.Log(buf)
   156  			}
   157  			if err != nil {
   158  				if notInstalled := (vcweb.ServerNotInstalledError{}); errors.As(err, &notInstalled) || errors.Is(err, exec.ErrNotFound) {
   159  					t.Skip(err)
   160  				}
   161  				t.Error(err)
   162  			}
   163  		})
   164  		return nil
   165  	})
   166  
   167  	if err != nil {
   168  		t.Error(err)
   169  	}
   170  }
   171  

View as plain text