func TestLongPath(t *testing.T) { ldb := db.OpenMemory() s := db.NewFileSet("test", ldb) var b bytes.Buffer for i := 0; i < 100; i++ { b.WriteString("012345678901234567890123456789012345678901234567890") } name := b.String() // 5000 characters local := []protocol.FileInfo{ {Name: string(name), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, } s.Replace(protocol.LocalDeviceID, local) gf := globalList(s) if l := len(gf); l != 1 { t.Fatalf("Incorrect len %d != 1 for global list", l) } if gf[0].Name != local[0].Name { t.Errorf("Incorrect long filename;\n%q !=\n%q", gf[0].Name, local[0].Name) } }
func TestSequence(t *testing.T) { ldb := db.OpenMemory() m := db.NewFileSet("test", ldb) local1 := []protocol.FileInfo{ {Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, {Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, } local2 := []protocol.FileInfo{ local1[0], // [1] deleted local1[2], {Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}}, {Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, } m.Replace(protocol.LocalDeviceID, local1) c0 := m.Sequence(protocol.LocalDeviceID) m.Replace(protocol.LocalDeviceID, local2) c1 := m.Sequence(protocol.LocalDeviceID) if !(c1 > c0) { t.Fatal("Local version number should have incremented") } }
func TestIndexID(t *testing.T) { ldb := db.OpenMemory() s := db.NewFileSet("test", ldb) // The Index ID for some random device is zero by default. id := s.IndexID(remoteDevice0) if id != 0 { t.Errorf("index ID for remote device should default to zero, not %d", id) } // The Index ID for someone else should be settable s.SetIndexID(remoteDevice0, 42) id = s.IndexID(remoteDevice0) if id != 42 { t.Errorf("index ID for remote device should be remembered; got %d, expected %d", id, 42) } // Our own index ID should be generated randomly. id = s.IndexID(protocol.LocalDeviceID) if id == 0 { t.Errorf("index ID for local device should be random, not zero") } t.Logf("random index ID is 0x%016x", id) // But of course always the same after that. again := s.IndexID(protocol.LocalDeviceID) if again != id { t.Errorf("index ID changed; %d != %d", again, id) } }
func TestCommitted(t *testing.T) { // Verify that the Committed counter increases when we change things and // doesn't increase when we don't. ldb := db.OpenMemory() s := db.NewFileSet("test", ldb) local := []protocol.FileInfo{ {Name: string("file"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, } // Adding a file should increase the counter c0 := ldb.Committed() s.Replace(protocol.LocalDeviceID, local) c1 := ldb.Committed() if c1 <= c0 { t.Errorf("committed data didn't increase; %d <= %d", c1, c0) } // Updating with something identical should not do anything s.Update(protocol.LocalDeviceID, local) c2 := ldb.Committed() if c2 > c1 { t.Errorf("replace with same contents should do nothing but %d > %d", c2, c1) } }
func TestUpdateToInvalid(t *testing.T) { ldb := db.OpenMemory() s := db.NewFileSet("test", ldb) localHave := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), Invalid: true}, protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)}, } s.Replace(protocol.LocalDeviceID, localHave) have := fileList(haveList(s, protocol.LocalDeviceID)) sort.Sort(have) if fmt.Sprint(have) != fmt.Sprint(localHave) { t.Errorf("Have incorrect before invalidation;\n A: %v !=\n E: %v", have, localHave) } localHave[1] = protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Invalid: true} s.Update(protocol.LocalDeviceID, localHave[1:2]) have = fileList(haveList(s, protocol.LocalDeviceID)) sort.Sort(have) if fmt.Sprint(have) != fmt.Sprint(localHave) { t.Errorf("Have incorrect after invalidation;\n A: %v !=\n E: %v", have, localHave) } }
func TestIssue3028(t *testing.T) { // Create two files that we'll delete, one with a name that is a prefix of the other. if err := ioutil.WriteFile("testdata/testrm", []byte("Hello"), 0644); err != nil { t.Fatal(err) } defer os.Remove("testdata/testrm") if err := ioutil.WriteFile("testdata/testrm2", []byte("Hello"), 0644); err != nil { t.Fatal(err) } defer os.Remove("testdata/testrm2") // Create a model and default folder db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) defCfg := defaultFolderConfig.Copy() defCfg.RescanIntervalS = 86400 m.AddFolder(defCfg) m.StartFolder("default") m.ServeBackground() // Ugly hack for testing: reach into the model for the rwfolder and wait // for it to complete the initial scan. The risk is that it otherwise // runs during our modifications and screws up the test. m.fmut.RLock() folder := m.folderRunners["default"].(*rwFolder) m.fmut.RUnlock() <-folder.initialScanCompleted // Get a count of how many files are there now locorigfiles := m.LocalSize("default").Files globorigfiles := m.GlobalSize("default").Files // Delete and rescan specifically these two os.Remove("testdata/testrm") os.Remove("testdata/testrm2") m.ScanFolderSubdirs("default", []string{"testrm", "testrm2"}) // Verify that the number of files decreased by two and the number of // deleted files increases by two loc := m.LocalSize("default") glob := m.GlobalSize("default") if loc.Files != locorigfiles-2 { t.Errorf("Incorrect local accounting; got %d current files, expected %d", loc.Files, locorigfiles-2) } if glob.Files != globorigfiles-2 { t.Errorf("Incorrect global accounting; got %d current files, expected %d", glob.Files, globorigfiles-2) } if loc.Deleted != 2 { t.Errorf("Incorrect local accounting; got %d deleted files, expected 2", loc.Deleted) } if glob.Deleted != 2 { t.Errorf("Incorrect global accounting; got %d deleted files, expected 2", glob.Deleted) } }
func setUpModel(file protocol.FileInfo) *Model { db := db.OpenMemory() model := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) model.AddFolder(defaultFolderConfig) // Update index model.updateLocals("default", []protocol.FileInfo{file}) return model }
func TestRequest(t *testing.T) { db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) // device1 shares default, but device2 doesn't m.AddFolder(defaultFolderConfig) m.StartFolder("default") m.ServeBackground() defer m.Stop() m.ScanFolder("default") bs := make([]byte, protocol.BlockSize) // Existing, shared file bs = bs[:6] err := m.Request(device1, "default", "foo", 0, nil, false, bs) if err != nil { t.Error(err) } if !bytes.Equal(bs, []byte("foobar")) { t.Errorf("Incorrect data from request: %q", string(bs)) } // Existing, nonshared file err = m.Request(device2, "default", "foo", 0, nil, false, bs) if err == nil { t.Error("Unexpected nil error on insecure file read") } // Nonexistent file err = m.Request(device1, "default", "nonexistent", 0, nil, false, bs) if err == nil { t.Error("Unexpected nil error on insecure file read") } // Shared folder, but disallowed file name err = m.Request(device1, "default", "../walk.go", 0, nil, false, bs) if err == nil { t.Error("Unexpected nil error on insecure file read") } // Negative offset err = m.Request(device1, "default", "foo", -4, nil, false, bs[:0]) if err == nil { t.Error("Unexpected nil error on insecure file read") } // Larger block than available bs = bs[:42] err = m.Request(device1, "default", "foo", 0, nil, false, bs) if err == nil { t.Error("Unexpected nil error on insecure file read") } }
func TestHandleFileWithTemp(t *testing.T) { // After diff between required and existing we should: // Copy: 2, 5, 8 // Pull: 1, 3, 4, 6, 7 // After dropping out blocks already on the temp file we should: // Copy: 5, 8 // Pull: 1, 6 // Create existing file existingFile := protocol.FileInfo{ Name: "file", Flags: 0, Modified: 0, Blocks: []protocol.BlockInfo{ blocks[0], blocks[2], blocks[0], blocks[0], blocks[5], blocks[0], blocks[0], blocks[8], }, } // Create target file requiredFile := existingFile requiredFile.Blocks = blocks[1:] db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) m.AddFolder(defaultFolderConfig) // Update index m.updateLocals("default", []protocol.FileInfo{existingFile}) p := rwFolder{ folder: "default", dir: "testdata", model: m, errors: make(map[string]string), errorsMut: sync.NewMutex(), } copyChan := make(chan copyBlocksState, 1) p.handleFile(requiredFile, copyChan, nil) // Receive the results toCopy := <-copyChan if len(toCopy.blocks) != 4 { t.Errorf("Unexpected count of copy blocks: %d != 4", len(toCopy.blocks)) } for i, eq := range []int{1, 5, 6, 8} { if string(toCopy.blocks[i].Hash) != string(blocks[eq].Hash) { t.Errorf("Block mismatch: %s != %s", toCopy.blocks[i].String(), blocks[eq].String()) } } }
func TestScanNoDatabaseWrite(t *testing.T) { // When scanning, nothing should be committed to database unless // something actually changed. db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) m.AddFolder(defaultFolderConfig) m.StartFolder("default") m.ServeBackground() // Start with no ignores, and restore the previous state when the test completes curIgn, _, err := m.GetIgnores("default") if err != nil { t.Fatal(err) } defer m.SetIgnores("default", curIgn) m.SetIgnores("default", nil) // Scan the folder twice. The second scan should be a no-op database wise m.ScanFolder("default") c0 := db.Committed() m.ScanFolder("default") c1 := db.Committed() if c1 != c0 { t.Errorf("scan should not commit data when nothing changed but %d != %d", c1, c0) } // Ignore a file we know exists. It'll be updated in the database. m.SetIgnores("default", []string{"foo"}) m.ScanFolder("default") c2 := db.Committed() if c2 <= c1 { t.Errorf("scan should commit data when something got ignored but %d <= %d", c2, c1) } // Scan again. Nothing should happen. m.ScanFolder("default") c3 := db.Committed() if c3 != c2 { t.Errorf("scan should not commit data when nothing changed (with ignores) but %d != %d", c3, c2) } }
// Test that updating a file removes it's old blocks from the blockmap func TestCopierCleanup(t *testing.T) { iterFn := func(folder, file string, index int32) bool { return true } db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) m.AddFolder(defaultFolderConfig) // Create a file file := protocol.FileInfo{ Name: "test", Flags: 0, Modified: 0, Blocks: []protocol.BlockInfo{blocks[0]}, } // Add file to index m.updateLocals("default", []protocol.FileInfo{file}) if !m.finder.Iterate(folders, blocks[0].Hash, iterFn) { t.Error("Expected block not found") } file.Blocks = []protocol.BlockInfo{blocks[1]} file.Version = file.Version.Update(protocol.LocalDeviceID.Short()) // Update index (removing old blocks) m.updateLocals("default", []protocol.FileInfo{file}) if m.finder.Iterate(folders, blocks[0].Hash, iterFn) { t.Error("Unexpected block found") } if !m.finder.Iterate(folders, blocks[1].Hash, iterFn) { t.Error("Expected block not found") } file.Blocks = []protocol.BlockInfo{blocks[0]} file.Version = file.Version.Update(protocol.LocalDeviceID.Short()) // Update index (removing old blocks) m.updateLocals("default", []protocol.FileInfo{file}) if !m.finder.Iterate(folders, blocks[0].Hash, iterFn) { t.Error("Unexpected block found") } if m.finder.Iterate(folders, blocks[1].Hash, iterFn) { t.Error("Expected block not found") } }
func TestListDropFolder(t *testing.T) { ldb := db.OpenMemory() s0 := db.NewFileSet("test0", ldb) local1 := []protocol.FileInfo{ {Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, } s0.Replace(protocol.LocalDeviceID, local1) s1 := db.NewFileSet("test1", ldb) local2 := []protocol.FileInfo{ {Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}}, {Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}}, {Name: "f", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}}, } s1.Replace(remoteDevice0, local2) // Check that we have both folders and their data is in the global list expectedFolderList := []string{"test0", "test1"} actualFolderList := ldb.ListFolders() if diff, equal := messagediff.PrettyDiff(expectedFolderList, actualFolderList); !equal { t.Fatalf("FolderList mismatch. Diff:\n%s", diff) } if l := len(globalList(s0)); l != 3 { t.Errorf("Incorrect global length %d != 3 for s0", l) } if l := len(globalList(s1)); l != 3 { t.Errorf("Incorrect global length %d != 3 for s1", l) } // Drop one of them and check that it's gone. db.DropFolder(ldb, "test1") expectedFolderList = []string{"test0"} actualFolderList = ldb.ListFolders() if diff, equal := messagediff.PrettyDiff(expectedFolderList, actualFolderList); !equal { t.Fatalf("FolderList mismatch. Diff:\n%s", diff) } if l := len(globalList(s0)); l != 3 { t.Errorf("Incorrect global length %d != 3 for s0", l) } if l := len(globalList(s1)); l != 0 { t.Errorf("Incorrect global length %d != 0 for s1", l) } }
func benchmarkIndex(b *testing.B, nfiles int) { db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) m.AddFolder(defaultFolderConfig) m.StartFolderRO("default") m.ServeBackground() files := genFiles(nfiles) m.Index(device1, "default", files, 0, nil) b.ResetTimer() for i := 0; i < b.N; i++ { m.Index(device1, "default", files, 0, nil) } b.ReportAllocs() }
func TestIssue2782(t *testing.T) { // CheckFolderHealth should accept a symlinked folder, when using tilde-expanded path. if runtime.GOOS == "windows" { t.Skip("not reliable on Windows") return } home := os.Getenv("HOME") if home == "" { t.Skip("no home") } // Create the test env. Needs to be based on $HOME as tilde expansion is // part of the issue. Skip the test if any of this fails, as we are a // bit outside of our stated domain here... testName := ".syncthing-test." + srand.String(16) testDir := filepath.Join(home, testName) if err := os.RemoveAll(testDir); err != nil { t.Skip(err) } if err := osutil.MkdirAll(testDir+"/syncdir", 0755); err != nil { t.Skip(err) } if err := ioutil.WriteFile(testDir+"/syncdir/file", []byte("hello, world\n"), 0644); err != nil { t.Skip(err) } if err := os.Symlink("syncdir", testDir+"/synclink"); err != nil { t.Skip(err) } defer os.RemoveAll(testDir) db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) m.AddFolder(config.NewFolderConfiguration("default", "~/"+testName+"/synclink/")) m.StartFolder("default") m.ServeBackground() defer m.Stop() if err := m.ScanFolder("default"); err != nil { t.Error("scan error:", err) } if err := m.CheckFolderHealth("default"); err != nil { t.Error("health check error:", err) } }
func benchmarkTree(b *testing.B, n1, n2 int) { db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) m.AddFolder(defaultFolderConfig) m.ServeBackground() m.ScanFolder("default") files := genDeepFiles(n1, n2) m.Index(device1, "default", files, 0, nil) b.ResetTimer() for i := 0; i < b.N; i++ { m.GlobalDirectoryTree("default", "", -1, false) } b.ReportAllocs() }
func BenchmarkRequest(b *testing.B) { db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) m.AddFolder(defaultFolderConfig) m.ServeBackground() m.ScanFolder("default") const n = 1000 files := make([]protocol.FileInfo, n) t := time.Now().Unix() for i := 0; i < n; i++ { files[i] = protocol.FileInfo{ Name: fmt.Sprintf("file%d", i), Modified: t, Blocks: []protocol.BlockInfo{{Offset: 0, Size: 100, Hash: []byte("some hash bytes")}}, } } fc := &FakeConnection{ id: device1, requestData: []byte("some data to return"), } m.AddConnection(connections.Connection{ IntermediateConnection: connections.IntermediateConnection{ Conn: tls.Client(&fakeConn{}, nil), Type: "foo", Priority: 10, }, Connection: fc, }, protocol.HelloResult{}) m.Index(device1, "default", files) b.ResetTimer() for i := 0; i < b.N; i++ { data, err := m.requestGlobal(device1, "default", files[i%n].Name, 0, 32, nil, false) if err != nil { b.Error(err) } if data == nil { b.Error("nil data") } } }
func setupModelWithConnection() (*Model, *fakeConnection) { cfg := defaultConfig.RawCopy() cfg.Folders[0] = config.NewFolderConfiguration("default", "_tmpfolder") cfg.Folders[0].PullerSleepS = 1 cfg.Folders[0].Devices = []config.FolderDeviceConfiguration{ {DeviceID: device1}, {DeviceID: device2}, } w := config.Wrap("/tmp/cfg", cfg) db := db.OpenMemory() m := NewModel(w, device1, "device", "syncthing", "dev", db, nil) m.AddFolder(cfg.Folders[0]) m.ServeBackground() m.StartFolder("default") fc := addFakeConn(m, device2) fc.folder = "default" return m, fc }
func BenchmarkRequest(b *testing.B) { db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) m.AddFolder(defaultFolderConfig) m.ServeBackground() m.ScanFolder("default") const n = 1000 files := make([]protocol.FileInfo, n) t := time.Now().Unix() for i := 0; i < n; i++ { files[i] = protocol.FileInfo{ Name: fmt.Sprintf("file%d", i), Modified: t, Blocks: []protocol.BlockInfo{{0, 100, []byte("some hash bytes")}}, } } fc := &FakeConnection{ id: device1, requestData: []byte("some data to return"), } m.AddConnection(Connection{ &net.TCPConn{}, fc, ConnectionTypeDirectAccept, }, protocol.HelloMessage{}) m.Index(device1, "default", files, 0, nil) b.ResetTimer() for i := 0; i < b.N; i++ { data, err := m.requestGlobal(device1, "default", files[i%n].Name, 0, 32, nil, false) if err != nil { b.Error(err) } if data == nil { b.Error("nil data") } } }
func TestIndexesForUnknownDevicesDropped(t *testing.T) { dbi := db.OpenMemory() files := db.NewFileSet("default", dbi) files.Replace(device1, genFiles(1)) files.Replace(device2, genFiles(1)) if len(files.ListDevices()) != 2 { t.Error("expected two devices") } m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", dbi, nil) m.AddFolder(defaultFolderConfig) m.StartFolder("default") // Remote sequence is cached, hence need to recreated. files = db.NewFileSet("default", dbi) if len(files.ListDevices()) != 1 { t.Error("Expected one device") } }
func TestRefuseUnknownBits(t *testing.T) { db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) m.AddFolder(defaultFolderConfig) m.ServeBackground() m.ScanFolder("default") m.Index(device1, "default", []protocol.FileInfo{ { Name: "invalid1", Flags: (protocol.FlagsAll + 1) &^ protocol.FlagInvalid, }, { Name: "invalid2", Flags: (protocol.FlagsAll + 2) &^ protocol.FlagInvalid, }, { Name: "invalid3", Flags: (1 << 31) &^ protocol.FlagInvalid, }, { Name: "valid", Flags: protocol.FlagsAll &^ (protocol.FlagInvalid | protocol.FlagSymlink), }, }, 0, nil) for _, name := range []string{"invalid1", "invalid2", "invalid3"} { f, ok := m.CurrentGlobalFile("default", name) if ok || f.Name == name { t.Error("Invalid file found or name match") } } f, ok := m.CurrentGlobalFile("default", "valid") if !ok || f.Name != "valid" { t.Error("Valid file not found or name mismatch", ok, f) } }
func TestNeed(t *testing.T) { ldb := db.OpenMemory() m := db.NewFileSet("test", ldb) local := []protocol.FileInfo{ {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, {Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, {Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, } remote := []protocol.FileInfo{ {Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}}, {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}}, {Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, } shouldNeed := []protocol.FileInfo{ {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}}, {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}}, {Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, } m.Replace(protocol.LocalDeviceID, local) m.Replace(remoteDevice0, remote) need := needList(m, protocol.LocalDeviceID) sort.Sort(fileList(need)) sort.Sort(fileList(shouldNeed)) if fmt.Sprint(need) != fmt.Sprint(shouldNeed) { t.Errorf("Need incorrect;\n%v !=\n%v", need, shouldNeed) } }
func TestGlobalReset(t *testing.T) { ldb := db.OpenMemory() m := db.NewFileSet("test", ldb) local := []protocol.FileInfo{ {Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, {Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, } remote := []protocol.FileInfo{ {Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, {Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}}, {Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}}, {Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}}, } m.Replace(protocol.LocalDeviceID, local) g := globalList(m) sort.Sort(fileList(g)) if fmt.Sprint(g) != fmt.Sprint(local) { t.Errorf("Global incorrect;\n%v !=\n%v", g, local) } m.Replace(remoteDevice0, remote) m.Replace(remoteDevice0, nil) g = globalList(m) sort.Sort(fileList(g)) if fmt.Sprint(g) != fmt.Sprint(local) { t.Errorf("Global incorrect;\n%v !=\n%v", g, local) } }
func TestInvalidAvailability(t *testing.T) { ldb := db.OpenMemory() s := db.NewFileSet("test", ldb) remote0Have := fileList{ protocol.FileInfo{Name: "both", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "r1only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), Invalid: true}, protocol.FileInfo{Name: "r0only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)}, protocol.FileInfo{Name: "none", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), Invalid: true}, } remote1Have := fileList{ protocol.FileInfo{Name: "both", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "r1only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)}, protocol.FileInfo{Name: "r0only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), Invalid: true}, protocol.FileInfo{Name: "none", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), Invalid: true}, } s.Replace(remoteDevice0, remote0Have) s.Replace(remoteDevice1, remote1Have) if av := s.Availability("both"); len(av) != 2 { t.Error("Incorrect availability for 'both':", av) } if av := s.Availability("r0only"); len(av) != 1 || av[0] != remoteDevice0 { t.Error("Incorrect availability for 'r0only':", av) } if av := s.Availability("r1only"); len(av) != 1 || av[0] != remoteDevice1 { t.Error("Incorrect availability for 'r1only':", av) } if av := s.Availability("none"); len(av) != 0 { t.Error("Incorrect availability for 'none':", av) } }
func TestNeedWithInvalid(t *testing.T) { ldb := db.OpenMemory() s := db.NewFileSet("test", ldb) localHave := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, } remote0Have := fileList{ protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), Invalid: true}, protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)}, } remote1Have := fileList{ protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)}, protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), Invalid: true}, protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), Invalid: true}, } expectedNeed := fileList{ protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)}, protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)}, } s.Replace(protocol.LocalDeviceID, localHave) s.Replace(remoteDevice0, remote0Have) s.Replace(remoteDevice1, remote1Have) need := fileList(needList(s, protocol.LocalDeviceID)) sort.Sort(need) if fmt.Sprint(need) != fmt.Sprint(expectedNeed) { t.Errorf("Need incorrect;\n A: %v !=\n E: %v", need, expectedNeed) } }
func BenchmarkRequest(b *testing.B) { db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) m.AddFolder(defaultFolderConfig) m.ServeBackground() defer m.Stop() m.ScanFolder("default") const n = 1000 files := genFiles(n) fc := &FakeConnection{ id: device1, requestData: []byte("some data to return"), } m.AddConnection(connections.Connection{ IntermediateConnection: connections.IntermediateConnection{ Conn: tls.Client(&fakeConn{}, nil), Type: "foo", Priority: 10, }, Connection: fc, }, protocol.HelloResult{}) m.Index(device1, "default", files) b.ResetTimer() for i := 0; i < b.N; i++ { data, err := m.requestGlobal(device1, "default", files[i%n].Name, 0, 32, nil, false) if err != nil { b.Error(err) } if data == nil { b.Error("nil data") } } }
func TestIgnoreDelete(t *testing.T) { db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) // This folder should ignore external deletes cfg := defaultFolderConfig cfg.IgnoreDelete = true m.AddFolder(cfg) m.ServeBackground() m.StartFolderRW("default") m.ScanFolder("default") // Get a currently existing file f, ok := m.CurrentGlobalFile("default", "foo") if !ok { t.Fatal("foo should exist") } // Mark it for deletion f.Flags = protocol.FlagDeleted f.Version = f.Version.Update(142) // arbitrary short remote ID f.Blocks = nil // Send the index m.Index(device1, "default", []protocol.FileInfo{f}, 0, nil) // Make sure we ignored it f, ok = m.CurrentGlobalFile("default", "foo") if !ok { t.Fatal("foo should exist") } if f.IsDeleted() { t.Fatal("foo should not be marked for deletion") } }
func TestGlobalNeedWithInvalid(t *testing.T) { ldb := db.OpenMemory() s := db.NewFileSet("test1", ldb) rem0 := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Invalid: true}, protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, } s.Replace(remoteDevice0, rem0) rem1 := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Invalid: true}, } s.Replace(remoteDevice1, rem1) total := fileList{ // There's a valid copy of each file, so it should be merged protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)}, } need := fileList(needList(s, protocol.LocalDeviceID)) if fmt.Sprint(need) != fmt.Sprint(total) { t.Errorf("Need incorrect;\n A: %v !=\n E: %v", need, total) } global := fileList(globalList(s)) if fmt.Sprint(global) != fmt.Sprint(total) { t.Errorf("Global incorrect;\n A: %v !=\n E: %v", global, total) } }
func TestDeregisterOnFailInPull(t *testing.T) { file := setUpFile("filex", []int{0, 2, 0, 0, 5, 0, 0, 8}) defer os.Remove("testdata/" + defTempNamer.TempName("filex")) db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) m.AddFolder(defaultFolderConfig) f := setUpRwFolder(m) // queue.Done should be called by the finisher routine f.queue.Push("filex", 0, 0) f.queue.Pop() if f.queue.lenProgress() != 1 { t.Fatal("Expected file in progress") } copyChan := make(chan copyBlocksState) pullChan := make(chan pullBlockState) finisherBufferChan := make(chan *sharedPullerState) finisherChan := make(chan *sharedPullerState) go f.copierRoutine(copyChan, pullChan, finisherBufferChan) go f.pullerRoutine(pullChan, finisherBufferChan) go f.finisherRoutine(finisherChan) f.handleFile(file, copyChan, finisherChan) // Receive at finisher, we should error out as puller has nowhere to pull // from. select { case state := <-finisherBufferChan: // At this point the file should still be registered with both the job // queue, and the progress emitter. Verify this. if f.model.progressEmitter.lenRegistry() != 1 || f.queue.lenProgress() != 1 || f.queue.lenQueued() != 0 { t.Fatal("Could not find file") } // Pass the file down the real finisher, and give it time to consume finisherChan <- state time.Sleep(100 * time.Millisecond) state.mut.Lock() stateFd := state.fd state.mut.Unlock() if stateFd != nil { t.Fatal("File not closed?") } if f.model.progressEmitter.lenRegistry() != 0 || f.queue.lenProgress() != 0 || f.queue.lenQueued() != 0 { t.Fatal("Still registered", f.model.progressEmitter.lenRegistry(), f.queue.lenProgress(), f.queue.lenQueued()) } // Doing it again should have no effect finisherChan <- state time.Sleep(100 * time.Millisecond) if f.model.progressEmitter.lenRegistry() != 0 || f.queue.lenProgress() != 0 || f.queue.lenQueued() != 0 { t.Fatal("Still registered", f.model.progressEmitter.lenRegistry(), f.queue.lenProgress(), f.queue.lenQueued()) } case <-time.After(time.Second): t.Fatal("Didn't get anything to the finisher") } }
func TestDeregisterOnFailInCopy(t *testing.T) { file := setUpFile("filex", []int{0, 2, 0, 0, 5, 0, 0, 8}) defer os.Remove("testdata/" + defTempNamer.TempName("filex")) db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) m.AddFolder(defaultFolderConfig) emitter := NewProgressEmitter(defaultConfig) go emitter.Serve() p := rwFolder{ folder: "default", dir: "testdata", model: m, queue: newJobQueue(), progressEmitter: emitter, errors: make(map[string]string), errorsMut: sync.NewMutex(), } // queue.Done should be called by the finisher routine p.queue.Push("filex", 0, 0) p.queue.Pop() if p.queue.lenProgress() != 1 { t.Fatal("Expected file in progress") } copyChan := make(chan copyBlocksState) pullChan := make(chan pullBlockState) finisherBufferChan := make(chan *sharedPullerState) finisherChan := make(chan *sharedPullerState) go p.copierRoutine(copyChan, pullChan, finisherBufferChan) go p.finisherRoutine(finisherChan) p.handleFile(file, copyChan, finisherChan) // Receive a block at puller, to indicate that at least a single copier // loop has been performed. toPull := <-pullChan // Wait until copier is trying to pass something down to the puller again time.Sleep(100 * time.Millisecond) // Close the file toPull.sharedPullerState.fail("test", os.ErrNotExist) // Unblock copier <-pullChan select { case state := <-finisherBufferChan: // At this point the file should still be registered with both the job // queue, and the progress emitter. Verify this. if p.progressEmitter.lenRegistry() != 1 || p.queue.lenProgress() != 1 || p.queue.lenQueued() != 0 { t.Fatal("Could not find file") } // Pass the file down the real finisher, and give it time to consume finisherChan <- state time.Sleep(100 * time.Millisecond) state.mut.Lock() stateFd := state.fd state.mut.Unlock() if stateFd != nil { t.Fatal("File not closed?") } if p.progressEmitter.lenRegistry() != 0 || p.queue.lenProgress() != 0 || p.queue.lenQueued() != 0 { t.Fatal("Still registered", p.progressEmitter.lenRegistry(), p.queue.lenProgress(), p.queue.lenQueued()) } // Doing it again should have no effect finisherChan <- state time.Sleep(100 * time.Millisecond) if p.progressEmitter.lenRegistry() != 0 || p.queue.lenProgress() != 0 || p.queue.lenQueued() != 0 { t.Fatal("Still registered", p.progressEmitter.lenRegistry(), p.queue.lenProgress(), p.queue.lenQueued()) } case <-time.After(time.Second): t.Fatal("Didn't get anything to the finisher") } }
func TestGlobalSet(t *testing.T) { ldb := db.OpenMemory() m := db.NewFileSet("test", ldb) local0 := fileList{ protocol.FileInfo{Name: "a", Sequence: 1, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, protocol.FileInfo{Name: "b", Sequence: 2, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "c", Sequence: 3, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(3)}, protocol.FileInfo{Name: "d", Sequence: 4, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(4)}, protocol.FileInfo{Name: "z", Sequence: 5, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(8)}, } local1 := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(3)}, protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(4)}, protocol.FileInfo{Name: "z", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Deleted: true}, } localTot := fileList{ local0[0], local0[1], local0[2], local0[3], protocol.FileInfo{Name: "z", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Deleted: true}, } remote0 := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)}, protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(5)}, } remote1 := fileList{ protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(6)}, protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(7)}, } remoteTot := fileList{ remote0[0], remote1[0], remote0[2], remote1[1], } expectedGlobal := fileList{ remote0[0], // a remote1[0], // b remote0[2], // c localTot[3], // d remote1[1], // e localTot[4], // z } expectedLocalNeed := fileList{ remote1[0], remote0[2], remote1[1], } expectedRemoteNeed := fileList{ local0[3], } m.Replace(protocol.LocalDeviceID, local0) m.Replace(protocol.LocalDeviceID, local1) m.Replace(remoteDevice0, remote0) m.Update(remoteDevice0, remote1) g := fileList(globalList(m)) sort.Sort(g) if fmt.Sprint(g) != fmt.Sprint(expectedGlobal) { t.Errorf("Global incorrect;\n A: %v !=\n E: %v", g, expectedGlobal) } globalFiles, globalDeleted, globalBytes := 0, 0, int64(0) for _, f := range g { if f.IsInvalid() { continue } if f.IsDeleted() { globalDeleted++ } else { globalFiles++ } globalBytes += f.FileSize() } gsFiles, gsDeleted, gsBytes := m.GlobalSize() if gsFiles != globalFiles { t.Errorf("Incorrect GlobalSize files; %d != %d", gsFiles, globalFiles) } if gsDeleted != globalDeleted { t.Errorf("Incorrect GlobalSize deleted; %d != %d", gsDeleted, globalDeleted) } if gsBytes != globalBytes { t.Errorf("Incorrect GlobalSize bytes; %d != %d", gsBytes, globalBytes) } h := fileList(haveList(m, protocol.LocalDeviceID)) sort.Sort(h) if fmt.Sprint(h) != fmt.Sprint(localTot) { t.Errorf("Have incorrect;\n A: %v !=\n E: %v", h, localTot) } haveFiles, haveDeleted, haveBytes := 0, 0, int64(0) for _, f := range h { if f.IsInvalid() { continue } if f.IsDeleted() { haveDeleted++ } else { haveFiles++ } haveBytes += f.FileSize() } lsFiles, lsDeleted, lsBytes := m.LocalSize() if lsFiles != haveFiles { t.Errorf("Incorrect LocalSize files; %d != %d", lsFiles, haveFiles) } if lsDeleted != haveDeleted { t.Errorf("Incorrect LocalSize deleted; %d != %d", lsDeleted, haveDeleted) } if lsBytes != haveBytes { t.Errorf("Incorrect LocalSize bytes; %d != %d", lsBytes, haveBytes) } h = fileList(haveList(m, remoteDevice0)) sort.Sort(h) if fmt.Sprint(h) != fmt.Sprint(remoteTot) { t.Errorf("Have incorrect;\n A: %v !=\n E: %v", h, remoteTot) } n := fileList(needList(m, protocol.LocalDeviceID)) sort.Sort(n) if fmt.Sprint(n) != fmt.Sprint(expectedLocalNeed) { t.Errorf("Need incorrect;\n A: %v !=\n E: %v", n, expectedLocalNeed) } n = fileList(needList(m, remoteDevice0)) sort.Sort(n) if fmt.Sprint(n) != fmt.Sprint(expectedRemoteNeed) { t.Errorf("Need incorrect;\n A: %v !=\n E: %v", n, expectedRemoteNeed) } f, ok := m.Get(protocol.LocalDeviceID, "b") if !ok { t.Error("Unexpectedly not OK") } if fmt.Sprint(f) != fmt.Sprint(localTot[1]) { t.Errorf("Get incorrect;\n A: %v !=\n E: %v", f, localTot[1]) } f, ok = m.Get(remoteDevice0, "b") if !ok { t.Error("Unexpectedly not OK") } if fmt.Sprint(f) != fmt.Sprint(remote1[0]) { t.Errorf("Get incorrect;\n A: %v !=\n E: %v", f, remote1[0]) } f, ok = m.GetGlobal("b") if !ok { t.Error("Unexpectedly not OK") } if fmt.Sprint(f) != fmt.Sprint(remote1[0]) { t.Errorf("GetGlobal incorrect;\n A: %v !=\n E: %v", f, remote1[0]) } f, ok = m.Get(protocol.LocalDeviceID, "zz") if ok { t.Error("Unexpectedly OK") } if f.Name != "" { t.Errorf("Get incorrect;\n A: %v !=\n E: %v", f, protocol.FileInfo{}) } f, ok = m.GetGlobal("zz") if ok { t.Error("Unexpectedly OK") } if f.Name != "" { t.Errorf("GetGlobal incorrect;\n A: %v !=\n E: %v", f, protocol.FileInfo{}) } av := []protocol.DeviceID{protocol.LocalDeviceID, remoteDevice0} a := m.Availability("a") if !(len(a) == 2 && (a[0] == av[0] && a[1] == av[1] || a[0] == av[1] && a[1] == av[0])) { t.Errorf("Availability incorrect;\n A: %v !=\n E: %v", a, av) } a = m.Availability("b") if len(a) != 1 || a[0] != remoteDevice0 { t.Errorf("Availability incorrect;\n A: %v !=\n E: %v", a, remoteDevice0) } a = m.Availability("d") if len(a) != 1 || a[0] != protocol.LocalDeviceID { t.Errorf("Availability incorrect;\n A: %v !=\n E: %v", a, protocol.LocalDeviceID) } }