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  }