github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/go/vcweb/vcstest/vcstest_test.go (about)

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