1
2
3
18
19 package fifo
20
21 import (
22 "bytes"
23 "context"
24 "io"
25 "os"
26 "path"
27 "path/filepath"
28 "syscall"
29 "testing"
30 "time"
31
32 "github.com/stretchr/testify/assert"
33 )
34
35 func TestRawReadWrite(t *testing.T) {
36 tmpdir, err := os.MkdirTemp("", "fifos")
37 assert.NoError(t, err)
38 defer os.RemoveAll(tmpdir)
39
40 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
41 defer cancel()
42
43 r, err := OpenFifo(ctx, filepath.Join(tmpdir, t.Name()), syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0600)
44 assert.NoError(t, err)
45 defer r.Close()
46 rawR := makeRawConn(t, r, false)
47 assert.Error(t, rawR.Write(func(uintptr) bool { return true }))
48
49 w, err := OpenFifo(ctx, filepath.Join(tmpdir, t.Name()), syscall.O_WRONLY|syscall.O_NONBLOCK, 0)
50 assert.NoError(t, err)
51 defer w.Close()
52 rawW := makeRawConn(t, w, false)
53 assert.Error(t, rawW.Read(func(uintptr) bool { return true }))
54
55 data := []byte("hello world")
56 rawWrite(t, rawW, data)
57
58 dataR := make([]byte, len(data))
59 rawRead(t, rawR, dataR)
60 assert.True(t, bytes.Equal(data, dataR))
61 }
62
63 func TestRawWriteUserRead(t *testing.T) {
64 tmpdir, err := os.MkdirTemp("", "fifos")
65 assert.NoError(t, err)
66 defer os.RemoveAll(tmpdir)
67
68 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
69 defer cancel()
70
71 w, err := OpenFifo(ctx, filepath.Join(tmpdir, t.Name()), syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0600)
72 assert.NoError(t, err)
73 defer w.Close()
74 rawW := makeRawConn(t, w, false)
75
76 r, err := OpenFifo(ctx, filepath.Join(tmpdir, t.Name()), syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0600)
77 assert.NoError(t, err)
78 defer r.Close()
79
80 data := []byte("hello world!")
81 rawWrite(t, rawW, data)
82 w.Close()
83
84 buf := make([]byte, len(data))
85 n, err := io.ReadFull(r, buf)
86 assert.NoError(t, err)
87 assert.True(t, bytes.Equal(data, buf[:n]))
88 }
89
90 func TestUserWriteRawRead(t *testing.T) {
91 tmpdir, err := os.MkdirTemp("", "fifos")
92 assert.NoError(t, err)
93 defer os.RemoveAll(tmpdir)
94
95 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
96 defer cancel()
97
98 w, err := OpenFifo(ctx, filepath.Join(tmpdir, t.Name()), syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0600)
99 assert.NoError(t, err)
100 defer w.Close()
101
102 r, err := OpenFifo(ctx, filepath.Join(tmpdir, t.Name()), syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0600)
103 assert.NoError(t, err)
104 defer r.Close()
105 rawR := makeRawConn(t, r, false)
106
107 data := []byte("hello world!")
108 n, err := w.Write(data)
109 assert.NoError(t, err)
110 assert.Equal(t, n, len(data))
111 w.Close()
112
113 buf := make([]byte, len(data))
114 rawRead(t, rawR, buf)
115 assert.True(t, bytes.Equal(data, buf[:n]))
116 }
117
118 func TestRawCloseError(t *testing.T) {
119 tmpdir, err := os.MkdirTemp("", "fifos")
120 assert.NoError(t, err)
121 defer os.RemoveAll(tmpdir)
122
123 t.Run("SyscallConnAfterClose", func(t *testing.T) {
124 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
125 defer cancel()
126
127 f, err := OpenFifo(ctx, filepath.Join(tmpdir, path.Base(t.Name())), syscall.O_RDWR|syscall.O_CREAT, 0600)
128 assert.NoError(t, err)
129
130 f.Close()
131
132 makeRawConn(t, f, true)
133 })
134
135 t.Run("RawOpsAfterClose", func(t *testing.T) {
136 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
137 defer cancel()
138 f, err := OpenFifo(ctx, filepath.Join(tmpdir, path.Base(t.Name())), syscall.O_RDWR|syscall.O_CREAT, 0600)
139 assert.NoError(t, err)
140 defer f.Close()
141
142 raw := makeRawConn(t, f, false)
143
144 f.Close()
145
146 assert.Error(t, raw.Control(func(uintptr) {}))
147 dummy := func(uintptr) bool { return true }
148 assert.Error(t, raw.Write(dummy))
149 assert.Error(t, raw.Read(dummy))
150 })
151
152 t.Run("NonBlockRawOpsAfterClose", func(t *testing.T) {
153 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
154 defer cancel()
155 dummy := func(uintptr) bool { return true }
156 r, err := OpenFifo(ctx, filepath.Join(tmpdir, path.Base(t.Name())), syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0600)
157 assert.NoError(t, err)
158 defer r.Close()
159 rawR := makeRawConn(t, r, false)
160 r.Close()
161
162 assert.Equal(t, ErrCtrlClosed, rawR.Control(func(uintptr) {}))
163 assert.Equal(t, ErrReadClosed, rawR.Read(dummy))
164
165 w, err := OpenFifo(ctx, filepath.Join(tmpdir, path.Base(t.Name())), syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0600)
166 assert.NoError(t, err)
167 defer w.Close()
168 rawW := makeRawConn(t, w, false)
169 w.Close()
170
171 assert.Equal(t, ErrCtrlClosed, rawW.Control(func(uintptr) {}))
172 assert.Equal(t, ErrWriteClosed, rawW.Write(dummy))
173 })
174 }
175
176 func TestRawWrongRdWrError(t *testing.T) {
177 tmpdir, err := os.MkdirTemp("", "fifos")
178 assert.NoError(t, err)
179 defer os.RemoveAll(tmpdir)
180
181 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
182 defer cancel()
183 dummy := func(uintptr) bool { return true }
184 r, err := OpenFifo(ctx, filepath.Join(tmpdir, path.Base(t.Name())), syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0600)
185 assert.NoError(t, err)
186 defer r.Close()
187 rawR := makeRawConn(t, r, false)
188
189 assert.Equal(t, ErrWrToRDONLY, rawR.Write(dummy))
190
191 w, err := OpenFifo(ctx, filepath.Join(tmpdir, path.Base(t.Name())), syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0600)
192 assert.NoError(t, err)
193 defer w.Close()
194 rawW := makeRawConn(t, w, false)
195
196 assert.Equal(t, ErrRdFrmWRONLY, rawW.Read(dummy))
197 }
198
199 func makeRawConn(t *testing.T, fifo io.ReadWriteCloser, expectError bool) syscall.RawConn {
200 sc, ok := fifo.(syscall.Conn)
201 assert.True(t, ok, "not a syscall.Conn")
202
203 raw, err := sc.SyscallConn()
204 if !expectError {
205 assert.NoError(t, err)
206 } else {
207 assert.Error(t, err)
208 }
209
210 return raw
211 }
212
213 func rawWrite(t *testing.T, rc syscall.RawConn, data []byte) {
214 var written int
215 var wErr error
216
217 err := rc.Write(func(fd uintptr) bool {
218 var n int
219 n, wErr = syscall.Write(int(fd), data[written:])
220 written += n
221 if wErr != nil || n == 0 || written == len(data) {
222 return true
223 }
224 return false
225 })
226 assert.NoError(t, err)
227 assert.NoError(t, wErr)
228 assert.Equal(t, written, len(data))
229 }
230
231 func rawRead(t *testing.T, rc syscall.RawConn, data []byte) {
232 var (
233 rErr error
234 read int
235 )
236
237 err := rc.Read(func(fd uintptr) bool {
238 var n int
239 n, rErr = syscall.Read(int(fd), data[read:])
240 read += n
241 if rErr != nil || n == 0 || read == len(data) {
242 return true
243 }
244 return false
245 })
246 assert.NoError(t, err)
247 assert.NoError(t, rErr)
248 assert.Equal(t, read, len(data))
249 }
250
View as plain text