db_test.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. // Copyright (C) 2014 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. "bytes"
  9. "context"
  10. "fmt"
  11. "testing"
  12. "github.com/syncthing/syncthing/lib/db/backend"
  13. "github.com/syncthing/syncthing/lib/events"
  14. "github.com/syncthing/syncthing/lib/protocol"
  15. )
  16. func genBlocks(n int) []protocol.BlockInfo {
  17. b := make([]protocol.BlockInfo, n)
  18. for i := range b {
  19. h := make([]byte, 32)
  20. for j := range h {
  21. h[j] = byte(i + j)
  22. }
  23. b[i].Size = i
  24. b[i].Hash = h
  25. }
  26. return b
  27. }
  28. const myID = 1
  29. var (
  30. remoteDevice0, remoteDevice1 protocol.DeviceID
  31. invalid = "invalid"
  32. slashPrefixed = "/notgood"
  33. haveUpdate0to3 map[protocol.DeviceID][]protocol.FileInfo
  34. )
  35. func init() {
  36. remoteDevice0, _ = protocol.DeviceIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR")
  37. remoteDevice1, _ = protocol.DeviceIDFromString("I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU")
  38. haveUpdate0to3 = map[protocol.DeviceID][]protocol.FileInfo{
  39. protocol.LocalDeviceID: {
  40. protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
  41. protocol.FileInfo{Name: slashPrefixed, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
  42. },
  43. remoteDevice0: {
  44. protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)},
  45. protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), RawInvalid: true},
  46. protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)},
  47. },
  48. remoteDevice1: {
  49. protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)},
  50. protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), RawInvalid: true},
  51. protocol.FileInfo{Name: invalid, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), RawInvalid: true},
  52. },
  53. }
  54. }
  55. // TestRepairSequence checks that a few hand-crafted messed-up sequence entries get fixed.
  56. func TestRepairSequence(t *testing.T) {
  57. db := newLowlevelMemory(t)
  58. defer db.Close()
  59. folderStr := "test"
  60. folder := []byte(folderStr)
  61. id := protocol.LocalDeviceID
  62. short := protocol.LocalDeviceID.Short()
  63. files := []protocol.FileInfo{
  64. {Name: "fine", Blocks: genBlocks(1)},
  65. {Name: "duplicate", Blocks: genBlocks(2)},
  66. {Name: "missing", Blocks: genBlocks(3)},
  67. {Name: "overwriting", Blocks: genBlocks(4)},
  68. {Name: "inconsistent", Blocks: genBlocks(5)},
  69. {Name: "inconsistentNotIndirected", Blocks: genBlocks(2)},
  70. }
  71. for i, f := range files {
  72. files[i].Version = f.Version.Update(short)
  73. }
  74. trans, err := db.newReadWriteTransaction()
  75. if err != nil {
  76. t.Fatal(err)
  77. }
  78. defer trans.close()
  79. addFile := func(f protocol.FileInfo, seq int64) {
  80. dk, err := trans.keyer.GenerateDeviceFileKey(nil, folder, id[:], []byte(f.Name))
  81. if err != nil {
  82. t.Fatal(err)
  83. }
  84. if err := trans.putFile(dk, f); err != nil {
  85. t.Fatal(err)
  86. }
  87. sk, err := trans.keyer.GenerateSequenceKey(nil, folder, seq)
  88. if err != nil {
  89. t.Fatal(err)
  90. }
  91. if err := trans.Put(sk, dk); err != nil {
  92. t.Fatal(err)
  93. }
  94. }
  95. // Plain normal entry
  96. var seq int64 = 1
  97. files[0].Sequence = 1
  98. addFile(files[0], seq)
  99. // Second entry once updated with original sequence still in place
  100. f := files[1]
  101. f.Sequence = int64(len(files) + 1)
  102. addFile(f, f.Sequence)
  103. // Original sequence entry
  104. seq++
  105. sk, err := trans.keyer.GenerateSequenceKey(nil, folder, seq)
  106. if err != nil {
  107. t.Fatal(err)
  108. }
  109. dk, err := trans.keyer.GenerateDeviceFileKey(nil, folder, id[:], []byte(f.Name))
  110. if err != nil {
  111. t.Fatal(err)
  112. }
  113. if err := trans.Put(sk, dk); err != nil {
  114. t.Fatal(err)
  115. }
  116. // File later overwritten thus missing sequence entry
  117. seq++
  118. files[2].Sequence = seq
  119. addFile(files[2], seq)
  120. // File overwriting previous sequence entry (no seq bump)
  121. seq++
  122. files[3].Sequence = seq
  123. addFile(files[3], seq)
  124. // Inconistent files
  125. seq++
  126. files[4].Sequence = 101
  127. addFile(files[4], seq)
  128. seq++
  129. files[5].Sequence = 102
  130. addFile(files[5], seq)
  131. // And a sequence entry pointing at nothing because why not
  132. sk, err = trans.keyer.GenerateSequenceKey(nil, folder, 100001)
  133. if err != nil {
  134. t.Fatal(err)
  135. }
  136. dk, err = trans.keyer.GenerateDeviceFileKey(nil, folder, id[:], []byte("nonexisting"))
  137. if err != nil {
  138. t.Fatal(err)
  139. }
  140. if err := trans.Put(sk, dk); err != nil {
  141. t.Fatal(err)
  142. }
  143. if err := trans.Commit(); err != nil {
  144. t.Fatal(err)
  145. }
  146. // Loading the metadata for the first time means a "re"calculation happens,
  147. // along which the sequences get repaired too.
  148. db.gcMut.RLock()
  149. _, err = db.loadMetadataTracker(folderStr)
  150. db.gcMut.RUnlock()
  151. if err != nil {
  152. t.Fatal(err)
  153. }
  154. // Check the db
  155. ro, err := db.newReadOnlyTransaction()
  156. if err != nil {
  157. t.Fatal(err)
  158. }
  159. defer ro.close()
  160. it, err := ro.NewPrefixIterator([]byte{KeyTypeDevice})
  161. if err != nil {
  162. t.Fatal(err)
  163. }
  164. defer it.Release()
  165. for it.Next() {
  166. fi, err := ro.unmarshalTrunc(it.Value(), true)
  167. if err != nil {
  168. t.Fatal(err)
  169. }
  170. if sk, err = ro.keyer.GenerateSequenceKey(sk, folder, fi.SequenceNo()); err != nil {
  171. t.Fatal(err)
  172. }
  173. dk, err := ro.Get(sk)
  174. if backend.IsNotFound(err) {
  175. t.Error("Missing sequence entry for", fi.FileName())
  176. } else if err != nil {
  177. t.Fatal(err)
  178. }
  179. if !bytes.Equal(it.Key(), dk) {
  180. t.Errorf("Wrong key for %v, expected %s, got %s", f.FileName(), it.Key(), dk)
  181. }
  182. }
  183. if err := it.Error(); err != nil {
  184. t.Fatal(err)
  185. }
  186. it.Release()
  187. it, err = ro.NewPrefixIterator([]byte{KeyTypeSequence})
  188. if err != nil {
  189. t.Fatal(err)
  190. }
  191. defer it.Release()
  192. for it.Next() {
  193. fi, ok, err := ro.getFileTrunc(it.Value(), false)
  194. if err != nil {
  195. t.Fatal(err)
  196. }
  197. seq := ro.keyer.SequenceFromSequenceKey(it.Key())
  198. if !ok {
  199. t.Errorf("Sequence entry %v points at nothing", seq)
  200. } else if fi.SequenceNo() != seq {
  201. t.Errorf("Inconsistent sequence entry for %v: %v != %v", fi.FileName(), fi.SequenceNo(), seq)
  202. }
  203. if len(fi.Blocks) == 0 {
  204. t.Error("Missing blocks in", fi.FileName())
  205. }
  206. }
  207. if err := it.Error(); err != nil {
  208. t.Fatal(err)
  209. }
  210. it.Release()
  211. }
  212. func TestDowngrade(t *testing.T) {
  213. db := newLowlevelMemory(t)
  214. defer db.Close()
  215. // sets the min version etc
  216. if err := UpdateSchema(db); err != nil {
  217. t.Fatal(err)
  218. }
  219. // Bump the database version to something newer than we actually support
  220. miscDB := NewMiscDataNamespace(db)
  221. if err := miscDB.PutInt64("dbVersion", dbVersion+1); err != nil {
  222. t.Fatal(err)
  223. }
  224. l.Infoln(dbVersion)
  225. // Pretend we just opened the DB and attempt to update it again
  226. err := UpdateSchema(db)
  227. if err, ok := err.(*databaseDowngradeError); !ok {
  228. t.Fatal("Expected error due to database downgrade, got", err)
  229. } else if err.minSyncthingVersion != dbMinSyncthingVersion {
  230. t.Fatalf("Error has %v as min Syncthing version, expected %v", err.minSyncthingVersion, dbMinSyncthingVersion)
  231. }
  232. }
  233. func TestCheckGlobals(t *testing.T) {
  234. db := newLowlevelMemory(t)
  235. defer db.Close()
  236. fs := newFileSet(t, "test", db)
  237. // Add any file
  238. name := "foo"
  239. fs.Update(protocol.LocalDeviceID, []protocol.FileInfo{
  240. {
  241. Name: name,
  242. Type: protocol.FileInfoTypeFile,
  243. Version: protocol.Vector{Counters: []protocol.Counter{{ID: 1, Value: 1001}}},
  244. },
  245. })
  246. // Remove just the file entry
  247. if err := db.dropPrefix([]byte{KeyTypeDevice}); err != nil {
  248. t.Fatal(err)
  249. }
  250. // Clean up global entry of the now missing file
  251. if repaired, err := db.checkGlobals(fs.folder); err != nil {
  252. t.Fatal(err)
  253. } else if repaired != 1 {
  254. t.Error("Expected 1 repaired global item, got", repaired)
  255. }
  256. // Check that the global entry is gone
  257. gk, err := db.keyer.GenerateGlobalVersionKey(nil, []byte(fs.folder), []byte(name))
  258. if err != nil {
  259. t.Fatal(err)
  260. }
  261. _, err = db.Get(gk)
  262. if !backend.IsNotFound(err) {
  263. t.Error("Expected key missing error, got", err)
  264. }
  265. }
  266. func TestDropDuplicates(t *testing.T) {
  267. names := []string{
  268. "foo",
  269. "bar",
  270. "dcxvoijnds",
  271. "3d/dsfase/4/ss2",
  272. }
  273. tcs := []struct{ in, out []int }{
  274. {[]int{0}, []int{0}},
  275. {[]int{0, 1}, []int{0, 1}},
  276. {[]int{0, 1, 0, 1}, []int{0, 1}},
  277. {[]int{0, 1, 1, 1, 1}, []int{0, 1}},
  278. {[]int{0, 0, 0, 1}, []int{0, 1}},
  279. {[]int{0, 1, 2, 3}, []int{0, 1, 2, 3}},
  280. {[]int{3, 2, 1, 0, 0, 1, 2, 3}, []int{0, 1, 2, 3}},
  281. {[]int{0, 1, 1, 3, 0, 1, 0, 1, 2, 3}, []int{0, 1, 2, 3}},
  282. }
  283. for tci, tc := range tcs {
  284. inp := make([]protocol.FileInfo, len(tc.in))
  285. expSeq := make(map[string]int)
  286. for i, j := range tc.in {
  287. inp[i] = protocol.FileInfo{Name: names[j], Sequence: int64(i)}
  288. expSeq[names[j]] = i
  289. }
  290. outp := normalizeFilenamesAndDropDuplicates(inp)
  291. if len(outp) != len(tc.out) {
  292. t.Errorf("tc %v: Expected %v entries, got %v", tci, len(tc.out), len(outp))
  293. continue
  294. }
  295. for i, f := range outp {
  296. if exp := names[tc.out[i]]; exp != f.Name {
  297. t.Errorf("tc %v: Got file %v at pos %v, expected %v", tci, f.Name, i, exp)
  298. }
  299. if exp := int64(expSeq[outp[i].Name]); exp != f.Sequence {
  300. t.Errorf("tc %v: Got sequence %v at pos %v, expected %v", tci, f.Sequence, i, exp)
  301. }
  302. }
  303. }
  304. }
  305. func TestGCIndirect(t *testing.T) {
  306. // Verify that the gcIndirect run actually removes block lists.
  307. db := newLowlevelMemory(t)
  308. defer db.Close()
  309. meta := newMetadataTracker(db.keyer, events.NoopLogger)
  310. // Add three files with different block lists
  311. files := []protocol.FileInfo{
  312. {Name: "a", Blocks: genBlocks(100)},
  313. {Name: "b", Blocks: genBlocks(200)},
  314. {Name: "c", Blocks: genBlocks(300)},
  315. }
  316. db.updateLocalFiles([]byte("folder"), files, meta)
  317. // Run a GC pass
  318. db.gcIndirect(context.Background())
  319. // Verify that we have three different block lists
  320. n, err := numBlockLists(db)
  321. if err != nil {
  322. t.Fatal(err)
  323. }
  324. if n != len(files) {
  325. t.Fatal("expected each file to have a block list")
  326. }
  327. // Change the block lists for each file
  328. for i := range files {
  329. files[i].Version = files[i].Version.Update(42)
  330. files[i].Blocks = genBlocks(len(files[i].Blocks) + 1)
  331. }
  332. db.updateLocalFiles([]byte("folder"), files, meta)
  333. // Verify that we now have *six* different block lists
  334. n, err = numBlockLists(db)
  335. if err != nil {
  336. t.Fatal(err)
  337. }
  338. if n != 2*len(files) {
  339. t.Fatal("expected both old and new block lists to exist")
  340. }
  341. // Run a GC pass
  342. db.gcIndirect(context.Background())
  343. // Verify that we now have just the three we need, again
  344. n, err = numBlockLists(db)
  345. if err != nil {
  346. t.Fatal(err)
  347. }
  348. if n != len(files) {
  349. t.Fatal("expected GC to collect all but the needed ones")
  350. }
  351. // Double check the correctness by loading the block lists and comparing with what we stored
  352. tr, err := db.newReadOnlyTransaction()
  353. if err != nil {
  354. t.Fatal()
  355. }
  356. defer tr.Release()
  357. for _, f := range files {
  358. fi, ok, err := tr.getFile([]byte("folder"), protocol.LocalDeviceID[:], []byte(f.Name))
  359. if err != nil {
  360. t.Fatal(err)
  361. }
  362. if !ok {
  363. t.Fatal("mysteriously missing")
  364. }
  365. if len(fi.Blocks) != len(f.Blocks) {
  366. t.Fatal("block list mismatch")
  367. }
  368. for i := range fi.Blocks {
  369. if !bytes.Equal(fi.Blocks[i].Hash, f.Blocks[i].Hash) {
  370. t.Fatal("hash mismatch")
  371. }
  372. }
  373. }
  374. }
  375. func TestUpdateTo14(t *testing.T) {
  376. db := newLowlevelMemory(t)
  377. defer db.Close()
  378. folderStr := "default"
  379. folder := []byte(folderStr)
  380. name := []byte("foo")
  381. file := protocol.FileInfo{Name: string(name), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(blocksIndirectionCutoff - 1)}
  382. file.BlocksHash = protocol.BlocksHash(file.Blocks)
  383. fileWOBlocks := file
  384. fileWOBlocks.Blocks = nil
  385. meta, err := db.loadMetadataTracker(folderStr)
  386. if err != nil {
  387. t.Fatal(err)
  388. }
  389. // Initially add the correct file the usual way, all good here.
  390. if err := db.updateLocalFiles(folder, []protocol.FileInfo{file}, meta); err != nil {
  391. t.Fatal(err)
  392. }
  393. // Simulate the previous bug, where .putFile could write a file info without
  394. // blocks, even though the file has them (and thus a non-nil BlocksHash).
  395. trans, err := db.newReadWriteTransaction()
  396. if err != nil {
  397. t.Fatal(err)
  398. }
  399. defer trans.close()
  400. key, err := db.keyer.GenerateDeviceFileKey(nil, folder, protocol.LocalDeviceID[:], name)
  401. if err != nil {
  402. t.Fatal(err)
  403. }
  404. fiBs := mustMarshal(fileWOBlocks.ToWire(true))
  405. if err := trans.Put(key, fiBs); err != nil {
  406. t.Fatal(err)
  407. }
  408. if err := trans.Commit(); err != nil {
  409. t.Fatal(err)
  410. }
  411. trans.close()
  412. // Run migration, pretending were still on schema 13.
  413. if err := (&schemaUpdater{db}).updateSchemaTo14(13); err != nil {
  414. t.Fatal(err)
  415. }
  416. // checks
  417. ro, err := db.newReadOnlyTransaction()
  418. if err != nil {
  419. t.Fatal(err)
  420. }
  421. defer ro.close()
  422. if f, ok, err := ro.getFileByKey(key); err != nil {
  423. t.Fatal(err)
  424. } else if !ok {
  425. t.Error("file missing")
  426. } else if !f.MustRescan() {
  427. t.Error("file not marked as MustRescan")
  428. }
  429. if vl, err := ro.getGlobalVersions(nil, folder, name); err != nil {
  430. t.Fatal(err)
  431. } else if fv, ok := vlGetGlobal(vl); !ok {
  432. t.Error("missing global")
  433. } else if !fvIsInvalid(fv) {
  434. t.Error("global not marked as invalid")
  435. }
  436. }
  437. func TestFlushRecursion(t *testing.T) {
  438. // Verify that a commit hook can write to the transaction without
  439. // causing another flush and thus recursion.
  440. db := newLowlevelMemory(t)
  441. defer db.Close()
  442. // A commit hook that writes a small piece of data to the transaction.
  443. hookFired := 0
  444. hook := func(tx backend.WriteTransaction) error {
  445. err := tx.Put([]byte(fmt.Sprintf("hook-key-%d", hookFired)), []byte(fmt.Sprintf("hook-value-%d", hookFired)))
  446. if err != nil {
  447. t.Fatal(err)
  448. }
  449. hookFired++
  450. return nil
  451. }
  452. // A transaction.
  453. tx, err := db.NewWriteTransaction(hook)
  454. if err != nil {
  455. t.Fatal(err)
  456. }
  457. defer tx.Release()
  458. // Write stuff until the transaction flushes, thus firing the hook.
  459. i := 0
  460. for hookFired == 0 {
  461. err := tx.Put([]byte(fmt.Sprintf("key-%d", i)), []byte(fmt.Sprintf("value-%d", i)))
  462. if err != nil {
  463. t.Fatal(err)
  464. }
  465. i++
  466. }
  467. // The hook should have fired precisely once.
  468. if hookFired != 1 {
  469. t.Error("expect one hook fire, not", hookFired)
  470. }
  471. }
  472. func TestCheckLocalNeed(t *testing.T) {
  473. db := newLowlevelMemory(t)
  474. defer db.Close()
  475. folderStr := "test"
  476. fs := newFileSet(t, folderStr, db)
  477. // Add files such that we are in sync for a and b, and need c and d.
  478. files := []protocol.FileInfo{
  479. {Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1}}}},
  480. {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1}}}},
  481. {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1}}}},
  482. {Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1}}}},
  483. }
  484. fs.Update(protocol.LocalDeviceID, files)
  485. files[2].Version = files[2].Version.Update(remoteDevice0.Short())
  486. files[3].Version = files[2].Version.Update(remoteDevice0.Short())
  487. fs.Update(remoteDevice0, files)
  488. checkNeed := func() {
  489. snap := snapshot(t, fs)
  490. defer snap.Release()
  491. c := snap.NeedSize(protocol.LocalDeviceID)
  492. if c.Files != 2 {
  493. t.Errorf("Expected 2 needed files locally, got %v in meta", c.Files)
  494. }
  495. needed := make([]protocol.FileInfo, 0, 2)
  496. snap.WithNeed(protocol.LocalDeviceID, func(fi protocol.FileInfo) bool {
  497. needed = append(needed, fi)
  498. return true
  499. })
  500. if l := len(needed); l != 2 {
  501. t.Errorf("Expected 2 needed files locally, got %v in db", l)
  502. } else if needed[0].Name != "c" || needed[1].Name != "d" {
  503. t.Errorf("Expected files c and d to be needed, got %v and %v", needed[0].Name, needed[1].Name)
  504. }
  505. }
  506. checkNeed()
  507. trans, err := db.newReadWriteTransaction()
  508. if err != nil {
  509. t.Fatal(err)
  510. }
  511. defer trans.close()
  512. // Add "b" to needed and remove "d"
  513. folder := []byte(folderStr)
  514. key, err := trans.keyer.GenerateNeedFileKey(nil, folder, []byte(files[1].Name))
  515. if err != nil {
  516. t.Fatal(err)
  517. }
  518. if err = trans.Put(key, nil); err != nil {
  519. t.Fatal(err)
  520. }
  521. key, err = trans.keyer.GenerateNeedFileKey(nil, folder, []byte(files[3].Name))
  522. if err != nil {
  523. t.Fatal(err)
  524. }
  525. if err = trans.Delete(key); err != nil {
  526. t.Fatal(err)
  527. }
  528. if err := trans.Commit(); err != nil {
  529. t.Fatal(err)
  530. }
  531. if repaired, err := db.checkLocalNeed(folder); err != nil {
  532. t.Fatal(err)
  533. } else if repaired != 2 {
  534. t.Error("Expected 2 repaired local need items, got", repaired)
  535. }
  536. checkNeed()
  537. }
  538. func TestDuplicateNeedCount(t *testing.T) {
  539. db := newLowlevelMemory(t)
  540. defer db.Close()
  541. folder := "test"
  542. fs := newFileSet(t, folder, db)
  543. files := []protocol.FileInfo{{Name: "foo", Version: protocol.Vector{}.Update(myID), Sequence: 1}}
  544. fs.Update(protocol.LocalDeviceID, files)
  545. files[0].Version = files[0].Version.Update(remoteDevice0.Short())
  546. fs.Update(remoteDevice0, files)
  547. db.checkRepair()
  548. fs = newFileSet(t, folder, db)
  549. found := false
  550. for _, c := range fs.meta.counts.Counts {
  551. if protocol.LocalDeviceID == c.DeviceID && c.LocalFlags == needFlag {
  552. if found {
  553. t.Fatal("second need count for local device encountered")
  554. }
  555. found = true
  556. }
  557. }
  558. if !found {
  559. t.Fatal("no need count for local device encountered")
  560. }
  561. }
  562. func TestNeedAfterDropGlobal(t *testing.T) {
  563. db := newLowlevelMemory(t)
  564. defer db.Close()
  565. folder := "test"
  566. fs := newFileSet(t, folder, db)
  567. // Initial:
  568. // Three devices and a file "test": local has Version 1, remoteDevice0
  569. // Version 2 and remoteDevice2 doesn't have it.
  570. // All of them have "bar", just so the db knows about remoteDevice2.
  571. files := []protocol.FileInfo{
  572. {Name: "foo", Version: protocol.Vector{}.Update(myID), Sequence: 1},
  573. {Name: "bar", Version: protocol.Vector{}.Update(myID), Sequence: 2},
  574. }
  575. fs.Update(protocol.LocalDeviceID, files)
  576. files[0].Version = files[0].Version.Update(myID)
  577. fs.Update(remoteDevice0, files)
  578. fs.Update(remoteDevice1, files[1:])
  579. // remoteDevice1 needs one file: test
  580. snap := snapshot(t, fs)
  581. c := snap.NeedSize(remoteDevice1)
  582. if c.Files != 1 {
  583. t.Errorf("Expected 1 needed files initially, got %v", c.Files)
  584. }
  585. snap.Release()
  586. // Drop remoteDevice0, i.e. remove all their files from db.
  587. // That changes the global file, which is now what local has.
  588. fs.Drop(remoteDevice0)
  589. // remoteDevice1 still needs test.
  590. snap = snapshot(t, fs)
  591. c = snap.NeedSize(remoteDevice1)
  592. if c.Files != 1 {
  593. t.Errorf("Expected still 1 needed files, got %v", c.Files)
  594. }
  595. snap.Release()
  596. }
  597. func numBlockLists(db *Lowlevel) (int, error) {
  598. it, err := db.Backend.NewPrefixIterator([]byte{KeyTypeBlockList})
  599. if err != nil {
  600. return 0, err
  601. }
  602. defer it.Release()
  603. n := 0
  604. for it.Next() {
  605. n++
  606. }
  607. if err := it.Error(); err != nil {
  608. return 0, err
  609. }
  610. return n, nil
  611. }