mirror of
https://github.com/criyle/go-sandbox.git
synced 2025-11-04 14:49:53 +08:00
test(pkg): add some unit tests
This commit is contained in:
parent
638345b6eb
commit
c63c27d3c2
@ -15,12 +15,12 @@ const roSeal = unix.F_SEAL_SEAL | unix.F_SEAL_SHRINK | unix.F_SEAL_GROW | unix.F
|
||||
func New(name string) (*os.File, error) {
|
||||
fd, err := unix.MemfdCreate(name, createFlag)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("memfd: memfd_create failed %v", err)
|
||||
return nil, fmt.Errorf("memfd: memfd_create: %w", err)
|
||||
}
|
||||
file := os.NewFile(uintptr(fd), name)
|
||||
if file == nil {
|
||||
unix.Close(fd)
|
||||
return nil, fmt.Errorf("memfd: NewFile failed for %v", name)
|
||||
return nil, fmt.Errorf("memfd: new file failed for %q", name)
|
||||
}
|
||||
return file, nil
|
||||
}
|
||||
@ -29,21 +29,21 @@ func New(name string) (*os.File, error) {
|
||||
func DupToMemfd(name string, reader io.Reader) (*os.File, error) {
|
||||
file, err := New(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("DupToMemfd: %v", err)
|
||||
return nil, fmt.Errorf("memfd: dup: %w", err)
|
||||
}
|
||||
// linux syscall sendfile might be more efficient here if reader is a file
|
||||
if _, err = file.ReadFrom(reader); err != nil {
|
||||
file.Close()
|
||||
return nil, fmt.Errorf("DupToMemfd: read from %v", err)
|
||||
return nil, fmt.Errorf("memfd: read from: %w", err)
|
||||
}
|
||||
// make memfd readonly
|
||||
if _, err = unix.FcntlInt(file.Fd(), unix.F_ADD_SEALS, roSeal); err != nil {
|
||||
file.Close()
|
||||
return nil, fmt.Errorf("DupToMemfd: memfd seal %v", err)
|
||||
return nil, fmt.Errorf("memfd: seal: %w", err)
|
||||
}
|
||||
if _, err := file.Seek(0, 0); err != nil {
|
||||
file.Close()
|
||||
return nil, fmt.Errorf("DupToMemfd: file seek %v", err)
|
||||
return nil, fmt.Errorf("memfd: seek: %w", err)
|
||||
}
|
||||
return file, nil
|
||||
}
|
||||
|
||||
80
pkg/memfd/memfd_linux_test.go
Normal file
80
pkg/memfd/memfd_linux_test.go
Normal file
@ -0,0 +1,80 @@
|
||||
package memfd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
f, err := New("test-memfd")
|
||||
if err != nil {
|
||||
t.Fatalf("New() error: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Write and read to verify it's a valid file
|
||||
data := []byte("hello world")
|
||||
n, err := f.Write(data)
|
||||
if err != nil {
|
||||
t.Fatalf("Write error: %v", err)
|
||||
}
|
||||
if n != len(data) {
|
||||
t.Errorf("Write n = %d, want %d", n, len(data))
|
||||
}
|
||||
_, err = f.Seek(0, io.SeekStart)
|
||||
if err != nil {
|
||||
t.Fatalf("Seek error: %v", err)
|
||||
}
|
||||
read := make([]byte, len(data))
|
||||
n, err = f.Read(read)
|
||||
if err != nil && err != io.EOF {
|
||||
t.Fatalf("Read error: %v", err)
|
||||
}
|
||||
if string(read[:n]) != string(data) {
|
||||
t.Errorf("Read = %q, want %q", string(read[:n]), string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDupToMemfd(t *testing.T) {
|
||||
content := []byte("memfd content")
|
||||
r := bytes.NewReader(content)
|
||||
f, err := DupToMemfd("dup-memfd", r)
|
||||
if err != nil {
|
||||
t.Fatalf("DupToMemfd error: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Should be sealed (readonly), so writing should fail
|
||||
_, err = f.Write([]byte("fail"))
|
||||
if err == nil {
|
||||
t.Error("expected write to sealed memfd to fail, but it succeeded")
|
||||
}
|
||||
|
||||
// Should be able to read the content
|
||||
_, err = f.Seek(0, io.SeekStart)
|
||||
if err != nil {
|
||||
t.Fatalf("Seek error: %v", err)
|
||||
}
|
||||
got, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadAll error: %v", err)
|
||||
}
|
||||
if string(got) != string(content) {
|
||||
t.Errorf("ReadAll = %q, want %q", string(got), string(content))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDupToMemfd_ErrorPropagation(t *testing.T) {
|
||||
// Pass a reader that always errors
|
||||
r := errorReader{}
|
||||
_, err := DupToMemfd("dup-memfd-err", r)
|
||||
if err == nil {
|
||||
t.Error("expected error from DupToMemfd, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
type errorReader struct{}
|
||||
|
||||
func (errorReader) Read([]byte) (int, error) { return 0, os.ErrInvalid }
|
||||
124
pkg/mount/builder_linux_test.go
Normal file
124
pkg/mount/builder_linux_test.go
Normal file
@ -0,0 +1,124 @@
|
||||
package mount
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBuilder_WithBind(t *testing.T) {
|
||||
b := NewBuilder().WithBind("/src", "/dst", true)
|
||||
if len(b.Mounts) != 1 {
|
||||
t.Fatalf("expected 1 mount, got %d", len(b.Mounts))
|
||||
}
|
||||
m := b.Mounts[0]
|
||||
if m.Source != "/src" || m.Target != "/dst" {
|
||||
t.Errorf("unexpected mount: %+v", m)
|
||||
}
|
||||
if !m.IsBindMount() {
|
||||
t.Errorf("expected bind mount")
|
||||
}
|
||||
if !m.IsReadOnly() {
|
||||
t.Errorf("expected readonly mount")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilder_WithTmpfs(t *testing.T) {
|
||||
b := NewBuilder().WithTmpfs("/tmp", "size=64m")
|
||||
if len(b.Mounts) != 1 {
|
||||
t.Fatalf("expected 1 mount, got %d", len(b.Mounts))
|
||||
}
|
||||
m := b.Mounts[0]
|
||||
if !m.IsTmpFs() {
|
||||
t.Errorf("expected tmpfs mount")
|
||||
}
|
||||
if m.Target != "/tmp" || m.Data != "size=64m" {
|
||||
t.Errorf("unexpected mount: %+v", m)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilder_WithProc(t *testing.T) {
|
||||
b := NewBuilder().WithProc()
|
||||
if len(b.Mounts) != 1 {
|
||||
t.Fatalf("expected 1 mount, got %d", len(b.Mounts))
|
||||
}
|
||||
m := b.Mounts[0]
|
||||
if m.FsType != "proc" {
|
||||
t.Errorf("expected proc fsType")
|
||||
}
|
||||
if !m.IsReadOnly() {
|
||||
t.Errorf("expected readonly proc mount")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilder_WithProcRW(t *testing.T) {
|
||||
b := NewBuilder().WithProcRW(true)
|
||||
if len(b.Mounts) != 1 {
|
||||
t.Fatalf("expected 1 mount, got %d", len(b.Mounts))
|
||||
}
|
||||
m := b.Mounts[0]
|
||||
if m.FsType != "proc" {
|
||||
t.Errorf("expected proc fsType")
|
||||
}
|
||||
if m.IsReadOnly() {
|
||||
t.Errorf("expected read-write proc mount")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilder_WithMounts(t *testing.T) {
|
||||
m1 := Mount{Source: "/a", Target: "/b"}
|
||||
m2 := Mount{Source: "/c", Target: "/d"}
|
||||
b := NewBuilder().WithMounts([]Mount{m1, m2})
|
||||
if len(b.Mounts) != 2 {
|
||||
t.Fatalf("expected 2 mounts, got %d", len(b.Mounts))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilder_WithMount(t *testing.T) {
|
||||
m := Mount{Source: "/a", Target: "/b"}
|
||||
b := NewBuilder().WithMount(m)
|
||||
if len(b.Mounts) != 1 {
|
||||
t.Fatalf("expected 1 mount, got %d", len(b.Mounts))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilder_String(t *testing.T) {
|
||||
b := NewBuilder().
|
||||
WithBind("/src", "/dst", false).
|
||||
WithTmpfs("/tmp", "size=1m").
|
||||
WithProc()
|
||||
s := b.String()
|
||||
if !strings.HasPrefix(s, "Mounts: ") {
|
||||
t.Errorf("unexpected prefix: %q", s)
|
||||
}
|
||||
if !strings.Contains(s, "bind[/src:/dst:rw]") {
|
||||
t.Errorf("missing bind: %q", s)
|
||||
}
|
||||
if !strings.Contains(s, "tmpfs[/tmp]") {
|
||||
t.Errorf("missing tmpfs: %q", s)
|
||||
}
|
||||
if !strings.Contains(s, "proc[ro]") {
|
||||
t.Errorf("missing proc: %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilder_FilterNotExist(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
tmpFilePath := tmpDir + "/mounttest"
|
||||
f, err := os.Create(tmpFilePath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
|
||||
b := NewBuilder().
|
||||
WithBind(f.Name(), "/dst1", false).
|
||||
WithBind("/not/exist", "/dst2", false)
|
||||
b.FilterNotExist()
|
||||
if len(b.Mounts) != 1 {
|
||||
t.Errorf("expected 1 mount after filter, got %d", len(b.Mounts))
|
||||
}
|
||||
if b.Mounts[0].Source != f.Name() {
|
||||
t.Errorf("unexpected mount: %+v", b.Mounts[0])
|
||||
}
|
||||
}
|
||||
112
pkg/mount/mount_linux_test.go
Normal file
112
pkg/mount/mount_linux_test.go
Normal file
@ -0,0 +1,112 @@
|
||||
package mount
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMount_IsBindMount(t *testing.T) {
|
||||
m := Mount{Flags: syscall.MS_BIND}
|
||||
if !m.IsBindMount() {
|
||||
t.Errorf("expected IsBindMount true")
|
||||
}
|
||||
m.Flags = 0
|
||||
if m.IsBindMount() {
|
||||
t.Errorf("expected IsBindMount false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMount_IsReadOnly(t *testing.T) {
|
||||
m := Mount{Flags: syscall.MS_RDONLY}
|
||||
if !m.IsReadOnly() {
|
||||
t.Errorf("expected IsReadOnly true")
|
||||
}
|
||||
m.Flags = 0
|
||||
if m.IsReadOnly() {
|
||||
t.Errorf("expected IsReadOnly false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMount_IsTmpFs(t *testing.T) {
|
||||
m := Mount{FsType: "tmpfs"}
|
||||
if !m.IsTmpFs() {
|
||||
t.Errorf("expected IsTmpFs true")
|
||||
}
|
||||
m.FsType = "other"
|
||||
if m.IsTmpFs() {
|
||||
t.Errorf("expected IsTmpFs false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMount_String(t *testing.T) {
|
||||
tests := []struct {
|
||||
m Mount
|
||||
want string
|
||||
}{
|
||||
{
|
||||
m: Mount{Source: "/src", Target: "/dst", Flags: syscall.MS_BIND, FsType: "", Data: ""},
|
||||
want: "bind[/src:/dst:rw]",
|
||||
},
|
||||
{
|
||||
m: Mount{Source: "/src", Target: "/dst", Flags: syscall.MS_BIND | syscall.MS_RDONLY, FsType: "", Data: ""},
|
||||
want: "bind[/src:/dst:ro]",
|
||||
},
|
||||
{
|
||||
m: Mount{Source: "", Target: "/tmp", FsType: "tmpfs"},
|
||||
want: "tmpfs[/tmp]",
|
||||
},
|
||||
{
|
||||
m: Mount{Source: "", Target: "proc", FsType: "proc", Flags: syscall.MS_RDONLY},
|
||||
want: "proc[ro]",
|
||||
},
|
||||
{
|
||||
m: Mount{Source: "src", Target: "dst", FsType: "other", Flags: 0, Data: "data"},
|
||||
want: "mount[other,src:dst:0,data]",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := tt.m.String()
|
||||
if got != tt.want {
|
||||
t.Errorf("Mount.String() = %q, want %q", got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureMountTargetExists_Dir(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
target := filepath.Join(tmpDir, "foo/bar")
|
||||
err := ensureMountTargetExists(tmpDir, target)
|
||||
if err != nil {
|
||||
t.Fatalf("ensureMountTargetExists error: %v", err)
|
||||
}
|
||||
info, err := os.Stat(target)
|
||||
if err != nil {
|
||||
t.Fatalf("stat error: %v", err)
|
||||
}
|
||||
if !info.IsDir() {
|
||||
t.Errorf("expected directory at %s", target)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureMountTargetExists_File(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
srcFile := filepath.Join(tmpDir, "srcfile")
|
||||
if err := os.WriteFile(srcFile, []byte("x"), 0644); err != nil {
|
||||
t.Fatalf("write srcfile: %v", err)
|
||||
}
|
||||
target := filepath.Join(tmpDir, "targetfile")
|
||||
err := ensureMountTargetExists(srcFile, target)
|
||||
if err != nil {
|
||||
t.Fatalf("ensureMountTargetExists error: %v", err)
|
||||
}
|
||||
// Should be a file or at least exist
|
||||
info, err := os.Lstat(target)
|
||||
if err != nil {
|
||||
t.Fatalf("lstat error: %v", err)
|
||||
}
|
||||
if info.IsDir() {
|
||||
t.Errorf("expected file at %s, got directory", target)
|
||||
}
|
||||
}
|
||||
101
pkg/pipe/buffer_test.go
Normal file
101
pkg/pipe/buffer_test.go
Normal file
@ -0,0 +1,101 @@
|
||||
package pipe
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNewBuffer_WriteAndRead(t *testing.T) {
|
||||
const max = 10
|
||||
buf, err := NewBuffer(max)
|
||||
if err != nil {
|
||||
t.Fatalf("NewBuffer error: %v", err)
|
||||
}
|
||||
defer buf.W.Close()
|
||||
|
||||
// Write less than max bytes
|
||||
input := "hello"
|
||||
n, err := buf.W.Write([]byte(input))
|
||||
if err != nil {
|
||||
t.Fatalf("Write error: %v", err)
|
||||
}
|
||||
if n != len(input) {
|
||||
t.Errorf("Write bytes = %d, want %d", n, len(input))
|
||||
}
|
||||
buf.W.Close()
|
||||
<-buf.Done
|
||||
|
||||
got := buf.Buffer.String()
|
||||
if got != input {
|
||||
t.Errorf("Buffer content = %q, want %q", got, input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewBuffer_MaxBytes(t *testing.T) {
|
||||
const max = 5
|
||||
buf, err := NewBuffer(max)
|
||||
if err != nil {
|
||||
t.Fatalf("NewBuffer error: %v", err)
|
||||
}
|
||||
defer buf.W.Close()
|
||||
|
||||
// Write more than max bytes
|
||||
input := "toolonginput"
|
||||
_, err = io.Copy(buf.W, strings.NewReader(input))
|
||||
if err != nil {
|
||||
t.Fatalf("Copy error: %v", err)
|
||||
}
|
||||
buf.W.Close()
|
||||
<-buf.Done
|
||||
|
||||
got := buf.Buffer.String()
|
||||
if len(got) != int(max+1) {
|
||||
t.Errorf("Buffer length = %d, want %d", len(got), max+1)
|
||||
}
|
||||
if got != input[:max+1] {
|
||||
t.Errorf("Buffer content = %q, want %q", got, input[:max+1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuffer_String(t *testing.T) {
|
||||
const max = 8
|
||||
buf, err := NewBuffer(max)
|
||||
if err != nil {
|
||||
t.Fatalf("NewBuffer error: %v", err)
|
||||
}
|
||||
defer buf.W.Close()
|
||||
|
||||
_, _ = buf.W.Write([]byte("abc"))
|
||||
buf.W.Close()
|
||||
<-buf.Done
|
||||
|
||||
want := "Buffer[3/8]"
|
||||
if buf.String() != want {
|
||||
t.Errorf("String() = %q, want %q", buf.String(), want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewBuffer_DoneCloses(t *testing.T) {
|
||||
const max = 4
|
||||
buf, err := NewBuffer(max)
|
||||
if err != nil {
|
||||
t.Fatalf("NewBuffer error: %v", err)
|
||||
}
|
||||
defer buf.W.Close()
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
_, _ = buf.W.Write([]byte("test"))
|
||||
buf.W.Close()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-buf.Done:
|
||||
// ok
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("timeout waiting for Done channel")
|
||||
}
|
||||
}
|
||||
136
pkg/rlimit/rlimit_test.go
Normal file
136
pkg/rlimit/rlimit_test.go
Normal file
@ -0,0 +1,136 @@
|
||||
//go:build linux
|
||||
|
||||
package rlimit
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPrepareRLimit(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rl RLimits
|
||||
expect []int
|
||||
}{
|
||||
{
|
||||
name: "Empty",
|
||||
rl: RLimits{},
|
||||
expect: []int{},
|
||||
},
|
||||
{
|
||||
name: "CPU only",
|
||||
rl: RLimits{CPU: 1},
|
||||
expect: []int{syscall.RLIMIT_CPU},
|
||||
},
|
||||
{
|
||||
name: "Data only",
|
||||
rl: RLimits{Data: 1024},
|
||||
expect: []int{syscall.RLIMIT_DATA},
|
||||
},
|
||||
{
|
||||
name: "All fields",
|
||||
rl: RLimits{CPU: 1, CPUHard: 2, Data: 1024, FileSize: 2048, Stack: 4096, AddressSpace: 8192, OpenFile: 16, DisableCore: true},
|
||||
expect: []int{syscall.RLIMIT_CPU, syscall.RLIMIT_DATA, syscall.RLIMIT_FSIZE, syscall.RLIMIT_STACK, syscall.RLIMIT_AS, syscall.RLIMIT_NOFILE, syscall.RLIMIT_CORE},
|
||||
},
|
||||
{
|
||||
name: "DisableCore only",
|
||||
rl: RLimits{DisableCore: true},
|
||||
expect: []int{syscall.RLIMIT_CORE},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
rls := tt.rl.PrepareRLimit()
|
||||
if len(rls) != len(tt.expect) {
|
||||
t.Fatalf("expected %d rlimits, got %d", len(tt.expect), len(rls))
|
||||
}
|
||||
for i, r := range rls {
|
||||
if r.Res != tt.expect[i] {
|
||||
t.Errorf("expected Res %d at %d, got %d", tt.expect[i], i, r.Res)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLimitString(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rl RLimit
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "CPU",
|
||||
rl: RLimit{Res: syscall.RLIMIT_CPU, Rlim: syscall.Rlimit{Cur: 1, Max: 2}},
|
||||
want: "CPU[1 s:2 s]",
|
||||
},
|
||||
{
|
||||
name: "NOFILE",
|
||||
rl: RLimit{Res: syscall.RLIMIT_NOFILE, Rlim: syscall.Rlimit{Cur: 10, Max: 20}},
|
||||
want: "OpenFile[10:20]",
|
||||
},
|
||||
{
|
||||
name: "DATA",
|
||||
rl: RLimit{Res: syscall.RLIMIT_DATA, Rlim: syscall.Rlimit{Cur: 1024, Max: 2048}},
|
||||
want: "Data[1.0 KiB:2.0 KiB]",
|
||||
},
|
||||
{
|
||||
name: "FSIZE",
|
||||
rl: RLimit{Res: syscall.RLIMIT_FSIZE, Rlim: syscall.Rlimit{Cur: 100, Max: 200}},
|
||||
want: "File[100 B:200 B]",
|
||||
},
|
||||
{
|
||||
name: "STACK",
|
||||
rl: RLimit{Res: syscall.RLIMIT_STACK, Rlim: syscall.Rlimit{Cur: 4096, Max: 8192}},
|
||||
want: "Stack[4.0 KiB:8.0 KiB]",
|
||||
},
|
||||
{
|
||||
name: "AS",
|
||||
rl: RLimit{Res: syscall.RLIMIT_AS, Rlim: syscall.Rlimit{Cur: 123, Max: 456}},
|
||||
want: "AddressSpace[123 B:456 B]",
|
||||
},
|
||||
{
|
||||
name: "CORE",
|
||||
rl: RLimit{Res: syscall.RLIMIT_CORE, Rlim: syscall.Rlimit{Cur: 0, Max: 0}},
|
||||
want: "Core[0 B:0 B]",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := tt.rl.String()
|
||||
if got != tt.want {
|
||||
t.Errorf("got %q, want %q", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLimitsString(t *testing.T) {
|
||||
rl := RLimits{
|
||||
CPU: 1,
|
||||
CPUHard: 2,
|
||||
Data: 1024,
|
||||
FileSize: 2048,
|
||||
Stack: 4096,
|
||||
AddressSpace: 8192,
|
||||
OpenFile: 16,
|
||||
DisableCore: true,
|
||||
}
|
||||
want := "RLimits[CPU[1 s:2 s],Data[1.0 KiB:1.0 KiB],File[2.0 KiB:2.0 KiB],Stack[4.0 KiB:4.0 KiB],AddressSpace[8.0 KiB:8.0 KiB],OpenFile[16:16],Core[0 B:0 B]]"
|
||||
got := rl.String()
|
||||
if got != want {
|
||||
t.Errorf("got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRLimitsString_Empty(t *testing.T) {
|
||||
rl := RLimits{}
|
||||
want := "RLimits[]"
|
||||
got := rl.String()
|
||||
if got != want {
|
||||
t.Errorf("got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,8 @@ package unixsocket
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@ -26,3 +28,115 @@ func TestBaseline(t *testing.T) {
|
||||
t.Fatal("not equal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendRecvMsg_Fds(t *testing.T) {
|
||||
a, b, err := NewSocketPair()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer a.Close()
|
||||
defer b.Close()
|
||||
|
||||
// Create a file to send its fd
|
||||
tmpfile, err := os.CreateTemp("", "unixsocket-fd")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(tmpfile.Name())
|
||||
defer tmpfile.Close()
|
||||
|
||||
msg := []byte("fdtest")
|
||||
go func() {
|
||||
a.SendMsg(msg, Msg{Fds: []int{int(tmpfile.Fd())}})
|
||||
}()
|
||||
|
||||
buf := make([]byte, 64)
|
||||
n, m, err := b.RecvMsg(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(buf[:n], msg) {
|
||||
t.Errorf("RecvMsg got %q, want %q", buf[:n], msg)
|
||||
}
|
||||
if len(m.Fds) != 1 {
|
||||
t.Errorf("expected 1 fd, got %d", len(m.Fds))
|
||||
}
|
||||
if m.Fds != nil {
|
||||
syscall.Close(m.Fds[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendRecvMsg_Cred(t *testing.T) {
|
||||
if os.Geteuid() != 0 {
|
||||
t.Skip("skipping credential test: requires root privileges")
|
||||
return
|
||||
}
|
||||
a, b, err := NewSocketPair()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer a.Close()
|
||||
defer b.Close()
|
||||
|
||||
// Enable credential passing
|
||||
if err := a.SetPassCred(1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := b.SetPassCred(1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
msg := []byte("credtest")
|
||||
go func() {
|
||||
a.SendMsg(msg, Msg{Cred: &syscall.Ucred{Pid: 123, Uid: 456, Gid: 789}})
|
||||
}()
|
||||
|
||||
buf := make([]byte, 64)
|
||||
n, m, err := b.RecvMsg(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(buf[:n], msg) {
|
||||
t.Errorf("RecvMsg got %q, want %q", buf[:n], msg)
|
||||
}
|
||||
if m.Cred == nil {
|
||||
t.Error("expected credential, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewSocketPair_Close(t *testing.T) {
|
||||
a, b, err := NewSocketPair()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := a.Close(); err != nil {
|
||||
t.Errorf("a.Close() error: %v", err)
|
||||
}
|
||||
if err := b.Close(); err != nil {
|
||||
t.Errorf("b.Close() error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewSocket_InvalidFd(t *testing.T) {
|
||||
// Use an invalid fd
|
||||
_, err := NewSocket(-1)
|
||||
if err == nil {
|
||||
t.Error("expected error for invalid fd, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetPassCred_InvalidSocket(t *testing.T) {
|
||||
a, b, err := NewSocketPair()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer a.Close()
|
||||
defer b.Close()
|
||||
|
||||
// Close the socket to make it invalid
|
||||
a.Close()
|
||||
err = a.SetPassCred(1)
|
||||
if err == nil {
|
||||
t.Error("expected error on SetPassCred for closed socket, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user