statfs_test.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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. package statfs_test
  15. import (
  16. "bytes"
  17. "fmt"
  18. "io/ioutil"
  19. "os/exec"
  20. "path"
  21. "path/filepath"
  22. "runtime"
  23. "strconv"
  24. "syscall"
  25. "testing"
  26. "github.com/jacobsa/fuse/fuseops"
  27. "github.com/jacobsa/fuse/fuseutil"
  28. "github.com/jacobsa/fuse/samples"
  29. "github.com/jacobsa/fuse/samples/statfs"
  30. . "github.com/jacobsa/ogletest"
  31. )
  32. func TestStatFS(t *testing.T) { RunTests(t) }
  33. const fsName = "some_fs_name"
  34. const volumeName = "Some volume"
  35. ////////////////////////////////////////////////////////////////////////
  36. // Helpers
  37. ////////////////////////////////////////////////////////////////////////
  38. // Ask `df` for statistics about the file system's capacity and free space,
  39. // useful for checking that our reading of statfs(2) output matches the
  40. // system's. The output is not guaranteed to have resolution greater than 2^10
  41. // (1 KiB).
  42. func df(dir string) (capacity, used, available uint64, err error) {
  43. // Call df with a block size of 1024 and capture its output.
  44. cmd := exec.Command("df", dir)
  45. cmd.Env = []string{"BLOCKSIZE=1024"}
  46. output, err := cmd.CombinedOutput()
  47. if err != nil {
  48. return
  49. }
  50. // Scrape it.
  51. for _, line := range bytes.Split(output, []byte{'\n'}) {
  52. // Is this the line we're interested in?
  53. if !bytes.Contains(line, []byte(dir)) {
  54. continue
  55. }
  56. submatches := gDfOutputRegexp.FindSubmatch(line)
  57. if submatches == nil {
  58. err = fmt.Errorf("Unable to parse line: %q", line)
  59. return
  60. }
  61. capacity, err = strconv.ParseUint(string(submatches[1]), 10, 64)
  62. if err != nil {
  63. return
  64. }
  65. used, err = strconv.ParseUint(string(submatches[2]), 10, 64)
  66. if err != nil {
  67. return
  68. }
  69. available, err = strconv.ParseUint(string(submatches[3]), 10, 64)
  70. if err != nil {
  71. return
  72. }
  73. // Scale appropriately based on the BLOCKSIZE set above.
  74. capacity *= 1024
  75. used *= 1024
  76. available *= 1024
  77. return
  78. }
  79. err = fmt.Errorf("Unable to parse df output:\n%s", output)
  80. return
  81. }
  82. ////////////////////////////////////////////////////////////////////////
  83. // Boilerplate
  84. ////////////////////////////////////////////////////////////////////////
  85. type StatFSTest struct {
  86. samples.SampleTest
  87. fs statfs.FS
  88. // t.Dir, with symlinks resolved and redundant path components removed.
  89. canonicalDir string
  90. }
  91. var _ SetUpInterface = &StatFSTest{}
  92. var _ TearDownInterface = &StatFSTest{}
  93. func init() { RegisterTestSuite(&StatFSTest{}) }
  94. func (t *StatFSTest) SetUp(ti *TestInfo) {
  95. var err error
  96. // Writeback caching can ruin our measurement of the write sizes the kernel
  97. // decides to give us, since it causes write acking to race against writes
  98. // being issued from the client.
  99. t.MountConfig.DisableWritebackCaching = true
  100. // Configure names.
  101. t.MountConfig.FSName = fsName
  102. t.MountConfig.VolumeName = volumeName
  103. // Create the file system.
  104. t.fs = statfs.New()
  105. t.Server = fuseutil.NewFileSystemServer(t.fs)
  106. // Mount it.
  107. t.SampleTest.SetUp(ti)
  108. // Canonicalize the mount point.
  109. t.canonicalDir, err = filepath.EvalSymlinks(t.Dir)
  110. AssertEq(nil, err)
  111. t.canonicalDir = path.Clean(t.canonicalDir)
  112. }
  113. ////////////////////////////////////////////////////////////////////////
  114. // Tests
  115. ////////////////////////////////////////////////////////////////////////
  116. func (t *StatFSTest) CapacityAndFreeSpace() {
  117. canned := fuseops.StatFSOp{
  118. Blocks: 1024,
  119. BlocksFree: 896,
  120. BlocksAvailable: 768,
  121. IoSize: 1024, // Shouldn't matter.
  122. }
  123. // Check that df agrees with us about a range of block sizes.
  124. for log2BlockSize := uint(9); log2BlockSize <= 17; log2BlockSize++ {
  125. bs := uint64(1) << log2BlockSize
  126. desc := fmt.Sprintf("block size: %d (2^%d)", bs, log2BlockSize)
  127. // Set up the canned response.
  128. canned.BlockSize = uint32(bs)
  129. t.fs.SetStatFSResponse(canned)
  130. // Call df.
  131. capacity, used, available, err := df(t.canonicalDir)
  132. AssertEq(nil, err)
  133. ExpectEq(bs*canned.Blocks, capacity, "%s", desc)
  134. ExpectEq(bs*(canned.Blocks-canned.BlocksFree), used, "%s", desc)
  135. ExpectEq(bs*canned.BlocksAvailable, available, "%s", desc)
  136. }
  137. }
  138. func (t *StatFSTest) WriteSize() {
  139. var err error
  140. // Set up a smallish block size.
  141. canned := fuseops.StatFSOp{
  142. BlockSize: 8192,
  143. IoSize: 16384,
  144. Blocks: 1234,
  145. BlocksFree: 1234,
  146. BlocksAvailable: 1234,
  147. }
  148. t.fs.SetStatFSResponse(canned)
  149. // Cause a large amount of date to be written.
  150. err = ioutil.WriteFile(
  151. path.Join(t.Dir, "foo"),
  152. bytes.Repeat([]byte{'x'}, 1<<22),
  153. 0400)
  154. AssertEq(nil, err)
  155. // Despite the small block size, the OS shouldn't have given us pitifully
  156. // small chunks of data.
  157. switch runtime.GOOS {
  158. case "linux":
  159. ExpectEq(1<<17, t.fs.MostRecentWriteSize())
  160. case "darwin":
  161. ExpectEq(1<<20, t.fs.MostRecentWriteSize())
  162. default:
  163. AddFailure("Unhandled OS: %s", runtime.GOOS)
  164. }
  165. }
  166. func (t *StatFSTest) StatBlocks() {
  167. var err error
  168. var stat syscall.Stat_t
  169. const fileName = "foo"
  170. const size = 1 << 22
  171. err = ioutil.WriteFile(
  172. path.Join(t.Dir, fileName),
  173. bytes.Repeat([]byte{'x'}, size),
  174. 0400)
  175. AssertEq(nil, err)
  176. t.fs.SetStatResponse(fuseops.InodeAttributes{
  177. Size: size,
  178. })
  179. err = syscall.Stat(path.Join(t.Dir, fileName), &stat)
  180. AssertEq(nil, err)
  181. ExpectEq(size/512, stat.Blocks)
  182. }