gopkg.in/ro-ag/posix.v1@v1.0.6/posix_test.go (about) 1 //go:build darwin || linux 2 3 package posix_test 4 5 import ( 6 "bytes" 7 "fmt" 8 "gopkg.in/ro-ag/posix.v1" 9 "io/ioutil" 10 "os" 11 "os/exec" 12 "runtime" 13 "sync" 14 "syscall" 15 "testing" 16 "time" 17 "unsafe" 18 ) 19 20 func TestShmOpen(t *testing.T) { 21 type args struct { 22 shmName string 23 oflag int 24 mode uint32 25 } 26 tests := []struct { 27 name string 28 args args 29 wantErr bool 30 unlink bool 31 }{ 32 {"regular", args{"test-1", posix.O_RDWR | posix.O_CREAT | posix.O_EXCL | posix.O_NOFOLLOW, posix.S_IRUSR | posix.S_IWUSR}, false, true}, 33 } 34 for _, tt := range tests { 35 t.Run(tt.name, func(t *testing.T) { 36 gotFd, err := posix.ShmOpen(tt.args.shmName, tt.args.oflag, tt.args.mode) 37 if (err != nil) != tt.wantErr { 38 t.Errorf("ShmOpen() error = %v, wantErr %v", err, tt.wantErr) 39 if err.(syscall.Errno) == syscall.EEXIST { 40 goto unlink 41 } 42 return 43 } 44 if gotFd == -1 { 45 t.Errorf("ShmOpen() gotFd = %v", gotFd) 46 } 47 unlink: 48 if tt.unlink { 49 if err := posix.ShmUnlink(tt.args.shmName); err != nil { 50 t.Errorf("ShmUnlink() error = %v", err) 51 } 52 return 53 } 54 }) 55 } 56 } 57 58 func TestClose(t *testing.T) { 59 type args struct { 60 fd int 61 } 62 tests := []struct { 63 name string 64 args args 65 create bool 66 wantErr bool 67 reErr bool 68 }{ 69 {"normal", args{fd: 0}, true, false, true}, 70 {"failure", args{fd: 10}, false, true, true}, 71 } 72 for _, tt := range tests { 73 t.Run(tt.name, func(t *testing.T) { 74 var fd int 75 var err error 76 if tt.create { 77 fd, err = posix.MemfdCreate("test", posix.MFD_ALLOW_SEALING) 78 if err != nil { 79 t.Errorf("ShmAnonymous = %v", err) 80 } else { 81 tt.args.fd = fd 82 } 83 } 84 85 if err := posix.Close(tt.args.fd); (err != nil) != tt.wantErr { 86 t.Errorf("Close() error = %v, wantErr %v", err, tt.wantErr) 87 } 88 89 if err := posix.Close(tt.args.fd); (err != nil) != tt.reErr { 90 t.Errorf("Close() error = %v, reErr %v", err, tt.reErr) 91 } 92 93 }) 94 } 95 } 96 97 func TestFchmod(t *testing.T) { 98 if runtime.GOOS == "darwin" { 99 t.Skip("fchmod doesn't work on shared memory in MacOSx") 100 return 101 } 102 type args struct { 103 fd int 104 mode int 105 } 106 tests := []struct { 107 name string 108 args args 109 create bool 110 wantErr bool 111 }{ 112 {"Zero", args{0, posix.S_IRUSR}, false, true}, 113 {"Fail", args{50, posix.S_IRUSR}, false, true}, 114 {"Normal", args{0, posix.S_IWGRP}, true, false}, 115 } 116 for _, tt := range tests { 117 t.Run(tt.name, func(t *testing.T) { 118 if tt.create { 119 tt.args.fd = createFd(t) 120 defer func() { 121 _ = posix.Close(tt.args.fd) 122 }() 123 } 124 if err := posix.Fchmod(tt.args.fd, tt.args.mode); (err != nil) != tt.wantErr { 125 t.Errorf("Fchmod() error = %v, wantErr %v", err, tt.wantErr) 126 } 127 }) 128 } 129 } 130 131 func TestFchown(t *testing.T) { 132 if runtime.GOOS == "darwin" { 133 t.Skip("fchown doesn't work on shared memory in MacOSx") 134 return 135 } 136 137 uid := os.Geteuid() 138 gid := os.Getgid() 139 type args struct { 140 fd int 141 uid int 142 gid int 143 } 144 tests := []struct { 145 name string 146 args args 147 create bool 148 wantErr bool 149 }{ 150 {"Zero", args{0, 0, 0}, false, true}, 151 {"Fail", args{50, uid, uid}, false, true}, 152 {"Normal", args{0, uid, gid}, true, false}, 153 } 154 155 for _, tt := range tests { 156 t.Run(tt.name, func(t *testing.T) { 157 if tt.create { 158 tt.args.fd = createFd(t) 159 defer func() { 160 _ = posix.Close(tt.args.fd) 161 }() 162 } 163 if err := posix.Fchown(tt.args.fd, tt.args.uid, tt.args.gid); (err != nil) != tt.wantErr { 164 t.Errorf("Fchown() error = %v, wantErr %v", err, tt.wantErr) 165 } 166 }) 167 } 168 } 169 170 func TestFcntl(t *testing.T) { 171 t.Parallel() 172 file, err := ioutil.TempFile("", "TestFcntlInt") 173 if err != nil { 174 t.Fatal(err) 175 } 176 defer func() { 177 _ = os.Remove(file.Name()) 178 }() 179 defer func() { 180 _ = file.Close() 181 }() 182 183 f := file.Fd() 184 displayInfo(int(f), t) 185 flags, err := posix.Fcntl(int(f), posix.F_GETFD, 0) 186 if err != nil { 187 t.Fatal(err) 188 } 189 if flags&posix.FD_CLOEXEC == 0 { 190 t.Errorf("flags %#x do not include FD_CLOEXEC", flags) 191 } 192 } 193 194 const FixedAddress uintptr = 0x20000000000 195 196 func TestMmapParallel(t *testing.T) { 197 for i := 0; i < 50; i++ { 198 name := fmt.Sprintf("mmap-%.3d", i) 199 t.Run(name, func(t *testing.T) { 200 t.Parallel() 201 b, a, err := posix.Mmap(unsafe.Pointer(FixedAddress), posix.Getpagesize()*500, posix.PROT_WRITE, posix.MAP_ANON|posix.MAP_SHARED, 0, 0) 202 if err != nil { 203 t.Errorf("Mmap: %v", err) 204 return 205 } 206 if err = posix.Mlock(b, len(b)-1); err != nil { 207 t.Errorf("Mlock: %v", err) 208 return 209 } 210 t.Logf("name: %s, orig: %p, addr: %p, diff: %p", name, unsafe.Pointer(FixedAddress), unsafe.Pointer(a), unsafe.Pointer(a-FixedAddress)) 211 time.Sleep(50 * time.Microsecond) 212 copy(b, name) 213 if err = posix.Munmap(b); err != nil { 214 t.Errorf("Munmap: %v", err) 215 } 216 }) 217 218 } 219 } 220 221 func TestMemory(t *testing.T) { 222 var ( 223 b []byte 224 a uintptr 225 err error 226 ) 227 228 t.Run("Mmap", func(t *testing.T) { 229 b, a, err = posix.Mmap(unsafe.Pointer(FixedAddress), posix.Getpagesize(), posix.PROT_NONE, posix.MAP_ANON|posix.MAP_PRIVATE, 0, 0) 230 if err != nil { 231 t.Fatalf("Mmap: %v", err) 232 } 233 if a != FixedAddress { 234 t.Fatalf("Expecting address %p but have %p", unsafe.Pointer(FixedAddress), unsafe.Pointer(a)) 235 } 236 }) 237 238 t.Run("Mprotect", func(t *testing.T) { 239 if err = posix.Mprotect(b, posix.PROT_READ|posix.PROT_WRITE); err != nil { 240 t.Fatalf("Mprotect: %v", err) 241 } 242 }) 243 244 t.Run("write", func(t *testing.T) { 245 b[0] = 42 246 }) 247 248 t.Run("Msync", func(t *testing.T) { 249 if err = posix.Msync(b, posix.MS_SYNC); err != nil { 250 t.Fatalf("Msync: %v", err) 251 } 252 }) 253 254 t.Run("Madvise", func(t *testing.T) { 255 if err = posix.Madvise(b, posix.MADV_DONTNEED); err != nil { 256 t.Fatalf("Madvise: %v", err) 257 } 258 }) 259 260 t.Run("Mlock", func(t *testing.T) { 261 if err := posix.Mlock(b, len(b)-1); err != nil { 262 t.Fatalf("Munlock: %v", err) 263 } 264 }) 265 266 t.Run("Munlock", func(t *testing.T) { 267 if err := posix.Munlock(b, len(b)-1); err != nil { 268 t.Fatalf("Munlock: %v", err) 269 } 270 }) 271 272 t.Run("Munlockall", func(t *testing.T) { 273 if err = posix.Munlockall(); err != nil { 274 if err.(posix.Errno) == syscall.ENOSYS { 275 t.Skip(err) 276 return 277 } 278 t.Errorf("Munlockall: %v", err) 279 } 280 }) 281 282 t.Run("Mlockall", func(t *testing.T) { 283 if err = posix.Mlockall(posix.MCL_CURRENT); err != nil { 284 enum := err.(posix.Errno) 285 if enum == syscall.ENOSYS { 286 t.Skip(err) 287 return 288 } else if enum == syscall.ENOMEM { 289 t.Skip(posix.ErrnoName(enum), posix.ErrnoString(enum), posix.ErrnoHelp(enum)) 290 } 291 292 t.Errorf("Mlockall: %v - No: %d - %s", err, enum, posix.ErrnoName(enum)) 293 } 294 }) 295 296 if err := posix.Munmap(b); err != nil { 297 t.Fatalf("Munmap: %v", err) 298 } 299 } 300 301 func createFd(t *testing.T) int { 302 fd, err := posix.MemfdCreate("test-anon", posix.MFD_ALLOW_SEALING) 303 if err != nil { 304 t.Error(err) 305 return -1 306 } 307 if err = posix.Ftruncate(fd, posix.Getpagesize()); err != nil { 308 t.Error(err) 309 return -1 310 } 311 displayInfo(fd, t) 312 return fd 313 } 314 315 func displayInfo(fd int, t *testing.T) { 316 var fs posix.Stat_t 317 if err := posix.Fstat(fd, &fs); err != nil { 318 t.Errorf("Fstat() error = %v", err) 319 } 320 fmt.Printf("Descriptor %d\n", fd) 321 fs.DisplayStatInfo() 322 } 323 324 func TestStress(t *testing.T) { 325 t.Log("build external test") 326 FileName := "./test/shm" 327 cmd := exec.Command("go", "build", "-o", FileName, "./test") 328 cmd.Stdout = os.Stdout 329 cmd.Stderr = os.Stderr 330 if err := cmd.Run(); err != nil { 331 t.Errorf("external test: %v", err) 332 return 333 } 334 335 type segment struct { 336 buf []byte 337 addr uintptr 338 fd int 339 f *os.File 340 cmd *exec.Cmd 341 pid int 342 stdout bytes.Buffer 343 stderr bytes.Buffer 344 err error 345 } 346 347 var err error 348 max := 600 349 for x := 0; x < 10; x++ { 350 name := fmt.Sprintf("(%d) create and delete %.3d maps", x, max) 351 t.Run(name, func(t *testing.T) { 352 var segments []*segment 353 hasError := false 354 var wg sync.WaitGroup 355 356 for i := 0; i < max; i++ { 357 seg := new(segment) 358 if seg.fd, err = posix.MemfdCreate("testing", posix.MFD_ALLOW_SEALING); err != nil { 359 t.Errorf("(%.3d) MemfdCreate: %v", i, err) 360 hasError = true 361 t.Fail() 362 goto close 363 } else { 364 segments = append(segments, seg) 365 } 366 } 367 368 for i, seg := range segments { 369 if seg.err = posix.Ftruncate(seg.fd, 50); seg.err != nil { 370 t.Errorf("(%.3d) Ftruncate fd=%d : %v", i, seg.fd, seg.err) 371 hasError = true 372 } 373 } 374 375 if hasError { 376 goto close 377 } 378 379 for i, seg := range segments { 380 seg.f = os.NewFile(uintptr(seg.fd), fmt.Sprintf("file-%d", seg.fd)) 381 if seg.f == nil { 382 seg.err = fmt.Errorf("(%.3d) NewFile fd=%d is null, probably bad descriptior", i, seg.fd) 383 t.Error(seg.err) 384 hasError = true 385 } 386 } 387 388 if hasError { 389 goto close 390 } 391 392 for i, seg := range segments { 393 if seg.buf, seg.addr, err = posix.Mmap(unsafe.Pointer(uintptr(0)), 50, posix.PROT_READ|posix.PROT_WRITE, posix.MAP_SHARED, seg.fd, 0); err != nil { 394 t.Errorf("(%.3d) Mmap: %v", i, err) 395 hasError = true 396 } 397 } 398 399 if hasError { 400 goto unmap 401 } 402 403 for i, seg := range segments { 404 text := fmt.Sprintf("(%.3d)", i) 405 if copy(seg.buf, text) != len(text) { 406 seg.err = fmt.Errorf("(%.3d) NewFile fd=%d wrong number of bytes writen", i, seg.fd) 407 t.Error(seg.err) 408 hasError = true 409 } 410 } 411 412 for i := range segments { 413 seg := segments[i] 414 seg.cmd = exec.Command(FileName) 415 seg.cmd.Stdout = &seg.stdout 416 seg.cmd.Stderr = &seg.stderr 417 seg.cmd.ExtraFiles = []*os.File{seg.f} 418 } 419 420 t.Log("write from external program") 421 422 for i := range segments { 423 seg := segments[i] 424 wg.Add(1) 425 go func() { 426 defer wg.Done() 427 seg.err = seg.cmd.Run() 428 }() 429 } 430 431 wg.Wait() 432 433 for i, seg := range segments { 434 if seg.err != nil { 435 t.Errorf("(%d) fd=%d: %v", i, seg.fd, seg.err) 436 hasError = true 437 } else { 438 seg.pid = seg.cmd.Process.Pid 439 } 440 } 441 442 if hasError { 443 goto unmap 444 } 445 446 t.Log("validate memory") 447 for i, seg := range segments { 448 if seg.err == nil { 449 want := fmt.Sprintf("PID: %.10d", seg.pid) 450 got := string(seg.buf[:len(want)]) 451 if got != want { 452 fmt.Println(seg.buf) 453 fmt.Println(seg.stdout.String()) 454 t.Errorf("(%d) fd=%d Got %s but want %s", i, seg.fd, got, want) 455 t.Fail() 456 hasError = true 457 } 458 } else { 459 t.Errorf("(%d) fd=%d %v", i, seg.fd, err) 460 hasError = true 461 } 462 } 463 t.Log("done memory validation") 464 unmap: 465 t.Log("unmmap") 466 for i, sg := range segments { 467 if err = posix.Munmap(sg.buf); err != nil { 468 t.Errorf("(%.3d) fd=%d Munmap(%p): %v", i, sg.fd, unsafe.Pointer(sg.addr), err) 469 t.Fail() 470 } 471 } 472 close: 473 t.Log("closing") 474 475 for i, sg := range segments { 476 if sg.fd > 3 { 477 err = sg.f.Close() 478 if err != nil { 479 t.Errorf("(%.3d) f=%p file Close(%d): %v", i, sg.f, sg.fd, err) 480 t.Fail() 481 return 482 } 483 } 484 } 485 t.Log("test done") 486 return 487 }) 488 489 if err != nil { 490 t.Fail() 491 return 492 } 493 } 494 }