1// Copyright 2019 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//go:build unix
6
7package runtime_test
8
9import (
10	"runtime"
11	"syscall"
12	"testing"
13	"unsafe"
14)
15
16func TestNonblockingPipe(t *testing.T) {
17	// NonblockingPipe is the test name for nonblockingPipe.
18	r, w, errno := runtime.NonblockingPipe()
19	if errno != 0 {
20		t.Fatal(syscall.Errno(errno))
21	}
22	defer runtime.Close(w)
23
24	checkIsPipe(t, r, w)
25	checkNonblocking(t, r, "reader")
26	checkCloseonexec(t, r, "reader")
27	checkNonblocking(t, w, "writer")
28	checkCloseonexec(t, w, "writer")
29
30	// Test that fcntl returns an error as expected.
31	if runtime.Close(r) != 0 {
32		t.Fatalf("Close(%d) failed", r)
33	}
34	val, errno := runtime.Fcntl(r, syscall.F_GETFD, 0)
35	if val != -1 {
36		t.Errorf("Fcntl succeeded unexpectedly")
37	} else if syscall.Errno(errno) != syscall.EBADF {
38		t.Errorf("Fcntl failed with error %v, expected %v", syscall.Errno(errno), syscall.EBADF)
39	}
40}
41
42func checkIsPipe(t *testing.T, r, w int32) {
43	bw := byte(42)
44	if n := runtime.Write(uintptr(w), unsafe.Pointer(&bw), 1); n != 1 {
45		t.Fatalf("Write(w, &b, 1) == %d, expected 1", n)
46	}
47	var br byte
48	if n := runtime.Read(r, unsafe.Pointer(&br), 1); n != 1 {
49		t.Fatalf("Read(r, &b, 1) == %d, expected 1", n)
50	}
51	if br != bw {
52		t.Errorf("pipe read %d, expected %d", br, bw)
53	}
54}
55
56func checkNonblocking(t *testing.T, fd int32, name string) {
57	t.Helper()
58	flags, errno := runtime.Fcntl(fd, syscall.F_GETFL, 0)
59	if flags == -1 {
60		t.Errorf("fcntl(%s, F_GETFL) failed: %v", name, syscall.Errno(errno))
61	} else if flags&syscall.O_NONBLOCK == 0 {
62		t.Errorf("O_NONBLOCK not set in %s flags %#x", name, flags)
63	}
64}
65
66func checkCloseonexec(t *testing.T, fd int32, name string) {
67	t.Helper()
68	flags, errno := runtime.Fcntl(fd, syscall.F_GETFD, 0)
69	if flags == -1 {
70		t.Errorf("fcntl(%s, F_GETFD) failed: %v", name, syscall.Errno(errno))
71	} else if flags&syscall.FD_CLOEXEC == 0 {
72		t.Errorf("FD_CLOEXEC not set in %s flags %#x", name, flags)
73	}
74}
75