github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/fdsrv/fdsrv_test.go (about) 1 // Copyright 2022 the u-root 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 package fdsrv 6 7 import ( 8 "os" 9 "syscall" 10 "testing" 11 "time" 12 ) 13 14 // Returns an fd of the read side of a pipe that has the value 'x' written in 15 // it. 16 func xPipe(t *testing.T) int { 17 var p [2]int 18 if err := syscall.Pipe(p[:]); err != nil { 19 t.Fatal("pipe:", err) 20 } 21 if _, err := syscall.Write(p[1], []byte("x")); err != nil { 22 t.Fatal("write:", err) 23 } 24 if err := syscall.Close(p[1]); err != nil { 25 t.Fatal("close:", err) 26 } 27 return p[0] 28 } 29 30 // Returns an *Server, serving an xPipe with "some_nonce" 31 func allocPipeFDs(t *testing.T, options ...func(*Server) error) *Server { 32 fd := xPipe(t) 33 fds, err := NewServer(fd, "some_nonce", options...) 34 if err != nil { 35 t.Fatal("alloc:", err) 36 } 37 if err := syscall.Close(fd); err != nil { 38 t.Fatal("close:", err) 39 } 40 return fds 41 } 42 43 // Read a string from an fd 44 func readString(t *testing.T, fd int) string { 45 buf := make([]byte, 128) 46 n, err := syscall.Read(fd, buf) 47 if err != nil { 48 t.Fatal("read:", err) 49 } 50 return string(buf[:n]) 51 } 52 53 // Gets a shared fd, makes sure we can read "x" from it 54 func testSharedOK(t *testing.T, udspath, nonce string) { 55 sfd, err := GetSharedFD(udspath, nonce) 56 if err != nil { 57 t.Error("getsharedfd:", err) 58 } 59 got := readString(t, sfd) 60 if got != "x" { 61 t.Errorf("expected x, got %s", got) 62 } 63 if err := syscall.Close(sfd); err != nil { 64 t.Error("close:", err) 65 } 66 } 67 68 func TestPassFD(t *testing.T) { 69 fds := allocPipeFDs(t, WithServeOnce()) 70 71 serveErr := make(chan error) 72 go func() { 73 serveErr <- fds.Serve() 74 }() 75 76 testSharedOK(t, fds.UDSPath(), "some_nonce") 77 78 fds.Close() 79 if err := <-serveErr; err != nil { 80 t.Errorf("Serve: %v", err) 81 } 82 } 83 84 func TestBadNonce(t *testing.T) { 85 fds := allocPipeFDs(t, WithServeOnce()) 86 87 serveErr := make(chan error) 88 go func() { 89 serveErr <- fds.Serve() 90 }() 91 92 sfd, err := GetSharedFD(fds.UDSPath(), "bad_nonce") 93 if err == nil { 94 t.Errorf("should have failed, but got sfd %d", sfd) 95 } 96 fds.Close() 97 if err := <-serveErr; err != nil { 98 t.Errorf("Serve: %v", err) 99 } 100 } 101 102 func TestBadSubsetNonce(t *testing.T) { 103 fds := allocPipeFDs(t, WithServeOnce()) 104 105 serveErr := make(chan error) 106 go func() { 107 serveErr <- fds.Serve() 108 }() 109 110 sfd, err := GetSharedFD(fds.UDSPath(), "some_non") 111 if err == nil { 112 t.Errorf("should have failed, but got sfd %d", sfd) 113 } 114 fds.Close() 115 if err := <-serveErr; err != nil { 116 t.Errorf("Serve: %v", err) 117 } 118 } 119 120 func TestBadEmptyNonce(t *testing.T) { 121 fds := allocPipeFDs(t, WithServeOnce()) 122 123 serveErr := make(chan error) 124 go func() { 125 serveErr <- fds.Serve() 126 }() 127 128 sfd, err := GetSharedFD(fds.UDSPath(), "") 129 if err == nil { 130 t.Errorf("should have failed, but got sfd %d", sfd) 131 } 132 133 fds.Close() 134 if err := <-serveErr; err != nil { 135 t.Errorf("Serve: %v", err) 136 } 137 } 138 139 func TestEmptyNonce(t *testing.T) { 140 fds, err := NewServer(0, "") 141 if err == nil { 142 t.Error("should have failed to alloc") 143 fds.Close() 144 } 145 } 146 147 // Might flake, based on timing 148 func TestTimeoutDoesntFire(t *testing.T) { 149 fds := allocPipeFDs(t, WithServeOnce(), WithTimeout(time.Second)) 150 151 serveErr := make(chan error) 152 go func() { 153 serveErr <- fds.Serve() 154 }() 155 156 testSharedOK(t, fds.UDSPath(), "some_nonce") 157 158 fds.Close() 159 if err := <-serveErr; err != nil { 160 t.Errorf("Serve: %v", err) 161 } 162 } 163 164 // Might flake, based on timing 165 func TestTimeoutFires(t *testing.T) { 166 fds := allocPipeFDs(t, WithServeOnce(), WithTimeout(100*time.Millisecond)) 167 168 serveErr := make(chan error) 169 go func() { 170 serveErr <- fds.Serve() 171 }() 172 173 time.Sleep(time.Millisecond * 1000) 174 175 sfd, err := GetSharedFD(fds.UDSPath(), "some_nonce") 176 if err == nil { 177 t.Errorf("should have timed out, but got sfd %d", sfd) 178 } 179 fds.Close() 180 if err := <-serveErr; err != nil && !os.IsTimeout(err) { 181 t.Errorf("Serve: %v", err) 182 } 183 } 184 185 func TestWaitTimeout(t *testing.T) { 186 fds := allocPipeFDs(t, WithServeOnce(), 187 WithTimeout(time.Millisecond*10)) 188 189 err := fds.Serve() 190 if err == nil || !os.IsTimeout(err) { 191 t.Error("expected timeout:", err) 192 } 193 fds.Close() 194 } 195 196 func TestMultiServe(t *testing.T) { 197 fds := allocPipeFDs(t, WithTimeout(time.Second*5)) 198 199 serveErr := make(chan error) 200 go func() { 201 serveErr <- fds.Serve() 202 }() 203 204 testSharedOK(t, fds.UDSPath(), "some_nonce") 205 // The second reader won't see 'x', the pipe was already drained 206 sfd, err := GetSharedFD(fds.UDSPath(), "some_nonce") 207 if err != nil { 208 t.Error("getsharedfd:", err) 209 } 210 if err := syscall.Close(sfd); err != nil { 211 t.Error("close:", err) 212 } 213 214 fds.Close() 215 if err := <-serveErr; err != nil && !os.IsTimeout(err) { 216 t.Errorf("Serve: %v", err) 217 } 218 }