github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/mongo/prealloc_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package mongo_test
     5  
     6  import (
     7  	"fmt"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  
    12  	"github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/mongo"
    17  	coretesting "github.com/juju/juju/testing"
    18  )
    19  
    20  type preallocSuite struct {
    21  	coretesting.BaseSuite
    22  }
    23  
    24  var _ = gc.Suite(&preallocSuite{})
    25  
    26  func (s *preallocSuite) TestOplogSize(c *gc.C) {
    27  	type test struct {
    28  		hostWordSize int
    29  		runtimeGOOS  string
    30  		availSpace   int
    31  		expected     int
    32  	}
    33  	tests := []test{{
    34  		hostWordSize: 64,
    35  		runtimeGOOS:  "darwin",
    36  		availSpace:   99999,
    37  		expected:     183,
    38  	}, {
    39  		hostWordSize: 64,
    40  		runtimeGOOS:  "windows",
    41  		availSpace:   99999,
    42  		expected:     512,
    43  	}, {
    44  		hostWordSize: 32,
    45  		runtimeGOOS:  "linux",
    46  		availSpace:   48,
    47  		expected:     48,
    48  	}, {
    49  		hostWordSize: 64,
    50  		runtimeGOOS:  "linux",
    51  		availSpace:   1024,
    52  		expected:     512,
    53  	}, {
    54  		hostWordSize: 64,
    55  		runtimeGOOS:  "linux",
    56  		availSpace:   420 * 1024,
    57  		expected:     1024,
    58  	}, {
    59  		hostWordSize: 64,
    60  		runtimeGOOS:  "linux",
    61  		availSpace:   1024 * 1024,
    62  		expected:     1024,
    63  	}}
    64  	var availSpace int
    65  	getAvailSpace := func(dir string) (float64, error) {
    66  		return float64(availSpace), nil
    67  	}
    68  	s.PatchValue(mongo.AvailSpace, getAvailSpace)
    69  	for i, test := range tests {
    70  		c.Logf("test %d: %+v", i, test)
    71  		s.PatchValue(mongo.HostWordSize, test.hostWordSize)
    72  		s.PatchValue(mongo.RuntimeGOOS, test.runtimeGOOS)
    73  		availSpace = test.availSpace
    74  		size, err := mongo.DefaultOplogSize("")
    75  		c.Check(err, jc.ErrorIsNil)
    76  		c.Check(size, gc.Equals, test.expected)
    77  	}
    78  }
    79  
    80  func (s *preallocSuite) TestFsAvailSpace(c *gc.C) {
    81  	output := `Filesystem     1K-blocks    Used Available Use% Mounted on
    82      /dev/vda1        8124856 1365292     12345  18% /`
    83  	testing.PatchExecutable(c, s, "df", "#!/bin/sh\ncat<<EOF\n"+output+"\nEOF")
    84  
    85  	mb, err := mongo.FsAvailSpace("")
    86  	c.Assert(err, jc.ErrorIsNil)
    87  	c.Assert(mb, gc.Equals, float64(12345)/1024)
    88  }
    89  
    90  func (s *preallocSuite) TestFsAvailSpaceErrors(c *gc.C) {
    91  	tests := []struct {
    92  		desc   string
    93  		output string
    94  		err    string
    95  	}{{
    96  		desc: "result is non-numeric",
    97  		output: `Filesystem     1K-blocks    Used Available Use% Mounted on
    98      /dev/vda1        8124856 1365292       abc  18% /`,
    99  		err: `strconv.(ParseInt|Atoi): parsing "abc": invalid syntax`,
   100  	}, {
   101  		desc:   "not enough lines",
   102  		output: "abc",
   103  		err:    `could not determine available space on ""`,
   104  	}, {
   105  		desc:   "not enough fields on second line",
   106  		output: "abc\ndef",
   107  		err:    `could not determine available space on ""`,
   108  	}}
   109  	for i, test := range tests {
   110  		c.Logf("test %d: %s", i, test.desc)
   111  		testing.PatchExecutable(c, s, "df", "#!/bin/sh\ncat<<EOF\n"+test.output+"\nEOF")
   112  		_, err := mongo.FsAvailSpace("")
   113  		c.Check(err, gc.ErrorMatches, test.err)
   114  	}
   115  }
   116  
   117  func (s *preallocSuite) TestPreallocFileSizes(c *gc.C) {
   118  	const MB = 1024 * 1024
   119  
   120  	tests := []struct {
   121  		desc   string
   122  		size   int
   123  		result []int
   124  	}{{
   125  		desc:   "zero size, zero files",
   126  		size:   0,
   127  		result: nil,
   128  	}, {
   129  		desc:   "exactly divides the max chunk size",
   130  		size:   1024 * MB,
   131  		result: []int{512 * MB, 512 * MB},
   132  	}, {
   133  		desc:   "remainder comes at the beginning",
   134  		size:   1025 * MB,
   135  		result: []int{1 * MB, 512 * MB, 512 * MB},
   136  	}, {
   137  		desc:   "remaining one byte must be padded out to 4096 bytes",
   138  		size:   1024*MB + 1,
   139  		result: []int{4096, 512 * MB, 512 * MB},
   140  	}}
   141  
   142  	for i, test := range tests {
   143  		c.Logf("test %d: %s", i, test.desc)
   144  		sizes := mongo.PreallocFileSizes(test.size)
   145  		c.Check(sizes, gc.DeepEquals, test.result)
   146  	}
   147  }
   148  
   149  func (s *preallocSuite) TestPreallocFiles(c *gc.C) {
   150  	dir := c.MkDir()
   151  	prefix := filepath.Join(dir, "test.")
   152  	err := mongo.PreallocFiles(prefix, 0, 4096, 8192)
   153  	c.Assert(err, jc.ErrorIsNil)
   154  
   155  	zeroes := [8192]byte{}
   156  	for i := 0; i < 3; i++ {
   157  		filename := fmt.Sprintf("%s%d", prefix, i)
   158  		data, err := ioutil.ReadFile(filename)
   159  		c.Check(err, jc.ErrorIsNil)
   160  		c.Check(data, gc.DeepEquals, zeroes[:i*4096])
   161  	}
   162  
   163  	_, err = os.Stat(prefix + "3")
   164  	c.Assert(err, jc.Satisfies, os.IsNotExist)
   165  }
   166  
   167  func (s *preallocSuite) TestPreallocFilesErrors(c *gc.C) {
   168  	err := mongo.PreallocFiles("", 123)
   169  	c.Assert(err, gc.ErrorMatches, `specified size 123 for file "0" is not a multiple of 4096`)
   170  }
   171  
   172  func (s *preallocSuite) TestPreallocFilesWriteErrors(c *gc.C) {
   173  	dir := c.MkDir()
   174  	prefix := filepath.Join(dir, "test.")
   175  	err := ioutil.WriteFile(prefix+"0", nil, 0644)
   176  	c.Assert(err, jc.ErrorIsNil)
   177  	err = ioutil.WriteFile(prefix+"1", nil, 0644)
   178  	c.Assert(err, jc.ErrorIsNil)
   179  
   180  	var called int
   181  	s.PatchValue(mongo.PreallocFile, func(filename string, size int) (bool, error) {
   182  		var created bool
   183  		var err error
   184  		called++
   185  		if called == 2 {
   186  			created = true
   187  			err = fmt.Errorf("failed to zero test.1")
   188  		}
   189  		return created, err
   190  	})
   191  
   192  	err = mongo.PreallocFiles(prefix, 4096, 8192)
   193  	c.Assert(err, gc.ErrorMatches, "failed to zero test.1")
   194  
   195  	// test.0 still exists because we said we didn't
   196  	// create it (i.e. it already existed)
   197  	_, err = os.Stat(prefix + "0")
   198  	c.Assert(err, jc.ErrorIsNil)
   199  
   200  	// test.1 no longer exists because we said we created
   201  	// it, but then failed to write to it.
   202  	_, err = os.Stat(prefix + "1")
   203  	c.Assert(err, jc.Satisfies, os.IsNotExist)
   204  }