gitlab.com/ethan.reesor/vscode-notebooks/yaegi@v0.0.0-20220417214422-5c573557938e/cmd/yaegi/yaegi_test.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"runtime"
    10  	"strconv"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  )
    15  
    16  const (
    17  	// CITimeoutMultiplier is the multiplier for all timeouts in the CI.
    18  	CITimeoutMultiplier = 3
    19  )
    20  
    21  // Sleep pauses the current goroutine for at least the duration d.
    22  func Sleep(d time.Duration) {
    23  	d = applyCIMultiplier(d)
    24  	time.Sleep(d)
    25  }
    26  
    27  func applyCIMultiplier(timeout time.Duration) time.Duration {
    28  	ci := os.Getenv("CI")
    29  	if ci == "" {
    30  		return timeout
    31  	}
    32  	b, err := strconv.ParseBool(ci)
    33  	if err != nil || !b {
    34  		return timeout
    35  	}
    36  	return time.Duration(float64(timeout) * CITimeoutMultiplier)
    37  }
    38  
    39  func TestYaegiCmdCancel(t *testing.T) {
    40  	tmp, err := os.MkdirTemp("", "yaegi-")
    41  	if err != nil {
    42  		t.Fatalf("failed to create tmp directory: %v", err)
    43  	}
    44  	defer func() {
    45  		err = os.RemoveAll(tmp)
    46  		if err != nil {
    47  			t.Errorf("failed to clean up %v: %v", tmp, err)
    48  		}
    49  	}()
    50  
    51  	yaegi := filepath.Join(tmp, "yaegi")
    52  
    53  	args := []string{"build"}
    54  	if raceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
    55  		args = append(args, "-race")
    56  	}
    57  	args = append(args, "-o", yaegi, ".")
    58  
    59  	build := exec.Command("go", args...)
    60  
    61  	out, err := build.CombinedOutput()
    62  	if err != nil {
    63  		t.Fatalf("failed to build yaegi command: %v: %s", err, out)
    64  	}
    65  
    66  	// Test src must be terminated by a single newline.
    67  	tests := []string{
    68  		"for {}\n",
    69  		"select {}\n",
    70  	}
    71  	for _, src := range tests {
    72  		cmd := exec.Command(yaegi)
    73  		in, err := cmd.StdinPipe()
    74  		if err != nil {
    75  			t.Errorf("failed to get stdin pipe to yaegi command: %v", err)
    76  		}
    77  		var outBuf, errBuf bytes.Buffer
    78  		cmd.Stdout = &outBuf
    79  		cmd.Stderr = &errBuf
    80  
    81  		// https://golang.org/doc/articles/race_detector.html#Options
    82  		cmd.Env = []string{`GORACE="halt_on_error=1"`}
    83  
    84  		err = cmd.Start()
    85  		if err != nil {
    86  			t.Fatalf("failed to start yaegi command: %v", err)
    87  		}
    88  
    89  		_, err = in.Write([]byte(src))
    90  		if err != nil {
    91  			t.Errorf("failed pipe test source to yaegi command: %v", err)
    92  		}
    93  		Sleep(500 * time.Millisecond)
    94  		err = cmd.Process.Signal(os.Interrupt)
    95  		if err != nil {
    96  			t.Errorf("failed to send os.Interrupt to yaegi command: %v", err)
    97  		}
    98  
    99  		_, err = in.Write([]byte("1+1\n"))
   100  		if err != nil {
   101  			t.Errorf("failed to probe race: %v", err)
   102  		}
   103  		err = in.Close()
   104  		if err != nil {
   105  			t.Errorf("failed to close stdin pipe: %v", err)
   106  		}
   107  
   108  		err = cmd.Wait()
   109  		if err != nil {
   110  			if cmd.ProcessState.ExitCode() == 66 { // See race_detector.html article.
   111  				t.Errorf("race detected running yaegi command canceling %q: %v", src, err)
   112  				if testing.Verbose() {
   113  					t.Log(&errBuf)
   114  				}
   115  			} else {
   116  				t.Errorf("error running yaegi command for %q: %v", src, err)
   117  			}
   118  			continue
   119  		}
   120  
   121  		if strings.TrimSuffix(errBuf.String(), "\n") != context.Canceled.Error() {
   122  			t.Errorf("unexpected error: %q", &errBuf)
   123  		}
   124  	}
   125  }
   126  
   127  func raceDetectorSupported(goos, goarch string) bool {
   128  	switch goos {
   129  	case "linux":
   130  		return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64"
   131  	case "darwin":
   132  		return goarch == "amd64" || goarch == "arm64"
   133  	case "freebsd", "netbsd", "openbsd", "windows":
   134  		return goarch == "amd64"
   135  	default:
   136  		return false
   137  	}
   138  }