fs.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. // Test suite for rclonefs
  2. package vfstest
  3. import (
  4. "bufio"
  5. "context"
  6. "flag"
  7. "fmt"
  8. "io"
  9. "os"
  10. "os/exec"
  11. "path"
  12. "path/filepath"
  13. "reflect"
  14. "runtime"
  15. "strings"
  16. "sync"
  17. "testing"
  18. "time"
  19. _ "github.com/rclone/rclone/backend/all" // import all the backends
  20. "github.com/rclone/rclone/cmd/mountlib"
  21. "github.com/rclone/rclone/fs"
  22. "github.com/rclone/rclone/fs/walk"
  23. "github.com/rclone/rclone/fstest"
  24. "github.com/rclone/rclone/vfs/vfscommon"
  25. "github.com/stretchr/testify/assert"
  26. "github.com/stretchr/testify/require"
  27. )
  28. const (
  29. waitForWritersDelay = 30 * time.Second // time to wait for existing writers
  30. )
  31. // RunTests runs all the tests against all the VFS cache modes
  32. //
  33. // If useVFS is set then it runs the tests against a VFS rather than a
  34. // mount
  35. //
  36. // If useVFS is not set then it runs the mount in a subprocess in
  37. // order to avoid kernel deadlocks.
  38. func RunTests(t *testing.T, useVFS bool, minimumRequiredCacheMode vfscommon.CacheMode, enableCacheTests bool, mountFn mountlib.MountFn) {
  39. flag.Parse()
  40. if isSubProcess() {
  41. startMount(mountFn, useVFS, *runMount)
  42. return
  43. }
  44. tests := []struct {
  45. cacheMode vfscommon.CacheMode
  46. writeBack fs.Duration
  47. }{
  48. {cacheMode: vfscommon.CacheModeOff},
  49. {cacheMode: vfscommon.CacheModeMinimal},
  50. {cacheMode: vfscommon.CacheModeWrites},
  51. {cacheMode: vfscommon.CacheModeFull},
  52. {cacheMode: vfscommon.CacheModeFull, writeBack: fs.Duration(100 * time.Millisecond)},
  53. }
  54. for _, test := range tests {
  55. if test.cacheMode < minimumRequiredCacheMode {
  56. continue
  57. }
  58. vfsOpt := vfscommon.Opt
  59. vfsOpt.CacheMode = test.cacheMode
  60. vfsOpt.WriteBack = test.writeBack
  61. run = newRun(useVFS, &vfsOpt, mountFn)
  62. what := fmt.Sprintf("CacheMode=%v", test.cacheMode)
  63. if test.writeBack > 0 {
  64. what += fmt.Sprintf(",WriteBack=%v", test.writeBack)
  65. }
  66. fs.Logf(nil, "Starting test run with %s", what)
  67. ok := t.Run(what, func(t *testing.T) {
  68. t.Run("TestTouchAndDelete", TestTouchAndDelete)
  69. t.Run("TestRenameOpenHandle", TestRenameOpenHandle)
  70. t.Run("TestDirLs", TestDirLs)
  71. t.Run("TestDirCreateAndRemoveDir", TestDirCreateAndRemoveDir)
  72. t.Run("TestDirCreateAndRemoveFile", TestDirCreateAndRemoveFile)
  73. t.Run("TestDirRenameFile", TestDirRenameFile)
  74. t.Run("TestDirRenameEmptyDir", TestDirRenameEmptyDir)
  75. t.Run("TestDirRenameFullDir", TestDirRenameFullDir)
  76. t.Run("TestDirModTime", TestDirModTime)
  77. if enableCacheTests {
  78. t.Run("TestDirCacheFlush", TestDirCacheFlush)
  79. }
  80. t.Run("TestDirCacheFlushOnDirRename", TestDirCacheFlushOnDirRename)
  81. t.Run("TestFileModTime", TestFileModTime)
  82. t.Run("TestFileModTimeWithOpenWriters", TestFileModTimeWithOpenWriters)
  83. t.Run("TestMount", TestMount)
  84. t.Run("TestRoot", TestRoot)
  85. t.Run("TestReadByByte", TestReadByByte)
  86. t.Run("TestReadChecksum", TestReadChecksum)
  87. t.Run("TestReadFileDoubleClose", TestReadFileDoubleClose)
  88. t.Run("TestReadSeek", TestReadSeek)
  89. t.Run("TestWriteFileNoWrite", TestWriteFileNoWrite)
  90. t.Run("TestWriteFileWrite", TestWriteFileWrite)
  91. t.Run("TestWriteFileOverwrite", TestWriteFileOverwrite)
  92. t.Run("TestWriteFileDoubleClose", TestWriteFileDoubleClose)
  93. t.Run("TestWriteFileFsync", TestWriteFileFsync)
  94. t.Run("TestWriteFileDup", TestWriteFileDup)
  95. t.Run("TestWriteFileAppend", TestWriteFileAppend)
  96. })
  97. fs.Logf(nil, "Finished test run with %s (ok=%v)", what, ok)
  98. run.Finalise()
  99. if !ok {
  100. break
  101. }
  102. }
  103. }
  104. // Run holds the remotes for a test run
  105. type Run struct {
  106. os Oser
  107. vfsOpt *vfscommon.Options
  108. useVFS bool // set if we are testing a VFS not a mount
  109. mountPath string
  110. fremote fs.Fs
  111. fremoteName string
  112. cleanRemote func()
  113. skip bool
  114. // For controlling the subprocess running the mount
  115. cmdMu sync.Mutex
  116. cmd *exec.Cmd
  117. in io.ReadCloser
  118. out io.WriteCloser
  119. scanner *bufio.Scanner
  120. }
  121. // run holds the master Run data
  122. var run *Run
  123. // newRun initialise the remote mount for testing and returns a run
  124. // object.
  125. //
  126. // r.fremote is an empty remote Fs
  127. //
  128. // Finalise() will tidy them away when done.
  129. func newRun(useVFS bool, vfsOpt *vfscommon.Options, mountFn mountlib.MountFn) *Run {
  130. r := &Run{
  131. useVFS: useVFS,
  132. vfsOpt: vfsOpt,
  133. }
  134. r.vfsOpt.Init()
  135. fstest.Initialise()
  136. var err error
  137. r.fremote, r.fremoteName, r.cleanRemote, err = fstest.RandomRemote()
  138. if err != nil {
  139. fs.Fatalf(nil, "Failed to open remote %q: %v", *fstest.RemoteName, err)
  140. }
  141. err = r.fremote.Mkdir(context.Background(), "")
  142. if err != nil {
  143. fs.Fatalf(nil, "Failed to open mkdir %q: %v", *fstest.RemoteName, err)
  144. }
  145. r.startMountSubProcess()
  146. return r
  147. }
  148. func (r *Run) skipIfNoFUSE(t *testing.T) {
  149. if r.skip {
  150. t.Skip("FUSE not found so skipping test")
  151. }
  152. }
  153. func (r *Run) skipIfVFS(t *testing.T) {
  154. if r.useVFS {
  155. t.Skip("Not running under VFS")
  156. }
  157. }
  158. // Finalise cleans the remote and unmounts
  159. func (r *Run) Finalise() {
  160. if !r.useVFS {
  161. r.sendMountCommand("exit")
  162. _, err := r.cmd.Process.Wait()
  163. if err != nil {
  164. fs.Fatalf(nil, "mount sub process failed: %v", err)
  165. }
  166. }
  167. r.cleanRemote()
  168. if !r.useVFS {
  169. err := os.RemoveAll(r.mountPath)
  170. if err != nil {
  171. fs.Logf(nil, "Failed to clean mountPath %q: %v", r.mountPath, err)
  172. }
  173. }
  174. }
  175. // path returns an OS local path for filepath
  176. func (r *Run) path(filePath string) string {
  177. if r.useVFS {
  178. return filePath
  179. }
  180. // return windows drive letter root as E:\
  181. if filePath == "" && runtime.GOOS == "windows" {
  182. return r.mountPath + `\`
  183. }
  184. return filepath.Join(r.mountPath, filepath.FromSlash(filePath))
  185. }
  186. type dirMap map[string]struct{}
  187. // Create a dirMap from a string
  188. func newDirMap(dirString string) (dm dirMap) {
  189. dm = make(dirMap)
  190. for _, entry := range strings.Split(dirString, "|") {
  191. if entry != "" {
  192. dm[entry] = struct{}{}
  193. }
  194. }
  195. return dm
  196. }
  197. // Returns a dirmap with only the files in
  198. func (dm dirMap) filesOnly() dirMap {
  199. newDm := make(dirMap)
  200. for name := range dm {
  201. if !strings.HasSuffix(name, "/") {
  202. newDm[name] = struct{}{}
  203. }
  204. }
  205. return newDm
  206. }
  207. // reads the local tree into dir
  208. func (r *Run) readLocal(t *testing.T, dir dirMap, filePath string) {
  209. realPath := r.path(filePath)
  210. files, err := r.os.ReadDir(realPath)
  211. require.NoError(t, err)
  212. for _, fi := range files {
  213. name := path.Join(filePath, fi.Name())
  214. if fi.IsDir() {
  215. dir[name+"/"] = struct{}{}
  216. r.readLocal(t, dir, name)
  217. assert.Equal(t, os.FileMode(r.vfsOpt.DirPerms)&os.ModePerm, fi.Mode().Perm())
  218. } else {
  219. dir[fmt.Sprintf("%s %d", name, fi.Size())] = struct{}{}
  220. assert.Equal(t, os.FileMode(r.vfsOpt.FilePerms)&os.ModePerm, fi.Mode().Perm())
  221. }
  222. }
  223. }
  224. // reads the remote tree into dir
  225. func (r *Run) readRemote(t *testing.T, dir dirMap, filepath string) {
  226. objs, dirs, err := walk.GetAll(context.Background(), r.fremote, filepath, true, 1)
  227. if err == fs.ErrorDirNotFound {
  228. return
  229. }
  230. require.NoError(t, err)
  231. for _, obj := range objs {
  232. dir[fmt.Sprintf("%s %d", obj.Remote(), obj.Size())] = struct{}{}
  233. }
  234. for _, d := range dirs {
  235. name := d.Remote()
  236. dir[name+"/"] = struct{}{}
  237. r.readRemote(t, dir, name)
  238. }
  239. }
  240. // checkDir checks the local and remote against the string passed in
  241. func (r *Run) checkDir(t *testing.T, dirString string) {
  242. var retries = *fstest.ListRetries
  243. sleep := time.Second / 5
  244. var remoteOK, fuseOK bool
  245. var dm, localDm, remoteDm dirMap
  246. for i := 1; i <= retries; i++ {
  247. dm = newDirMap(dirString)
  248. localDm = make(dirMap)
  249. r.readLocal(t, localDm, "")
  250. remoteDm = make(dirMap)
  251. r.readRemote(t, remoteDm, "")
  252. // Ignore directories for remote compare
  253. remoteOK = reflect.DeepEqual(dm.filesOnly(), remoteDm.filesOnly())
  254. fuseOK = reflect.DeepEqual(dm, localDm)
  255. if remoteOK && fuseOK {
  256. return
  257. }
  258. sleep *= 2
  259. t.Logf("Sleeping for %v for list eventual consistency: %d/%d", sleep, i, retries)
  260. time.Sleep(sleep)
  261. }
  262. assert.Equal(t, dm.filesOnly(), remoteDm.filesOnly(), "expected vs remote")
  263. assert.Equal(t, dm, localDm, "expected vs fuse mount")
  264. }
  265. // writeFile writes data to a file named by filename.
  266. // If the file does not exist, WriteFile creates it with permissions perm;
  267. // otherwise writeFile truncates it before writing.
  268. // If there is an error writing then writeFile
  269. // deletes it an existing file and tries again.
  270. func writeFile(filename string, data []byte, perm os.FileMode) error {
  271. f, err := run.os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
  272. if err != nil {
  273. err = run.os.Remove(filename)
  274. if err != nil {
  275. return err
  276. }
  277. f, err = run.os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, perm)
  278. if err != nil {
  279. return err
  280. }
  281. }
  282. n, err := f.Write(data)
  283. if err == nil && n < len(data) {
  284. err = io.ErrShortWrite
  285. }
  286. if err1 := f.Close(); err == nil {
  287. err = err1
  288. }
  289. return err
  290. }
  291. func (r *Run) createFile(t *testing.T, filepath string, contents string) {
  292. filepath = r.path(filepath)
  293. err := writeFile(filepath, []byte(contents), 0644)
  294. require.NoError(t, err)
  295. r.waitForWriters()
  296. }
  297. func (r *Run) readFile(t *testing.T, filepath string) string {
  298. filepath = r.path(filepath)
  299. result, err := r.os.ReadFile(filepath)
  300. require.NoError(t, err)
  301. return string(result)
  302. }
  303. func (r *Run) mkdir(t *testing.T, filepath string) {
  304. filepath = r.path(filepath)
  305. err := r.os.Mkdir(filepath, 0755)
  306. require.NoError(t, err)
  307. }
  308. func (r *Run) rm(t *testing.T, filepath string) {
  309. filepath = r.path(filepath)
  310. err := r.os.Remove(filepath)
  311. require.NoError(t, err)
  312. // Wait for file to disappear from listing
  313. for i := 0; i < 100; i++ {
  314. _, err := r.os.Stat(filepath)
  315. if os.IsNotExist(err) {
  316. return
  317. }
  318. time.Sleep(100 * time.Millisecond)
  319. }
  320. assert.Fail(t, "failed to delete file", filepath)
  321. }
  322. func (r *Run) rmdir(t *testing.T, filepath string) {
  323. filepath = r.path(filepath)
  324. err := r.os.Remove(filepath)
  325. require.NoError(t, err)
  326. }
  327. // TestMount checks that the Fs is mounted by seeing if the mountpoint
  328. // is in the mount output
  329. func TestMount(t *testing.T) {
  330. run.skipIfVFS(t)
  331. run.skipIfNoFUSE(t)
  332. if runtime.GOOS == "windows" {
  333. t.Skip("not running on windows")
  334. }
  335. out, err := exec.Command("mount").Output()
  336. require.NoError(t, err)
  337. assert.Contains(t, string(out), run.mountPath)
  338. }
  339. // TestRoot checks root directory is present and correct
  340. func TestRoot(t *testing.T) {
  341. run.skipIfVFS(t)
  342. run.skipIfNoFUSE(t)
  343. fi, err := os.Lstat(run.mountPath)
  344. require.NoError(t, err)
  345. assert.True(t, fi.IsDir())
  346. assert.Equal(t, os.FileMode(run.vfsOpt.DirPerms)&os.ModePerm, fi.Mode().Perm())
  347. }