Documentation
¶
Overview ¶
Package fdooze complements the Gingko/Gomega testing and matchers framework with matchers for file descriptor leakage detection.
Please note that due to technical restrictions this (experimental) package is available only for Linux.
Basic Usage ¶
In your project (with a go.mod) run
go get github.com/thediveo/fdooze
to get and install the latest stable release.
A typical usage in your tests and using Ginkgo then is, after dot-importing the fdooze package:
import . "github.com/thediveo/fdooze" var _ = Describe("...", func() { BeforeEach(func() { goodfds := Filedescriptors() DeferCleanup(func() { Expect(Filedescriptors()).NotTo(HaveLeakedFds(goodfds)) }) }) })
This takes a snapshot of “good” file descriptors before each test and then after each test it checks to see if there are any leftover file descriptors that weren't already in use before a test. The fdooze package does not blindly just compare fd numbers, but takes as much additional detail information as possible into account: like file paths, socket domains, types, protocols and addresses, et cetera.
On finding leaked file descriptors, fdooze dumps these leaked fds in the failure message of the HaveLeakedFds matcher. For instance:
Expected not to leak 1 file descriptors: fd 7, flags 0xa0000 (O_RDONLY,O_CLOEXEC) path: "/home/leaky/module/oozing_test.go"
For other types of file descriptors, such as pipes and sockets, several details will differ: instead of a path, other parameters will be shown, like pipe inode numbers or socket addresses. Due to the limitations of the existing fd discovery API, it is not possible to see where the file descriptor was opened (which might be deep inside some 3rd party package anyway).
Expect or Eventually ¶
In case you are already familiar with Gomega's github.com/onsi/gomega/gleak goroutine leak detection package, then please note that typical fdooze usage doesn't require Eventually, so Expect is fine most of the time. However, in situations where goroutines open file descriptors it might be a good idea to first wait for goroutines to terminate and not leak and only then test for any file descriptor leaks.
When using Eventually make sure to pass the Filedescriptors function itself, but not the result of calling Filedescriptors.
// ✔✔✔ Correct ✔✔✔ Eventually(Filedescriptors).ShouldNot(HaveLeakedFds(...)) // ❌❌❌ WRONG WRONG WRONG ❌❌❌ Eventually(Filedescriptors()).ShouldNot(HaveLeakedFds(...))
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func HaveLeakedFds ¶
func HaveLeakedFds(fds []FileDescriptor, ignoring ...types.GomegaMatcher) types.GomegaMatcher
HaveLeakedFds succeeds if after filtering out expected file descriptors from the list of actual file descriptors the remaining list is non-empty. The file descriptors not filtered out are considered to have been leaked.
Optional additional filter matchers can be specified that can filter out use case-specific file descriptors based on various fd properties. Please refer to the github.com/thediveo/fdooze/filedesc package for details about the defined github.com/thediveo/fdooze/filedesc.FileDescriptor implementations for various types of file descriptors.
File descriptors are identified not only based on the fd number, but also additional associated information, such as a file path they link to, type, socket inode number, et cetera. As fd numbers tend to quickly get reused this allows detecting changed fds in many (if not most) situations with enough accuracy.
HaveLeakedFds does not assume any well-known fds, and in particular, it does not make any assumptions about fds with numbers 0, 1, 2.
A typical way to check for leaked (“oozed”) file descriptors is as follows, after dot-importing the fdooze package:
import . "github.com/thediveo/fdooze" var _ = Describe("...", { BeforeEach(func() { goodfds := Filedescriptors() DeferCleanup(func() { Expect(Filedescriptors()).NotTo(HaveLeakedFds(goodfds)) }) }) })
HaveLeakedFds accepts optional Gomega matchers (of type types.GomegaMatcher) that it will repeatedly pass FileDescriptor values to: if a matcher succeeds, the particular file descriptor is considered not to be leaked and thus filtered out. Especially Gomega's HaveField matcher can be quite useful in covering specific use cases where the otherwise straightforward before-after fd comparism isn't enough.
func IgnoringFiledescriptors ¶
func IgnoringFiledescriptors(fds []FileDescriptor) types.GomegaMatcher
IgnoringFiledescriptors succeeds if an actual FileDescriptor in contained in a slice of expected file descriptors. An actual FileDescriptor is considered to be contained, if the slice must contains a FileDescriptor with the same fd number and filedesc.FileDescriptor.Equal considers both file descriptors to be equal.
Please note that fd flags and file offsets are ignored when testing for equality, in order to avoid spurious false positives.
Types ¶
type FileDescriptor ¶ added in v0.1.4
type FileDescriptor = filedesc.FileDescriptor
FileDescriptor describes a Linux “fd” file descriptor in more detail than just its fd int number; it is a type alias of filedesc.FileDescriptor.
func Filedescriptors ¶
func Filedescriptors() []FileDescriptor
Filedescriptors returns the list of currently open file descriptors for this process.
Directories
¶
Path | Synopsis |
---|---|
Package filedesc implements file descriptor (“fd”) discovery beyond just the plain fd numbers.
|
Package filedesc implements file descriptor (“fd”) discovery beyond just the plain fd numbers. |
Package session implements retrieving the open file descriptors from a Gomega gexec.Session (Linux only).
|
Package session implements retrieving the open file descriptors from a Gomega gexec.Session (Linux only). |