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 }