2015-03-23 08:15:53 +03:00
|
|
|
// Copyright 2015 Google Inc. All Rights Reserved.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package samples
|
|
|
|
|
|
|
|
import (
|
2015-03-23 08:26:15 +03:00
|
|
|
"fmt"
|
2015-03-23 08:15:53 +03:00
|
|
|
"io"
|
2015-03-23 08:26:15 +03:00
|
|
|
"io/ioutil"
|
2015-03-24 01:07:02 +03:00
|
|
|
"log"
|
2015-03-23 08:26:15 +03:00
|
|
|
"os/exec"
|
2015-03-23 08:31:52 +03:00
|
|
|
"path"
|
|
|
|
"sync"
|
2015-03-23 08:15:53 +03:00
|
|
|
|
2015-03-23 08:26:15 +03:00
|
|
|
"github.com/jacobsa/ogletest"
|
2015-03-23 08:15:53 +03:00
|
|
|
"golang.org/x/net/context"
|
|
|
|
)
|
|
|
|
|
|
|
|
// A struct that implements common behavior needed by tests in the samples/
|
|
|
|
// directory where the file system is mounted by a subprocess. Use it as an
|
|
|
|
// embedded field in your test fixture, calling its SetUp method from your
|
|
|
|
// SetUp method after setting the MountType and MountFlags fields.
|
|
|
|
type SubprocessTest struct {
|
|
|
|
// The type of the file system to mount. Must be recognized by mount_sample.
|
|
|
|
MountType string
|
|
|
|
|
|
|
|
// Additional flags to be passed to the mount_sample tool.
|
|
|
|
MountFlags []string
|
|
|
|
|
|
|
|
// A context object that can be used for long-running operations.
|
|
|
|
Ctx context.Context
|
|
|
|
|
|
|
|
// The directory at which the file system is mounted.
|
|
|
|
Dir string
|
|
|
|
|
|
|
|
// Anothing non-nil in this slice will be closed by TearDown. The test will
|
|
|
|
// fail if closing fails.
|
|
|
|
ToClose []io.Closer
|
|
|
|
|
2015-03-23 08:26:15 +03:00
|
|
|
mountCmd *exec.Cmd
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mount the file system and initialize the other exported fields of the
|
|
|
|
// struct. Panics on error.
|
|
|
|
//
|
|
|
|
// REQUIRES: t.FileSystem has been set.
|
|
|
|
func (t *SubprocessTest) SetUp(ti *ogletest.TestInfo) {
|
|
|
|
err := t.initialize()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-23 08:31:52 +03:00
|
|
|
// Set by buildMountSample.
|
|
|
|
var mountSamplePath string
|
|
|
|
var mountSampleErr error
|
|
|
|
var mountSampleOnce sync.Once
|
|
|
|
|
2015-03-23 08:26:15 +03:00
|
|
|
// Build the mount_sample tool if it has not yet been built for this process.
|
|
|
|
// Return a path to the binary.
|
2015-03-23 08:31:52 +03:00
|
|
|
func buildMountSample() (toolPath string, err error) {
|
|
|
|
// Build if we haven't yet.
|
|
|
|
mountSampleOnce.Do(func() {
|
|
|
|
// Create a temporary directory.
|
|
|
|
tempDir, err := ioutil.TempDir("", "")
|
|
|
|
if err != nil {
|
|
|
|
mountSampleErr = fmt.Errorf("TempDir: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
mountSamplePath = path.Join(tempDir, "mount_sample")
|
|
|
|
|
|
|
|
// Build the command.
|
|
|
|
cmd := exec.Command(
|
|
|
|
"go",
|
|
|
|
"build",
|
|
|
|
"-o",
|
2015-03-24 00:53:52 +03:00
|
|
|
mountSamplePath,
|
|
|
|
"github.com/jacobsa/fuse/samples/mount_sample")
|
2015-03-23 08:31:52 +03:00
|
|
|
|
|
|
|
output, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
mountSampleErr = fmt.Errorf(
|
|
|
|
"mount_sample exited with %v, output:\n%s",
|
|
|
|
err,
|
|
|
|
string(output))
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
if mountSampleErr != nil {
|
|
|
|
err = mountSampleErr
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
toolPath = mountSamplePath
|
|
|
|
return
|
|
|
|
}
|
2015-03-23 08:26:15 +03:00
|
|
|
|
|
|
|
// Invoke mount_sample, returning a running command.
|
2015-03-23 08:32:50 +03:00
|
|
|
func invokeMountSample(path string, args []string) (cmd *exec.Cmd, err error) {
|
|
|
|
cmd = exec.Command(path, args...)
|
|
|
|
if err = cmd.Start(); err != nil {
|
|
|
|
err = fmt.Errorf("Start: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
2015-03-23 08:26:15 +03:00
|
|
|
|
|
|
|
// Like SetUp, but doens't panic.
|
|
|
|
func (t *SubprocessTest) initialize() (err error) {
|
|
|
|
// Initialize the context.
|
|
|
|
t.Ctx = context.Background()
|
|
|
|
|
|
|
|
// Set up a temporary directory.
|
|
|
|
t.Dir, err = ioutil.TempDir("", "sample_test")
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("TempDir: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build the mount_sample tool.
|
|
|
|
toolPath, err := buildMountSample()
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("buildMountSample: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Invoke it.
|
|
|
|
args := []string{"--type", t.MountType}
|
|
|
|
args = append(args, t.MountFlags...)
|
|
|
|
|
|
|
|
t.mountCmd, err = invokeMountSample(toolPath, args)
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("invokeMountSample: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(jacobsa): Probably need some sort of signalling (on stderr? write to
|
|
|
|
// a flag-controlled file?) when WaitForReady has returned.
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unmount the file system and clean up. Panics on error.
|
|
|
|
func (t *SubprocessTest) TearDown() {
|
|
|
|
err := t.destroy()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Like TearDown, but doesn't panic.
|
|
|
|
func (t *SubprocessTest) destroy() (err error) {
|
|
|
|
// Close what is necessary.
|
|
|
|
for _, c := range t.ToClose {
|
|
|
|
if c == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
ogletest.ExpectEq(nil, c.Close())
|
|
|
|
}
|
|
|
|
|
2015-03-24 01:07:02 +03:00
|
|
|
// If we didn't try to mount the file system, there's nothing further to do.
|
2015-03-23 08:26:15 +03:00
|
|
|
if t.mountCmd == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-03-24 01:07:02 +03:00
|
|
|
// In the background, initiate an unmount.
|
|
|
|
unmountErrChan := make(chan error)
|
|
|
|
go func() {
|
|
|
|
unmountErrChan <- unmount(t.Dir)
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Make sure we wait for the unmount, even if we've already returned early in
|
|
|
|
// error. Return its error if we haven't seen any other error.
|
|
|
|
defer func() {
|
|
|
|
unmountErr := <-unmountErrChan
|
|
|
|
if unmountErr != nil {
|
|
|
|
if err != nil {
|
|
|
|
log.Println("unmount:", unmountErr)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
err = fmt.Errorf("unmount: %v", unmountErr)
|
|
|
|
}
|
|
|
|
}()
|
2015-03-23 08:26:15 +03:00
|
|
|
|
|
|
|
// Wait for the subprocess.
|
|
|
|
if err = t.mountCmd.Wait(); err != nil {
|
|
|
|
err = fmt.Errorf("Cmd.Wait: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
2015-03-23 08:15:53 +03:00
|
|
|
}
|