2018-05-16 06:44:08 +00:00
|
|
|
// 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"
|
2024-08-28 13:00:19 +00:00
|
|
|
"errors"
|
2018-05-16 06:44:08 +00:00
|
|
|
"io"
|
|
|
|
"os"
|
2020-12-21 11:59:22 +00:00
|
|
|
"testing"
|
2018-05-16 06:44:08 +00:00
|
|
|
|
2019-11-29 08:11:52 +00:00
|
|
|
"github.com/syncthing/syncthing/lib/db/backend"
|
2020-12-21 11:59:22 +00:00
|
|
|
"github.com/syncthing/syncthing/lib/events"
|
2024-08-28 13:00:19 +00:00
|
|
|
"github.com/syncthing/syncthing/lib/protocol"
|
2018-05-16 06:44:08 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// writeJSONS serializes the database to a JSON stream that can be checked
|
|
|
|
// in to the repo and used for tests.
|
2019-11-29 08:11:52 +00:00
|
|
|
func writeJSONS(w io.Writer, db backend.Backend) {
|
|
|
|
it, err := db.NewPrefixIterator(nil)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2018-05-16 06:44:08 +00:00
|
|
|
defer it.Release()
|
|
|
|
enc := json.NewEncoder(w)
|
|
|
|
for it.Next() {
|
2019-11-29 08:11:52 +00:00
|
|
|
err := enc.Encode(map[string][]byte{
|
2018-05-16 06:44:08 +00:00
|
|
|
"k": it.Key(),
|
|
|
|
"v": it.Value(),
|
|
|
|
})
|
2019-11-29 08:11:52 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2018-05-16 06:44:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-02 10:02:28 +00:00
|
|
|
// we know this function isn't generally used, nonetheless we want it in
|
|
|
|
// here and the linter to not complain.
|
|
|
|
var _ = writeJSONS
|
|
|
|
|
2019-11-29 08:11:52 +00:00
|
|
|
// openJSONS reads a JSON stream file into a backend DB
|
|
|
|
func openJSONS(file string) (backend.Backend, error) {
|
2018-05-16 06:44:08 +00:00
|
|
|
fd, err := os.Open(file)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
dec := json.NewDecoder(fd)
|
|
|
|
|
2019-11-29 08:11:52 +00:00
|
|
|
db := backend.OpenMemory()
|
2018-05-16 06:44:08 +00:00
|
|
|
|
|
|
|
for {
|
|
|
|
var row map[string][]byte
|
|
|
|
|
|
|
|
err := dec.Decode(&row)
|
|
|
|
if err == io.EOF {
|
|
|
|
break
|
|
|
|
} else if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-11-29 08:11:52 +00:00
|
|
|
if err := db.Put(row["k"], row["v"]); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-05-16 06:44:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return db, nil
|
|
|
|
}
|
|
|
|
|
2020-12-21 11:59:22 +00:00
|
|
|
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())
|
|
|
|
}
|
|
|
|
|
2022-01-31 09:12:52 +00:00
|
|
|
func newFileSet(t testing.TB, folder string, db *Lowlevel) *FileSet {
|
2020-12-21 11:59:22 +00:00
|
|
|
t.Helper()
|
2022-01-31 09:12:52 +00:00
|
|
|
fset, err := NewFileSet(folder, db)
|
2020-12-21 11:59:22 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
return fset
|
|
|
|
}
|
|
|
|
|
2021-03-07 12:43:22 +00:00
|
|
|
func snapshot(t testing.TB, fset *FileSet) *Snapshot {
|
|
|
|
t.Helper()
|
|
|
|
snap, err := fset.Snapshot()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
return snap
|
|
|
|
}
|
|
|
|
|
2018-06-02 13:08:32 +00:00
|
|
|
// The following commented tests were used to generate jsons files to stdout for
|
|
|
|
// future tests and are kept here for reference (reuse).
|
2018-05-16 06:44:08 +00:00
|
|
|
|
2018-06-02 13:08:32 +00:00
|
|
|
// 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()
|
2020-12-21 11:59:22 +00:00
|
|
|
// fs := newFileSet(t, "test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), db)
|
2018-06-02 13:08:32 +00:00
|
|
|
// 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()
|
2020-12-21 11:59:22 +00:00
|
|
|
// fs := newFileSet(t, update0to3Folder, fs.NewFilesystem(fs.FilesystemTypeBasic, "."), db)
|
2018-06-02 13:08:32 +00:00
|
|
|
// for devID, files := range haveUpdate0to3 {
|
|
|
|
// fs.Update(devID, files)
|
|
|
|
// }
|
|
|
|
// writeJSONS(os.Stdout, db.DB)
|
|
|
|
// }
|
2020-05-11 13:07:06 +00:00
|
|
|
|
|
|
|
// func TestGenerateUpdateTo10(t *testing.T) {
|
2020-12-21 11:59:22 +00:00
|
|
|
// db := newLowlevelMemory(t)
|
2020-05-11 13:07:06 +00:00
|
|
|
// defer db.Close()
|
|
|
|
|
|
|
|
// if err := UpdateSchema(db); err != nil {
|
|
|
|
// t.Fatal(err)
|
|
|
|
// }
|
|
|
|
|
2020-12-21 11:59:22 +00:00
|
|
|
// fs := newFileSet(t, "test", fs.NewFilesystem(fs.FilesystemTypeFake, ""), db)
|
2020-05-11 13:07:06 +00:00
|
|
|
|
|
|
|
// 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)
|
|
|
|
// }
|
2024-08-28 13:00:19 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|