123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- // Copyright (C) 2018 The Syncthing Authors.
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this file,
- // You can obtain one at https://mozilla.org/MPL/2.0/.
- package db
- import (
- "encoding/json"
- "errors"
- "io"
- "os"
- "testing"
- "github.com/syncthing/syncthing/lib/db/backend"
- "github.com/syncthing/syncthing/lib/events"
- "github.com/syncthing/syncthing/lib/protocol"
- )
- // writeJSONS serializes the database to a JSON stream that can be checked
- // in to the repo and used for tests.
- func writeJSONS(w io.Writer, db backend.Backend) {
- it, err := db.NewPrefixIterator(nil)
- if err != nil {
- panic(err)
- }
- defer it.Release()
- enc := json.NewEncoder(w)
- for it.Next() {
- err := enc.Encode(map[string][]byte{
- "k": it.Key(),
- "v": it.Value(),
- })
- if err != nil {
- panic(err)
- }
- }
- }
- // we know this function isn't generally used, nonetheless we want it in
- // here and the linter to not complain.
- var _ = writeJSONS
- // openJSONS reads a JSON stream file into a backend DB
- func openJSONS(file string) (backend.Backend, error) {
- fd, err := os.Open(file)
- if err != nil {
- return nil, err
- }
- dec := json.NewDecoder(fd)
- db := backend.OpenMemory()
- for {
- var row map[string][]byte
- err := dec.Decode(&row)
- if err == io.EOF {
- break
- } else if err != nil {
- return nil, err
- }
- if err := db.Put(row["k"], row["v"]); err != nil {
- return nil, err
- }
- }
- return db, nil
- }
- func newLowlevel(t testing.TB, backend backend.Backend) *Lowlevel {
- t.Helper()
- ll, err := NewLowlevel(backend, events.NoopLogger)
- if err != nil {
- t.Fatal(err)
- }
- return ll
- }
- func newLowlevelMemory(t testing.TB) *Lowlevel {
- return newLowlevel(t, backend.OpenMemory())
- }
- func newFileSet(t testing.TB, folder string, db *Lowlevel) *FileSet {
- t.Helper()
- fset, err := NewFileSet(folder, db)
- if err != nil {
- t.Fatal(err)
- }
- return fset
- }
- func snapshot(t testing.TB, fset *FileSet) *Snapshot {
- t.Helper()
- snap, err := fset.Snapshot()
- if err != nil {
- t.Fatal(err)
- }
- return snap
- }
- // The following commented tests were used to generate jsons files to stdout for
- // future tests and are kept here for reference (reuse).
- // TestGenerateIgnoredFilesDB generates a database with files with invalid flags,
- // local and remote, in the format used in 0.14.48.
- // func TestGenerateIgnoredFilesDB(t *testing.T) {
- // db := OpenMemory()
- // fs := newFileSet(t, "test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), db)
- // fs.Update(protocol.LocalDeviceID, []protocol.FileInfo{
- // { // invalid (ignored) file
- // Name: "foo",
- // Type: protocol.FileInfoTypeFile,
- // Invalid: true,
- // Version: protocol.Vector{Counters: []protocol.Counter{{ID: 1, Value: 1000}}},
- // },
- // { // regular file
- // Name: "bar",
- // Type: protocol.FileInfoTypeFile,
- // Version: protocol.Vector{Counters: []protocol.Counter{{ID: 1, Value: 1001}}},
- // },
- // })
- // fs.Update(protocol.DeviceID{42}, []protocol.FileInfo{
- // { // invalid file
- // Name: "baz",
- // Type: protocol.FileInfoTypeFile,
- // Invalid: true,
- // Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 1000}}},
- // },
- // { // regular file
- // Name: "quux",
- // Type: protocol.FileInfoTypeFile,
- // Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 1002}}},
- // },
- // })
- // writeJSONS(os.Stdout, db.DB)
- // }
- // TestGenerateUpdate0to3DB generates a database with files with invalid flags, prefixed
- // by a slash and other files to test database migration from version 0 to 3, in the
- // format used in 0.14.45.
- // func TestGenerateUpdate0to3DB(t *testing.T) {
- // db := OpenMemory()
- // fs := newFileSet(t, update0to3Folder, fs.NewFilesystem(fs.FilesystemTypeBasic, "."), db)
- // for devID, files := range haveUpdate0to3 {
- // fs.Update(devID, files)
- // }
- // writeJSONS(os.Stdout, db.DB)
- // }
- // func TestGenerateUpdateTo10(t *testing.T) {
- // db := newLowlevelMemory(t)
- // defer db.Close()
- // if err := UpdateSchema(db); err != nil {
- // t.Fatal(err)
- // }
- // fs := newFileSet(t, "test", fs.NewFilesystem(fs.FilesystemTypeFake, ""), db)
- // files := []protocol.FileInfo{
- // {Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Deleted: true, Sequence: 1},
- // {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(2), Sequence: 2},
- // {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Deleted: true, Sequence: 3},
- // }
- // fs.Update(protocol.LocalDeviceID, files)
- // files[1].Version = files[1].Version.Update(remoteDevice0.Short())
- // files[1].Deleted = true
- // files[2].Version = files[2].Version.Update(remoteDevice0.Short())
- // files[2].Blocks = genBlocks(1)
- // files[2].Deleted = false
- // fs.Update(remoteDevice0, files)
- // fd, err := os.Create("./testdata/v1.4.0-updateTo10.json")
- // if err != nil {
- // panic(err)
- // }
- // defer fd.Close()
- // writeJSONS(fd, db)
- // }
- func TestFileInfoBatchError(t *testing.T) {
- // Verify behaviour of the flush function returning an error.
- var errReturn error
- var called int
- b := NewFileInfoBatch(func([]protocol.FileInfo) error {
- called += 1
- return errReturn
- })
- // Flush should work when the flush function error is nil
- b.Append(protocol.FileInfo{Name: "test"})
- if err := b.Flush(); err != nil {
- t.Fatalf("expected nil, got %v", err)
- }
- if called != 1 {
- t.Fatalf("expected 1, got %d", called)
- }
- // Flush should fail with an error retur
- errReturn = errors.New("problem")
- b.Append(protocol.FileInfo{Name: "test"})
- if err := b.Flush(); err != errReturn {
- t.Fatalf("expected %v, got %v", errReturn, err)
- }
- if called != 2 {
- t.Fatalf("expected 2, got %d", called)
- }
- // Flush function should not be called again when it's already errored,
- // same error should be returned by Flush()
- if err := b.Flush(); err != errReturn {
- t.Fatalf("expected %v, got %v", errReturn, err)
- }
- if called != 2 {
- t.Fatalf("expected 2, got %d", called)
- }
- // Reset should clear the error (and the file list)
- errReturn = nil
- b.Reset()
- b.Append(protocol.FileInfo{Name: "test"})
- if err := b.Flush(); err != nil {
- t.Fatalf("expected nil, got %v", err)
- }
- if called != 3 {
- t.Fatalf("expected 3, got %d", called)
- }
- }
|