| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 | 
							- // 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 (
 
- 	"context"
 
- 	"os"
 
- 	"testing"
 
- 	"time"
 
- 	"github.com/syncthing/syncthing/internal/db/sqlite"
 
- 	"github.com/syncthing/syncthing/lib/config"
 
- 	"github.com/syncthing/syncthing/lib/events"
 
- 	"github.com/syncthing/syncthing/lib/fs"
 
- 	"github.com/syncthing/syncthing/lib/ignore"
 
- 	"github.com/syncthing/syncthing/lib/protocol"
 
- 	"github.com/syncthing/syncthing/lib/protocol/mocks"
 
- 	"github.com/syncthing/syncthing/lib/rand"
 
- )
 
- var (
 
- 	myID, device1, device2  protocol.DeviceID
 
- 	defaultCfgWrapper       config.Wrapper
 
- 	defaultCfgWrapperCancel context.CancelFunc
 
- 	defaultFolderConfig     config.FolderConfiguration
 
- 	defaultCfg              config.Configuration
 
- 	defaultAutoAcceptCfg    config.Configuration
 
- 	device1Conn             = &mocks.Connection{}
 
- 	device2Conn             = &mocks.Connection{}
 
- )
 
- 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")
 
- 	device1Conn.DeviceIDReturns(device1)
 
- 	device1Conn.ConnectionIDReturns(rand.String(16))
 
- 	device2Conn.DeviceIDReturns(device2)
 
- 	device2Conn.ConnectionIDReturns(rand.String(16))
 
- 	cfg := config.New(myID)
 
- 	cfg.Options.MinHomeDiskFree.Value = 0 // avoids unnecessary free space checks
 
- 	defaultCfgWrapper, defaultCfgWrapperCancel = newConfigWrapper(cfg)
 
- 	defaultFolderConfig = newFolderConfig()
 
- 	waiter, _ := defaultCfgWrapper.Modify(func(cfg *config.Configuration) {
 
- 		cfg.SetDevice(newDeviceConfiguration(cfg.Defaults.Device, device1, "device1"))
 
- 		cfg.SetFolder(defaultFolderConfig)
 
- 		cfg.Options.KeepTemporariesH = 1
 
- 	})
 
- 	waiter.Wait()
 
- 	defaultCfg = defaultCfgWrapper.RawCopy()
 
- 	defaultAutoAcceptCfg = config.Configuration{
 
- 		Version: config.CurrentVersion,
 
- 		Devices: []config.DeviceConfiguration{
 
- 			{
 
- 				DeviceID: myID, // self
 
- 			},
 
- 			{
 
- 				DeviceID:          device1,
 
- 				AutoAcceptFolders: true,
 
- 			},
 
- 			{
 
- 				DeviceID:          device2,
 
- 				AutoAcceptFolders: true,
 
- 			},
 
- 		},
 
- 		Defaults: config.Defaults{
 
- 			Folder: config.FolderConfiguration{
 
- 				FilesystemType: config.FilesystemTypeFake,
 
- 				Path:           rand.String(32),
 
- 			},
 
- 		},
 
- 		Options: config.OptionsConfiguration{
 
- 			MinHomeDiskFree: config.Size{}, // avoids unnecessary free space checks
 
- 		},
 
- 	}
 
- }
 
- func newConfigWrapper(cfg config.Configuration) (config.Wrapper, context.CancelFunc) {
 
- 	wrapper := config.Wrap("", cfg, myID, events.NoopLogger)
 
- 	ctx, cancel := context.WithCancel(context.Background())
 
- 	go wrapper.Serve(ctx)
 
- 	return wrapper, cancel
 
- }
 
- func newDefaultCfgWrapper() (config.Wrapper, config.FolderConfiguration, context.CancelFunc) {
 
- 	w, cancel := newConfigWrapper(defaultCfgWrapper.RawCopy())
 
- 	fcfg := newFolderConfig()
 
- 	_, _ = w.Modify(func(cfg *config.Configuration) {
 
- 		cfg.SetFolder(fcfg)
 
- 	})
 
- 	return w, fcfg, cancel
 
- }
 
- func newFolderConfig() config.FolderConfiguration {
 
- 	cfg := newFolderConfiguration(defaultCfgWrapper, "default", "default", config.FilesystemTypeFake, rand.String(32)+"?content=true")
 
- 	cfg.FSWatcherEnabled = false
 
- 	cfg.PullerDelayS = 0
 
- 	cfg.Devices = append(cfg.Devices, config.FolderDeviceConfiguration{DeviceID: device1})
 
- 	return cfg
 
- }
 
- func setupModelWithConnection(t testing.TB) (*testModel, *fakeConnection, config.FolderConfiguration, context.CancelFunc) {
 
- 	t.Helper()
 
- 	w, fcfg, cancel := newDefaultCfgWrapper()
 
- 	m, fc := setupModelWithConnectionFromWrapper(t, w)
 
- 	return m, fc, fcfg, cancel
 
- }
 
- func setupModelWithConnectionFromWrapper(t testing.TB, w config.Wrapper) (*testModel, *fakeConnection) {
 
- 	t.Helper()
 
- 	m := setupModel(t, w)
 
- 	fc := addFakeConn(m, device1, "default")
 
- 	fc.folder = "default"
 
- 	_ = m.ScanFolder("default")
 
- 	return m, fc
 
- }
 
- func setupModel(t testing.TB, w config.Wrapper) *testModel {
 
- 	t.Helper()
 
- 	m := newModel(t, w, myID, nil)
 
- 	m.ServeBackground()
 
- 	<-m.started
 
- 	m.ScanFolders()
 
- 	return m
 
- }
 
- type testModel struct {
 
- 	*model
 
- 	t        testing.TB
 
- 	cancel   context.CancelFunc
 
- 	evCancel context.CancelFunc
 
- 	stopped  chan struct{}
 
- }
 
- func newModel(t testing.TB, cfg config.Wrapper, id protocol.DeviceID, protectedFiles []string) *testModel {
 
- 	t.Helper()
 
- 	evLogger := events.NewLogger()
 
- 	mdb, err := sqlite.Open(t.TempDir())
 
- 	if err != nil {
 
- 		t.Fatal(err)
 
- 	}
 
- 	t.Cleanup(func() {
 
- 		mdb.Close()
 
- 	})
 
- 	m := NewModel(cfg, id, mdb, protectedFiles, evLogger, protocol.NewKeyGenerator()).(*model)
 
- 	ctx, cancel := context.WithCancel(context.Background())
 
- 	go evLogger.Serve(ctx)
 
- 	return &testModel{
 
- 		model:    m,
 
- 		evCancel: cancel,
 
- 		stopped:  make(chan struct{}),
 
- 		t:        t,
 
- 	}
 
- }
 
- func (m *testModel) ServeBackground() {
 
- 	ctx, cancel := context.WithCancel(context.Background())
 
- 	m.cancel = cancel
 
- 	go func() {
 
- 		m.model.Serve(ctx)
 
- 		close(m.stopped)
 
- 	}()
 
- 	<-m.started
 
- }
 
- func (m *testModel) testCurrentFolderFile(folder string, file string) (protocol.FileInfo, bool) {
 
- 	f, ok, err := m.model.CurrentFolderFile(folder, file)
 
- 	must(m.t, err)
 
- 	return f, ok
 
- }
 
- func (m *testModel) testCompletion(device protocol.DeviceID, folder string) FolderCompletion {
 
- 	comp, err := m.Completion(device, folder)
 
- 	must(m.t, err)
 
- 	return comp
 
- }
 
- func cleanupModel(m *testModel) {
 
- 	if m.cancel != nil {
 
- 		m.cancel()
 
- 		<-m.stopped
 
- 	}
 
- 	m.evCancel()
 
- 	m.sdb.Close()
 
- 	os.Remove(m.cfg.ConfigPath())
 
- }
 
- func cleanupModelAndRemoveDir(m *testModel, dir string) {
 
- 	cleanupModel(m)
 
- 	os.RemoveAll(dir)
 
- }
 
- type alwaysChangedKey struct {
 
- 	fs   fs.Filesystem
 
- 	name string
 
- }
 
- // alwaysChanges is an ignore.ChangeDetector that always returns true on Changed()
 
- type alwaysChanged struct {
 
- 	seen map[alwaysChangedKey]struct{}
 
- }
 
- func newAlwaysChanged() *alwaysChanged {
 
- 	return &alwaysChanged{
 
- 		seen: make(map[alwaysChangedKey]struct{}),
 
- 	}
 
- }
 
- func (c *alwaysChanged) Remember(fs fs.Filesystem, name string, _ time.Time) {
 
- 	c.seen[alwaysChangedKey{fs, name}] = struct{}{}
 
- }
 
- func (c *alwaysChanged) Reset() {
 
- 	c.seen = make(map[alwaysChangedKey]struct{})
 
- }
 
- func (c *alwaysChanged) Seen(fs fs.Filesystem, name string) bool {
 
- 	_, ok := c.seen[alwaysChangedKey{fs, name}]
 
- 	return ok
 
- }
 
- func (*alwaysChanged) Changed() bool {
 
- 	return true
 
- }
 
- // Reach in and update the ignore matcher to one that always does
 
- // reloads when asked to, instead of checking file mtimes. This is
 
- // because we will be changing the files on disk often enough that the
 
- // mtimes will be unreliable to determine change status.
 
- func folderIgnoresAlwaysReload(t testing.TB, m *testModel, fcfg config.FolderConfiguration) {
 
- 	t.Helper()
 
- 	m.removeFolder(fcfg)
 
- 	ignores := ignore.New(fcfg.Filesystem(), ignore.WithCache(true), ignore.WithChangeDetector(newAlwaysChanged()))
 
- 	m.mut.Lock()
 
- 	m.addAndStartFolderLockedWithIgnores(fcfg, ignores)
 
- 	m.mut.Unlock()
 
- }
 
- func basicClusterConfig(local, remote protocol.DeviceID, folders ...string) *protocol.ClusterConfig {
 
- 	var cc protocol.ClusterConfig
 
- 	for _, folder := range folders {
 
- 		cc.Folders = append(cc.Folders, protocol.Folder{
 
- 			ID: folder,
 
- 			Devices: []protocol.Device{
 
- 				{
 
- 					ID: local,
 
- 				},
 
- 				{
 
- 					ID: remote,
 
- 				},
 
- 			},
 
- 		})
 
- 	}
 
- 	return &cc
 
- }
 
- func localIndexUpdate(m *testModel, folder string, fs []protocol.FileInfo) {
 
- 	m.sdb.Update(folder, protocol.LocalDeviceID, fs)
 
- 	seq, err := m.sdb.GetDeviceSequence(folder, protocol.LocalDeviceID)
 
- 	if err != nil {
 
- 		panic(err)
 
- 	}
 
- 	filenames := make([]string, len(fs))
 
- 	for i, file := range fs {
 
- 		filenames[i] = file.Name
 
- 	}
 
- 	m.evLogger.Log(events.LocalIndexUpdated, map[string]interface{}{
 
- 		"folder":    folder,
 
- 		"items":     len(fs),
 
- 		"filenames": filenames,
 
- 		"sequence":  seq,
 
- 		"version":   seq, // legacy for sequence
 
- 	})
 
- }
 
- func newDeviceConfiguration(defaultCfg config.DeviceConfiguration, id protocol.DeviceID, name string) config.DeviceConfiguration {
 
- 	cfg := defaultCfg.Copy()
 
- 	cfg.DeviceID = id
 
- 	cfg.Name = name
 
- 	return cfg
 
- }
 
- func replace(t testing.TB, w config.Wrapper, to config.Configuration) {
 
- 	t.Helper()
 
- 	waiter, err := w.Modify(func(cfg *config.Configuration) {
 
- 		*cfg = to
 
- 	})
 
- 	if err != nil {
 
- 		t.Fatal(err)
 
- 	}
 
- 	waiter.Wait()
 
- }
 
- func pauseFolder(t testing.TB, w config.Wrapper, id string, paused bool) {
 
- 	t.Helper()
 
- 	waiter, err := w.Modify(func(cfg *config.Configuration) {
 
- 		_, i, _ := cfg.Folder(id)
 
- 		cfg.Folders[i].Paused = paused
 
- 	})
 
- 	if err != nil {
 
- 		t.Fatal(err)
 
- 	}
 
- 	waiter.Wait()
 
- }
 
- func setFolder(t testing.TB, w config.Wrapper, fcfg config.FolderConfiguration) {
 
- 	t.Helper()
 
- 	waiter, err := w.Modify(func(cfg *config.Configuration) {
 
- 		cfg.SetFolder(fcfg)
 
- 	})
 
- 	if err != nil {
 
- 		t.Fatal(err)
 
- 	}
 
- 	waiter.Wait()
 
- }
 
- func pauseDevice(t testing.TB, w config.Wrapper, id protocol.DeviceID, paused bool) {
 
- 	t.Helper()
 
- 	waiter, err := w.Modify(func(cfg *config.Configuration) {
 
- 		_, i, _ := cfg.Device(id)
 
- 		cfg.Devices[i].Paused = paused
 
- 	})
 
- 	if err != nil {
 
- 		t.Fatal(err)
 
- 	}
 
- 	waiter.Wait()
 
- }
 
- func setDevice(t testing.TB, w config.Wrapper, device config.DeviceConfiguration) {
 
- 	t.Helper()
 
- 	waiter, err := w.Modify(func(cfg *config.Configuration) {
 
- 		cfg.SetDevice(device)
 
- 	})
 
- 	if err != nil {
 
- 		t.Fatal(err)
 
- 	}
 
- 	waiter.Wait()
 
- }
 
- func addDevice2(t testing.TB, w config.Wrapper, fcfg config.FolderConfiguration) {
 
- 	waiter, err := w.Modify(func(cfg *config.Configuration) {
 
- 		cfg.SetDevice(newDeviceConfiguration(cfg.Defaults.Device, device2, "device2"))
 
- 		fcfg.Devices = append(fcfg.Devices, config.FolderDeviceConfiguration{DeviceID: device2})
 
- 		cfg.SetFolder(fcfg)
 
- 	})
 
- 	must(t, err)
 
- 	waiter.Wait()
 
- }
 
- func writeFile(t testing.TB, filesystem fs.Filesystem, name string, data []byte) {
 
- 	t.Helper()
 
- 	fd, err := filesystem.Create(name)
 
- 	must(t, err)
 
- 	defer fd.Close()
 
- 	_, err = fd.Write(data)
 
- 	must(t, err)
 
- }
 
- func writeFilePerm(t testing.TB, filesystem fs.Filesystem, name string, data []byte, perm fs.FileMode) {
 
- 	t.Helper()
 
- 	writeFile(t, filesystem, name, data)
 
- 	must(t, filesystem.Chmod(name, perm))
 
- }
 
 
  |