2014-11-16 20:13:20 +00:00
|
|
|
// Copyright (C) 2014 The Syncthing Authors.
|
2014-10-07 13:05:04 +00:00
|
|
|
//
|
2015-03-07 20:36:35 +00:00
|
|
|
// 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,
|
2017-02-09 06:52:18 +00:00
|
|
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
2014-10-07 13:05:04 +00:00
|
|
|
|
2015-01-12 13:50:30 +00:00
|
|
|
package db
|
2014-10-07 13:05:04 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"testing"
|
2018-03-10 10:42:01 +00:00
|
|
|
|
2018-05-16 06:44:08 +00:00
|
|
|
"github.com/syncthing/syncthing/lib/fs"
|
2018-03-10 10:42:01 +00:00
|
|
|
"github.com/syncthing/syncthing/lib/protocol"
|
2014-10-07 13:05:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestDeviceKey(t *testing.T) {
|
|
|
|
fld := []byte("folder6789012345678901234567890123456789012345678901234567890123")
|
|
|
|
dev := []byte("device67890123456789012345678901")
|
|
|
|
name := []byte("name")
|
|
|
|
|
2016-01-03 18:08:19 +00:00
|
|
|
db := OpenMemory()
|
|
|
|
db.folderIdx.ID(fld)
|
|
|
|
db.deviceIdx.ID(dev)
|
2014-10-07 13:05:04 +00:00
|
|
|
|
2015-10-31 06:20:35 +00:00
|
|
|
key := db.deviceKey(fld, dev, name)
|
|
|
|
|
|
|
|
fld2 := db.deviceKeyFolder(key)
|
2016-03-31 15:12:46 +00:00
|
|
|
if !bytes.Equal(fld2, fld) {
|
2014-10-07 13:05:04 +00:00
|
|
|
t.Errorf("wrong folder %q != %q", fld2, fld)
|
|
|
|
}
|
2015-10-31 06:20:35 +00:00
|
|
|
dev2 := db.deviceKeyDevice(key)
|
2016-03-31 15:12:46 +00:00
|
|
|
if !bytes.Equal(dev2, dev) {
|
2014-10-07 13:05:04 +00:00
|
|
|
t.Errorf("wrong device %q != %q", dev2, dev)
|
|
|
|
}
|
2015-10-31 06:20:35 +00:00
|
|
|
name2 := db.deviceKeyName(key)
|
2016-03-31 15:12:46 +00:00
|
|
|
if !bytes.Equal(name2, name) {
|
2014-10-07 13:05:04 +00:00
|
|
|
t.Errorf("wrong name %q != %q", name2, name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGlobalKey(t *testing.T) {
|
|
|
|
fld := []byte("folder6789012345678901234567890123456789012345678901234567890123")
|
|
|
|
name := []byte("name")
|
|
|
|
|
2016-01-03 18:08:19 +00:00
|
|
|
db := OpenMemory()
|
|
|
|
db.folderIdx.ID(fld)
|
2015-10-31 06:20:35 +00:00
|
|
|
|
|
|
|
key := db.globalKey(fld, name)
|
2014-10-07 13:05:04 +00:00
|
|
|
|
2017-01-04 10:34:52 +00:00
|
|
|
fld2, ok := db.globalKeyFolder(key)
|
|
|
|
if !ok {
|
|
|
|
t.Error("should have been found")
|
|
|
|
}
|
2016-03-31 15:12:46 +00:00
|
|
|
if !bytes.Equal(fld2, fld) {
|
2014-10-07 13:05:04 +00:00
|
|
|
t.Errorf("wrong folder %q != %q", fld2, fld)
|
|
|
|
}
|
2015-10-31 06:20:35 +00:00
|
|
|
name2 := db.globalKeyName(key)
|
2016-03-31 15:12:46 +00:00
|
|
|
if !bytes.Equal(name2, name) {
|
2014-10-07 13:05:04 +00:00
|
|
|
t.Errorf("wrong name %q != %q", name2, name)
|
|
|
|
}
|
2017-01-04 10:34:52 +00:00
|
|
|
|
|
|
|
_, ok = db.globalKeyFolder([]byte{1, 2, 3, 4, 5})
|
|
|
|
if ok {
|
|
|
|
t.Error("should not have been found")
|
|
|
|
}
|
2014-10-07 13:05:04 +00:00
|
|
|
}
|
2018-03-10 10:42:01 +00:00
|
|
|
|
|
|
|
func TestDropIndexIDs(t *testing.T) {
|
|
|
|
db := OpenMemory()
|
|
|
|
|
|
|
|
d1 := []byte("device67890123456789012345678901")
|
|
|
|
d2 := []byte("device12345678901234567890123456")
|
|
|
|
|
|
|
|
// Set some index IDs
|
|
|
|
|
|
|
|
db.setIndexID(protocol.LocalDeviceID[:], []byte("foo"), 1)
|
|
|
|
db.setIndexID(protocol.LocalDeviceID[:], []byte("bar"), 2)
|
|
|
|
db.setIndexID(d1, []byte("foo"), 3)
|
|
|
|
db.setIndexID(d1, []byte("bar"), 4)
|
|
|
|
db.setIndexID(d2, []byte("foo"), 5)
|
|
|
|
db.setIndexID(d2, []byte("bar"), 6)
|
|
|
|
|
|
|
|
// Verify them
|
|
|
|
|
|
|
|
if db.getIndexID(protocol.LocalDeviceID[:], []byte("foo")) != 1 {
|
|
|
|
t.Fatal("fail local 1")
|
|
|
|
}
|
|
|
|
if db.getIndexID(protocol.LocalDeviceID[:], []byte("bar")) != 2 {
|
|
|
|
t.Fatal("fail local 2")
|
|
|
|
}
|
|
|
|
if db.getIndexID(d1, []byte("foo")) != 3 {
|
|
|
|
t.Fatal("fail remote 1")
|
|
|
|
}
|
|
|
|
if db.getIndexID(d1, []byte("bar")) != 4 {
|
|
|
|
t.Fatal("fail remote 2")
|
|
|
|
}
|
|
|
|
if db.getIndexID(d2, []byte("foo")) != 5 {
|
|
|
|
t.Fatal("fail remote 3")
|
|
|
|
}
|
|
|
|
if db.getIndexID(d2, []byte("bar")) != 6 {
|
|
|
|
t.Fatal("fail remote 4")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Drop the local ones, verify only they got dropped
|
|
|
|
|
|
|
|
db.DropLocalDeltaIndexIDs()
|
|
|
|
|
|
|
|
if db.getIndexID(protocol.LocalDeviceID[:], []byte("foo")) != 0 {
|
|
|
|
t.Fatal("fail local 1")
|
|
|
|
}
|
|
|
|
if db.getIndexID(protocol.LocalDeviceID[:], []byte("bar")) != 0 {
|
|
|
|
t.Fatal("fail local 2")
|
|
|
|
}
|
|
|
|
if db.getIndexID(d1, []byte("foo")) != 3 {
|
|
|
|
t.Fatal("fail remote 1")
|
|
|
|
}
|
|
|
|
if db.getIndexID(d1, []byte("bar")) != 4 {
|
|
|
|
t.Fatal("fail remote 2")
|
|
|
|
}
|
|
|
|
if db.getIndexID(d2, []byte("foo")) != 5 {
|
|
|
|
t.Fatal("fail remote 3")
|
|
|
|
}
|
|
|
|
if db.getIndexID(d2, []byte("bar")) != 6 {
|
|
|
|
t.Fatal("fail remote 4")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set local ones again
|
|
|
|
|
|
|
|
db.setIndexID(protocol.LocalDeviceID[:], []byte("foo"), 1)
|
|
|
|
db.setIndexID(protocol.LocalDeviceID[:], []byte("bar"), 2)
|
|
|
|
|
|
|
|
// Drop the remote ones, verify only they got dropped
|
|
|
|
|
|
|
|
db.DropRemoteDeltaIndexIDs()
|
|
|
|
|
|
|
|
if db.getIndexID(protocol.LocalDeviceID[:], []byte("foo")) != 1 {
|
|
|
|
t.Fatal("fail local 1")
|
|
|
|
}
|
|
|
|
if db.getIndexID(protocol.LocalDeviceID[:], []byte("bar")) != 2 {
|
|
|
|
t.Fatal("fail local 2")
|
|
|
|
}
|
|
|
|
if db.getIndexID(d1, []byte("foo")) != 0 {
|
|
|
|
t.Fatal("fail remote 1")
|
|
|
|
}
|
|
|
|
if db.getIndexID(d1, []byte("bar")) != 0 {
|
|
|
|
t.Fatal("fail remote 2")
|
|
|
|
}
|
|
|
|
if db.getIndexID(d2, []byte("foo")) != 0 {
|
|
|
|
t.Fatal("fail remote 3")
|
|
|
|
}
|
|
|
|
if db.getIndexID(d2, []byte("bar")) != 0 {
|
|
|
|
t.Fatal("fail remote 4")
|
|
|
|
}
|
|
|
|
}
|
2018-05-16 06:44:08 +00:00
|
|
|
|
2018-06-24 07:50:18 +00:00
|
|
|
func TestIgnoredFiles(t *testing.T) {
|
2018-05-16 06:44:08 +00:00
|
|
|
ldb, err := openJSONS("testdata/v0.14.48-ignoredfiles.db.jsons")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
db := newDBInstance(ldb, "<memory>")
|
|
|
|
fs := NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), db)
|
|
|
|
|
|
|
|
// The contents of the database are like this:
|
|
|
|
//
|
|
|
|
// fs := NewFileSet("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}}},
|
|
|
|
// },
|
|
|
|
// })
|
|
|
|
|
2018-06-24 07:50:18 +00:00
|
|
|
// Local files should have the "ignored" bit in addition to just being
|
|
|
|
// generally invalid if we want to look at the simulation of that bit.
|
|
|
|
|
2018-05-16 06:44:08 +00:00
|
|
|
fi, ok := fs.Get(protocol.LocalDeviceID, "foo")
|
|
|
|
if !ok {
|
|
|
|
t.Fatal("foo should exist")
|
|
|
|
}
|
2018-06-24 07:50:18 +00:00
|
|
|
if !fi.IsInvalid() {
|
2018-05-16 06:44:08 +00:00
|
|
|
t.Error("foo should be invalid")
|
|
|
|
}
|
2018-06-24 07:50:18 +00:00
|
|
|
if !fi.IsIgnored() {
|
|
|
|
t.Error("foo should be ignored")
|
|
|
|
}
|
2018-05-16 06:44:08 +00:00
|
|
|
|
|
|
|
fi, ok = fs.Get(protocol.LocalDeviceID, "bar")
|
|
|
|
if !ok {
|
|
|
|
t.Fatal("bar should exist")
|
|
|
|
}
|
2018-06-24 07:50:18 +00:00
|
|
|
if fi.IsInvalid() {
|
2018-05-16 06:44:08 +00:00
|
|
|
t.Error("bar should not be invalid")
|
|
|
|
}
|
2018-06-24 07:50:18 +00:00
|
|
|
if fi.IsIgnored() {
|
|
|
|
t.Error("bar should not be ignored")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remote files have the invalid bit as usual, and the IsInvalid() method
|
|
|
|
// should pick this up too.
|
2018-05-16 06:44:08 +00:00
|
|
|
|
|
|
|
fi, ok = fs.Get(protocol.DeviceID{42}, "baz")
|
|
|
|
if !ok {
|
|
|
|
t.Fatal("baz should exist")
|
|
|
|
}
|
2018-06-24 07:50:18 +00:00
|
|
|
if !fi.IsInvalid() {
|
|
|
|
t.Error("baz should be invalid")
|
|
|
|
}
|
|
|
|
if !fi.IsInvalid() {
|
2018-05-16 06:44:08 +00:00
|
|
|
t.Error("baz should be invalid")
|
|
|
|
}
|
|
|
|
|
|
|
|
fi, ok = fs.Get(protocol.DeviceID{42}, "quux")
|
|
|
|
if !ok {
|
|
|
|
t.Fatal("quux should exist")
|
|
|
|
}
|
2018-06-24 07:50:18 +00:00
|
|
|
if fi.IsInvalid() {
|
|
|
|
t.Error("quux should not be invalid")
|
|
|
|
}
|
|
|
|
if fi.IsInvalid() {
|
2018-05-16 06:44:08 +00:00
|
|
|
t.Error("quux should not be invalid")
|
|
|
|
}
|
|
|
|
}
|
2018-06-02 13:08:32 +00:00
|
|
|
|
|
|
|
const myID = 1
|
|
|
|
|
|
|
|
var (
|
|
|
|
remoteDevice0, remoteDevice1 protocol.DeviceID
|
|
|
|
update0to3Folder = "UpdateSchema0to3"
|
|
|
|
invalid = "invalid"
|
|
|
|
slashPrefixed = "/notgood"
|
|
|
|
haveUpdate0to3 map[protocol.DeviceID]fileList
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
remoteDevice0, _ = protocol.DeviceIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR")
|
|
|
|
remoteDevice1, _ = protocol.DeviceIDFromString("I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU")
|
|
|
|
haveUpdate0to3 = map[protocol.DeviceID]fileList{
|
|
|
|
protocol.LocalDeviceID: {
|
|
|
|
protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
|
|
|
|
protocol.FileInfo{Name: slashPrefixed, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
|
|
|
|
},
|
|
|
|
remoteDevice0: {
|
|
|
|
protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)},
|
2018-06-24 07:50:18 +00:00
|
|
|
protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), RawInvalid: true},
|
2018-06-02 13:08:32 +00:00
|
|
|
protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)},
|
|
|
|
},
|
|
|
|
remoteDevice1: {
|
|
|
|
protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)},
|
2018-06-24 07:50:18 +00:00
|
|
|
protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), RawInvalid: true},
|
|
|
|
protocol.FileInfo{Name: invalid, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), RawInvalid: true},
|
2018-06-02 13:08:32 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestUpdate0to3(t *testing.T) {
|
|
|
|
ldb, err := openJSONS("testdata/v0.14.45-update0to3.db.jsons")
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
db := newDBInstance(ldb, "<memory>")
|
|
|
|
|
|
|
|
folder := []byte(update0to3Folder)
|
|
|
|
|
|
|
|
db.updateSchema0to1()
|
|
|
|
|
|
|
|
if _, ok := db.getFile(db.deviceKey(folder, protocol.LocalDeviceID[:], []byte(slashPrefixed))); ok {
|
|
|
|
t.Error("File prefixed by '/' was not removed during transition to schema 1")
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := db.Get(db.globalKey(folder, []byte(invalid)), nil); err != nil {
|
|
|
|
t.Error("Invalid file wasn't added to global list")
|
|
|
|
}
|
|
|
|
|
|
|
|
db.updateSchema1to2()
|
|
|
|
|
|
|
|
found := false
|
|
|
|
db.withHaveSequence(folder, 0, func(fi FileIntf) bool {
|
|
|
|
f := fi.(protocol.FileInfo)
|
|
|
|
l.Infoln(f)
|
|
|
|
if found {
|
|
|
|
t.Error("Unexpected additional file via sequence", f.FileName())
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if e := haveUpdate0to3[protocol.LocalDeviceID][0]; f.IsEquivalent(e, true, true) {
|
|
|
|
found = true
|
|
|
|
} else {
|
|
|
|
t.Errorf("Wrong file via sequence, got %v, expected %v", f, e)
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
if !found {
|
|
|
|
t.Error("Local file wasn't added to sequence bucket", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
db.updateSchema2to3()
|
|
|
|
|
|
|
|
need := map[string]protocol.FileInfo{
|
|
|
|
haveUpdate0to3[remoteDevice0][0].Name: haveUpdate0to3[remoteDevice0][0],
|
|
|
|
haveUpdate0to3[remoteDevice1][0].Name: haveUpdate0to3[remoteDevice1][0],
|
|
|
|
haveUpdate0to3[remoteDevice0][2].Name: haveUpdate0to3[remoteDevice0][2],
|
|
|
|
}
|
|
|
|
db.withNeed(folder, protocol.LocalDeviceID[:], false, func(fi FileIntf) bool {
|
|
|
|
e, ok := need[fi.FileName()]
|
|
|
|
if !ok {
|
|
|
|
t.Error("Got unexpected needed file:", fi.FileName())
|
|
|
|
}
|
|
|
|
f := fi.(protocol.FileInfo)
|
|
|
|
delete(need, f.Name)
|
|
|
|
if !f.IsEquivalent(e, true, true) {
|
|
|
|
t.Errorf("Wrong needed file, got %v, expected %v", f, e)
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
for n := range need {
|
|
|
|
t.Errorf(`Missing needed file "%v"`, n)
|
|
|
|
}
|
|
|
|
}
|