1// Copyright 2023 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// Not all systems have syscall.Mkfifo.
6//go:build !aix && !plan9 && !solaris && !wasm && !windows
7
8package wasi_test
9
10import (
11	"bufio"
12	"fmt"
13	"io"
14	"math/rand"
15	"os"
16	"os/exec"
17	"path/filepath"
18	"syscall"
19	"testing"
20)
21
22// This test creates a set of FIFOs and writes to them in reverse order. It
23// checks that the output order matches the write order. The test binary opens
24// the FIFOs in their original order and spawns a goroutine for each that reads
25// from the FIFO and writes the result to stderr. If I/O was blocking, all
26// goroutines would be blocked waiting for one read call to return, and the
27// output order wouldn't match.
28
29type fifo struct {
30	file *os.File
31	path string
32}
33
34func TestNonblock(t *testing.T) {
35	if target != "wasip1/wasm" {
36		t.Skip()
37	}
38
39	switch os.Getenv("GOWASIRUNTIME") {
40	case "wasmer":
41		t.Skip("wasmer does not support non-blocking I/O")
42	}
43
44	for _, mode := range []string{"os.OpenFile", "os.NewFile"} {
45		t.Run(mode, func(t *testing.T) {
46			args := []string{"run", "./testdata/nonblock.go", mode}
47
48			fifos := make([]*fifo, 8)
49			for i := range fifos {
50				path := filepath.Join(t.TempDir(), fmt.Sprintf("wasip1-nonblock-fifo-%d-%d", rand.Uint32(), i))
51				if err := syscall.Mkfifo(path, 0666); err != nil {
52					t.Fatal(err)
53				}
54
55				file, err := os.OpenFile(path, os.O_RDWR, 0)
56				if err != nil {
57					t.Fatal(err)
58				}
59				defer file.Close()
60
61				args = append(args, path)
62				fifos[len(fifos)-i-1] = &fifo{file, path}
63			}
64
65			subProcess := exec.Command("go", args...)
66
67			subProcess.Env = append(os.Environ(), "GOOS=wasip1", "GOARCH=wasm")
68
69			pr, pw := io.Pipe()
70			defer pw.Close()
71
72			subProcess.Stderr = pw
73
74			if err := subProcess.Start(); err != nil {
75				t.Fatal(err)
76			}
77
78			scanner := bufio.NewScanner(pr)
79			if !scanner.Scan() {
80				t.Fatal("expected line:", scanner.Err())
81			} else if scanner.Text() != "waiting" {
82				t.Fatal("unexpected output:", scanner.Text())
83			}
84
85			for _, fifo := range fifos {
86				if _, err := fifo.file.WriteString(fifo.path + "\n"); err != nil {
87					t.Fatal(err)
88				}
89				if !scanner.Scan() {
90					t.Fatal("expected line:", scanner.Err())
91				} else if scanner.Text() != fifo.path {
92					t.Fatal("unexpected line:", scanner.Text())
93				}
94			}
95
96			if err := subProcess.Wait(); err != nil {
97				t.Fatal(err)
98			}
99		})
100	}
101}
102