github.com/evdatsion/aphelion-dpos-bft@v0.32.1/libs/autofile/group_test.go (about)

     1  package autofile
     2  
     3  import (
     4  	"io"
     5  	"io/ioutil"
     6  	"os"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common"
    13  )
    14  
    15  func createTestGroupWithHeadSizeLimit(t *testing.T, headSizeLimit int64) *Group {
    16  	testID := cmn.RandStr(12)
    17  	testDir := "_test_" + testID
    18  	err := cmn.EnsureDir(testDir, 0700)
    19  	require.NoError(t, err, "Error creating dir")
    20  
    21  	headPath := testDir + "/myfile"
    22  	g, err := OpenGroup(headPath, GroupHeadSizeLimit(headSizeLimit))
    23  	require.NoError(t, err, "Error opening Group")
    24  	require.NotEqual(t, nil, g, "Failed to create Group")
    25  
    26  	return g
    27  }
    28  
    29  func destroyTestGroup(t *testing.T, g *Group) {
    30  	g.Close()
    31  
    32  	err := os.RemoveAll(g.Dir)
    33  	require.NoError(t, err, "Error removing test Group directory")
    34  }
    35  
    36  func assertGroupInfo(t *testing.T, gInfo GroupInfo, minIndex, maxIndex int, totalSize, headSize int64) {
    37  	assert.Equal(t, minIndex, gInfo.MinIndex)
    38  	assert.Equal(t, maxIndex, gInfo.MaxIndex)
    39  	assert.Equal(t, totalSize, gInfo.TotalSize)
    40  	assert.Equal(t, headSize, gInfo.HeadSize)
    41  }
    42  
    43  func TestCheckHeadSizeLimit(t *testing.T) {
    44  	g := createTestGroupWithHeadSizeLimit(t, 1000*1000)
    45  
    46  	// At first, there are no files.
    47  	assertGroupInfo(t, g.ReadGroupInfo(), 0, 0, 0, 0)
    48  
    49  	// Write 1000 bytes 999 times.
    50  	for i := 0; i < 999; i++ {
    51  		err := g.WriteLine(cmn.RandStr(999))
    52  		require.NoError(t, err, "Error appending to head")
    53  	}
    54  	g.FlushAndSync()
    55  	assertGroupInfo(t, g.ReadGroupInfo(), 0, 0, 999000, 999000)
    56  
    57  	// Even calling checkHeadSizeLimit manually won't rotate it.
    58  	g.checkHeadSizeLimit()
    59  	assertGroupInfo(t, g.ReadGroupInfo(), 0, 0, 999000, 999000)
    60  
    61  	// Write 1000 more bytes.
    62  	err := g.WriteLine(cmn.RandStr(999))
    63  	require.NoError(t, err, "Error appending to head")
    64  	g.FlushAndSync()
    65  
    66  	// Calling checkHeadSizeLimit this time rolls it.
    67  	g.checkHeadSizeLimit()
    68  	assertGroupInfo(t, g.ReadGroupInfo(), 0, 1, 1000000, 0)
    69  
    70  	// Write 1000 more bytes.
    71  	err = g.WriteLine(cmn.RandStr(999))
    72  	require.NoError(t, err, "Error appending to head")
    73  	g.FlushAndSync()
    74  
    75  	// Calling checkHeadSizeLimit does nothing.
    76  	g.checkHeadSizeLimit()
    77  	assertGroupInfo(t, g.ReadGroupInfo(), 0, 1, 1001000, 1000)
    78  
    79  	// Write 1000 bytes 999 times.
    80  	for i := 0; i < 999; i++ {
    81  		err = g.WriteLine(cmn.RandStr(999))
    82  		require.NoError(t, err, "Error appending to head")
    83  	}
    84  	g.FlushAndSync()
    85  	assertGroupInfo(t, g.ReadGroupInfo(), 0, 1, 2000000, 1000000)
    86  
    87  	// Calling checkHeadSizeLimit rolls it again.
    88  	g.checkHeadSizeLimit()
    89  	assertGroupInfo(t, g.ReadGroupInfo(), 0, 2, 2000000, 0)
    90  
    91  	// Write 1000 more bytes.
    92  	_, err = g.Head.Write([]byte(cmn.RandStr(999) + "\n"))
    93  	require.NoError(t, err, "Error appending to head")
    94  	g.FlushAndSync()
    95  	assertGroupInfo(t, g.ReadGroupInfo(), 0, 2, 2001000, 1000)
    96  
    97  	// Calling checkHeadSizeLimit does nothing.
    98  	g.checkHeadSizeLimit()
    99  	assertGroupInfo(t, g.ReadGroupInfo(), 0, 2, 2001000, 1000)
   100  
   101  	// Cleanup
   102  	destroyTestGroup(t, g)
   103  }
   104  
   105  func TestRotateFile(t *testing.T) {
   106  	g := createTestGroupWithHeadSizeLimit(t, 0)
   107  	g.WriteLine("Line 1")
   108  	g.WriteLine("Line 2")
   109  	g.WriteLine("Line 3")
   110  	g.FlushAndSync()
   111  	g.RotateFile()
   112  	g.WriteLine("Line 4")
   113  	g.WriteLine("Line 5")
   114  	g.WriteLine("Line 6")
   115  	g.FlushAndSync()
   116  
   117  	// Read g.Head.Path+"000"
   118  	body1, err := ioutil.ReadFile(g.Head.Path + ".000")
   119  	assert.NoError(t, err, "Failed to read first rolled file")
   120  	if string(body1) != "Line 1\nLine 2\nLine 3\n" {
   121  		t.Errorf("Got unexpected contents: [%v]", string(body1))
   122  	}
   123  
   124  	// Read g.Head.Path
   125  	body2, err := ioutil.ReadFile(g.Head.Path)
   126  	assert.NoError(t, err, "Failed to read first rolled file")
   127  	if string(body2) != "Line 4\nLine 5\nLine 6\n" {
   128  		t.Errorf("Got unexpected contents: [%v]", string(body2))
   129  	}
   130  
   131  	// Cleanup
   132  	destroyTestGroup(t, g)
   133  }
   134  
   135  func TestWrite(t *testing.T) {
   136  	g := createTestGroupWithHeadSizeLimit(t, 0)
   137  
   138  	written := []byte("Medusa")
   139  	g.Write(written)
   140  	g.FlushAndSync()
   141  
   142  	read := make([]byte, len(written))
   143  	gr, err := g.NewReader(0)
   144  	require.NoError(t, err, "failed to create reader")
   145  
   146  	_, err = gr.Read(read)
   147  	assert.NoError(t, err, "failed to read data")
   148  	assert.Equal(t, written, read)
   149  
   150  	// Cleanup
   151  	destroyTestGroup(t, g)
   152  }
   153  
   154  // test that Read reads the required amount of bytes from all the files in the
   155  // group and returns no error if n == size of the given slice.
   156  func TestGroupReaderRead(t *testing.T) {
   157  	g := createTestGroupWithHeadSizeLimit(t, 0)
   158  
   159  	professor := []byte("Professor Monster")
   160  	g.Write(professor)
   161  	g.FlushAndSync()
   162  	g.RotateFile()
   163  	frankenstein := []byte("Frankenstein's Monster")
   164  	g.Write(frankenstein)
   165  	g.FlushAndSync()
   166  
   167  	totalWrittenLength := len(professor) + len(frankenstein)
   168  	read := make([]byte, totalWrittenLength)
   169  	gr, err := g.NewReader(0)
   170  	require.NoError(t, err, "failed to create reader")
   171  
   172  	n, err := gr.Read(read)
   173  	assert.NoError(t, err, "failed to read data")
   174  	assert.Equal(t, totalWrittenLength, n, "not enough bytes read")
   175  	professorPlusFrankenstein := professor
   176  	professorPlusFrankenstein = append(professorPlusFrankenstein, frankenstein...)
   177  	assert.Equal(t, professorPlusFrankenstein, read)
   178  
   179  	// Cleanup
   180  	destroyTestGroup(t, g)
   181  }
   182  
   183  // test that Read returns an error if number of bytes read < size of
   184  // the given slice. Subsequent call should return 0, io.EOF.
   185  func TestGroupReaderRead2(t *testing.T) {
   186  	g := createTestGroupWithHeadSizeLimit(t, 0)
   187  
   188  	professor := []byte("Professor Monster")
   189  	g.Write(professor)
   190  	g.FlushAndSync()
   191  	g.RotateFile()
   192  	frankenstein := []byte("Frankenstein's Monster")
   193  	frankensteinPart := []byte("Frankenstein")
   194  	g.Write(frankensteinPart) // note writing only a part
   195  	g.FlushAndSync()
   196  
   197  	totalLength := len(professor) + len(frankenstein)
   198  	read := make([]byte, totalLength)
   199  	gr, err := g.NewReader(0)
   200  	require.NoError(t, err, "failed to create reader")
   201  
   202  	// 1) n < (size of the given slice), io.EOF
   203  	n, err := gr.Read(read)
   204  	assert.Equal(t, io.EOF, err)
   205  	assert.Equal(t, len(professor)+len(frankensteinPart), n, "Read more/less bytes than it is in the group")
   206  
   207  	// 2) 0, io.EOF
   208  	n, err = gr.Read([]byte("0"))
   209  	assert.Equal(t, io.EOF, err)
   210  	assert.Equal(t, 0, n)
   211  
   212  	// Cleanup
   213  	destroyTestGroup(t, g)
   214  }
   215  
   216  func TestMinIndex(t *testing.T) {
   217  	g := createTestGroupWithHeadSizeLimit(t, 0)
   218  
   219  	assert.Zero(t, g.MinIndex(), "MinIndex should be zero at the beginning")
   220  
   221  	// Cleanup
   222  	destroyTestGroup(t, g)
   223  }
   224  
   225  func TestMaxIndex(t *testing.T) {
   226  	g := createTestGroupWithHeadSizeLimit(t, 0)
   227  
   228  	assert.Zero(t, g.MaxIndex(), "MaxIndex should be zero at the beginning")
   229  
   230  	g.WriteLine("Line 1")
   231  	g.FlushAndSync()
   232  	g.RotateFile()
   233  
   234  	assert.Equal(t, 1, g.MaxIndex(), "MaxIndex should point to the last file")
   235  
   236  	// Cleanup
   237  	destroyTestGroup(t, g)
   238  }