From 2b246eeb528b331182c197b039e386e7f2c473e2 Mon Sep 17 00:00:00 2001 From: Simon Frei Date: Fri, 10 May 2019 13:33:45 +0200 Subject: [PATCH] lib/model: Move test utilities to separate files (#5694) --- lib/model/fakeconns_test.go | 246 ++++++++++++++++++++++++++++++ lib/model/model_test.go | 291 ------------------------------------ lib/model/requests_test.go | 44 ------ lib/model/testutils_test.go | 123 +++++++++++++++ 4 files changed, 369 insertions(+), 335 deletions(-) create mode 100644 lib/model/fakeconns_test.go create mode 100644 lib/model/testutils_test.go diff --git a/lib/model/fakeconns_test.go b/lib/model/fakeconns_test.go new file mode 100644 index 00000000..301e56cf --- /dev/null +++ b/lib/model/fakeconns_test.go @@ -0,0 +1,246 @@ +// Copyright (C) 2014 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" + "net" + "sync" + "time" + + "github.com/syncthing/syncthing/lib/protocol" + "github.com/syncthing/syncthing/lib/scanner" +) + +type downloadProgressMessage struct { + folder string + updates []protocol.FileDownloadProgressUpdate +} + +type fakeConnection struct { + id protocol.DeviceID + downloadProgressMessages []downloadProgressMessage + closed bool + files []protocol.FileInfo + fileData map[string][]byte + folder string + model *model + indexFn func(string, []protocol.FileInfo) + requestFn func(folder, name string, offset int64, size int, hash []byte, fromTemporary bool) ([]byte, error) + closeFn func(error) + mut sync.Mutex +} + +func (f *fakeConnection) Close(err error) { + f.mut.Lock() + defer f.mut.Unlock() + if f.closeFn != nil { + f.closeFn(err) + return + } + f.closed = true + f.model.Closed(f, err) +} + +func (f *fakeConnection) Start() { +} + +func (f *fakeConnection) ID() protocol.DeviceID { + return f.id +} + +func (f *fakeConnection) Name() string { + return "" +} + +func (f *fakeConnection) String() string { + return "" +} + +func (f *fakeConnection) Option(string) string { + return "" +} + +func (f *fakeConnection) Index(folder string, fs []protocol.FileInfo) error { + f.mut.Lock() + defer f.mut.Unlock() + if f.indexFn != nil { + f.indexFn(folder, fs) + } + return nil +} + +func (f *fakeConnection) IndexUpdate(folder string, fs []protocol.FileInfo) error { + f.mut.Lock() + defer f.mut.Unlock() + if f.indexFn != nil { + f.indexFn(folder, fs) + } + return nil +} + +func (f *fakeConnection) Request(folder, name string, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) { + f.mut.Lock() + defer f.mut.Unlock() + if f.requestFn != nil { + return f.requestFn(folder, name, offset, size, hash, fromTemporary) + } + return f.fileData[name], nil +} + +func (f *fakeConnection) ClusterConfig(protocol.ClusterConfig) {} + +func (f *fakeConnection) Ping() bool { + f.mut.Lock() + defer f.mut.Unlock() + return f.closed +} + +func (f *fakeConnection) Closed() bool { + f.mut.Lock() + defer f.mut.Unlock() + return f.closed +} + +func (f *fakeConnection) Statistics() protocol.Statistics { + return protocol.Statistics{} +} + +func (f *fakeConnection) RemoteAddr() net.Addr { + return &fakeAddr{} +} + +func (f *fakeConnection) Type() string { + return "fake" +} + +func (f *fakeConnection) Crypto() string { + return "fake" +} + +func (f *fakeConnection) Transport() string { + return "fake" +} + +func (f *fakeConnection) Priority() int { + return 9000 +} + +func (f *fakeConnection) DownloadProgress(folder string, updates []protocol.FileDownloadProgressUpdate) { + f.downloadProgressMessages = append(f.downloadProgressMessages, downloadProgressMessage{ + folder: folder, + updates: updates, + }) +} + +func (f *fakeConnection) addFileLocked(name string, flags uint32, ftype protocol.FileInfoType, data []byte, version protocol.Vector) { + blockSize := protocol.BlockSize(int64(len(data))) + blocks, _ := scanner.Blocks(context.TODO(), bytes.NewReader(data), blockSize, int64(len(data)), nil, true) + + if ftype == protocol.FileInfoTypeFile || ftype == protocol.FileInfoTypeDirectory { + f.files = append(f.files, protocol.FileInfo{ + Name: name, + Type: ftype, + Size: int64(len(data)), + ModifiedS: time.Now().Unix(), + Permissions: flags, + Version: version, + Sequence: time.Now().UnixNano(), + RawBlockSize: int32(blockSize), + Blocks: blocks, + }) + } else { + // Symlink + f.files = append(f.files, protocol.FileInfo{ + Name: name, + Type: ftype, + Version: version, + Sequence: time.Now().UnixNano(), + SymlinkTarget: string(data), + NoPermissions: true, + }) + } + + if f.fileData == nil { + f.fileData = make(map[string][]byte) + } + f.fileData[name] = data +} + +func (f *fakeConnection) addFile(name string, flags uint32, ftype protocol.FileInfoType, data []byte) { + f.mut.Lock() + defer f.mut.Unlock() + + var version protocol.Vector + version = version.Update(f.id.Short()) + f.addFileLocked(name, flags, ftype, data, version) +} + +func (f *fakeConnection) updateFile(name string, flags uint32, ftype protocol.FileInfoType, data []byte) { + f.mut.Lock() + defer f.mut.Unlock() + + for i, fi := range f.files { + if fi.Name == name { + f.files = append(f.files[:i], f.files[i+1:]...) + f.addFileLocked(name, flags, ftype, data, fi.Version.Update(f.id.Short())) + return + } + } +} + +func (f *fakeConnection) deleteFile(name string) { + f.mut.Lock() + defer f.mut.Unlock() + + for i, fi := range f.files { + if fi.Name == name { + fi.Deleted = true + fi.ModifiedS = time.Now().Unix() + fi.Version = fi.Version.Update(f.id.Short()) + fi.Sequence = time.Now().UnixNano() + fi.Blocks = nil + + f.files = append(append(f.files[:i], f.files[i+1:]...), fi) + return + } + } +} + +func (f *fakeConnection) sendIndexUpdate() { + f.model.IndexUpdate(f.id, f.folder, f.files) +} + +func addFakeConn(m *model, dev protocol.DeviceID) *fakeConnection { + fc := &fakeConnection{id: dev, model: m} + m.AddConnection(fc, protocol.HelloResult{}) + + m.ClusterConfig(dev, protocol.ClusterConfig{ + Folders: []protocol.Folder{ + { + ID: "default", + Devices: []protocol.Device{ + {ID: myID}, + {ID: device1}, + }, + }, + }, + }) + + return fc +} + +type fakeAddr struct{} + +func (fakeAddr) Network() string { + return "network" +} + +func (fakeAddr) String() string { + return "address" +} diff --git a/lib/model/model_test.go b/lib/model/model_test.go index 15963ea0..1b1fea15 100644 --- a/lib/model/model_test.go +++ b/lib/model/model_test.go @@ -8,13 +8,11 @@ package model import ( "bytes" - "context" "encoding/json" "fmt" "io" "io/ioutil" "math/rand" - "net" "os" "path/filepath" "runtime" @@ -33,55 +31,9 @@ import ( "github.com/syncthing/syncthing/lib/osutil" "github.com/syncthing/syncthing/lib/protocol" srand "github.com/syncthing/syncthing/lib/rand" - "github.com/syncthing/syncthing/lib/scanner" "github.com/syncthing/syncthing/lib/versioner" ) -var myID, device1, device2 protocol.DeviceID -var defaultCfgWrapper config.Wrapper -var defaultFolderConfig config.FolderConfiguration -var defaultFs fs.Filesystem -var defaultCfg config.Configuration -var defaultAutoAcceptCfg config.Configuration - -func init() { - myID, _ = protocol.DeviceIDFromString("ZNWFSWE-RWRV2BD-45BLMCV-LTDE2UR-4LJDW6J-R5BPWEB-TXD27XJ-IZF5RA4") - device1, _ = protocol.DeviceIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR") - device2, _ = protocol.DeviceIDFromString("GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY") - - defaultFs = fs.NewFilesystem(fs.FilesystemTypeBasic, "testdata") - - defaultFolderConfig = testFolderConfig("testdata") - - defaultCfgWrapper = createTmpWrapper(config.New(myID)) - defaultCfgWrapper.SetDevice(config.NewDeviceConfiguration(device1, "device1")) - defaultCfgWrapper.SetFolder(defaultFolderConfig) - opts := defaultCfgWrapper.Options() - opts.KeepTemporariesH = 1 - defaultCfgWrapper.SetOptions(opts) - - defaultCfg = defaultCfgWrapper.RawCopy() - - defaultAutoAcceptCfg = config.Configuration{ - Devices: []config.DeviceConfiguration{ - { - DeviceID: myID, // self - }, - { - DeviceID: device1, - AutoAcceptFolders: true, - }, - { - DeviceID: device2, - AutoAcceptFolders: true, - }, - }, - Options: config.OptionsConfiguration{ - DefaultFolderPath: ".", - }, - } -} - var testDataExpected = map[string]protocol.FileInfo{ "foo": { Name: "foo", @@ -177,22 +129,6 @@ func newState(cfg config.Configuration) (config.Wrapper, *model) { return wcfg, m } -func setupModel(w config.Wrapper) *model { - db := db.OpenMemory() - m := newModel(w, myID, "syncthing", "dev", db, nil) - m.ServeBackground() - for id, cfg := range w.Folders() { - if !cfg.Paused { - m.AddFolder(cfg) - m.StartFolder(id) - } - } - - m.ScanFolders() - - return m -} - func TestRequest(t *testing.T) { m := setupModel(defaultCfgWrapper) defer func() { @@ -313,204 +249,6 @@ func benchmarkIndexUpdate(b *testing.B, nfiles, nufiles int) { b.ReportAllocs() } -type downloadProgressMessage struct { - folder string - updates []protocol.FileDownloadProgressUpdate -} - -type fakeConnection struct { - id protocol.DeviceID - downloadProgressMessages []downloadProgressMessage - closed bool - files []protocol.FileInfo - fileData map[string][]byte - folder string - model *model - indexFn func(string, []protocol.FileInfo) - requestFn func(folder, name string, offset int64, size int, hash []byte, fromTemporary bool) ([]byte, error) - closeFn func(error) - mut sync.Mutex -} - -func (f *fakeConnection) Close(err error) { - f.mut.Lock() - defer f.mut.Unlock() - if f.closeFn != nil { - f.closeFn(err) - return - } - f.closed = true - f.model.Closed(f, err) -} - -func (f *fakeConnection) Start() { -} - -func (f *fakeConnection) ID() protocol.DeviceID { - return f.id -} - -func (f *fakeConnection) Name() string { - return "" -} - -func (f *fakeConnection) String() string { - return "" -} - -func (f *fakeConnection) Option(string) string { - return "" -} - -func (f *fakeConnection) Index(folder string, fs []protocol.FileInfo) error { - f.mut.Lock() - defer f.mut.Unlock() - if f.indexFn != nil { - f.indexFn(folder, fs) - } - return nil -} - -func (f *fakeConnection) IndexUpdate(folder string, fs []protocol.FileInfo) error { - f.mut.Lock() - defer f.mut.Unlock() - if f.indexFn != nil { - f.indexFn(folder, fs) - } - return nil -} - -func (f *fakeConnection) Request(folder, name string, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) { - f.mut.Lock() - defer f.mut.Unlock() - if f.requestFn != nil { - return f.requestFn(folder, name, offset, size, hash, fromTemporary) - } - return f.fileData[name], nil -} - -func (f *fakeConnection) ClusterConfig(protocol.ClusterConfig) {} - -func (f *fakeConnection) Ping() bool { - f.mut.Lock() - defer f.mut.Unlock() - return f.closed -} - -func (f *fakeConnection) Closed() bool { - f.mut.Lock() - defer f.mut.Unlock() - return f.closed -} - -func (f *fakeConnection) Statistics() protocol.Statistics { - return protocol.Statistics{} -} - -func (f *fakeConnection) RemoteAddr() net.Addr { - return &fakeAddr{} -} - -func (f *fakeConnection) Type() string { - return "fake" -} - -func (f *fakeConnection) Crypto() string { - return "fake" -} - -func (f *fakeConnection) Transport() string { - return "fake" -} - -func (f *fakeConnection) Priority() int { - return 9000 -} - -func (f *fakeConnection) DownloadProgress(folder string, updates []protocol.FileDownloadProgressUpdate) { - f.downloadProgressMessages = append(f.downloadProgressMessages, downloadProgressMessage{ - folder: folder, - updates: updates, - }) -} - -func (f *fakeConnection) addFileLocked(name string, flags uint32, ftype protocol.FileInfoType, data []byte, version protocol.Vector) { - blockSize := protocol.BlockSize(int64(len(data))) - blocks, _ := scanner.Blocks(context.TODO(), bytes.NewReader(data), blockSize, int64(len(data)), nil, true) - - if ftype == protocol.FileInfoTypeFile || ftype == protocol.FileInfoTypeDirectory { - f.files = append(f.files, protocol.FileInfo{ - Name: name, - Type: ftype, - Size: int64(len(data)), - ModifiedS: time.Now().Unix(), - Permissions: flags, - Version: version, - Sequence: time.Now().UnixNano(), - RawBlockSize: int32(blockSize), - Blocks: blocks, - }) - } else { - // Symlink - f.files = append(f.files, protocol.FileInfo{ - Name: name, - Type: ftype, - Version: version, - Sequence: time.Now().UnixNano(), - SymlinkTarget: string(data), - NoPermissions: true, - }) - } - - if f.fileData == nil { - f.fileData = make(map[string][]byte) - } - f.fileData[name] = data -} -func (f *fakeConnection) addFile(name string, flags uint32, ftype protocol.FileInfoType, data []byte) { - f.mut.Lock() - defer f.mut.Unlock() - - var version protocol.Vector - version = version.Update(f.id.Short()) - f.addFileLocked(name, flags, ftype, data, version) -} - -func (f *fakeConnection) updateFile(name string, flags uint32, ftype protocol.FileInfoType, data []byte) { - f.mut.Lock() - defer f.mut.Unlock() - - for i, fi := range f.files { - if fi.Name == name { - f.files = append(f.files[:i], f.files[i+1:]...) - f.addFileLocked(name, flags, ftype, data, fi.Version.Update(f.id.Short())) - return - } - } -} - -func (f *fakeConnection) deleteFile(name string) { - f.mut.Lock() - defer f.mut.Unlock() - - for i, fi := range f.files { - if fi.Name == name { - fi.Deleted = true - fi.ModifiedS = time.Now().Unix() - fi.Version = fi.Version.Update(f.id.Short()) - fi.Sequence = time.Now().UnixNano() - fi.Blocks = nil - - f.files = append(append(f.files[:i], f.files[i+1:]...), fi) - return - } - } -} - -func (f *fakeConnection) sendIndexUpdate() { - f.model.IndexUpdate(f.id, f.folder, f.files) -} - func BenchmarkRequestOut(b *testing.B) { m := setupModel(defaultCfgWrapper) defer func() { @@ -3527,25 +3265,6 @@ func TestParentOfUnignored(t *testing.T) { } } -func addFakeConn(m *model, dev protocol.DeviceID) *fakeConnection { - fc := &fakeConnection{id: dev, model: m} - m.AddConnection(fc, protocol.HelloResult{}) - - m.ClusterConfig(dev, protocol.ClusterConfig{ - Folders: []protocol.Folder{ - { - ID: "default", - Devices: []protocol.Device{ - {ID: myID}, - {ID: device1}, - }, - }, - }, - }) - - return fc -} - // TestFolderRestartZombies reproduces issue 5233, where multiple concurrent folder // restarts would leave more than one folder runner alive. func TestFolderRestartZombies(t *testing.T) { @@ -3599,16 +3318,6 @@ func TestFolderRestartZombies(t *testing.T) { } } -type fakeAddr struct{} - -func (fakeAddr) Network() string { - return "network" -} - -func (fakeAddr) String() string { - return "address" -} - type alwaysChangedKey struct { fs fs.Filesystem name string diff --git a/lib/model/requests_test.go b/lib/model/requests_test.go index 91f4707a..378cd8bc 100644 --- a/lib/model/requests_test.go +++ b/lib/model/requests_test.go @@ -687,50 +687,6 @@ func TestRequestSymlinkWindows(t *testing.T) { } } -func tmpDefaultWrapper() (config.Wrapper, config.FolderConfiguration) { - w := createTmpWrapper(defaultCfgWrapper.RawCopy()) - fcfg := testFolderConfigTmp() - w.SetFolder(fcfg) - return w, fcfg -} - -func testFolderConfigTmp() config.FolderConfiguration { - tmpDir := createTmpDir() - return testFolderConfig(tmpDir) -} - -func testFolderConfig(path string) config.FolderConfiguration { - cfg := config.NewFolderConfiguration(myID, "default", "default", fs.FilesystemTypeBasic, path) - cfg.FSWatcherEnabled = false - cfg.Devices = append(cfg.Devices, config.FolderDeviceConfiguration{DeviceID: device1}) - return cfg -} - -func setupModelWithConnection() (*model, *fakeConnection, config.FolderConfiguration, config.Wrapper) { - w, fcfg := tmpDefaultWrapper() - m, fc := setupModelWithConnectionFromWrapper(w) - return m, fc, fcfg, w -} - -func setupModelWithConnectionFromWrapper(w config.Wrapper) (*model, *fakeConnection) { - m := setupModel(w) - - fc := addFakeConn(m, device1) - fc.folder = "default" - - m.ScanFolder("default") - - return m, fc -} - -func createTmpDir() string { - tmpDir, err := ioutil.TempDir("", "syncthing_testFolder-") - if err != nil { - panic("Failed to create temporary testing dir") - } - return tmpDir -} - func equalContents(path string, contents []byte) error { if bs, err := ioutil.ReadFile(path); err != nil { return err diff --git a/lib/model/testutils_test.go b/lib/model/testutils_test.go new file mode 100644 index 00000000..40775472 --- /dev/null +++ b/lib/model/testutils_test.go @@ -0,0 +1,123 @@ +// Copyright (C) 2016 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 ( + "io/ioutil" + + "github.com/syncthing/syncthing/lib/config" + "github.com/syncthing/syncthing/lib/db" + "github.com/syncthing/syncthing/lib/fs" + "github.com/syncthing/syncthing/lib/protocol" +) + +var ( + myID, device1, device2 protocol.DeviceID + defaultCfgWrapper config.Wrapper + defaultFolderConfig config.FolderConfiguration + defaultFs fs.Filesystem + defaultCfg config.Configuration + defaultAutoAcceptCfg config.Configuration +) + +func init() { + myID, _ = protocol.DeviceIDFromString("ZNWFSWE-RWRV2BD-45BLMCV-LTDE2UR-4LJDW6J-R5BPWEB-TXD27XJ-IZF5RA4") + device1, _ = protocol.DeviceIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR") + device2, _ = protocol.DeviceIDFromString("GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY") + + defaultFs = fs.NewFilesystem(fs.FilesystemTypeBasic, "testdata") + + defaultFolderConfig = testFolderConfig("testdata") + + defaultCfgWrapper = createTmpWrapper(config.New(myID)) + _, _ = defaultCfgWrapper.SetDevice(config.NewDeviceConfiguration(device1, "device1")) + _, _ = defaultCfgWrapper.SetFolder(defaultFolderConfig) + opts := defaultCfgWrapper.Options() + opts.KeepTemporariesH = 1 + _, _ = defaultCfgWrapper.SetOptions(opts) + + defaultCfg = defaultCfgWrapper.RawCopy() + + defaultAutoAcceptCfg = config.Configuration{ + Devices: []config.DeviceConfiguration{ + { + DeviceID: myID, // self + }, + { + DeviceID: device1, + AutoAcceptFolders: true, + }, + { + DeviceID: device2, + AutoAcceptFolders: true, + }, + }, + Options: config.OptionsConfiguration{ + DefaultFolderPath: ".", + }, + } +} + +func tmpDefaultWrapper() (config.Wrapper, config.FolderConfiguration) { + w := createTmpWrapper(defaultCfgWrapper.RawCopy()) + fcfg := testFolderConfigTmp() + _, _ = w.SetFolder(fcfg) + return w, fcfg +} + +func testFolderConfigTmp() config.FolderConfiguration { + tmpDir := createTmpDir() + return testFolderConfig(tmpDir) +} + +func testFolderConfig(path string) config.FolderConfiguration { + cfg := config.NewFolderConfiguration(myID, "default", "default", fs.FilesystemTypeBasic, path) + cfg.FSWatcherEnabled = false + cfg.Devices = append(cfg.Devices, config.FolderDeviceConfiguration{DeviceID: device1}) + return cfg +} + +func setupModelWithConnection() (*model, *fakeConnection, config.FolderConfiguration, config.Wrapper) { + w, fcfg := tmpDefaultWrapper() + m, fc := setupModelWithConnectionFromWrapper(w) + return m, fc, fcfg, w +} + +func setupModelWithConnectionFromWrapper(w config.Wrapper) (*model, *fakeConnection) { + m := setupModel(w) + + fc := addFakeConn(m, device1) + fc.folder = "default" + + _ = m.ScanFolder("default") + + return m, fc +} + +func setupModel(w config.Wrapper) *model { + db := db.OpenMemory() + m := newModel(w, myID, "syncthing", "dev", db, nil) + m.ServeBackground() + for id, cfg := range w.Folders() { + if !cfg.Paused { + m.AddFolder(cfg) + m.StartFolder(id) + } + } + + m.ScanFolders() + + return m +} + +func createTmpDir() string { + tmpDir, err := ioutil.TempDir("", "syncthing_testFolder-") + if err != nil { + panic("Failed to create temporary testing dir") + } + return tmpDir +}