// Copyright (C) 2018 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 ( "path/filepath" "testing" "github.com/d4l3k/messagediff" "github.com/syncthing/syncthing/lib/build" "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/rand" ) type unifySubsCase struct { in []string // input to unifySubs exists []string // paths that exist in the database out []string // expected output } func unifySubsCases() []unifySubsCase { cases := []unifySubsCase{ { // 0. trailing slashes are cleaned, known paths are just passed on []string{"foo/", "bar//"}, []string{"foo", "bar"}, []string{"bar", "foo"}, // the output is sorted }, { // 1. "foo/bar" gets trimmed as it's covered by foo []string{"foo", "bar/", "foo/bar/"}, []string{"foo", "bar"}, []string{"bar", "foo"}, }, { // 2. "" gets simplified to the empty list; ie scan all []string{"foo", ""}, []string{"foo"}, nil, }, { // 3. "foo/bar" is unknown, but it's kept // because its parent is known []string{"foo/bar"}, []string{"foo"}, []string{"foo/bar"}, }, { // 4. two independent known paths, both are kept // "usr/lib" is not a prefix of "usr/libexec" []string{"usr/lib", "usr/libexec"}, []string{"usr", "usr/lib", "usr/libexec"}, []string{"usr/lib", "usr/libexec"}, }, { // 5. "usr/lib" is a prefix of "usr/lib/exec" []string{"usr/lib", "usr/lib/exec"}, []string{"usr", "usr/lib", "usr/libexec"}, []string{"usr/lib"}, }, { // 6. .stignore and .stfolder are special and are passed on // verbatim even though they are unknown []string{config.DefaultMarkerName, ".stignore"}, []string{}, []string{config.DefaultMarkerName, ".stignore"}, }, { // 7. but the presence of something else unknown forces an actual // scan []string{config.DefaultMarkerName, ".stignore", "foo/bar"}, []string{}, []string{config.DefaultMarkerName, ".stignore", "foo"}, }, { // 8. explicit request to scan all nil, []string{"foo"}, nil, }, { // 9. empty list of subs []string{}, []string{"foo"}, nil, }, { // 10. absolute path []string{"/foo"}, []string{"foo"}, []string{"foo"}, }, } if build.IsWindows { // Fixup path separators for i := range cases { for j, p := range cases[i].in { cases[i].in[j] = filepath.FromSlash(p) } for j, p := range cases[i].exists { cases[i].exists[j] = filepath.FromSlash(p) } for j, p := range cases[i].out { cases[i].out[j] = filepath.FromSlash(p) } } } return cases } func unifyExists(f string, tc unifySubsCase) bool { for _, e := range tc.exists { if f == e { return true } } return false } func TestUnifySubs(t *testing.T) { cases := unifySubsCases() for i, tc := range cases { exists := func(f string) bool { return unifyExists(f, tc) } out := unifySubs(tc.in, exists) if diff, equal := messagediff.PrettyDiff(tc.out, out); !equal { t.Errorf("Case %d failed; got %v, expected %v, diff:\n%s", i, out, tc.out, diff) } } } func BenchmarkUnifySubs(b *testing.B) { cases := unifySubsCases() b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { for _, tc := range cases { exists := func(f string) bool { return unifyExists(f, tc) } unifySubs(tc.in, exists) } } } func TestSetPlatformData(t *testing.T) { // Checks that setPlatformData runs without error when applied to a temp // file, named differently than the given FileInfo. fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32)) if fd, err := fs.Create("file.tmp"); err != nil { t.Fatal(err) } else { fd.Close() } xattr := []protocol.Xattr{{Name: "user.foo", Value: []byte("bar")}} fi := &protocol.FileInfo{ Name: "should be ignored", Permissions: 0o400, ModifiedS: 1234567890, Platform: protocol.PlatformData{ Linux: &protocol.XattrData{Xattrs: xattr}, Darwin: &protocol.XattrData{Xattrs: xattr}, FreeBSD: &protocol.XattrData{Xattrs: xattr}, NetBSD: &protocol.XattrData{Xattrs: xattr}, }, } // Minimum required to support setPlatformData sr := &sendReceiveFolder{ folder: folder{ FolderConfiguration: config.FolderConfiguration{ SyncXattrs: true, }, mtimefs: fs, }, } if err := sr.setPlatformData(fi, "file.tmp"); err != nil { t.Error(err) } }