github.com/scaleoutsean/fusego@v0.0.0-20220224074057-4a6429e46bb8/samples/statfs/statfs_test.go (about)

     1  // Copyright 2015 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package statfs_test
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"io/ioutil"
    21  	"os/exec"
    22  	"path"
    23  	"path/filepath"
    24  	"runtime"
    25  	"strconv"
    26  	"syscall"
    27  	"testing"
    28  
    29  	"github.com/scaleoutsean/fusego/fuseops"
    30  	"github.com/scaleoutsean/fusego/fuseutil"
    31  	"github.com/scaleoutsean/fusego/samples"
    32  	"github.com/scaleoutsean/fusego/samples/statfs"
    33  	. "github.com/jacobsa/ogletest"
    34  )
    35  
    36  func TestStatFS(t *testing.T) { RunTests(t) }
    37  
    38  const fsName = "some_fs_name"
    39  const volumeName = "Some volume"
    40  
    41  ////////////////////////////////////////////////////////////////////////
    42  // Helpers
    43  ////////////////////////////////////////////////////////////////////////
    44  
    45  // Ask `df` for statistics about the file system's capacity and free space,
    46  // useful for checking that our reading of statfs(2) output matches the
    47  // system's. The output is not guaranteed to have resolution greater than 2^10
    48  // (1 KiB).
    49  func df(dir string) (capacity, used, available uint64, err error) {
    50  	// Call df with a block size of 1024 and capture its output.
    51  	cmd := exec.Command("df", dir)
    52  	cmd.Env = []string{"BLOCKSIZE=1024"}
    53  
    54  	output, err := cmd.CombinedOutput()
    55  	if err != nil {
    56  		return 0, 0, 0, err
    57  	}
    58  
    59  	// Scrape it.
    60  	for _, line := range bytes.Split(output, []byte{'\n'}) {
    61  		// Is this the line we're interested in?
    62  		if !bytes.Contains(line, []byte(dir)) {
    63  			continue
    64  		}
    65  
    66  		submatches := gDfOutputRegexp.FindSubmatch(line)
    67  		if submatches == nil {
    68  			return 0, 0, 0, fmt.Errorf("Unable to parse line: %q", line)
    69  		}
    70  
    71  		capacity, err = strconv.ParseUint(string(submatches[1]), 10, 64)
    72  		if err != nil {
    73  			return 0, 0, 0, err
    74  		}
    75  
    76  		used, err = strconv.ParseUint(string(submatches[2]), 10, 64)
    77  		if err != nil {
    78  			return 0, 0, 0, err
    79  		}
    80  
    81  		available, err = strconv.ParseUint(string(submatches[3]), 10, 64)
    82  		if err != nil {
    83  			return 0, 0, 0, err
    84  		}
    85  
    86  		// Scale appropriately based on the BLOCKSIZE set above.
    87  		capacity *= 1024
    88  		used *= 1024
    89  		available *= 1024
    90  
    91  		return capacity, used, available, nil
    92  	}
    93  
    94  	return 0, 0, 0, fmt.Errorf("Unable to parse df output:\n%s", output)
    95  }
    96  
    97  ////////////////////////////////////////////////////////////////////////
    98  // Boilerplate
    99  ////////////////////////////////////////////////////////////////////////
   100  
   101  type StatFSTest struct {
   102  	samples.SampleTest
   103  	fs statfs.FS
   104  
   105  	// t.Dir, with symlinks resolved and redundant path components removed.
   106  	canonicalDir string
   107  }
   108  
   109  var _ SetUpInterface = &StatFSTest{}
   110  var _ TearDownInterface = &StatFSTest{}
   111  
   112  func init() { RegisterTestSuite(&StatFSTest{}) }
   113  
   114  func (t *StatFSTest) SetUp(ti *TestInfo) {
   115  	var err error
   116  
   117  	// Writeback caching can ruin our measurement of the write sizes the kernel
   118  	// decides to give us, since it causes write acking to race against writes
   119  	// being issued from the client.
   120  	t.MountConfig.DisableWritebackCaching = true
   121  
   122  	// Configure names.
   123  	t.MountConfig.FSName = fsName
   124  	t.MountConfig.VolumeName = volumeName
   125  
   126  	// Create the file system.
   127  	t.fs = statfs.New()
   128  	t.Server = fuseutil.NewFileSystemServer(t.fs)
   129  
   130  	// Mount it.
   131  	t.SampleTest.SetUp(ti)
   132  
   133  	// Canonicalize the mount point.
   134  	t.canonicalDir, err = filepath.EvalSymlinks(t.Dir)
   135  	AssertEq(nil, err)
   136  	t.canonicalDir = path.Clean(t.canonicalDir)
   137  }
   138  
   139  ////////////////////////////////////////////////////////////////////////
   140  // Tests
   141  ////////////////////////////////////////////////////////////////////////
   142  
   143  func (t *StatFSTest) CapacityAndFreeSpace() {
   144  	canned := fuseops.StatFSOp{
   145  		Blocks:          1024,
   146  		BlocksFree:      896,
   147  		BlocksAvailable: 768,
   148  
   149  		IoSize: 1024, // Shouldn't matter.
   150  	}
   151  
   152  	// Check that df agrees with us about a range of block sizes.
   153  	for log2BlockSize := uint(9); log2BlockSize <= 17; log2BlockSize++ {
   154  		bs := uint64(1) << log2BlockSize
   155  		desc := fmt.Sprintf("block size: %d (2^%d)", bs, log2BlockSize)
   156  
   157  		// Set up the canned response.
   158  		canned.BlockSize = uint32(bs)
   159  		t.fs.SetStatFSResponse(canned)
   160  
   161  		// Call df.
   162  		capacity, used, available, err := df(t.canonicalDir)
   163  		AssertEq(nil, err)
   164  
   165  		ExpectEq(bs*canned.Blocks, capacity, "%s", desc)
   166  		ExpectEq(bs*(canned.Blocks-canned.BlocksFree), used, "%s", desc)
   167  		ExpectEq(bs*canned.BlocksAvailable, available, "%s", desc)
   168  	}
   169  }
   170  
   171  func (t *StatFSTest) WriteSize() {
   172  	var err error
   173  
   174  	// Set up a smallish block size.
   175  	canned := fuseops.StatFSOp{
   176  		BlockSize:       8192,
   177  		IoSize:          16384,
   178  		Blocks:          1234,
   179  		BlocksFree:      1234,
   180  		BlocksAvailable: 1234,
   181  	}
   182  
   183  	t.fs.SetStatFSResponse(canned)
   184  
   185  	// Cause a large amount of date to be written.
   186  	err = ioutil.WriteFile(
   187  		path.Join(t.Dir, "foo"),
   188  		bytes.Repeat([]byte{'x'}, 1<<22),
   189  		0400)
   190  
   191  	AssertEq(nil, err)
   192  
   193  	// Despite the small block size, the OS shouldn't have given us pitifully
   194  	// small chunks of data.
   195  	switch runtime.GOOS {
   196  	case "linux":
   197  		ExpectEq(1<<20, t.fs.MostRecentWriteSize())
   198  
   199  	case "darwin":
   200  		ExpectEq(1<<20, t.fs.MostRecentWriteSize())
   201  
   202  	default:
   203  		AddFailure("Unhandled OS: %s", runtime.GOOS)
   204  	}
   205  }
   206  
   207  func (t *StatFSTest) StatBlocks() {
   208  	var err error
   209  	var stat syscall.Stat_t
   210  	const fileName = "foo"
   211  	const size = 1 << 22
   212  
   213  	err = ioutil.WriteFile(
   214  		path.Join(t.Dir, fileName),
   215  		bytes.Repeat([]byte{'x'}, size),
   216  		0400)
   217  	AssertEq(nil, err)
   218  
   219  	t.fs.SetStatResponse(fuseops.InodeAttributes{
   220  		Size: size,
   221  	})
   222  
   223  	err = syscall.Stat(path.Join(t.Dir, fileName), &stat)
   224  	AssertEq(nil, err)
   225  	ExpectEq(size/512, stat.Blocks)
   226  }