lib/model: Move test utilities to separate files (#5694)

This commit is contained in:
Simon Frei 2019-05-10 13:33:45 +02:00 committed by GitHub
parent 2558b021e5
commit 2b246eeb52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 369 additions and 335 deletions

246
lib/model/fakeconns_test.go Normal file
View File

@ -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"
}

View File

@ -8,13 +8,11 @@ package model
import ( import (
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"math/rand" "math/rand"
"net"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
@ -33,55 +31,9 @@ import (
"github.com/syncthing/syncthing/lib/osutil" "github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
srand "github.com/syncthing/syncthing/lib/rand" srand "github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/scanner"
"github.com/syncthing/syncthing/lib/versioner" "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{ var testDataExpected = map[string]protocol.FileInfo{
"foo": { "foo": {
Name: "foo", Name: "foo",
@ -177,22 +129,6 @@ func newState(cfg config.Configuration) (config.Wrapper, *model) {
return wcfg, m 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) { func TestRequest(t *testing.T) {
m := setupModel(defaultCfgWrapper) m := setupModel(defaultCfgWrapper)
defer func() { defer func() {
@ -313,204 +249,6 @@ func benchmarkIndexUpdate(b *testing.B, nfiles, nufiles int) {
b.ReportAllocs() 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) { func BenchmarkRequestOut(b *testing.B) {
m := setupModel(defaultCfgWrapper) m := setupModel(defaultCfgWrapper)
defer func() { 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 // TestFolderRestartZombies reproduces issue 5233, where multiple concurrent folder
// restarts would leave more than one folder runner alive. // restarts would leave more than one folder runner alive.
func TestFolderRestartZombies(t *testing.T) { 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 { type alwaysChangedKey struct {
fs fs.Filesystem fs fs.Filesystem
name string name string

View File

@ -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 { func equalContents(path string, contents []byte) error {
if bs, err := ioutil.ReadFile(path); err != nil { if bs, err := ioutil.ReadFile(path); err != nil {
return err return err

123
lib/model/testutils_test.go Normal file
View File

@ -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
}