@@ -3,11 +3,15 @@ package nfs_test
33import (
44 "bytes"
55 "fmt"
6+ "math/rand"
67 "net"
8+ "os"
79 "reflect"
810 "sort"
11+ "sync"
912 "testing"
1013
14+ "github.com/go-git/go-billy/v5"
1115 nfs "github.com/willscott/go-nfs"
1216 "github.com/willscott/go-nfs/helpers"
1317 "github.com/willscott/go-nfs/helpers/memfs"
@@ -18,6 +22,75 @@ import (
1822 "github.com/willscott/go-nfs-client/nfs/xdr"
1923)
2024
25+ type OpenArgs struct {
26+ File string
27+ Flag int
28+ Perm os.FileMode
29+ }
30+
31+ func (o * OpenArgs ) String () string {
32+ return fmt .Sprintf ("\" %s\" ; %05xd %s" , o .File , o .Flag , o .Perm )
33+ }
34+
35+ // NewTrackingFS wraps fs to detect file handle leaks.
36+ func NewTrackingFS (fs billy.Filesystem ) * trackingFS {
37+ return & trackingFS {Filesystem : fs , open : make (map [int64 ]OpenArgs )}
38+ }
39+
40+ // trackingFS wraps a Filesystem to detect file handle leaks.
41+ type trackingFS struct {
42+ billy.Filesystem
43+ mu sync.Mutex
44+ open map [int64 ]OpenArgs
45+ }
46+
47+ func (t * trackingFS ) ListOpened () []OpenArgs {
48+ t .mu .Lock ()
49+ defer t .mu .Unlock ()
50+ ret := make ([]OpenArgs , 0 , len (t .open ))
51+ for _ , o := range t .open {
52+ ret = append (ret , o )
53+ }
54+ return ret
55+ }
56+
57+ func (t * trackingFS ) Create (filename string ) (billy.File , error ) {
58+ return t .OpenFile (filename , os .O_RDWR | os .O_CREATE | os .O_TRUNC , 0666 )
59+ }
60+
61+ func (t * trackingFS ) Open (filename string ) (billy.File , error ) {
62+ return t .OpenFile (filename , os .O_RDONLY , 0 )
63+ }
64+
65+ func (t * trackingFS ) OpenFile (filename string , flag int , perm os.FileMode ) (billy.File , error ) {
66+ open , err := t .Filesystem .OpenFile (filename , flag , perm )
67+ if err != nil {
68+ return nil , err
69+ }
70+ t .mu .Lock ()
71+ defer t .mu .Unlock ()
72+ id := rand .Int63 ()
73+ t .open [id ] = OpenArgs {filename , flag , perm }
74+ closer := func () {
75+ delete (t .open , id )
76+ }
77+ open = & trackingFile {
78+ File : open ,
79+ onClose : closer ,
80+ }
81+ return open , err
82+ }
83+
84+ type trackingFile struct {
85+ billy.File
86+ onClose func ()
87+ }
88+
89+ func (f * trackingFile ) Close () error {
90+ f .onClose ()
91+ return f .File .Close ()
92+ }
93+
2194func TestNFS (t * testing.T ) {
2295 if testing .Verbose () {
2396 util .DefaultLogger .SetDebug (true )
@@ -29,9 +102,17 @@ func TestNFS(t *testing.T) {
29102 t .Fatal (err )
30103 }
31104
32- mem := memfs .New ()
105+ mem := NewTrackingFS (memfs .New ())
106+
107+ defer func () {
108+ if opened := mem .ListOpened (); len (opened ) > 0 {
109+ t .Errorf ("Unclosed files: %v" , opened )
110+ }
111+ }()
112+
33113 // File needs to exist in the root for memfs to acknowledge the root exists.
34- _ , _ = mem .Create ("/test" )
114+ r , _ := mem .Create ("/test" )
115+ r .Close ()
35116
36117 handler := helpers .NewNullAuthHandler (mem )
37118 cacheHelper := helpers .NewCachingHandler (handler , 1024 )
@@ -78,12 +159,18 @@ func TestNFS(t *testing.T) {
78159 if err != nil {
79160 t .Fatal (err )
80161 }
162+ defer f .Close ()
81163 b := []byte ("hello world" )
82164 _ , err = f .Write (b )
83165 if err != nil {
84166 t .Fatal (err )
85167 }
86- mf , _ := mem .Open ("/helloworld.txt" )
168+
169+ mf , err := target .Open ("/helloworld.txt" )
170+ if err != nil {
171+ t .Fatal (err )
172+ }
173+ defer mf .Close ()
87174 buf := make ([]byte , len (b ))
88175 if _ , err = mf .Read (buf [:]); err != nil {
89176 t .Fatal (err )
0 commit comments