unix: make solaris syscall tests less flaky

Fixes golang/go#58259

Change-Id: I1e8a83ed6ee3be8165c771b81a3cbdd474216c02
Reviewed-on: https://go-review.googlesource.com/c/sys/+/465055
Auto-Submit: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Auto-Submit: Bryan Mills <bcmills@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
This commit is contained in:
Nahum Shalman
2023-03-16 22:08:31 +00:00
committed by Gopher Robot
parent ff18efa0a3
commit d0781cc69b
2 changed files with 93 additions and 21 deletions
+39 -5
View File
@@ -28,6 +28,40 @@ func (e *EventPort) checkInternals(t *testing.T, fds, paths, cookies, pending in
} }
} }
// getOneRetry wraps EventPort.GetOne which in turn wraps a syscall that can be
// interrupted causing us to receive EINTR.
// To prevent our tests from flaking, we retry the syscall until it works
// rather than get unexpected results in our tests.
func getOneRetry(t *testing.T, p *EventPort, timeout *Timespec) (e *PortEvent, err error) {
t.Helper()
for {
e, err = p.GetOne(timeout)
if err != EINTR {
break
}
}
return e, err
}
// getRetry wraps EventPort.Get which in turn wraps a syscall that can be
// interrupted causing us to receive EINTR.
// To prevent our tests from flaking, we retry the syscall until it works
// rather than get unexpected results in our tests.
func getRetry(t *testing.T, p *EventPort, s []PortEvent, min int, timeout *Timespec) (n int, err error) {
t.Helper()
for {
n, err = p.Get(s, min, timeout)
if err != EINTR {
break
}
// If we did get EINTR, make sure we got 0 events
if n != 0 {
t.Fatalf("EventPort.Get returned events on EINTR.\ngot: %d\nexpected: 0", n)
}
}
return n, err
}
// Regression test for DissociatePath returning ENOENT // Regression test for DissociatePath returning ENOENT
// This test is intended to create a linear worst // This test is intended to create a linear worst
// case scenario of events being associated and // case scenario of events being associated and
@@ -143,7 +177,7 @@ func TestEventPortDissociateAlreadyGone(t *testing.T) {
runtime.GC() runtime.GC()
// Before the fix, this would cause a nil pointer exception // Before the fix, this would cause a nil pointer exception
e, err := port.GetOne(nil) e, err := getOneRetry(t, port, nil)
if err != nil { if err != nil {
t.Errorf("failed to get an event: %v", err) t.Errorf("failed to get an event: %v", err)
} }
@@ -152,7 +186,7 @@ func TestEventPortDissociateAlreadyGone(t *testing.T) {
t.Errorf(`expected "cookie1", got "%v"`, e.Cookie) t.Errorf(`expected "cookie1", got "%v"`, e.Cookie)
} }
// Make sure that a cookie of the same value doesn't cause removal from the paths map incorrectly // Make sure that a cookie of the same value doesn't cause removal from the paths map incorrectly
e, err = port.GetOne(nil) e, err = getOneRetry(t, port, nil)
if err != nil { if err != nil {
t.Errorf("failed to get an event: %v", err) t.Errorf("failed to get an event: %v", err)
} }
@@ -167,7 +201,7 @@ func TestEventPortDissociateAlreadyGone(t *testing.T) {
} }
// Event has fired, but until processed it should still be in the map // Event has fired, but until processed it should still be in the map
port.checkInternals(t, 0, 1, 1, 1) port.checkInternals(t, 0, 1, 1, 1)
e, err = port.GetOne(nil) e, err = getOneRetry(t, port, nil)
if err != nil { if err != nil {
t.Errorf("failed to get an event: %v", err) t.Errorf("failed to get an event: %v", err)
} }
@@ -221,12 +255,12 @@ func TestEventPortGetAfterClose(t *testing.T) {
port.paths = nil port.paths = nil
port.cookies = nil port.cookies = nil
// Ensure that we get back reasonable errors rather than panic // Ensure that we get back reasonable errors rather than panic
_, err = port.GetOne(nil) _, err = getOneRetry(t, port, nil)
if err == nil || err.Error() != "this EventPort is already closed" { if err == nil || err.Error() != "this EventPort is already closed" {
t.Errorf("didn't receive expected error of 'this EventPort is already closed'; got: %v", err) t.Errorf("didn't receive expected error of 'this EventPort is already closed'; got: %v", err)
} }
events := make([]PortEvent, 2) events := make([]PortEvent, 2)
n, err = port.Get(events, 1, nil) n, err = getRetry(t, port, events, 1, nil)
if n != 0 { if n != 0 {
t.Errorf("expected to get back 0 events, got %d", n) t.Errorf("expected to get back 0 events, got %d", n)
} }
+54 -16
View File
@@ -18,6 +18,40 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
// getOneRetry wraps EventPort.GetOne which in turn wraps a syscall that can be
// interrupted causing us to receive EINTR.
// To prevent our tests from flaking, we retry the syscall until it works
// rather than get unexpected results in our tests.
func getOneRetry(t *testing.T, p *unix.EventPort, timeout *unix.Timespec) (e *unix.PortEvent, err error) {
t.Helper()
for {
e, err = p.GetOne(timeout)
if err != unix.EINTR {
break
}
}
return e, err
}
// getRetry wraps EventPort.Get which in turn wraps a syscall that can be
// interrupted causing us to receive EINTR.
// To prevent our tests from flaking, we retry the syscall until it works
// rather than get unexpected results in our tests.
func getRetry(t *testing.T, p *unix.EventPort, s []unix.PortEvent, min int, timeout *unix.Timespec) (n int, err error) {
t.Helper()
for {
n, err = p.Get(s, min, timeout)
if err != unix.EINTR {
break
}
// If we did get EINTR, make sure we got 0 events
if n != 0 {
t.Fatalf("EventPort.Get returned events on EINTR.\ngot: %d\nexpected: 0", n)
}
}
return n, err
}
func TestStatvfs(t *testing.T) { func TestStatvfs(t *testing.T) {
if err := unix.Statvfs("", nil); err == nil { if err := unix.Statvfs("", nil); err == nil {
t.Fatal(`Statvfs("") expected failure`) t.Fatal(`Statvfs("") expected failure`)
@@ -84,13 +118,13 @@ func TestBasicEventPort(t *testing.T) {
bs := []byte{42} bs := []byte{42}
tmpfile.Write(bs) tmpfile.Write(bs)
timeout := new(unix.Timespec) timeout := new(unix.Timespec)
timeout.Sec = 1 timeout.Nsec = 100
pevent, err := port.GetOne(timeout) pevent, err := getOneRetry(t, port, timeout)
if err == unix.ETIME { if err == unix.ETIME {
t.Errorf("GetOne timed out: %v", err) t.Errorf("GetOne timed out: %v", err)
} }
if err != nil { if err != nil {
t.Errorf("GetOne failed: %v", err) t.Fatalf("GetOne failed: %v", err)
} }
if pevent.Path != path { if pevent.Path != path {
t.Errorf("Path mismatch: %v != %v", pevent.Path, path) t.Errorf("Path mismatch: %v != %v", pevent.Path, path)
@@ -135,13 +169,13 @@ func TestEventPortFds(t *testing.T) {
t.Errorf("Pending() failed: %v, %v", n, err) t.Errorf("Pending() failed: %v, %v", n, err)
} }
timeout := new(unix.Timespec) timeout := new(unix.Timespec)
timeout.Sec = 1 timeout.Nsec = 100
pevent, err := port.GetOne(timeout) pevent, err := getOneRetry(t, port, timeout)
if err == unix.ETIME { if err == unix.ETIME {
t.Errorf("GetOne timed out: %v", err) t.Errorf("GetOne timed out: %v", err)
} }
if err != nil { if err != nil {
t.Errorf("GetOne failed: %v", err) t.Fatalf("GetOne failed: %v", err)
} }
if pevent.Fd != fd { if pevent.Fd != fd {
t.Errorf("Fd mismatch: %v != %v", pevent.Fd, fd) t.Errorf("Fd mismatch: %v != %v", pevent.Fd, fd)
@@ -181,10 +215,8 @@ func TestEventPortErrors(t *testing.T) {
} }
timeout := new(unix.Timespec) timeout := new(unix.Timespec)
timeout.Nsec = 1 timeout.Nsec = 1
_, err = port.GetOne(timeout) _, err = getOneRetry(t, port, timeout)
if err != unix.ETIME { if err != unix.ETIME {
// See https://go.dev/issue/58259
// Perhaps we sometimes get EINTR ???
t.Errorf("port.GetOne(%v) returned error %v, want %v", timeout, err, unix.ETIME) t.Errorf("port.GetOne(%v) returned error %v, want %v", timeout, err, unix.ETIME)
} }
err = port.DissociateFd(uintptr(0)) err = port.DissociateFd(uintptr(0))
@@ -192,15 +224,15 @@ func TestEventPortErrors(t *testing.T) {
t.Errorf("unexpected success dissociating unassociated fd") t.Errorf("unexpected success dissociating unassociated fd")
} }
events := make([]unix.PortEvent, 4) events := make([]unix.PortEvent, 4)
_, err = port.Get(events, 5, nil) _, err = getRetry(t, port, events, 5, nil)
if err == nil { if err == nil {
t.Errorf("unexpected success calling Get with min greater than len of slice") t.Errorf("unexpected success calling Get with min greater than len of slice")
} }
_, err = port.Get(nil, 1, nil) _, err = getRetry(t, port, nil, 1, nil)
if err == nil { if err == nil {
t.Errorf("unexpected success calling Get with nil slice") t.Errorf("unexpected success calling Get with nil slice")
} }
_, err = port.Get(nil, 0, nil) _, err = getRetry(t, port, nil, 0, nil)
if err == nil { if err == nil {
t.Errorf("unexpected success calling Get with nil slice") t.Errorf("unexpected success calling Get with nil slice")
} }
@@ -232,7 +264,13 @@ func ExamplePortEvent() {
w.Write(bs) w.Write(bs)
timeout := new(unix.Timespec) timeout := new(unix.Timespec)
timeout.Sec = 1 timeout.Sec = 1
pevent, err := port.GetOne(timeout) var pevent *unix.PortEvent
for {
pevent, err = port.GetOne(timeout)
if err != unix.EINTR {
break
}
}
if err != nil { if err != nil {
fmt.Printf("didn't get the expected event: %v\n", err) fmt.Printf("didn't get the expected event: %v\n", err)
} }
@@ -278,7 +316,7 @@ func TestPortEventSlices(t *testing.T) {
timeout := new(unix.Timespec) timeout := new(unix.Timespec)
timeout.Nsec = 1 timeout.Nsec = 1
events := make([]unix.PortEvent, 4) events := make([]unix.PortEvent, 4)
n, err = port.Get(events, 3, timeout) n, err = getRetry(t, port, events, 3, timeout)
if err != nil { if err != nil {
t.Errorf("Get failed: %v", err) t.Errorf("Get failed: %v", err)
} }
@@ -291,7 +329,7 @@ func TestPortEventSlices(t *testing.T) {
t.Errorf("unexpected event. got %v, expected %v", p.Events, unix.FILE_DELETE) t.Errorf("unexpected event. got %v, expected %v", p.Events, unix.FILE_DELETE)
} }
} }
n, err = port.Get(events, 3, timeout) n, err = getRetry(t, port, events, 3, timeout)
if err != unix.ETIME { if err != unix.ETIME {
t.Errorf("unexpected error. got %v, expected %v", err, unix.ETIME) t.Errorf("unexpected error. got %v, expected %v", err, unix.ETIME)
} }
@@ -314,7 +352,7 @@ func TestPortEventSlices(t *testing.T) {
bs := []byte{41} bs := []byte{41}
w.Write(bs) w.Write(bs)
n, err = port.Get(events, 1, timeout) n, err = getRetry(t, port, events, 1, timeout)
if err != nil { if err != nil {
t.Errorf("Get failed: %v", err) t.Errorf("Get failed: %v", err)
} }