// 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" "sync" "time" "github.com/syncthing/syncthing/lib/protocol" protocolmocks "github.com/syncthing/syncthing/lib/protocol/mocks" "github.com/syncthing/syncthing/lib/rand" "github.com/syncthing/syncthing/lib/scanner" ) type downloadProgressMessage struct { folder string updates []protocol.FileDownloadProgressUpdate } func newFakeConnection(id protocol.DeviceID, model Model) *fakeConnection { f := &fakeConnection{ Connection: new(protocolmocks.Connection), id: id, model: model, closed: make(chan struct{}), } f.RequestCalls(func(ctx context.Context, folder, name string, blockNo int, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) { return f.fileData[name], nil }) f.DeviceIDReturns(id) f.ConnectionIDReturns(rand.String(16)) f.CloseCalls(func(err error) { f.closeOnce.Do(func() { close(f.closed) model.Closed(f, err) }) f.ClosedReturns(f.closed) }) f.StringReturns(rand.String(8)) return f } type fakeConnection struct { *protocolmocks.Connection id protocol.DeviceID downloadProgressMessages []downloadProgressMessage files []protocol.FileInfo fileData map[string][]byte folder string model Model closed chan struct{} closeOnce sync.Once mut sync.Mutex } func (f *fakeConnection) setIndexFn(fn func(_ context.Context, folder string, fs []protocol.FileInfo) error) { f.IndexCalls(fn) f.IndexUpdateCalls(fn) } func (f *fakeConnection) DownloadProgress(_ context.Context, 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, localFlags uint32) { blockSize := protocol.BlockSize(int64(len(data))) blocks, _ := scanner.Blocks(context.TODO(), bytes.NewReader(data), blockSize, int64(len(data)), nil, true) file := protocol.FileInfo{ Name: name, Type: ftype, Version: version, Sequence: time.Now().UnixNano(), LocalFlags: localFlags, } switch ftype { case protocol.FileInfoTypeFile, protocol.FileInfoTypeDirectory: file.ModifiedS = time.Now().Unix() file.Permissions = flags if ftype == protocol.FileInfoTypeFile { file.Size = int64(len(data)) file.RawBlockSize = blockSize file.Blocks = blocks } default: // Symlink file.Name = name file.Type = ftype file.Version = version file.SymlinkTarget = string(data) file.NoPermissions = true } f.files = append(f.files, file) if f.fileData == nil { f.fileData = make(map[string][]byte) } f.fileData[name] = data } func (f *fakeConnection) addFileWithLocalFlags(name string, ftype protocol.FileInfoType, localFlags uint32) { f.mut.Lock() defer f.mut.Unlock() var version protocol.Vector version = version.Update(f.id.Short()) f.addFileLocked(name, 0, ftype, nil, version, localFlags) } 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, 0) } 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()), 0) 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() { toSend := make([]protocol.FileInfo, len(f.files)) for i := range f.files { toSend[i] = prepareFileInfoForIndex(f.files[i]) } f.model.IndexUpdate(f, &protocol.IndexUpdate{Folder: f.folder, Files: toSend}) } func addFakeConn(m *testModel, dev protocol.DeviceID, folderID string) *fakeConnection { fc := newFakeConnection(dev, m) fc.folder = folderID m.AddConnection(fc, protocol.Hello{}) m.ClusterConfig(fc, &protocol.ClusterConfig{ Folders: []protocol.Folder{ { ID: folderID, Devices: []protocol.Device{ {ID: myID}, {ID: dev}, }, }, }, }) return fc }