github.com/consideritdone/landslidecore@v0.0.0-20230718131026-a8b21c5cf8a7/libs/autofile/group_test.go (about) 1 package autofile 2 3 import ( 4 "io" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "testing" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 tmos "github.com/consideritdone/landslidecore/libs/os" 14 tmrand "github.com/consideritdone/landslidecore/libs/rand" 15 ) 16 17 func createTestGroupWithHeadSizeLimit(t *testing.T, headSizeLimit int64) *Group { 18 testID := tmrand.Str(12) 19 testDir := "_test_" + testID 20 err := tmos.EnsureDir(testDir, 0700) 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 g.Close() 33 34 err := os.RemoveAll(g.Dir) 35 require.NoError(t, err, "Error removing test Group directory") 36 } 37 38 func assertGroupInfo(t *testing.T, gInfo GroupInfo, minIndex, maxIndex int, totalSize, headSize int64) { 39 assert.Equal(t, minIndex, gInfo.MinIndex) 40 assert.Equal(t, maxIndex, gInfo.MaxIndex) 41 assert.Equal(t, totalSize, gInfo.TotalSize) 42 assert.Equal(t, headSize, gInfo.HeadSize) 43 } 44 45 func TestCheckHeadSizeLimit(t *testing.T) { 46 g := createTestGroupWithHeadSizeLimit(t, 1000*1000) 47 48 // At first, there are no files. 49 assertGroupInfo(t, g.ReadGroupInfo(), 0, 0, 0, 0) 50 51 // Write 1000 bytes 999 times. 52 for i := 0; i < 999; i++ { 53 err := g.WriteLine(tmrand.Str(999)) 54 require.NoError(t, err, "Error appending to head") 55 } 56 err := g.FlushAndSync() 57 require.NoError(t, err) 58 assertGroupInfo(t, g.ReadGroupInfo(), 0, 0, 999000, 999000) 59 60 // Even calling checkHeadSizeLimit manually won't rotate it. 61 g.checkHeadSizeLimit() 62 assertGroupInfo(t, g.ReadGroupInfo(), 0, 0, 999000, 999000) 63 64 // Write 1000 more bytes. 65 err = g.WriteLine(tmrand.Str(999)) 66 require.NoError(t, err, "Error appending to head") 67 err = g.FlushAndSync() 68 require.NoError(t, err) 69 70 // Calling checkHeadSizeLimit this time rolls it. 71 g.checkHeadSizeLimit() 72 assertGroupInfo(t, g.ReadGroupInfo(), 0, 1, 1000000, 0) 73 74 // Write 1000 more bytes. 75 err = g.WriteLine(tmrand.Str(999)) 76 require.NoError(t, err, "Error appending to head") 77 err = g.FlushAndSync() 78 require.NoError(t, err) 79 80 // Calling checkHeadSizeLimit does nothing. 81 g.checkHeadSizeLimit() 82 assertGroupInfo(t, g.ReadGroupInfo(), 0, 1, 1001000, 1000) 83 84 // Write 1000 bytes 999 times. 85 for i := 0; i < 999; i++ { 86 err = g.WriteLine(tmrand.Str(999)) 87 require.NoError(t, err, "Error appending to head") 88 } 89 err = g.FlushAndSync() 90 require.NoError(t, err) 91 assertGroupInfo(t, g.ReadGroupInfo(), 0, 1, 2000000, 1000000) 92 93 // Calling checkHeadSizeLimit rolls it again. 94 g.checkHeadSizeLimit() 95 assertGroupInfo(t, g.ReadGroupInfo(), 0, 2, 2000000, 0) 96 97 // Write 1000 more bytes. 98 _, err = g.Head.Write([]byte(tmrand.Str(999) + "\n")) 99 require.NoError(t, err, "Error appending to head") 100 err = g.FlushAndSync() 101 require.NoError(t, err) 102 assertGroupInfo(t, g.ReadGroupInfo(), 0, 2, 2001000, 1000) 103 104 // Calling checkHeadSizeLimit does nothing. 105 g.checkHeadSizeLimit() 106 assertGroupInfo(t, g.ReadGroupInfo(), 0, 2, 2001000, 1000) 107 108 // Cleanup 109 destroyTestGroup(t, g) 110 } 111 112 func TestRotateFile(t *testing.T) { 113 g := createTestGroupWithHeadSizeLimit(t, 0) 114 115 // Create a different temporary directory and move into it, to make sure 116 // relative paths are resolved at Group creation 117 origDir, err := os.Getwd() 118 require.NoError(t, err) 119 defer func() { 120 if err := os.Chdir(origDir); err != nil { 121 t.Error(err) 122 } 123 }() 124 125 dir, err := ioutil.TempDir("", "rotate_test") 126 require.NoError(t, err) 127 defer os.RemoveAll(dir) 128 err = os.Chdir(dir) 129 require.NoError(t, err) 130 131 require.True(t, filepath.IsAbs(g.Head.Path)) 132 require.True(t, filepath.IsAbs(g.Dir)) 133 134 // Create and rotate files 135 err = g.WriteLine("Line 1") 136 require.NoError(t, err) 137 err = g.WriteLine("Line 2") 138 require.NoError(t, err) 139 err = g.WriteLine("Line 3") 140 require.NoError(t, err) 141 err = g.FlushAndSync() 142 require.NoError(t, err) 143 g.RotateFile() 144 err = g.WriteLine("Line 4") 145 require.NoError(t, err) 146 err = g.WriteLine("Line 5") 147 require.NoError(t, err) 148 err = g.WriteLine("Line 6") 149 require.NoError(t, err) 150 err = g.FlushAndSync() 151 require.NoError(t, err) 152 153 // Read g.Head.Path+"000" 154 body1, err := ioutil.ReadFile(g.Head.Path + ".000") 155 assert.NoError(t, err, "Failed to read first rolled file") 156 if string(body1) != "Line 1\nLine 2\nLine 3\n" { 157 t.Errorf("got unexpected contents: [%v]", string(body1)) 158 } 159 160 // Read g.Head.Path 161 body2, err := ioutil.ReadFile(g.Head.Path) 162 assert.NoError(t, err, "Failed to read first rolled file") 163 if string(body2) != "Line 4\nLine 5\nLine 6\n" { 164 t.Errorf("got unexpected contents: [%v]", string(body2)) 165 } 166 167 // Make sure there are no files in the current, temporary directory 168 files, err := ioutil.ReadDir(".") 169 require.NoError(t, err) 170 assert.Empty(t, files) 171 172 // Cleanup 173 destroyTestGroup(t, g) 174 } 175 176 func TestWrite(t *testing.T) { 177 g := createTestGroupWithHeadSizeLimit(t, 0) 178 179 written := []byte("Medusa") 180 _, err := g.Write(written) 181 require.NoError(t, err) 182 err = g.FlushAndSync() 183 require.NoError(t, err) 184 185 read := make([]byte, len(written)) 186 gr, err := g.NewReader(0) 187 require.NoError(t, err, "failed to create reader") 188 189 _, err = gr.Read(read) 190 assert.NoError(t, err, "failed to read data") 191 assert.Equal(t, written, read) 192 193 // Cleanup 194 destroyTestGroup(t, g) 195 } 196 197 // test that Read reads the required amount of bytes from all the files in the 198 // group and returns no error if n == size of the given slice. 199 func TestGroupReaderRead(t *testing.T) { 200 g := createTestGroupWithHeadSizeLimit(t, 0) 201 202 professor := []byte("Professor Monster") 203 _, err := g.Write(professor) 204 require.NoError(t, err) 205 err = g.FlushAndSync() 206 require.NoError(t, err) 207 g.RotateFile() 208 frankenstein := []byte("Frankenstein's Monster") 209 _, err = g.Write(frankenstein) 210 require.NoError(t, err) 211 err = g.FlushAndSync() 212 require.NoError(t, err) 213 214 totalWrittenLength := len(professor) + len(frankenstein) 215 read := make([]byte, totalWrittenLength) 216 gr, err := g.NewReader(0) 217 require.NoError(t, err, "failed to create reader") 218 219 n, err := gr.Read(read) 220 assert.NoError(t, err, "failed to read data") 221 assert.Equal(t, totalWrittenLength, n, "not enough bytes read") 222 professorPlusFrankenstein := professor 223 professorPlusFrankenstein = append(professorPlusFrankenstein, frankenstein...) 224 assert.Equal(t, professorPlusFrankenstein, read) 225 226 // Cleanup 227 destroyTestGroup(t, g) 228 } 229 230 // test that Read returns an error if number of bytes read < size of 231 // the given slice. Subsequent call should return 0, io.EOF. 232 func TestGroupReaderRead2(t *testing.T) { 233 g := createTestGroupWithHeadSizeLimit(t, 0) 234 235 professor := []byte("Professor Monster") 236 _, err := g.Write(professor) 237 require.NoError(t, err) 238 err = g.FlushAndSync() 239 require.NoError(t, err) 240 g.RotateFile() 241 frankenstein := []byte("Frankenstein's Monster") 242 frankensteinPart := []byte("Frankenstein") 243 _, err = g.Write(frankensteinPart) // note writing only a part 244 require.NoError(t, err) 245 err = g.FlushAndSync() 246 require.NoError(t, err) 247 248 totalLength := len(professor) + len(frankenstein) 249 read := make([]byte, totalLength) 250 gr, err := g.NewReader(0) 251 require.NoError(t, err, "failed to create reader") 252 253 // 1) n < (size of the given slice), io.EOF 254 n, err := gr.Read(read) 255 assert.Equal(t, io.EOF, err) 256 assert.Equal(t, len(professor)+len(frankensteinPart), n, "Read more/less bytes than it is in the group") 257 258 // 2) 0, io.EOF 259 n, err = gr.Read([]byte("0")) 260 assert.Equal(t, io.EOF, err) 261 assert.Equal(t, 0, n) 262 263 // Cleanup 264 destroyTestGroup(t, g) 265 } 266 267 func TestMinIndex(t *testing.T) { 268 g := createTestGroupWithHeadSizeLimit(t, 0) 269 270 assert.Zero(t, g.MinIndex(), "MinIndex should be zero at the beginning") 271 272 // Cleanup 273 destroyTestGroup(t, g) 274 } 275 276 func TestMaxIndex(t *testing.T) { 277 g := createTestGroupWithHeadSizeLimit(t, 0) 278 279 assert.Zero(t, g.MaxIndex(), "MaxIndex should be zero at the beginning") 280 281 err := g.WriteLine("Line 1") 282 require.NoError(t, err) 283 err = g.FlushAndSync() 284 require.NoError(t, err) 285 g.RotateFile() 286 287 assert.Equal(t, 1, g.MaxIndex(), "MaxIndex should point to the last file") 288 289 // Cleanup 290 destroyTestGroup(t, g) 291 }