Commit f48dd028 authored by Jakob Unterwurzacher's avatar Jakob Unterwurzacher Committed by Han-Wen Nienhuys

fuse: add unit-tests for loopback Utimens()

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 ( https://github.com/rfjakob/gocryptfs/issues/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
[...]
parent 41df6ec8
// Copyright 2018 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)
}
// Copyright 2018 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)
}
// Copyright 2018 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 (
"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("Stat", 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("utimensFn", 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: got %v want %v", a2.Atime, t0.Unix())
}
// Change mtime, keep atime
t1 := time.Unix(t0sec+123, 0)
status = utimensFn(nil, &t1)
if !status.Ok() {
t.Fatal("utimensFn", status)
}
err = syscall.Stat(path, &st)
if err != nil {
t.Fatal("Stat", 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("got mtime %v, want %v", a3.Mtime, t1.Unix())
}
// 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("utimensFn", status)
}
err = syscall.Stat(path, &st)
if err != nil {
t.Fatal("Stat", err)
}
var a4 fuse.Attr
a4.FromStat(&st)
if a4.Atime != uint64(ta.Unix()) {
t.Errorf("got atime %v, want %v", a4.Atime, ta.Unix())
}
if a4.Mtime != uint64(tm.Unix()) {
t.Errorf("got mtime %v, want %v", a4.Mtime, tm.Unix())
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment