123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- // Copyright (C) 2018 The Syncthing Authors.
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this file,
- // You can obtain one at https://mozilla.org/MPL/2.0/.
- package model
- import (
- "bytes"
- "context"
- "io/ioutil"
- "os"
- "testing"
- "time"
- "github.com/syncthing/syncthing/lib/config"
- "github.com/syncthing/syncthing/lib/db"
- "github.com/syncthing/syncthing/lib/fs"
- "github.com/syncthing/syncthing/lib/protocol"
- "github.com/syncthing/syncthing/lib/scanner"
- )
- func TestRecvOnlyRevertDeletes(t *testing.T) {
- testOs := &fatalOs{t}
- // Make sure that we delete extraneous files and directories when we hit
- // Revert.
- testOs.RemoveAll("_recvonly")
- defer testOs.RemoveAll("_recvonly")
- // Create some test data
- testOs.MkdirAll("_recvonly/.stfolder", 0755)
- testOs.MkdirAll("_recvonly/ignDir", 0755)
- testOs.MkdirAll("_recvonly/unknownDir", 0755)
- if err := ioutil.WriteFile("_recvonly/ignDir/ignFile", []byte("hello\n"), 0644); err != nil {
- t.Fatal(err)
- }
- if err := ioutil.WriteFile("_recvonly/unknownDir/unknownFile", []byte("hello\n"), 0644); err != nil {
- t.Fatal(err)
- }
- if err := ioutil.WriteFile("_recvonly/.stignore", []byte("ignDir\n"), 0644); err != nil {
- t.Fatal(err)
- }
- knownFiles := setupKnownFiles(t, []byte("hello\n"))
- // Get us a model up and running
- m := setupROFolder()
- defer m.Stop()
- // Send and index update for the known stuff
- m.Index(device1, "ro", knownFiles)
- m.updateLocalsFromScanning("ro", knownFiles)
- size := m.GlobalSize("ro")
- if size.Files != 1 || size.Directories != 1 {
- t.Fatalf("Global: expected 1 file and 1 directory: %+v", size)
- }
- // Start the folder. This will cause a scan, should discover the other stuff in the folder
- m.StartFolder("ro")
- m.ScanFolder("ro")
- // We should now have two files and two directories.
- size = m.GlobalSize("ro")
- if size.Files != 2 || size.Directories != 2 {
- t.Fatalf("Global: expected 2 files and 2 directories: %+v", size)
- }
- size = m.LocalSize("ro")
- if size.Files != 2 || size.Directories != 2 {
- t.Fatalf("Local: expected 2 files and 2 directories: %+v", size)
- }
- size = m.ReceiveOnlyChangedSize("ro")
- if size.Files+size.Directories == 0 {
- t.Fatalf("ROChanged: expected something: %+v", size)
- }
- // Revert should delete the unknown stuff
- m.Revert("ro")
- // These should still exist
- for _, p := range []string{"_recvonly/knownDir/knownFile", "_recvonly/ignDir/ignFile"} {
- _, err := os.Stat(p)
- if err != nil {
- t.Error("Unexpected error:", err)
- }
- }
- // These should have been removed
- for _, p := range []string{"_recvonly/unknownDir", "_recvonly/unknownDir/unknownFile"} {
- _, err := os.Stat(p)
- if !os.IsNotExist(err) {
- t.Error("Unexpected existing thing:", p)
- }
- }
- // We should now have one file and directory again.
- size = m.GlobalSize("ro")
- if size.Files != 1 || size.Directories != 1 {
- t.Fatalf("Global: expected 1 files and 1 directories: %+v", size)
- }
- size = m.LocalSize("ro")
- if size.Files != 1 || size.Directories != 1 {
- t.Fatalf("Local: expected 1 files and 1 directories: %+v", size)
- }
- }
- func TestRecvOnlyRevertNeeds(t *testing.T) {
- testOs := &fatalOs{t}
- // Make sure that a new file gets picked up and considered latest, then
- // gets considered old when we hit Revert.
- testOs.RemoveAll("_recvonly")
- defer testOs.RemoveAll("_recvonly")
- // Create some test data
- testOs.MkdirAll("_recvonly/.stfolder", 0755)
- oldData := []byte("hello\n")
- knownFiles := setupKnownFiles(t, oldData)
- // Get us a model up and running
- m := setupROFolder()
- defer m.Stop()
- // Send and index update for the known stuff
- m.Index(device1, "ro", knownFiles)
- m.updateLocalsFromScanning("ro", knownFiles)
- // Start the folder. This will cause a scan.
- m.StartFolder("ro")
- m.ScanFolder("ro")
- // Everything should be in sync.
- size := m.GlobalSize("ro")
- if size.Files != 1 || size.Directories != 1 {
- t.Fatalf("Global: expected 1 file and 1 directory: %+v", size)
- }
- size = m.LocalSize("ro")
- if size.Files != 1 || size.Directories != 1 {
- t.Fatalf("Local: expected 1 file and 1 directory: %+v", size)
- }
- size = m.NeedSize("ro")
- if size.Files+size.Directories > 0 {
- t.Fatalf("Need: expected nothing: %+v", size)
- }
- size = m.ReceiveOnlyChangedSize("ro")
- if size.Files+size.Directories > 0 {
- t.Fatalf("ROChanged: expected nothing: %+v", size)
- }
- // Update the file.
- newData := []byte("totally different data\n")
- if err := ioutil.WriteFile("_recvonly/knownDir/knownFile", newData, 0644); err != nil {
- t.Fatal(err)
- }
- // Rescan.
- if err := m.ScanFolder("ro"); err != nil {
- t.Fatal(err)
- }
- // We now have a newer file than the rest of the cluster. Global state should reflect this.
- size = m.GlobalSize("ro")
- const sizeOfDir = 128
- if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(oldData)) {
- t.Fatalf("Global: expected no change due to the new file: %+v", size)
- }
- size = m.LocalSize("ro")
- if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(newData)) {
- t.Fatalf("Local: expected the new file to be reflected: %+v", size)
- }
- size = m.NeedSize("ro")
- if size.Files+size.Directories > 0 {
- t.Fatalf("Need: expected nothing: %+v", size)
- }
- size = m.ReceiveOnlyChangedSize("ro")
- if size.Files+size.Directories == 0 {
- t.Fatalf("ROChanged: expected something: %+v", size)
- }
- // We hit the Revert button. The file that was new should become old.
- m.Revert("ro")
- size = m.GlobalSize("ro")
- if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(oldData)) {
- t.Fatalf("Global: expected the global size to revert: %+v", size)
- }
- size = m.LocalSize("ro")
- if size.Files != 1 || size.Bytes != sizeOfDir+int64(len(newData)) {
- t.Fatalf("Local: expected the local size to remain: %+v", size)
- }
- size = m.NeedSize("ro")
- if size.Files != 1 || size.Bytes != int64(len(oldData)) {
- t.Fatalf("Local: expected to need the old file data: %+v", size)
- }
- }
- func TestRecvOnlyUndoChanges(t *testing.T) {
- testOs := &fatalOs{t}
- testOs.RemoveAll("_recvonly")
- defer testOs.RemoveAll("_recvonly")
- // Create some test data
- testOs.MkdirAll("_recvonly/.stfolder", 0755)
- oldData := []byte("hello\n")
- knownFiles := setupKnownFiles(t, oldData)
- // Get us a model up and running
- m := setupROFolder()
- defer m.Stop()
- m.fmut.Lock()
- fset := m.folderFiles["ro"]
- m.fmut.Unlock()
- folderFs := fset.MtimeFS()
- // Send and index update for the known stuff
- m.Index(device1, "ro", knownFiles)
- m.updateLocalsFromScanning("ro", knownFiles)
- // Start the folder. This will cause a scan.
- m.StartFolder("ro")
- m.ScanFolder("ro")
- // Everything should be in sync.
- size := m.GlobalSize("ro")
- if size.Files != 1 || size.Directories != 1 {
- t.Fatalf("Global: expected 1 file and 1 directory: %+v", size)
- }
- size = m.LocalSize("ro")
- if size.Files != 1 || size.Directories != 1 {
- t.Fatalf("Local: expected 1 file and 1 directory: %+v", size)
- }
- size = m.NeedSize("ro")
- if size.Files+size.Directories > 0 {
- t.Fatalf("Need: expected nothing: %+v", size)
- }
- size = m.ReceiveOnlyChangedSize("ro")
- if size.Files+size.Directories > 0 {
- t.Fatalf("ROChanged: expected nothing: %+v", size)
- }
- // Create a file and modify another
- file := "_recvonly/foo"
- if err := ioutil.WriteFile(file, []byte("hello\n"), 0644); err != nil {
- t.Fatal(err)
- }
- if err := ioutil.WriteFile("_recvonly/knownDir/knownFile", []byte("bye\n"), 0644); err != nil {
- t.Fatal(err)
- }
- m.ScanFolder("ro")
- size = m.ReceiveOnlyChangedSize("ro")
- if size.Files != 2 {
- t.Fatalf("Receive only: expected 2 files: %+v", size)
- }
- // Remove the file again and undo the modification
- testOs.Remove(file)
- if err := ioutil.WriteFile("_recvonly/knownDir/knownFile", oldData, 0644); err != nil {
- t.Fatal(err)
- }
- folderFs.Chtimes("knownDir/knownFile", knownFiles[1].ModTime(), knownFiles[1].ModTime())
- m.ScanFolder("ro")
- size = m.ReceiveOnlyChangedSize("ro")
- if size.Files+size.Directories+size.Deleted != 0 {
- t.Fatalf("Receive only: expected all zero: %+v", size)
- }
- }
- func setupKnownFiles(t *testing.T, data []byte) []protocol.FileInfo {
- testOs := &fatalOs{t}
- testOs.MkdirAll("_recvonly/knownDir", 0755)
- if err := ioutil.WriteFile("_recvonly/knownDir/knownFile", data, 0644); err != nil {
- t.Fatal(err)
- }
- t0 := time.Now().Add(-1 * time.Minute)
- testOs.Chtimes("_recvonly/knownDir/knownFile", t0, t0)
- fi := testOs.Stat("_recvonly/knownDir/knownFile")
- blocks, _ := scanner.Blocks(context.TODO(), bytes.NewReader(data), protocol.BlockSize(int64(len(data))), int64(len(data)), nil, true)
- knownFiles := []protocol.FileInfo{
- {
- Name: "knownDir",
- Type: protocol.FileInfoTypeDirectory,
- Permissions: 0755,
- Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 42}}},
- Sequence: 42,
- },
- {
- Name: "knownDir/knownFile",
- Type: protocol.FileInfoTypeFile,
- Permissions: 0644,
- Size: fi.Size(),
- ModifiedS: fi.ModTime().Unix(),
- ModifiedNs: int32(fi.ModTime().UnixNano() % 1e9),
- Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 42}}},
- Sequence: 42,
- Blocks: blocks,
- },
- }
- return knownFiles
- }
- func setupROFolder() *model {
- fcfg := config.NewFolderConfiguration(myID, "ro", "receive only test", fs.FilesystemTypeBasic, "_recvonly")
- fcfg.Type = config.FolderTypeReceiveOnly
- fcfg.Devices = []config.FolderDeviceConfiguration{{DeviceID: device1}}
- fcfg.FSWatcherEnabled = false
- fcfg.RescanIntervalS = 86400
- cfg := defaultCfg.Copy()
- cfg.Folders = append(cfg.Folders, fcfg)
- wrp := createTmpWrapper(cfg)
- db := db.OpenMemory()
- m := newModel(wrp, myID, "syncthing", "dev", db, nil)
- m.ServeBackground()
- m.AddFolder(fcfg)
- return m
- }
|