trashcan_test.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // Copyright (C) 2015 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package versioner
  7. import (
  8. "context"
  9. "io/ioutil"
  10. "os"
  11. "path/filepath"
  12. "testing"
  13. "time"
  14. "github.com/syncthing/syncthing/lib/config"
  15. "github.com/syncthing/syncthing/lib/fs"
  16. )
  17. func TestTrashcanCleanout(t *testing.T) {
  18. // Verify that files older than the cutoff are removed, that files newer
  19. // than the cutoff are *not* removed, and that empty directories are
  20. // removed (best effort).
  21. var testcases = []struct {
  22. file string
  23. shouldRemove bool
  24. }{
  25. {"testdata/.stversions/file1", false},
  26. {"testdata/.stversions/file2", true},
  27. {"testdata/.stversions/keep1/file1", false},
  28. {"testdata/.stversions/keep1/file2", false},
  29. {"testdata/.stversions/keep2/file1", false},
  30. {"testdata/.stversions/keep2/file2", true},
  31. {"testdata/.stversions/keep3/keepsubdir/file1", false},
  32. {"testdata/.stversions/remove/file1", true},
  33. {"testdata/.stversions/remove/file2", true},
  34. {"testdata/.stversions/remove/removesubdir/file1", true},
  35. }
  36. os.RemoveAll("testdata")
  37. defer os.RemoveAll("testdata")
  38. oldTime := time.Now().Add(-8 * 24 * time.Hour)
  39. for _, tc := range testcases {
  40. os.MkdirAll(filepath.Dir(tc.file), 0777)
  41. if err := ioutil.WriteFile(tc.file, []byte("data"), 0644); err != nil {
  42. t.Fatal(err)
  43. }
  44. if tc.shouldRemove {
  45. if err := os.Chtimes(tc.file, oldTime, oldTime); err != nil {
  46. t.Fatal(err)
  47. }
  48. }
  49. }
  50. cfg := config.FolderConfiguration{
  51. FilesystemType: fs.FilesystemTypeBasic,
  52. Path: "testdata",
  53. Versioning: config.VersioningConfiguration{
  54. Params: map[string]string{
  55. "cleanoutDays": "7",
  56. },
  57. },
  58. }
  59. versioner := newTrashcan(cfg).(*trashcan)
  60. if err := versioner.Clean(context.Background()); err != nil {
  61. t.Fatal(err)
  62. }
  63. for _, tc := range testcases {
  64. _, err := os.Lstat(tc.file)
  65. if tc.shouldRemove && !os.IsNotExist(err) {
  66. t.Error(tc.file, "should have been removed")
  67. } else if !tc.shouldRemove && err != nil {
  68. t.Error(tc.file, "should not have been removed")
  69. }
  70. }
  71. if _, err := os.Lstat("testdata/.stversions/keep3"); os.IsNotExist(err) {
  72. t.Error("directory with non empty subdirs should not be removed")
  73. }
  74. if _, err := os.Lstat("testdata/.stversions/remove"); !os.IsNotExist(err) {
  75. t.Error("empty directory should have been removed")
  76. }
  77. }
  78. func TestTrashcanArchiveRestoreSwitcharoo(t *testing.T) {
  79. // This tests that trashcan versioner restoration correctly archives existing file, because trashcan versioner
  80. // files are untagged, archiving existing file to replace with a restored version technically should collide in
  81. // in names.
  82. tmpDir1, err := ioutil.TempDir("", "")
  83. if err != nil {
  84. t.Fatal(err)
  85. }
  86. tmpDir2, err := ioutil.TempDir("", "")
  87. if err != nil {
  88. t.Fatal(err)
  89. }
  90. cfg := config.FolderConfiguration{
  91. FilesystemType: fs.FilesystemTypeBasic,
  92. Path: tmpDir1,
  93. Versioning: config.VersioningConfiguration{
  94. Params: map[string]string{
  95. "fsType": "basic",
  96. "fsPath": tmpDir2,
  97. },
  98. },
  99. }
  100. folderFs := cfg.Filesystem()
  101. versionsFs := fs.NewFilesystem(fs.FilesystemTypeBasic, tmpDir2)
  102. writeFile(t, folderFs, "file", "A")
  103. versioner := newTrashcan(cfg)
  104. if err := versioner.Archive("file"); err != nil {
  105. t.Fatal(err)
  106. }
  107. if _, err := folderFs.Stat("file"); !fs.IsNotExist(err) {
  108. t.Fatal(err)
  109. }
  110. // Check versions
  111. versions, err := versioner.GetVersions()
  112. if err != nil {
  113. t.Fatal(err)
  114. }
  115. fileVersions := versions["file"]
  116. if len(fileVersions) != 1 {
  117. t.Fatalf("unexpected number of versions: %d != 1", len(fileVersions))
  118. }
  119. fileVersion := fileVersions[0]
  120. if !fileVersion.ModTime.Equal(fileVersion.VersionTime) {
  121. t.Error("time mismatch")
  122. }
  123. if content := readFile(t, versionsFs, "file"); content != "A" {
  124. t.Errorf("expected A got %s", content)
  125. }
  126. writeFile(t, folderFs, "file", "B")
  127. versionInfo, err := versionsFs.Stat("file")
  128. if err != nil {
  129. t.Fatal(err)
  130. }
  131. if !versionInfo.ModTime().Truncate(time.Second).Equal(fileVersion.ModTime) {
  132. t.Error("time mismatch")
  133. }
  134. if err := versioner.Restore("file", fileVersion.VersionTime); err != nil {
  135. t.Fatal(err)
  136. }
  137. if content := readFile(t, folderFs, "file"); content != "A" {
  138. t.Errorf("expected A got %s", content)
  139. }
  140. if content := readFile(t, versionsFs, "file"); content != "B" {
  141. t.Errorf("expected B got %s", content)
  142. }
  143. }
  144. func readFile(t *testing.T, filesystem fs.Filesystem, name string) string {
  145. fd, err := filesystem.Open(name)
  146. if err != nil {
  147. t.Fatal(err)
  148. }
  149. defer fd.Close()
  150. buf, err := ioutil.ReadAll(fd)
  151. if err != nil {
  152. t.Fatal(err)
  153. }
  154. return string(buf)
  155. }
  156. func writeFile(t *testing.T, filesystem fs.Filesystem, name, content string) {
  157. fd, err := filesystem.OpenFile(name, fs.OptReadWrite|fs.OptCreate, 0777)
  158. if err != nil {
  159. t.Fatal(err)
  160. }
  161. defer fd.Close()
  162. if err := fd.Truncate(int64(len(content))); err != nil {
  163. t.Fatal(err)
  164. }
  165. if n, err := fd.Write([]byte(content)); err != nil || n != len(content) {
  166. t.Fatal(n, len(content), err)
  167. }
  168. }