func TestLocalVersion(t *testing.T) { ldb := db.OpenMemory() m := db.NewFileSet("test", ldb) local1 := []protocol.FileInfo{ {Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}}, {Name: "b", Version: protocol.Vector{{ID: myID, Value: 1000}}}, {Name: "c", Version: protocol.Vector{{ID: myID, Value: 1000}}}, {Name: "d", Version: protocol.Vector{{ID: myID, Value: 1000}}}, } local2 := []protocol.FileInfo{ local1[0], // [1] deleted local1[2], {Name: "d", Version: protocol.Vector{{ID: myID, Value: 1002}}}, {Name: "e", Version: protocol.Vector{{ID: myID, Value: 1000}}}, } m.Replace(protocol.LocalDeviceID, local1) c0 := m.LocalVersion(protocol.LocalDeviceID) m.Replace(protocol.LocalDeviceID, local2) c1 := m.LocalVersion(protocol.LocalDeviceID) if !(c1 > c0) { t.Fatal("Local version number should have incremented") } }
func TestUpdateToInvalid(t *testing.T) { ldb := db.OpenMemory() s := db.NewFileSet("test", ldb) localHave := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(1)}, protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1001}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(5), Flags: protocol.FlagInvalid}, protocol.FileInfo{Name: "d", Version: protocol.Vector{{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{{ID: myID, Value: 1001}}, Flags: protocol.FlagInvalid} 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 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{{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 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.StartFolderRO("default") m.ServeBackground() m.ScanFolder("default") bs := make([]byte, protocol.BlockSize) // Existing, shared file bs = bs[:6] err := m.Request(device1, "default", "foo", 0, nil, 0, nil, bs) if err != nil { t.Error(err) } if bytes.Compare(bs, []byte("foobar")) != 0 { t.Errorf("Incorrect data from request: %q", string(bs)) } // Existing, nonshared file err = m.Request(device2, "default", "foo", 0, nil, 0, nil, bs) if err == nil { t.Error("Unexpected nil error on insecure file read") } // Nonexistent file err = m.Request(device1, "default", "nonexistent", 0, nil, 0, nil, 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, 0, nil, bs) if err == nil { t.Error("Unexpected nil error on insecure file read") } // Negative offset err = m.Request(device1, "default", "foo", -4, nil, 0, nil, 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, 0, nil, bs) if err == nil { t.Error("Unexpected nil error on insecure file read") } }
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 TestListDropFolder(t *testing.T) { ldb := db.OpenMemory() s0 := db.NewFileSet("test0", ldb) local1 := []protocol.FileInfo{ {Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}}, {Name: "b", Version: protocol.Vector{{ID: myID, Value: 1000}}}, {Name: "c", Version: protocol.Vector{{ID: myID, Value: 1000}}}, } s0.Replace(protocol.LocalDeviceID, local1) s1 := db.NewFileSet("test1", ldb) local2 := []protocol.FileInfo{ {Name: "d", Version: protocol.Vector{{ID: myID, Value: 1002}}}, {Name: "e", Version: protocol.Vector{{ID: myID, Value: 1002}}}, {Name: "f", Version: protocol.Vector{{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"} if actualFolderList := ldb.ListFolders(); !reflect.DeepEqual(actualFolderList, expectedFolderList) { t.Fatalf("FolderList mismatch\nE: %v\nA: %v", expectedFolderList, actualFolderList) } 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"} if actualFolderList := ldb.ListFolders(); !reflect.DeepEqual(actualFolderList, expectedFolderList) { t.Fatalf("FolderList mismatch\nE: %v\nA: %v", expectedFolderList, actualFolderList) } 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 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{{0, 100, []byte("some hash bytes")}}, } } fc := FakeConnection{ id: device1, requestData: []byte("some data to return"), } m.AddConnection(Connection{ &net.TCPConn{}, fc, ConnectionTypeDirectAccept, }) 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, 0, nil) if err != nil { b.Error(err) } if data == nil { b.Error("nil data") } } }
func TestGlobalReset(t *testing.T) { ldb := db.OpenMemory() m := db.NewFileSet("test", ldb) local := []protocol.FileInfo{ {Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}}, {Name: "b", Version: protocol.Vector{{ID: myID, Value: 1000}}}, {Name: "c", Version: protocol.Vector{{ID: myID, Value: 1000}}}, {Name: "d", Version: protocol.Vector{{ID: myID, Value: 1000}}}, } remote := []protocol.FileInfo{ {Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}}, {Name: "b", Version: protocol.Vector{{ID: myID, Value: 1001}}}, {Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}}, {Name: "e", Version: protocol.Vector{{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 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 TestInvalidAvailability(t *testing.T) { ldb := db.OpenMemory() s := db.NewFileSet("test", ldb) remote0Have := fileList{ protocol.FileInfo{Name: "both", Version: protocol.Vector{{ID: myID, Value: 1001}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "r1only", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(5), Flags: protocol.FlagInvalid}, protocol.FileInfo{Name: "r0only", Version: protocol.Vector{{ID: myID, Value: 1003}}, Blocks: genBlocks(7)}, protocol.FileInfo{Name: "none", Version: protocol.Vector{{ID: myID, Value: 1004}}, Blocks: genBlocks(5), Flags: protocol.FlagInvalid}, } remote1Have := fileList{ protocol.FileInfo{Name: "both", Version: protocol.Vector{{ID: myID, Value: 1001}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "r1only", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(7)}, protocol.FileInfo{Name: "r0only", Version: protocol.Vector{{ID: myID, Value: 1003}}, Blocks: genBlocks(5), Flags: protocol.FlagInvalid}, protocol.FileInfo{Name: "none", Version: protocol.Vector{{ID: myID, Value: 1004}}, Blocks: genBlocks(5), Flags: protocol.FlagInvalid}, } 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 TestNeed(t *testing.T) { ldb := db.OpenMemory() m := db.NewFileSet("test", ldb) local := []protocol.FileInfo{ {Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}}, {Name: "b", Version: protocol.Vector{{ID: myID, Value: 1000}}}, {Name: "c", Version: protocol.Vector{{ID: myID, Value: 1000}}}, {Name: "d", Version: protocol.Vector{{ID: myID, Value: 1000}}}, } remote := []protocol.FileInfo{ {Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}}, {Name: "b", Version: protocol.Vector{{ID: myID, Value: 1001}}}, {Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}}, {Name: "e", Version: protocol.Vector{{ID: myID, Value: 1000}}}, } shouldNeed := []protocol.FileInfo{ {Name: "b", Version: protocol.Vector{{ID: myID, Value: 1001}}}, {Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}}, {Name: "e", Version: protocol.Vector{{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 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 TestNeedWithInvalid(t *testing.T) { ldb := db.OpenMemory() s := db.NewFileSet("test", ldb) localHave := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(1)}, } remote0Have := fileList{ protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1001}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(5), Flags: protocol.FlagInvalid}, protocol.FileInfo{Name: "d", Version: protocol.Vector{{ID: myID, Value: 1003}}, Blocks: genBlocks(7)}, } remote1Have := fileList{ protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(7)}, protocol.FileInfo{Name: "d", Version: protocol.Vector{{ID: myID, Value: 1003}}, Blocks: genBlocks(5), Flags: protocol.FlagInvalid}, protocol.FileInfo{Name: "e", Version: protocol.Vector{{ID: myID, Value: 1004}}, Blocks: genBlocks(5), Flags: protocol.FlagInvalid}, } expectedNeed := fileList{ protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1001}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(7)}, protocol.FileInfo{Name: "d", Version: protocol.Vector{{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 TestGlobalNeedWithInvalid(t *testing.T) { ldb := db.OpenMemory() s := db.NewFileSet("test1", ldb) rem0 := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(4)}, protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1002}}, Flags: protocol.FlagInvalid}, protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(4)}, } s.Replace(remoteDevice0, rem0) rem1 := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(4)}, protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(4)}, protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}, Flags: protocol.FlagInvalid}, } 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{{ID: myID, Value: 1002}}, Blocks: genBlocks(4)}, protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(4)}, protocol.FileInfo{Name: "c", Version: protocol.Vector{{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 TestRWScanRecovery(t *testing.T) { ldb := db.OpenMemory() set := db.NewFileSet("default", ldb) set.Update(protocol.LocalDeviceID, []protocol.FileInfo{ {Name: "dummyfile"}, }) fcfg := config.FolderConfiguration{ ID: "default", RawPath: "testdata/rwtestfolder", RescanIntervalS: 1, } cfg := config.Wrap("/tmp/test", config.Configuration{ Folders: []config.FolderConfiguration{fcfg}, Devices: []config.DeviceConfiguration{ { DeviceID: device1, }, }, }) os.RemoveAll(fcfg.RawPath) m := NewModel(cfg, protocol.LocalDeviceID, "device", "syncthing", "dev", ldb, nil) m.AddFolder(fcfg) m.StartFolderRW("default") m.ServeBackground() waitFor := func(status string) error { timeout := time.Now().Add(2 * time.Second) for { if time.Now().After(timeout) { return fmt.Errorf("Timed out waiting for status: %s, current status: %s", status, m.cfg.Folders()["default"].Invalid) } _, _, err := m.State("default") if err == nil && status == "" { return nil } if err != nil && err.Error() == status { return nil } time.Sleep(10 * time.Millisecond) } } if err := waitFor("folder path missing"); err != nil { t.Error(err) return } os.Mkdir(fcfg.RawPath, 0700) if err := waitFor("folder marker missing"); err != nil { t.Error(err) return } fd, err := os.Create(filepath.Join(fcfg.RawPath, ".stfolder")) if err != nil { t.Error(err) return } fd.Close() if err := waitFor(""); err != nil { t.Error(err) return } os.Remove(filepath.Join(fcfg.RawPath, ".stfolder")) if err := waitFor("folder marker missing"); err != nil { t.Error(err) return } os.Remove(fcfg.RawPath) if err := waitFor("folder path missing"); err != nil { t.Error(err) return } }
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) 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.pullerRoutine(pullChan, finisherBufferChan) go p.finisherRoutine(finisherChan) p.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 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 TestFolderErrors(t *testing.T) { // This test intentionally avoids starting the folders. If they are // started, they will perform an initial scan, which will create missing // folder markers and race with the stuff we do in the test. fcfg := config.FolderConfiguration{ ID: "folder", RawPath: "testdata/testfolder", } cfg := config.Wrap("/tmp/test", config.Configuration{ Folders: []config.FolderConfiguration{fcfg}, }) for _, file := range []string{".stfolder", "testfolder/.stfolder", "testfolder"} { if err := os.Remove("testdata/" + file); err != nil && !os.IsNotExist(err) { t.Fatal(err) } } ldb := db.OpenMemory() // Case 1 - new folder, directory and marker created m := model.NewModel(cfg, protocol.LocalDeviceID, "device", "syncthing", "dev", ldb, nil) m.AddFolder(fcfg) if err := m.CheckFolderHealth("folder"); err != nil { t.Error("Unexpected error", cfg.Folders()["folder"].Invalid) } s, err := os.Stat("testdata/testfolder") if err != nil || !s.IsDir() { t.Error(err) } _, err = os.Stat("testdata/testfolder/.stfolder") if err != nil { t.Error(err) } if err := os.Remove("testdata/testfolder/.stfolder"); err != nil { t.Fatal(err) } if err := os.Remove("testdata/testfolder/"); err != nil { t.Fatal(err) } // Case 2 - new folder, marker created fcfg.RawPath = "testdata/" cfg = config.Wrap("/tmp/test", config.Configuration{ Folders: []config.FolderConfiguration{fcfg}, }) m = model.NewModel(cfg, protocol.LocalDeviceID, "device", "syncthing", "dev", ldb, nil) m.AddFolder(fcfg) if err := m.CheckFolderHealth("folder"); err != nil { t.Error("Unexpected error", cfg.Folders()["folder"].Invalid) } _, err = os.Stat("testdata/.stfolder") if err != nil { t.Error(err) } if err := os.Remove("testdata/.stfolder"); err != nil { t.Fatal(err) } // Case 3 - Folder marker missing set := db.NewFileSet("folder", ldb) set.Update(protocol.LocalDeviceID, []protocol.FileInfo{ {Name: "dummyfile"}, }) m = model.NewModel(cfg, protocol.LocalDeviceID, "device", "syncthing", "dev", ldb, nil) m.AddFolder(fcfg) if err := m.CheckFolderHealth("folder"); err == nil || err.Error() != "folder marker missing" { t.Error("Incorrect error: Folder marker missing !=", m.CheckFolderHealth("folder")) } // Case 3.1 - recover after folder marker missing if err = fcfg.CreateMarker(); err != nil { t.Error(err) } if err := m.CheckFolderHealth("folder"); err != nil { t.Error("Unexpected error", cfg.Folders()["folder"].Invalid) } // Case 4 - Folder path missing if err := os.Remove("testdata/testfolder/.stfolder"); err != nil && !os.IsNotExist(err) { t.Fatal(err) } if err := os.Remove("testdata/testfolder"); err != nil && !os.IsNotExist(err) { t.Fatal(err) } fcfg.RawPath = "testdata/testfolder" cfg = config.Wrap("testdata/subfolder", config.Configuration{ Folders: []config.FolderConfiguration{fcfg}, }) m = model.NewModel(cfg, protocol.LocalDeviceID, "device", "syncthing", "dev", ldb, nil) m.AddFolder(fcfg) if err := m.CheckFolderHealth("folder"); err == nil || err.Error() != "folder path missing" { t.Error("Incorrect error: Folder path missing !=", m.CheckFolderHealth("folder")) } // Case 4.1 - recover after folder path missing if err := os.Mkdir("testdata/testfolder", 0700); err != nil { t.Fatal(err) } if err := m.CheckFolderHealth("folder"); err == nil || err.Error() != "folder marker missing" { t.Error("Incorrect error: Folder marker missing !=", m.CheckFolderHealth("folder")) } // Case 4.2 - recover after missing marker if err = fcfg.CreateMarker(); err != nil { t.Error(err) } if err := m.CheckFolderHealth("folder"); err != nil { t.Error("Unexpected error", cfg.Folders()["folder"].Invalid) } }
func TestGlobalSet(t *testing.T) { ldb := db.OpenMemory() m := db.NewFileSet("test", ldb) local0 := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(1)}, protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(3)}, protocol.FileInfo{Name: "d", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(4)}, protocol.FileInfo{Name: "z", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(8)}, } local1 := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(1)}, protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(3)}, protocol.FileInfo{Name: "d", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(4)}, protocol.FileInfo{Name: "z", Version: protocol.Vector{{ID: myID, Value: 1001}}, Flags: protocol.FlagDeleted}, } localTot := fileList{ local0[0], local0[1], local0[2], local0[3], protocol.FileInfo{Name: "z", Version: protocol.Vector{{ID: myID, Value: 1001}}, Flags: protocol.FlagDeleted}, } remote0 := fileList{ protocol.FileInfo{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(1)}, protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(2)}, protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1001}}, Blocks: genBlocks(5)}, } remote1 := fileList{ protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1001}}, Blocks: genBlocks(6)}, protocol.FileInfo{Name: "e", Version: protocol.Vector{{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.Size() } 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.Size() } 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) } }
func TestGlobalDirectorySelfFixing(t *testing.T) { db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) m.AddFolder(defaultFolderConfig) m.ServeBackground() b := func(isfile bool, path ...string) protocol.FileInfo { flags := uint32(protocol.FlagDirectory) blocks := []protocol.BlockInfo{} if isfile { flags = 0 blocks = []protocol.BlockInfo{{Offset: 0x0, Size: 0xa, Hash: []uint8{0x2f, 0x72, 0xcc, 0x11, 0xa6, 0xfc, 0xd0, 0x27, 0x1e, 0xce, 0xf8, 0xc6, 0x10, 0x56, 0xee, 0x1e, 0xb1, 0x24, 0x3b, 0xe3, 0x80, 0x5b, 0xf9, 0xa9, 0xdf, 0x98, 0xf9, 0x2f, 0x76, 0x36, 0xb0, 0x5c}}} } return protocol.FileInfo{ Name: filepath.Join(path...), Flags: flags, Modified: 0x666, Blocks: blocks, } } filedata := []interface{}{time.Unix(0x666, 0).Format(time.RFC3339), 0xa} testdata := []protocol.FileInfo{ b(true, "another", "directory", "afile"), b(true, "another", "directory", "with", "a", "file"), b(true, "another", "directory", "with", "file"), b(false, "other", "random", "dirx"), b(false, "other", "randomx"), b(false, "some", "directory", "with", "x"), b(true, "some", "directory", "with", "a", "file"), b(false, "this", "is", "a", "deep", "invalid", "directory"), b(true, "xthis", "is", "a", "deep", "invalid", "file"), } expectedResult := map[string]interface{}{ "another": map[string]interface{}{ "directory": map[string]interface{}{ "afile": filedata, "with": map[string]interface{}{ "a": map[string]interface{}{ "file": filedata, }, "file": filedata, }, }, }, "other": map[string]interface{}{ "random": map[string]interface{}{ "dirx": map[string]interface{}{}, }, "randomx": map[string]interface{}{}, }, "some": map[string]interface{}{ "directory": map[string]interface{}{ "with": map[string]interface{}{ "a": map[string]interface{}{ "file": filedata, }, "x": map[string]interface{}{}, }, }, }, "this": map[string]interface{}{ "is": map[string]interface{}{ "a": map[string]interface{}{ "deep": map[string]interface{}{ "invalid": map[string]interface{}{ "directory": map[string]interface{}{}, }, }, }, }, }, "xthis": map[string]interface{}{ "is": map[string]interface{}{ "a": map[string]interface{}{ "deep": map[string]interface{}{ "invalid": map[string]interface{}{ "file": filedata, }, }, }, }, }, } mm := func(data interface{}) string { bytes, err := json.Marshal(data) if err != nil { panic(err) } return string(bytes) } m.Index(device1, "default", testdata, 0, nil) result := m.GlobalDirectoryTree("default", "", -1, false) if mm(result) != mm(expectedResult) { t.Errorf("Does not match:\n%s\n%s", mm(result), mm(expectedResult)) } result = m.GlobalDirectoryTree("default", "xthis/is/a/deep", -1, false) currentResult := map[string]interface{}{ "invalid": map[string]interface{}{ "file": filedata, }, } if mm(result) != mm(currentResult) { t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } result = m.GlobalDirectoryTree("default", "xthis/is/a/deep", -1, true) currentResult = map[string]interface{}{ "invalid": map[string]interface{}{}, } if mm(result) != mm(currentResult) { t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } // !!! This is actually BAD, because we don't have enough level allowance // to accept this file, hence the tree is left unbuilt !!! result = m.GlobalDirectoryTree("default", "xthis", 1, false) currentResult = map[string]interface{}{} if mm(result) != mm(currentResult) { t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } }
func TestDeviceRename(t *testing.T) { ccm := protocol.ClusterConfigMessage{ ClientName: "syncthing", ClientVersion: "v0.9.4", } defer os.Remove("tmpconfig.xml") rawCfg := config.New(device1) rawCfg.Devices = []config.DeviceConfiguration{ { DeviceID: device1, }, } cfg := config.Wrap("tmpconfig.xml", rawCfg) db := db.OpenMemory() m := NewModel(cfg, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) fc := FakeConnection{ id: device1, requestData: []byte("some data to return"), } m.AddConnection(Connection{ &net.TCPConn{}, fc, ConnectionTypeDirectAccept, }) m.ServeBackground() if cfg.Devices()[device1].Name != "" { t.Errorf("Device already has a name") } m.ClusterConfig(device1, ccm) if cfg.Devices()[device1].Name != "" { t.Errorf("Device already has a name") } ccm.DeviceName = "tester" m.ClusterConfig(device1, ccm) if cfg.Devices()[device1].Name != "tester" { t.Errorf("Device did not get a name") } ccm.DeviceName = "tester2" m.ClusterConfig(device1, ccm) if cfg.Devices()[device1].Name != "tester" { t.Errorf("Device name got overwritten") } cfgw, err := config.Load("tmpconfig.xml", protocol.LocalDeviceID) if err != nil { t.Error(err) return } if cfgw.Devices()[device1].Name != "tester" { t.Errorf("Device name not saved in config") } }
func TestClusterConfig(t *testing.T) { cfg := config.New(device1) cfg.Devices = []config.DeviceConfiguration{ { DeviceID: device1, Introducer: true, }, { DeviceID: device2, }, } cfg.Folders = []config.FolderConfiguration{ { ID: "folder1", Devices: []config.FolderDeviceConfiguration{ {DeviceID: device1}, {DeviceID: device2}, }, }, { ID: "folder2", Devices: []config.FolderDeviceConfiguration{ {DeviceID: device1}, {DeviceID: device2}, }, }, } db := db.OpenMemory() m := NewModel(config.Wrap("/tmp/test", cfg), protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) m.AddFolder(cfg.Folders[0]) m.AddFolder(cfg.Folders[1]) m.ServeBackground() cm := m.generateClusterConfig(device2) if l := len(cm.Folders); l != 2 { t.Fatalf("Incorrect number of folders %d != 2", l) } r := cm.Folders[0] if r.ID != "folder1" { t.Errorf("Incorrect folder %q != folder1", r.ID) } if l := len(r.Devices); l != 2 { t.Errorf("Incorrect number of devices %d != 2", l) } if id := r.Devices[0].ID; bytes.Compare(id, device1[:]) != 0 { t.Errorf("Incorrect device ID %x != %x", id, device1) } if r.Devices[0].Flags&protocol.FlagIntroducer == 0 { t.Error("Device1 should be flagged as Introducer") } if id := r.Devices[1].ID; bytes.Compare(id, device2[:]) != 0 { t.Errorf("Incorrect device ID %x != %x", id, device2) } if r.Devices[1].Flags&protocol.FlagIntroducer != 0 { t.Error("Device2 should not be flagged as Introducer") } r = cm.Folders[1] if r.ID != "folder2" { t.Errorf("Incorrect folder %q != folder2", r.ID) } if l := len(r.Devices); l != 2 { t.Errorf("Incorrect number of devices %d != 2", l) } if id := r.Devices[0].ID; bytes.Compare(id, device1[:]) != 0 { t.Errorf("Incorrect device ID %x != %x", id, device1) } if r.Devices[0].Flags&protocol.FlagIntroducer == 0 { t.Error("Device1 should be flagged as Introducer") } if id := r.Devices[1].ID; bytes.Compare(id, device2[:]) != 0 { t.Errorf("Incorrect device ID %x != %x", id, device2) } if r.Devices[1].Flags&protocol.FlagIntroducer != 0 { t.Error("Device2 should not be flagged as Introducer") } }
func TestGlobalDirectoryTree(t *testing.T) { db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) m.AddFolder(defaultFolderConfig) m.ServeBackground() b := func(isfile bool, path ...string) protocol.FileInfo { flags := uint32(protocol.FlagDirectory) blocks := []protocol.BlockInfo{} if isfile { flags = 0 blocks = []protocol.BlockInfo{{Offset: 0x0, Size: 0xa, Hash: []uint8{0x2f, 0x72, 0xcc, 0x11, 0xa6, 0xfc, 0xd0, 0x27, 0x1e, 0xce, 0xf8, 0xc6, 0x10, 0x56, 0xee, 0x1e, 0xb1, 0x24, 0x3b, 0xe3, 0x80, 0x5b, 0xf9, 0xa9, 0xdf, 0x98, 0xf9, 0x2f, 0x76, 0x36, 0xb0, 0x5c}}} } return protocol.FileInfo{ Name: filepath.Join(path...), Flags: flags, Modified: 0x666, Blocks: blocks, } } filedata := []interface{}{time.Unix(0x666, 0), 0xa} testdata := []protocol.FileInfo{ b(false, "another"), b(false, "another", "directory"), b(true, "another", "directory", "afile"), b(false, "another", "directory", "with"), b(false, "another", "directory", "with", "a"), b(true, "another", "directory", "with", "a", "file"), b(true, "another", "directory", "with", "file"), b(true, "another", "file"), b(false, "other"), b(false, "other", "rand"), b(false, "other", "random"), b(false, "other", "random", "dir"), b(false, "other", "random", "dirx"), b(false, "other", "randomx"), b(false, "some"), b(false, "some", "directory"), b(false, "some", "directory", "with"), b(false, "some", "directory", "with", "a"), b(true, "some", "directory", "with", "a", "file"), b(true, "rootfile"), } expectedResult := map[string]interface{}{ "another": map[string]interface{}{ "directory": map[string]interface{}{ "afile": filedata, "with": map[string]interface{}{ "a": map[string]interface{}{ "file": filedata, }, "file": filedata, }, }, "file": filedata, }, "other": map[string]interface{}{ "rand": map[string]interface{}{}, "random": map[string]interface{}{ "dir": map[string]interface{}{}, "dirx": map[string]interface{}{}, }, "randomx": map[string]interface{}{}, }, "some": map[string]interface{}{ "directory": map[string]interface{}{ "with": map[string]interface{}{ "a": map[string]interface{}{ "file": filedata, }, }, }, }, "rootfile": filedata, } mm := func(data interface{}) string { bytes, err := json.Marshal(data) if err != nil { panic(err) } return string(bytes) } m.Index(device1, "default", testdata, 0, nil) result := m.GlobalDirectoryTree("default", "", -1, false) if mm(result) != mm(expectedResult) { t.Errorf("Does not match:\n%#v\n%#v", result, expectedResult) } result = m.GlobalDirectoryTree("default", "another", -1, false) if mm(result) != mm(expectedResult["another"]) { t.Errorf("Does not match:\n%s\n%s", mm(result), mm(expectedResult["another"])) } result = m.GlobalDirectoryTree("default", "", 0, false) currentResult := map[string]interface{}{ "another": map[string]interface{}{}, "other": map[string]interface{}{}, "some": map[string]interface{}{}, "rootfile": filedata, } if mm(result) != mm(currentResult) { t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } result = m.GlobalDirectoryTree("default", "", 1, false) currentResult = map[string]interface{}{ "another": map[string]interface{}{ "directory": map[string]interface{}{}, "file": filedata, }, "other": map[string]interface{}{ "rand": map[string]interface{}{}, "random": map[string]interface{}{}, "randomx": map[string]interface{}{}, }, "some": map[string]interface{}{ "directory": map[string]interface{}{}, }, "rootfile": filedata, } if mm(result) != mm(currentResult) { t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } result = m.GlobalDirectoryTree("default", "", -1, true) currentResult = map[string]interface{}{ "another": map[string]interface{}{ "directory": map[string]interface{}{ "with": map[string]interface{}{ "a": map[string]interface{}{}, }, }, }, "other": map[string]interface{}{ "rand": map[string]interface{}{}, "random": map[string]interface{}{ "dir": map[string]interface{}{}, "dirx": map[string]interface{}{}, }, "randomx": map[string]interface{}{}, }, "some": map[string]interface{}{ "directory": map[string]interface{}{ "with": map[string]interface{}{ "a": map[string]interface{}{}, }, }, }, } if mm(result) != mm(currentResult) { t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } result = m.GlobalDirectoryTree("default", "", 1, true) currentResult = map[string]interface{}{ "another": map[string]interface{}{ "directory": map[string]interface{}{}, }, "other": map[string]interface{}{ "rand": map[string]interface{}{}, "random": map[string]interface{}{}, "randomx": map[string]interface{}{}, }, "some": map[string]interface{}{ "directory": map[string]interface{}{}, }, } if mm(result) != mm(currentResult) { t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } result = m.GlobalDirectoryTree("default", "another", 0, false) currentResult = map[string]interface{}{ "directory": map[string]interface{}{}, "file": filedata, } if mm(result) != mm(currentResult) { t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } result = m.GlobalDirectoryTree("default", "some/directory", 0, false) currentResult = map[string]interface{}{ "with": map[string]interface{}{}, } if mm(result) != mm(currentResult) { t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } result = m.GlobalDirectoryTree("default", "some/directory", 1, false) currentResult = map[string]interface{}{ "with": map[string]interface{}{ "a": map[string]interface{}{}, }, } if mm(result) != mm(currentResult) { t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } result = m.GlobalDirectoryTree("default", "some/directory", 2, false) currentResult = map[string]interface{}{ "with": map[string]interface{}{ "a": map[string]interface{}{ "file": filedata, }, }, } if mm(result) != mm(currentResult) { t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } result = m.GlobalDirectoryTree("default", "another", -1, true) currentResult = map[string]interface{}{ "directory": map[string]interface{}{ "with": map[string]interface{}{ "a": map[string]interface{}{}, }, }, } if mm(result) != mm(currentResult) { t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } // No prefix matching! result = m.GlobalDirectoryTree("default", "som", -1, false) currentResult = map[string]interface{}{} if mm(result) != mm(currentResult) { t.Errorf("Does not match:\n%s\n%s", mm(result), mm(currentResult)) } }
func TestIgnores(t *testing.T) { arrEqual := func(a, b []string) bool { if len(a) != len(b) { return false } for i := range a { if a[i] != b[i] { return false } } return true } // Assure a clean start state ioutil.WriteFile("testdata/.stfolder", nil, 0644) ioutil.WriteFile("testdata/.stignore", []byte(".*\nquux\n"), 0644) db := db.OpenMemory() m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil) m.AddFolder(defaultFolderConfig) m.StartFolderRO("default") m.ServeBackground() expected := []string{ ".*", "quux", } ignores, _, err := m.GetIgnores("default") if err != nil { t.Error(err) } if !arrEqual(ignores, expected) { t.Errorf("Incorrect ignores: %v != %v", ignores, expected) } ignores = append(ignores, "pox") err = m.SetIgnores("default", ignores) if err != nil { t.Error(err) } ignores2, _, err := m.GetIgnores("default") if err != nil { t.Error(err) } if arrEqual(expected, ignores2) { t.Errorf("Incorrect ignores: %v == %v", ignores2, expected) } if !arrEqual(ignores, ignores2) { t.Errorf("Incorrect ignores: %v != %v", ignores2, ignores) } err = m.SetIgnores("default", expected) if err != nil { t.Error(err) } ignores, _, err = m.GetIgnores("default") if err != nil { t.Error(err) } if !arrEqual(ignores, expected) { t.Errorf("Incorrect ignores: %v != %v", ignores, expected) } ignores, _, err = m.GetIgnores("doesnotexist") if err == nil { t.Error("No error") } err = m.SetIgnores("doesnotexist", expected) if err == nil { t.Error("No error") } // Invalid path, marker should be missing, hence returns an error. m.AddFolder(config.FolderConfiguration{ID: "fresh", RawPath: "XXX"}) ignores, _, err = m.GetIgnores("fresh") if err == nil { t.Error("No error") } }