فهرست منبع

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

Simon Frei 6 سال پیش
والد
کامیت
2b246eeb52
4فایلهای تغییر یافته به همراه369 افزوده شده و 335 حذف شده
  1. 246 0
      lib/model/fakeconns_test.go
  2. 0 291
      lib/model/model_test.go
  3. 0 44
      lib/model/requests_test.go
  4. 123 0
      lib/model/testutils_test.go

+ 246 - 0
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"
+}

+ 0 - 291
lib/model/model_test.go

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

+ 0 - 44
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 {
 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 - 0
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
+}