Files
moxa/cmd/yaegi/yaegi_test.go
secDre4mer 938db1c6c2 fix: use path instead of filepath
Currently, yaegi uses the `filepath` package to join paths.

However, this has issues: YAEGI uses an `fs.FS` internally for source code access, and `fs` specifies that `/` separated paths must be used. On Unix systems, this obviously makes no difference; but on Windows, with a non-default FS, this causes errors (since YAEGI creates `\` separated paths, but the FS expects `/` separated paths).

Furthermore, generally speaking, Golang import paths and the like are always `/` separated anyway, regardless of OS.

To clear this up, this PR changes path handling to `/` separated paths. This has the advantage of being accepted by all OS (even Windows will accept `/` separated paths). Paths passed by the user to `CompilePath` or similar are changed to `/` separators, if necessary.
2025-05-21 21:22:05 +02:00

138 lines
3.3 KiB
Go

package main
import (
"bytes"
"context"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"testing"
"time"
)
const (
// CITimeoutMultiplier is the multiplier for all timeouts in the CI.
CITimeoutMultiplier = 3
)
// Sleep pauses the current goroutine for at least the duration d.
func Sleep(d time.Duration) {
d = applyCIMultiplier(d)
time.Sleep(d)
}
func applyCIMultiplier(timeout time.Duration) time.Duration {
ci := os.Getenv("CI")
if ci == "" {
return timeout
}
b, err := strconv.ParseBool(ci)
if err != nil || !b {
return timeout
}
return time.Duration(float64(timeout) * CITimeoutMultiplier)
}
func TestYaegiCmdCancel(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping cancel test since windows has no os.Interrupt signal")
}
tmp := t.TempDir()
yaegi := filepath.Join(tmp, "yaegi")
args := []string{"build"}
if raceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
args = append(args, "-race")
}
args = append(args, "-o", yaegi, ".")
build := exec.Command("go", args...)
out, err := build.CombinedOutput()
if err != nil {
t.Fatalf("failed to build yaegi command: %v: %s", err, out)
}
// Test src must be terminated by a single newline.
tests := []string{
"for {}\n",
"select {}\n",
}
for _, src := range tests {
cmd := exec.Command(yaegi)
in, err := cmd.StdinPipe()
if err != nil {
t.Errorf("failed to get stdin pipe to yaegi command: %v", err)
}
var outBuf, errBuf bytes.Buffer
cmd.Stdout = &outBuf
cmd.Stderr = &errBuf
// https://golang.org/doc/articles/race_detector.html#Options
cmd.Env = []string{`GORACE="halt_on_error=1"`}
err = cmd.Start()
if err != nil {
t.Fatalf("failed to start yaegi command: %v", err)
}
_, err = in.Write([]byte(src))
if err != nil {
t.Errorf("failed pipe test source to yaegi command: %v", err)
}
Sleep(500 * time.Millisecond)
err = cmd.Process.Signal(os.Interrupt)
if err != nil {
t.Errorf("failed to send os.Interrupt to yaegi command: %v", err)
}
_, err = in.Write([]byte("1+1\n"))
if err != nil {
t.Errorf("failed to probe race: %v", err)
}
err = in.Close()
if err != nil {
t.Errorf("failed to close stdin pipe: %v", err)
}
err = cmd.Wait()
if err != nil {
if cmd.ProcessState.ExitCode() == 66 { // See race_detector.html article.
t.Errorf("race detected running yaegi command canceling %q: %v", src, err)
if testing.Verbose() {
t.Log(&errBuf)
}
} else {
t.Errorf("error running yaegi command for %q: %v", src, err)
}
continue
}
if strings.TrimSuffix(errBuf.String(), "\n") != context.Canceled.Error() {
t.Errorf("unexpected error: %q", &errBuf)
}
}
}
func raceDetectorSupported(goos, goarch string) bool {
if strings.Contains(os.Getenv("GOFLAGS"), "-buildmode=pie") {
// The Go race detector is not compatible with position independent code (pie).
// We read the conventional GOFLAGS env variable used for example on AlpineLinux
// to build packages, as there is no way to get this information from the runtime.
return false
}
switch goos {
case "linux":
return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64"
case "darwin":
return goarch == "amd64" || goarch == "arm64"
case "freebsd", "netbsd", "openbsd", "windows":
return goarch == "amd64"
default:
return false
}
}