diff options
Diffstat (limited to '')
-rw-r--r-- | dependencies/pkg/mod/golang.org/x/sys@v0.1.0/unix/syscall_internal_solaris_test.go | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/dependencies/pkg/mod/golang.org/x/sys@v0.1.0/unix/syscall_internal_solaris_test.go b/dependencies/pkg/mod/golang.org/x/sys@v0.1.0/unix/syscall_internal_solaris_test.go new file mode 100644 index 0000000..a92753a --- /dev/null +++ b/dependencies/pkg/mod/golang.org/x/sys@v0.1.0/unix/syscall_internal_solaris_test.go @@ -0,0 +1,236 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build solaris +// +build solaris + +package unix + +import ( + "fmt" + "os" + "runtime" + "testing" +) + +func (e *EventPort) checkInternals(t *testing.T, fds, paths, cookies, pending int) { + t.Helper() + p, err := e.Pending() + if err != nil { + t.Fatalf("failed to query how many events are pending") + } + if len(e.fds) != fds || len(e.paths) != paths || len(e.cookies) != cookies || p != pending { + format := "| fds: %d | paths: %d | cookies: %d | pending: %d |" + expected := fmt.Sprintf(format, fds, paths, cookies, pending) + got := fmt.Sprintf(format, len(e.fds), len(e.paths), len(e.cookies), p) + t.Errorf("Internal state mismatch\nfound: %s\nexpected: %s", got, expected) + } +} + +// Regression test for DissociatePath returning ENOENT +// This test is intended to create a linear worst +// case scenario of events being associated and +// fired but not consumed before additional +// calls to dissociate and associate happen +// This needs to be an internal test so that +// we can validate the state of the private maps +func TestEventPortDissociateAlreadyGone(t *testing.T) { + port, err := NewEventPort() + if err != nil { + t.Fatalf("failed to create an EventPort") + } + defer port.Close() + dir := t.TempDir() + tmpfile, err := os.CreateTemp(dir, "eventport") + if err != nil { + t.Fatalf("unable to create a tempfile: %v", err) + } + path := tmpfile.Name() + stat, err := os.Stat(path) + if err != nil { + t.Fatalf("unexpected failure to Stat file: %v", err) + } + err = port.AssociatePath(path, stat, FILE_MODIFIED, "cookie1") + if err != nil { + t.Fatalf("unexpected failure associating file: %v", err) + } + // We should have 1 path registered and 1 cookie in the jar + port.checkInternals(t, 0, 1, 1, 0) + // The path is associated, let's delete it. + err = os.Remove(path) + if err != nil { + t.Fatalf("unexpected failure deleting file: %v", err) + } + // The file has been deleted, some sort of pending event is probably + // queued in the kernel waiting for us to get it AND the kernel is + // no longer watching for events on it. BUT... Because we haven't + // consumed the event, this API thinks it's still watched: + watched := port.PathIsWatched(path) + if !watched { + t.Errorf("unexpected result from PathIsWatched") + } + // Ok, let's dissociate the file even before reading the event. + // Oh, ENOENT. I guess it's not associated any more + err = port.DissociatePath(path) + if err != ENOENT { + t.Errorf("unexpected result dissociating a seemingly associated path we know isn't: %v", err) + } + // As established by the return value above, this should clearly be false now: + // Narrator voice: in the first version of this API, it wasn't. + watched = port.PathIsWatched(path) + if watched { + t.Errorf("definitively unwatched file still in the map") + } + // We should have nothing registered, but 1 pending event corresponding + // to the cookie in the jar + port.checkInternals(t, 0, 0, 1, 1) + f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + err = f.Close() + if err != nil { + t.Fatalf("unexpected failure closing file: %v", err) + } + stat, err = os.Stat(path) + if err != nil { + t.Fatalf("unexpected failure to Stat file: %v", err) + } + c := "cookie2" // c is for cookie, that's good enough for me + err = port.AssociatePath(path, stat, FILE_MODIFIED, c) + if err != nil { + t.Errorf("unexpected failure associating file: %v", err) + } + // We should have 1 registered path and its cookie + // as well as a second cookie corresponding to the pending event + port.checkInternals(t, 0, 1, 2, 1) + + // Fire another event + err = os.Remove(path) + if err != nil { + t.Fatalf("unexpected failure deleting file: %v", err) + } + port.checkInternals(t, 0, 1, 2, 2) + err = port.DissociatePath(path) + if err != ENOENT { + t.Errorf("unexpected result dissociating a seemingly associated path we know isn't: %v", err) + } + // By dissociating this path after deletion we ensure that the paths map is now empty + // If we're not careful we could trigger a nil pointer exception + port.checkInternals(t, 0, 0, 2, 2) + + f, err = os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + err = f.Close() + if err != nil { + t.Fatalf("unexpected failure closing file: %v", err) + } + stat, err = os.Stat(path) + if err != nil { + t.Fatalf("unexpected failure to Stat file: %v", err) + } + // Put a seemingly duplicate cookie in the jar to see if we can trigger an incorrect removal from the paths map + err = port.AssociatePath(path, stat, FILE_MODIFIED, c) + if err != nil { + t.Errorf("unexpected failure associating file: %v", err) + } + port.checkInternals(t, 0, 1, 3, 2) + + // run the garbage collector so that if we messed up it should be painfully clear + runtime.GC() + + // Before the fix, this would cause a nil pointer exception + e, err := port.GetOne(nil) + if err != nil { + t.Errorf("failed to get an event: %v", err) + } + port.checkInternals(t, 0, 1, 2, 1) + if e.Cookie != "cookie1" { + 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 + e, err = port.GetOne(nil) + if err != nil { + t.Errorf("failed to get an event: %v", err) + } + port.checkInternals(t, 0, 1, 1, 0) + if e.Cookie != "cookie2" { + t.Errorf(`expected "cookie2", got "%v"`, e.Cookie) + } + + err = os.Remove(path) + if err != nil { + t.Fatalf("unexpected failure deleting file: %v", err) + } + // Event has fired, but until processed it should still be in the map + port.checkInternals(t, 0, 1, 1, 1) + e, err = port.GetOne(nil) + if err != nil { + t.Errorf("failed to get an event: %v", err) + } + if e.Cookie != "cookie2" { + t.Errorf(`expected "cookie2", got "%v"`, e.Cookie) + } + // The maps should be empty and there should be no pending events + port.checkInternals(t, 0, 0, 0, 0) +} + +// Regression test for spuriously triggering a panic about memory mismanagement +// that can be triggered by an event processing thread trying to process an event +// after a different thread has already called port.Close(). +// Implemented as an internal test so that we can just simulate the Close() +// because if you call close first in the same thread, things work properly +// anyway. +func TestEventPortGetAfterClose(t *testing.T) { + port, err := NewEventPort() + if err != nil { + t.Fatalf("NewEventPort failed: %v", err) + } + // Create, associate, and delete 2 files + for i := 0; i < 2; i++ { + tmpfile, err := os.CreateTemp("", "eventport") + if err != nil { + t.Fatalf("unable to create tempfile: %v", err) + } + path := tmpfile.Name() + stat, err := os.Stat(path) + if err != nil { + t.Fatalf("unable to stat tempfile: %v", err) + } + err = port.AssociatePath(path, stat, FILE_MODIFIED, nil) + if err != nil { + t.Fatalf("unable to AssociatePath tempfile: %v", err) + } + err = os.Remove(path) + if err != nil { + t.Fatalf("unable to Remove tempfile: %v", err) + } + } + n, err := port.Pending() + if err != nil { + t.Errorf("Pending failed: %v", err) + } + if n != 2 { + t.Errorf("expected 2 pending events, got %d", n) + } + // Simulate a close from a different thread + port.fds = nil + port.paths = nil + port.cookies = nil + // Ensure that we get back reasonable errors rather than panic + _, err = port.GetOne(nil) + 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) + } + events := make([]PortEvent, 2) + n, err = port.Get(events, 1, nil) + if n != 0 { + t.Errorf("expected to get back 0 events, got %d", n) + } + 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) + } +} |