You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

fs.go 9.0 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. // Test suite for rclonefs
  2. package vfstest
  3. import (
  4. "fmt"
  5. "io"
  6. "os"
  7. "path"
  8. "path/filepath"
  9. "reflect"
  10. "runtime"
  11. "strings"
  12. "testing"
  13. "time"
  14. "github.com/stretchr/testify/assert"
  15. "github.com/stretchr/testify/require"
  16. "gitlink.org.cn/cloudream/common/pkgs/logger"
  17. "gitlink.org.cn/cloudream/jcs-pub/client/internal/mount"
  18. )
  19. const (
  20. waitForWritersDelay = 30 * time.Second // time to wait for existing writers
  21. testRoot = "b1/p1"
  22. )
  23. // RunTests runs all the tests against all the VFS cache modes
  24. //
  25. // If useVFS is set then it runs the tests against a VFS rather than a
  26. // mount
  27. //
  28. // If useVFS is not set then it runs the mount in a subprocess in
  29. // order to avoid kernel deadlocks.
  30. func RunTests(t *testing.T, mnt *mount.Mount) {
  31. run = &Run{
  32. os: realOs{},
  33. mountPath: mnt.MountPoint(),
  34. mnt: mnt,
  35. }
  36. run.Init()
  37. logger.Infof("Starting test run")
  38. ok := t.Run("", func(t *testing.T) {
  39. t.Run("TestTouchAndDelete", TestTouchAndDelete)
  40. t.Run("TestRenameOpenHandle", TestRenameOpenHandle)
  41. t.Run("TestDirLs", TestDirLs)
  42. t.Run("TestDirCreateAndRemoveDir", TestDirCreateAndRemoveDir)
  43. t.Run("TestDirCreateAndRemoveFile", TestDirCreateAndRemoveFile)
  44. t.Run("TestDirRenameFile", TestDirRenameFile)
  45. t.Run("TestDirRenameEmptyDir", TestDirRenameEmptyDir)
  46. t.Run("TestDirRenameFullDir", TestDirRenameFullDir)
  47. t.Run("TestDirModTime", TestDirModTime)
  48. // if enableCacheTests {
  49. // t.Run("TestDirCacheFlush", TestDirCacheFlush)
  50. // }
  51. // t.Run("TestDirCacheFlushOnDirRename", TestDirCacheFlushOnDirRename)
  52. t.Run("TestFileModTime", TestFileModTime)
  53. t.Run("TestFileModTimeWithOpenWriters", TestFileModTimeWithOpenWriters)
  54. // t.Run("TestMount", TestMount)
  55. t.Run("TestRoot", TestRoot)
  56. t.Run("TestReadByByte", TestReadByByte)
  57. t.Run("TestReadChecksum", TestReadChecksum)
  58. t.Run("TestReadFileDoubleClose", TestReadFileDoubleClose)
  59. t.Run("TestReadSeek", TestReadSeek)
  60. t.Run("TestWriteFileNoWrite", TestWriteFileNoWrite)
  61. t.Run("TestWriteFileWrite", TestWriteFileWrite)
  62. t.Run("TestWriteFileOverwrite", TestWriteFileOverwrite)
  63. t.Run("TestWriteFileDoubleClose", TestWriteFileDoubleClose)
  64. t.Run("TestWriteFileFsync", TestWriteFileFsync)
  65. t.Run("TestWriteFileDup", TestWriteFileDup)
  66. t.Run("TestWriteFileAppend", TestWriteFileAppend)
  67. })
  68. logger.Infof("Finished test run (ok=%v)", ok)
  69. run.Finalise()
  70. }
  71. // Run holds the remotes for a test run
  72. type Run struct {
  73. os Oser
  74. mountPath string
  75. skip bool
  76. mnt *mount.Mount
  77. }
  78. // run holds the master Run data
  79. var run *Run
  80. func (r *Run) skipIfNoFUSE(t *testing.T) {
  81. if r.skip {
  82. t.Skip("FUSE not found so skipping test")
  83. }
  84. }
  85. func (r *Run) skipIfVFS(t *testing.T) {
  86. if r.skip {
  87. t.Skip("Not running under VFS")
  88. }
  89. }
  90. func (r *Run) Init() {
  91. testRootDir := filepath.Join(r.mountPath, testRoot)
  92. err := os.MkdirAll(testRootDir, 0644)
  93. if err != nil {
  94. logger.Infof("Failed to make test root dir %q: %v", testRootDir, err)
  95. }
  96. }
  97. // Finalise cleans the remote and unmounts
  98. func (r *Run) Finalise() {
  99. r.mnt.Stop()
  100. err := os.RemoveAll(r.mountPath)
  101. if err != nil {
  102. logger.Infof("Failed to clean mountPath %q: %v", r.mountPath, err)
  103. }
  104. }
  105. // path returns an OS local path for filepath
  106. func (r *Run) path(filePath string) string {
  107. // return windows drive letter root as E:\
  108. if filePath == "" && runtime.GOOS == "windows" {
  109. return r.mountPath + `\`
  110. }
  111. return filepath.Join(r.mountPath, testRoot, filepath.FromSlash(filePath))
  112. }
  113. type dirMap map[string]struct{}
  114. // Create a dirMap from a string
  115. func newDirMap(dirString string) (dm dirMap) {
  116. dm = make(dirMap)
  117. for _, entry := range strings.Split(dirString, "|") {
  118. if entry != "" {
  119. dm[entry] = struct{}{}
  120. }
  121. }
  122. return dm
  123. }
  124. // Returns a dirmap with only the files in
  125. func (dm dirMap) filesOnly() dirMap {
  126. newDm := make(dirMap)
  127. for name := range dm {
  128. if !strings.HasSuffix(name, "/") {
  129. newDm[name] = struct{}{}
  130. }
  131. }
  132. return newDm
  133. }
  134. // reads the local tree into dir
  135. func (r *Run) readLocal(t *testing.T, dir dirMap, filePath string) {
  136. realPath := r.path(filePath)
  137. files, err := r.os.ReadDir(realPath)
  138. require.NoError(t, err)
  139. for _, fi := range files {
  140. name := path.Join(filePath, fi.Name())
  141. if fi.IsDir() {
  142. dir[name+"/"] = struct{}{}
  143. r.readLocal(t, dir, name)
  144. // assert.Equal(t, os.FileMode(r.vfsOpt.DirPerms)&os.ModePerm, fi.Mode().Perm())
  145. } else {
  146. dir[fmt.Sprintf("%s %d", name, fi.Size())] = struct{}{}
  147. // assert.Equal(t, os.FileMode(r.vfsOpt.FilePerms)&os.ModePerm, fi.Mode().Perm())
  148. }
  149. }
  150. }
  151. // reads the remote tree into dir
  152. // func (r *Run) readRemote(t *testing.T, dir dirMap, filepath string) {
  153. // // objs, dirs, err := walk.GetAll(context.Background(), r.fremote, filepath, true, 1)
  154. // // if err == fs.ErrorDirNotFound {
  155. // // return
  156. // // }
  157. // // require.NoError(t, err)
  158. // // for _, obj := range objs {
  159. // // dir[fmt.Sprintf("%s %d", obj.Remote(), obj.Size())] = struct{}{}
  160. // // }
  161. // // for _, d := range dirs {
  162. // // name := d.Remote()
  163. // // dir[name+"/"] = struct{}{}
  164. // // r.readRemote(t, dir, name)
  165. // // }
  166. // }
  167. // checkDir checks the local and remote against the string passed in
  168. func (r *Run) checkDir(t *testing.T, dirString string) {
  169. var retries = 3
  170. sleep := time.Second / 5
  171. var fuseOK bool
  172. var dm, localDm dirMap
  173. for i := 1; i <= retries; i++ {
  174. dm = newDirMap(dirString)
  175. localDm = make(dirMap)
  176. r.readLocal(t, localDm, "")
  177. fuseOK = reflect.DeepEqual(dm, localDm)
  178. if fuseOK {
  179. return
  180. }
  181. sleep *= 2
  182. t.Logf("Sleeping for %v for list eventual consistency: %d/%d", sleep, i, retries)
  183. time.Sleep(sleep)
  184. }
  185. assert.Equal(t, dm, localDm, "expected vs fuse mount")
  186. }
  187. // writeFile writes data to a file named by filename.
  188. // If the file does not exist, WriteFile creates it with permissions perm;
  189. // otherwise writeFile truncates it before writing.
  190. // If there is an error writing then writeFile
  191. // deletes it an existing file and tries again.
  192. func writeFile(filename string, data []byte, perm os.FileMode) error {
  193. f, err := run.os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
  194. if err != nil {
  195. err = run.os.Remove(filename)
  196. if err != nil {
  197. return err
  198. }
  199. f, err = run.os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, perm)
  200. if err != nil {
  201. return err
  202. }
  203. }
  204. n, err := f.Write(data)
  205. if err == nil && n < len(data) {
  206. err = io.ErrShortWrite
  207. }
  208. if err1 := f.Close(); err == nil {
  209. err = err1
  210. }
  211. return err
  212. }
  213. func (r *Run) createFile(t *testing.T, filepath string, contents string) {
  214. filepath = r.path(filepath)
  215. err := writeFile(filepath, []byte(contents), 0644)
  216. require.NoError(t, err)
  217. r.waitForWriters()
  218. }
  219. func (r *Run) readFile(t *testing.T, filepath string) string {
  220. filepath = r.path(filepath)
  221. result, err := r.os.ReadFile(filepath)
  222. require.NoError(t, err)
  223. return string(result)
  224. }
  225. func (r *Run) mkdir(t *testing.T, filepath string) {
  226. filepath = r.path(filepath)
  227. err := r.os.Mkdir(filepath, 0755)
  228. require.NoError(t, err)
  229. }
  230. func (r *Run) rm(t *testing.T, filepath string) {
  231. filepath = r.path(filepath)
  232. err := r.os.Remove(filepath)
  233. require.NoError(t, err)
  234. // Wait for file to disappear from listing
  235. for i := 0; i < 100; i++ {
  236. _, err := r.os.Stat(filepath)
  237. if os.IsNotExist(err) {
  238. return
  239. }
  240. time.Sleep(100 * time.Millisecond)
  241. }
  242. assert.Fail(t, "failed to delete file", filepath)
  243. }
  244. func (r *Run) rmdir(t *testing.T, filepath string) {
  245. filepath = r.path(filepath)
  246. err := r.os.Remove(filepath)
  247. require.NoError(t, err)
  248. }
  249. func (r *Run) waitForWriters() {
  250. timeout := waitForWritersDelay
  251. tickTime := 10 * time.Millisecond
  252. deadline := time.NewTimer(timeout)
  253. defer deadline.Stop()
  254. tick := time.NewTimer(tickTime)
  255. defer tick.Stop()
  256. tick.Stop()
  257. for {
  258. writers := 0
  259. cacheInUse := 0
  260. status := r.mnt.Dump()
  261. for _, f := range status.Cache.ActiveFiles {
  262. if f.RefCount > 0 {
  263. writers++
  264. }
  265. }
  266. cacheInUse = len(status.Cache.ActiveFiles)
  267. if writers == 0 && cacheInUse == 0 {
  268. return
  269. }
  270. logger.Debugf("Still %d writers active and %d cache items in use, waiting %v", writers, cacheInUse, tickTime)
  271. tick.Reset(tickTime)
  272. select {
  273. case <-tick.C:
  274. case <-deadline.C:
  275. logger.Errorf("Exiting even though %d writers active and %d cache items in use after %v\n%v", writers, cacheInUse, timeout, status)
  276. return
  277. }
  278. tickTime *= 2
  279. if tickTime > time.Second {
  280. tickTime = time.Second
  281. }
  282. }
  283. }
  284. // TestMount checks that the Fs is mounted by seeing if the mountpoint
  285. // is in the mount output
  286. // func TestMount(t *testing.T) {
  287. // run.skipIfVFS(t)
  288. // run.skipIfNoFUSE(t)
  289. // if runtime.GOOS == "windows" {
  290. // t.Skip("not running on windows")
  291. // }
  292. // out, err := exec.Command("mount").Output()
  293. // require.NoError(t, err)
  294. // assert.Contains(t, string(out), run.mountPath)
  295. // }
  296. // TestRoot checks root directory is present and correct
  297. func TestRoot(t *testing.T) {
  298. run.skipIfVFS(t)
  299. run.skipIfNoFUSE(t)
  300. fi, err := os.Lstat(run.mountPath)
  301. require.NoError(t, err)
  302. assert.True(t, fi.IsDir())
  303. // assert.Equal(t, os.FileMode(run.vfsOpt.DirPerms)&os.ModePerm, fi.Mode().Perm())
  304. }

本项目旨在将云际存储公共基础设施化,使个人及企业可低门槛使用高效的云际存储服务(安装开箱即用云际存储客户端即可,无需关注其他组件的部署),同时支持用户灵活便捷定制云际存储的功能细节。