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 }