Skip to content

Commit

Permalink
Merge pull request #2266 from restic/fix-windows-tests
Browse files Browse the repository at this point in the history
Fix tests on Windows
  • Loading branch information
fd0 committed May 5, 2019
2 parents 7dcd296 + 920d458 commit cf3fc2a
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 118 deletions.
98 changes: 0 additions & 98 deletions internal/archiver/archiver_nowin_test.go

This file was deleted.

114 changes: 95 additions & 19 deletions internal/archiver/archiver_test.go
Expand Up @@ -14,6 +14,7 @@ import (
"testing"
"time"

"github.com/google/go-cmp/cmp"
"github.com/restic/restic/internal/checker"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/fs"
Expand Down Expand Up @@ -556,11 +557,12 @@ func TestFileChanged(t *testing.T) {
}

var tests = []struct {
Name string
Content []byte
Modify func(t testing.TB, filename string)
IgnoreInode bool
Check bool
Name string
SkipForWindows bool
Content []byte
Modify func(t testing.TB, filename string)
IgnoreInode bool
SameFile bool
}{
{
Name: "same-content-new-file",
Expand All @@ -579,6 +581,10 @@ func TestFileChanged(t *testing.T) {
},
{
Name: "new-content-same-timestamp",
// on Windows, there's no "create time" field users cannot modify,
// so we're unable to detect if a file has been modified when the
// timestamps are reset, so we skip this test for Windows
SkipForWindows: true,
Modify: func(t testing.TB, filename string) {
fi, err := os.Stat(filename)
if err != nil {
Expand Down Expand Up @@ -622,12 +628,16 @@ func TestFileChanged(t *testing.T) {
setTimestamp(t, filename, fi.ModTime(), fi.ModTime())
},
IgnoreInode: true,
Check: true,
SameFile: true,
},
}

for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
if runtime.GOOS == "windows" && test.SkipForWindows {
t.Skip("don't run test on Windows")
}

tempdir, cleanup := restictest.TempDir(t)
defer cleanup()

Expand All @@ -648,10 +658,15 @@ func TestFileChanged(t *testing.T) {
test.Modify(t, filename)

fiAfter := lstat(t, filename)
if test.Check == fileChanged(fiAfter, node, test.IgnoreInode) {
if test.Check {

if test.SameFile {
// file should be detected as unchanged
if fileChanged(fiAfter, node, test.IgnoreInode) {
t.Fatalf("unmodified file detected as changed")
} else {
}
} else {
// file should be detected as changed
if !fileChanged(fiAfter, node, test.IgnoreInode) && !test.SameFile {
t.Fatalf("modified file detected as unchanged")
}
}
Expand Down Expand Up @@ -2007,16 +2022,77 @@ func (f fileStat) Stat() (os.FileInfo, error) {
return f.fi, nil
}

type wrappedFileInfo struct {
os.FileInfo
sys interface{}
mode os.FileMode
}
// used by wrapFileInfo, use untyped const in order to avoid having a version
// of wrapFileInfo for each OS
const (
mockFileInfoMode = 0400
mockFileInfoUID = 51234
mockFileInfoGID = 51235
)

func (fi wrappedFileInfo) Sys() interface{} {
return fi.sys
}
func TestMetadataChanged(t *testing.T) {
files := TestDir{
"testfile": TestFile{
Content: "foo bar test file",
},
}

tempdir, repo, cleanup := prepareTempdirRepoSrc(t, files)
defer cleanup()

back := fs.TestChdir(t, tempdir)
defer back()

// get metadata
fi := lstat(t, "testfile")
want, err := restic.NodeFromFileInfo("testfile", fi)
if err != nil {
t.Fatal(err)
}

fs := &StatFS{
FS: fs.Local{},
OverrideLstat: map[string]os.FileInfo{
"testfile": fi,
},
}

snapshotID, node2 := snapshot(t, repo, fs, restic.ID{}, "testfile")

// set some values so we can then compare the nodes
want.Content = node2.Content
want.Path = ""
want.ExtendedAttributes = nil

// make sure that metadata was recorded successfully
if !cmp.Equal(want, node2) {
t.Fatalf("metadata does not match:\n%v", cmp.Diff(want, node2))
}

// modify the mode by wrapping it in a new struct, uses the consts defined above
fs.OverrideLstat["testfile"] = wrapFileInfo(t, fi)

// set the override values in the 'want' node which
want.Mode = 0400
// ignore UID and GID on Windows
if runtime.GOOS != "windows" {
want.UID = 51234
want.GID = 51235
}
// no user and group name
want.User = ""
want.Group = ""

// make another snapshot
snapshotID, node3 := snapshot(t, repo, fs, snapshotID, "testfile")

// make sure that metadata was recorded successfully
if !cmp.Equal(want, node3) {
t.Fatalf("metadata does not match:\n%v", cmp.Diff(want, node3))
}

// make sure the content matches
TestEnsureFileContent(context.Background(), t, repo, "testfile", node3, files["testfile"].(TestFile))

func (fi wrappedFileInfo) Mode() os.FileMode {
return fi.mode
checker.TestCheckRepo(t, repo)
}
41 changes: 41 additions & 0 deletions internal/archiver/archiver_unix_test.go
@@ -0,0 +1,41 @@
// +build !windows

package archiver

import (
"os"
"syscall"
"testing"
)

type wrappedFileInfo struct {
os.FileInfo
sys interface{}
mode os.FileMode
}

func (fi wrappedFileInfo) Sys() interface{} {
return fi.sys
}

func (fi wrappedFileInfo) Mode() os.FileMode {
return fi.mode
}

// wrapFileInfo returns a new os.FileInfo with the mode, owner, and group fields changed.
func wrapFileInfo(t testing.TB, fi os.FileInfo) os.FileInfo {
// get the underlying stat_t and modify the values
stat := fi.Sys().(*syscall.Stat_t)
stat.Mode = mockFileInfoMode
stat.Uid = mockFileInfoUID
stat.Gid = mockFileInfoGID

// wrap the os.FileInfo so we can return a modified stat_t
res := wrappedFileInfo{
FileInfo: fi,
sys: stat,
mode: mockFileInfoMode,
}

return res
}
28 changes: 28 additions & 0 deletions internal/archiver/archiver_windows_test.go
@@ -0,0 +1,28 @@
// +build windows

package archiver

import (
"os"
"testing"
)

type wrappedFileInfo struct {
os.FileInfo
mode os.FileMode
}

func (fi wrappedFileInfo) Mode() os.FileMode {
return fi.mode
}

// wrapFileInfo returns a new os.FileInfo with the mode, owner, and group fields changed.
func wrapFileInfo(t testing.TB, fi os.FileInfo) os.FileInfo {
// wrap the os.FileInfo and return the modified mode, uid and gid are ignored on Windows
res := wrappedFileInfo{
FileInfo: fi,
mode: mockFileInfoMode,
}

return res
}
3 changes: 2 additions & 1 deletion internal/restic/node_windows.go
Expand Up @@ -76,5 +76,6 @@ func (s statWin) mtim() syscall.Timespec {
}

func (s statWin) ctim() syscall.Timespec {
return syscall.NsecToTimespec(s.CreationTime.Nanoseconds())
// Windows does not have the concept of a "change time" in the sense Unix uses it, so we're using the LastWriteTime here.
return syscall.NsecToTimespec(s.LastWriteTime.Nanoseconds())
}

0 comments on commit cf3fc2a

Please sign in to comment.