util_test.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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 db
  7. import (
  8. "encoding/json"
  9. "errors"
  10. "io"
  11. "os"
  12. "testing"
  13. "github.com/syncthing/syncthing/lib/db/backend"
  14. "github.com/syncthing/syncthing/lib/events"
  15. "github.com/syncthing/syncthing/lib/protocol"
  16. )
  17. // writeJSONS serializes the database to a JSON stream that can be checked
  18. // in to the repo and used for tests.
  19. func writeJSONS(w io.Writer, db backend.Backend) {
  20. it, err := db.NewPrefixIterator(nil)
  21. if err != nil {
  22. panic(err)
  23. }
  24. defer it.Release()
  25. enc := json.NewEncoder(w)
  26. for it.Next() {
  27. err := enc.Encode(map[string][]byte{
  28. "k": it.Key(),
  29. "v": it.Value(),
  30. })
  31. if err != nil {
  32. panic(err)
  33. }
  34. }
  35. }
  36. // we know this function isn't generally used, nonetheless we want it in
  37. // here and the linter to not complain.
  38. var _ = writeJSONS
  39. // openJSONS reads a JSON stream file into a backend DB
  40. func openJSONS(file string) (backend.Backend, error) {
  41. fd, err := os.Open(file)
  42. if err != nil {
  43. return nil, err
  44. }
  45. dec := json.NewDecoder(fd)
  46. db := backend.OpenMemory()
  47. for {
  48. var row map[string][]byte
  49. err := dec.Decode(&row)
  50. if err == io.EOF {
  51. break
  52. } else if err != nil {
  53. return nil, err
  54. }
  55. if err := db.Put(row["k"], row["v"]); err != nil {
  56. return nil, err
  57. }
  58. }
  59. return db, nil
  60. }
  61. func newLowlevel(t testing.TB, backend backend.Backend) *Lowlevel {
  62. t.Helper()
  63. ll, err := NewLowlevel(backend, events.NoopLogger)
  64. if err != nil {
  65. t.Fatal(err)
  66. }
  67. return ll
  68. }
  69. func newLowlevelMemory(t testing.TB) *Lowlevel {
  70. return newLowlevel(t, backend.OpenMemory())
  71. }
  72. func newFileSet(t testing.TB, folder string, db *Lowlevel) *FileSet {
  73. t.Helper()
  74. fset, err := NewFileSet(folder, db)
  75. if err != nil {
  76. t.Fatal(err)
  77. }
  78. return fset
  79. }
  80. func snapshot(t testing.TB, fset *FileSet) *Snapshot {
  81. t.Helper()
  82. snap, err := fset.Snapshot()
  83. if err != nil {
  84. t.Fatal(err)
  85. }
  86. return snap
  87. }
  88. // The following commented tests were used to generate jsons files to stdout for
  89. // future tests and are kept here for reference (reuse).
  90. // TestGenerateIgnoredFilesDB generates a database with files with invalid flags,
  91. // local and remote, in the format used in 0.14.48.
  92. // func TestGenerateIgnoredFilesDB(t *testing.T) {
  93. // db := OpenMemory()
  94. // fs := newFileSet(t, "test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), db)
  95. // fs.Update(protocol.LocalDeviceID, []protocol.FileInfo{
  96. // { // invalid (ignored) file
  97. // Name: "foo",
  98. // Type: protocol.FileInfoTypeFile,
  99. // Invalid: true,
  100. // Version: protocol.Vector{Counters: []protocol.Counter{{ID: 1, Value: 1000}}},
  101. // },
  102. // { // regular file
  103. // Name: "bar",
  104. // Type: protocol.FileInfoTypeFile,
  105. // Version: protocol.Vector{Counters: []protocol.Counter{{ID: 1, Value: 1001}}},
  106. // },
  107. // })
  108. // fs.Update(protocol.DeviceID{42}, []protocol.FileInfo{
  109. // { // invalid file
  110. // Name: "baz",
  111. // Type: protocol.FileInfoTypeFile,
  112. // Invalid: true,
  113. // Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 1000}}},
  114. // },
  115. // { // regular file
  116. // Name: "quux",
  117. // Type: protocol.FileInfoTypeFile,
  118. // Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 1002}}},
  119. // },
  120. // })
  121. // writeJSONS(os.Stdout, db.DB)
  122. // }
  123. // TestGenerateUpdate0to3DB generates a database with files with invalid flags, prefixed
  124. // by a slash and other files to test database migration from version 0 to 3, in the
  125. // format used in 0.14.45.
  126. // func TestGenerateUpdate0to3DB(t *testing.T) {
  127. // db := OpenMemory()
  128. // fs := newFileSet(t, update0to3Folder, fs.NewFilesystem(fs.FilesystemTypeBasic, "."), db)
  129. // for devID, files := range haveUpdate0to3 {
  130. // fs.Update(devID, files)
  131. // }
  132. // writeJSONS(os.Stdout, db.DB)
  133. // }
  134. // func TestGenerateUpdateTo10(t *testing.T) {
  135. // db := newLowlevelMemory(t)
  136. // defer db.Close()
  137. // if err := UpdateSchema(db); err != nil {
  138. // t.Fatal(err)
  139. // }
  140. // fs := newFileSet(t, "test", fs.NewFilesystem(fs.FilesystemTypeFake, ""), db)
  141. // files := []protocol.FileInfo{
  142. // {Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Deleted: true, Sequence: 1},
  143. // {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(2), Sequence: 2},
  144. // {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Deleted: true, Sequence: 3},
  145. // }
  146. // fs.Update(protocol.LocalDeviceID, files)
  147. // files[1].Version = files[1].Version.Update(remoteDevice0.Short())
  148. // files[1].Deleted = true
  149. // files[2].Version = files[2].Version.Update(remoteDevice0.Short())
  150. // files[2].Blocks = genBlocks(1)
  151. // files[2].Deleted = false
  152. // fs.Update(remoteDevice0, files)
  153. // fd, err := os.Create("./testdata/v1.4.0-updateTo10.json")
  154. // if err != nil {
  155. // panic(err)
  156. // }
  157. // defer fd.Close()
  158. // writeJSONS(fd, db)
  159. // }
  160. func TestFileInfoBatchError(t *testing.T) {
  161. // Verify behaviour of the flush function returning an error.
  162. var errReturn error
  163. var called int
  164. b := NewFileInfoBatch(func([]protocol.FileInfo) error {
  165. called += 1
  166. return errReturn
  167. })
  168. // Flush should work when the flush function error is nil
  169. b.Append(protocol.FileInfo{Name: "test"})
  170. if err := b.Flush(); err != nil {
  171. t.Fatalf("expected nil, got %v", err)
  172. }
  173. if called != 1 {
  174. t.Fatalf("expected 1, got %d", called)
  175. }
  176. // Flush should fail with an error retur
  177. errReturn = errors.New("problem")
  178. b.Append(protocol.FileInfo{Name: "test"})
  179. if err := b.Flush(); err != errReturn {
  180. t.Fatalf("expected %v, got %v", errReturn, err)
  181. }
  182. if called != 2 {
  183. t.Fatalf("expected 2, got %d", called)
  184. }
  185. // Flush function should not be called again when it's already errored,
  186. // same error should be returned by Flush()
  187. if err := b.Flush(); err != errReturn {
  188. t.Fatalf("expected %v, got %v", errReturn, err)
  189. }
  190. if called != 2 {
  191. t.Fatalf("expected 2, got %d", called)
  192. }
  193. // Reset should clear the error (and the file list)
  194. errReturn = nil
  195. b.Reset()
  196. b.Append(protocol.FileInfo{Name: "test"})
  197. if err := b.Flush(); err != nil {
  198. t.Fatalf("expected nil, got %v", err)
  199. }
  200. if called != 3 {
  201. t.Fatalf("expected 3, got %d", called)
  202. }
  203. }