Skip to content

Commit

Permalink
fuse: add unit-tests for loopback Utimens()
Browse files Browse the repository at this point in the history
Tests loopbackFileSystem.Utimens() and loopbackfile.Utimens()
at 1-second precision.

The exising TestUtimesNano() test only works on Linux
because it relies on syscall.UtimesNano(), which is not
available on Darwin. The new tests call the Utimens()
functions directly, bypassing FUSE, and work on all
platforms.

Because Darwin does not have syscall.UtimesNano(),
getting the Utimens() implementation right is hard.

The tests currently fail on Darwin, underlining the
need for them ( rfjakob/gocryptfs#229 ):

$ go test ./fuse/nodefs
[...]
--- FAIL: TestLoopbackFileUtimens (0.00s)
	helpers.go:51: mtime has changed: 1525384186 -> 1073
	helpers.go:70: atime has changed: 1525291058 -> 1073
[...]

$ go test ./fuse/pathfs
--- FAIL: TestLoopbackFileSystemUtimens (0.00s)
	helpers.go:51: mtime has changed: 1525384379 -> 1073
	helpers.go:70: atime has changed: 1525291058 -> 1073
[...]
  • Loading branch information
rfjakob committed May 3, 2018
1 parent 41df6ec commit bb848eb
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 0 deletions.
32 changes: 32 additions & 0 deletions fuse/nodefs/files_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package nodefs

import (
"io/ioutil"
"os"
"testing"
"time"

"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal/testutil"
)

// Check that loopbackFile.Utimens() works as expected
func TestLoopbackFileUtimens(t *testing.T) {
f2, err := ioutil.TempFile("", "TestLoopbackFileUtimens")
if err != nil {
t.Fatal(err)
}
path := f2.Name()
defer os.Remove(path)
defer f2.Close()
f := NewLoopbackFile(f2)

utimensFn := func(atime *time.Time, mtime *time.Time) fuse.Status {
return f.Utimens(atime, mtime)
}
testutil.TestLoopbackUtimens(t, path, utimensFn)
}
35 changes: 35 additions & 0 deletions fuse/pathfs/loopback_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pathfs

import (
"io/ioutil"
"os"
"path/filepath"
"syscall"
"testing"
"time"

"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal/testutil"
)

// Check that loopbackFileSystem.Utimens() works as expected
func TestLoopbackFileSystemUtimens(t *testing.T) {
fs := NewLoopbackFileSystem(os.TempDir())
f, err := ioutil.TempFile("", "TestLoopbackFileSystemUtimens")
if err != nil {
t.Fatal(err)
}
path := f.Name()
name := filepath.Base(path)
f.Close()
defer syscall.Unlink(path)

utimensFn := func(atime *time.Time, mtime *time.Time) fuse.Status {
return fs.Utimens(name, atime, mtime, nil)
}
testutil.TestLoopbackUtimens(t, path, utimensFn)
}
107 changes: 107 additions & 0 deletions internal/testutil/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package testutil

import (
"io/ioutil"
"os"
"syscall"
"testing"
"time"

"github.com/hanwen/go-fuse/fuse"
)

// Check that loopback Utimens() works as expected.
// Called by TestLoopbackFileUtimens and TestLoopbackFileSystemUtimens.
//
// Parameters:
// path ........ path to the backing file
// utimensFn ... Utimens() function that acts on the backing file
func TestLoopbackUtimens(t *testing.T, path string, utimensFn func(atime *time.Time, mtime *time.Time) fuse.Status) {
// Arbitrary date: 05/02/2018 @ 7:57pm (UTC)
t0sec := int64(1525291058)

// Read original timestamp
var st syscall.Stat_t
err := syscall.Stat(path, &st)
if err != nil {
t.Fatal(err)
}
// FromStat handles the differently-named Stat_t fields on Linux and
// Darwin
var a1 fuse.Attr
a1.FromStat(&st)

// Change atime, keep mtime
t0 := time.Unix(t0sec, 0)
status := utimensFn(&t0, nil)
if !status.Ok() {
t.Fatal(status)
}
err = syscall.Stat(path, &st)
if err != nil {
t.Fatal(err)
}
var a2 fuse.Attr
a2.FromStat(&st)
if a1.Mtime != a2.Mtime {
t.Errorf("mtime has changed: %v -> %v", a1.Mtime, a2.Mtime)
}
if a2.Atime != uint64(t0.Unix()) {
t.Errorf("wrong atime")
}

// Change mtime, keep atime
t1 := time.Unix(t0sec+123, 0)
status = utimensFn(nil, &t1)
if !status.Ok() {
t.Fatal(status)
}
err = syscall.Stat(path, &st)
if err != nil {
t.Fatal(err)
}
var a3 fuse.Attr
a3.FromStat(&st)
if a2.Atime != a3.Atime {
t.Errorf("atime has changed: %v -> %v", a2.Atime, a3.Atime)
}
if a3.Mtime != uint64(t1.Unix()) {
t.Errorf("wrong mtime")
}

// Change both mtime and atime
ta := time.Unix(t0sec+456, 0)
tm := time.Unix(t0sec+789, 0)
status = utimensFn(&ta, &tm)
if !status.Ok() {
t.Fatal(status)
}
err = syscall.Stat(path, &st)
if err != nil {
t.Fatal(err)
}
var a4 fuse.Attr
a4.FromStat(&st)
if a4.Atime != uint64(ta.Unix()) {
t.Errorf("wrong atime")
}
if a4.Mtime != uint64(tm.Unix()) {
t.Errorf("wrong mtime")
}
}

// Check that loopbackFile.Utimens() works as expected
func TestLoopbackFileUtimens(t *testing.T) {
f2, err := ioutil.TempFile("", "TestLoopbackFileUtimens")
if err != nil {
t.Fatal(err)
}
path := f2.Name()
defer os.Remove(path)
defer f2.Close()

}

0 comments on commit bb848eb

Please sign in to comment.