github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/autofile/group_test.go (about)

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