folder_test.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // Copyright (C) 2018 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 model
  7. import (
  8. "path/filepath"
  9. "slices"
  10. "testing"
  11. "github.com/d4l3k/messagediff"
  12. "github.com/syncthing/syncthing/lib/build"
  13. "github.com/syncthing/syncthing/lib/config"
  14. "github.com/syncthing/syncthing/lib/fs"
  15. "github.com/syncthing/syncthing/lib/protocol"
  16. "github.com/syncthing/syncthing/lib/rand"
  17. )
  18. type unifySubsCase struct {
  19. in []string // input to unifySubs
  20. exists []string // paths that exist in the database
  21. out []string // expected output
  22. }
  23. func unifySubsCases() []unifySubsCase {
  24. cases := []unifySubsCase{
  25. {
  26. // 0. trailing slashes are cleaned, known paths are just passed on
  27. []string{"foo/", "bar//"},
  28. []string{"foo", "bar"},
  29. []string{"bar", "foo"}, // the output is sorted
  30. },
  31. {
  32. // 1. "foo/bar" gets trimmed as it's covered by foo
  33. []string{"foo", "bar/", "foo/bar/"},
  34. []string{"foo", "bar"},
  35. []string{"bar", "foo"},
  36. },
  37. {
  38. // 2. "" gets simplified to the empty list; ie scan all
  39. []string{"foo", ""},
  40. []string{"foo"},
  41. nil,
  42. },
  43. {
  44. // 3. "foo/bar" is unknown, but it's kept
  45. // because its parent is known
  46. []string{"foo/bar"},
  47. []string{"foo"},
  48. []string{"foo/bar"},
  49. },
  50. {
  51. // 4. two independent known paths, both are kept
  52. // "usr/lib" is not a prefix of "usr/libexec"
  53. []string{"usr/lib", "usr/libexec"},
  54. []string{"usr", "usr/lib", "usr/libexec"},
  55. []string{"usr/lib", "usr/libexec"},
  56. },
  57. {
  58. // 5. "usr/lib" is a prefix of "usr/lib/exec"
  59. []string{"usr/lib", "usr/lib/exec"},
  60. []string{"usr", "usr/lib", "usr/libexec"},
  61. []string{"usr/lib"},
  62. },
  63. {
  64. // 6. .stignore and .stfolder are special and are passed on
  65. // verbatim even though they are unknown
  66. []string{config.DefaultMarkerName, ".stignore"},
  67. []string{},
  68. []string{config.DefaultMarkerName, ".stignore"},
  69. },
  70. {
  71. // 7. but the presence of something else unknown forces an actual
  72. // scan
  73. []string{config.DefaultMarkerName, ".stignore", "foo/bar"},
  74. []string{},
  75. []string{config.DefaultMarkerName, ".stignore", "foo"},
  76. },
  77. {
  78. // 8. explicit request to scan all
  79. nil,
  80. []string{"foo"},
  81. nil,
  82. },
  83. {
  84. // 9. empty list of subs
  85. []string{},
  86. []string{"foo"},
  87. nil,
  88. },
  89. {
  90. // 10. absolute path
  91. []string{"/foo"},
  92. []string{"foo"},
  93. []string{"foo"},
  94. },
  95. }
  96. if build.IsWindows {
  97. // Fixup path separators
  98. for i := range cases {
  99. for j, p := range cases[i].in {
  100. cases[i].in[j] = filepath.FromSlash(p)
  101. }
  102. for j, p := range cases[i].exists {
  103. cases[i].exists[j] = filepath.FromSlash(p)
  104. }
  105. for j, p := range cases[i].out {
  106. cases[i].out[j] = filepath.FromSlash(p)
  107. }
  108. }
  109. }
  110. return cases
  111. }
  112. func TestUnifySubs(t *testing.T) {
  113. cases := unifySubsCases()
  114. for i, tc := range cases {
  115. exists := func(f string) bool {
  116. return slices.Contains(tc.exists, f)
  117. }
  118. out := unifySubs(tc.in, exists)
  119. if diff, equal := messagediff.PrettyDiff(tc.out, out); !equal {
  120. t.Errorf("Case %d failed; got %v, expected %v, diff:\n%s", i, out, tc.out, diff)
  121. }
  122. }
  123. }
  124. func BenchmarkUnifySubs(b *testing.B) {
  125. cases := unifySubsCases()
  126. b.ReportAllocs()
  127. b.ResetTimer()
  128. for i := 0; i < b.N; i++ {
  129. for _, tc := range cases {
  130. exists := func(f string) bool {
  131. return slices.Contains(tc.exists, f)
  132. }
  133. unifySubs(tc.in, exists)
  134. }
  135. }
  136. }
  137. func TestSetPlatformData(t *testing.T) {
  138. // Checks that setPlatformData runs without error when applied to a temp
  139. // file, named differently than the given FileInfo.
  140. fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32))
  141. if fd, err := fs.Create("file.tmp"); err != nil {
  142. t.Fatal(err)
  143. } else {
  144. fd.Close()
  145. }
  146. xattr := []protocol.Xattr{{Name: "user.foo", Value: []byte("bar")}}
  147. fi := &protocol.FileInfo{
  148. Name: "should be ignored",
  149. Permissions: 0o400,
  150. ModifiedS: 1234567890,
  151. Platform: protocol.PlatformData{
  152. Linux: &protocol.XattrData{Xattrs: xattr},
  153. Darwin: &protocol.XattrData{Xattrs: xattr},
  154. FreeBSD: &protocol.XattrData{Xattrs: xattr},
  155. NetBSD: &protocol.XattrData{Xattrs: xattr},
  156. },
  157. }
  158. // Minimum required to support setPlatformData
  159. sr := &sendReceiveFolder{
  160. folder: &folder{
  161. FolderConfiguration: config.FolderConfiguration{
  162. SyncXattrs: true,
  163. },
  164. mtimefs: fs,
  165. },
  166. }
  167. if err := sr.setPlatformData(fi, "file.tmp"); err != nil {
  168. t.Error(err)
  169. }
  170. }