This commit is contained in:
iratusmachina 2024-06-24 13:40:04 -04:00
parent 96e877b0c2
commit 6806a81588
3 changed files with 212 additions and 6 deletions

View File

@ -94,13 +94,13 @@ func (lf *LogFile) truncate() error {
func prettyByteSize(b int64) string { func prettyByteSize(b int64) string {
bf := float64(b) bf := float64(b)
for _, unit := range []string{"", "K", "M", "G", "T", "P", "E", "Z"} { for _, unit := range []string{"", "K", "M", "G", "T", "P"} {
if math.Abs(bf) < 1024.0 { if math.Abs(bf) < 1024.0 {
return fmt.Sprintf("%3.1f%sB", bf, unit) return fmt.Sprintf("%3.1f%sB", bf, unit)
} }
bf /= 1024.0 bf /= 1024.0
} }
return fmt.Sprintf("%.1fYB", bf) return fmt.Sprintf("%.1fEB", bf)
} }
func compressOldFile(fname string) error { func compressOldFile(fname string) error {

View File

@ -2,7 +2,10 @@ package main
import ( import (
"fmt" "fmt"
"os"
"testing" "testing"
"github.com/stretchr/testify/assert"
) )
func TestTruncate(t *testing.T) { func TestTruncate(t *testing.T) {
@ -11,14 +14,123 @@ func TestTruncate(t *testing.T) {
mkDirForTest(t, fmt.Sprintf("%s/tmp", tmpDir)) mkDirForTest(t, fmt.Sprintf("%s/tmp", tmpDir))
rulesJsonFp := "testData/app_over_size.log" rulesJsonFp := "testData/app_over_size.log"
expected := fmt.Sprintf("%s/tmp/app.log", tmpDir) tmpLf := fmt.Sprintf("%s/tmp/app.log", tmpDir)
cpFileForTest(t, rulesJsonFp, expected) cpFileForTest(t, rulesJsonFp, tmpLf)
cfg := &LogFile{ lf := &LogFile{
path: expected, path: tmpLf,
} }
lf.path = tmpLf
// Continue from here // Continue from here
isEmpty := isFileEmpty(t, tmpLf)
assert.Equal(t, isEmpty, false)
err := lf.truncate()
assert.Equal(t, err, nil)
isEmpty = isFileEmpty(t, tmpLf)
assert.Equal(t, isEmpty, true)
}
func TestMakeCopyTo(t *testing.T) {
tmpDir := t.TempDir()
mkDirForTest(t, fmt.Sprintf("%s/tmp", tmpDir))
rulesJsonFp := "testData/app_over_size.log"
tmpLf := fmt.Sprintf("%s/tmp/app.log", tmpDir)
cpFileForTest(t, rulesJsonFp, tmpLf)
newLocation := fmt.Sprintf("%s/tmp/app.1.log", tmpDir)
lf := &LogFile{
path: tmpLf,
}
lf.path = tmpLf
isEmpty := isFileEmpty(t, tmpLf)
assert.Equal(t, isEmpty, false)
err := lf.makeCopyTo(newLocation)
assert.Equal(t, err, nil)
isEmpty = isFileEmpty(t, tmpLf)
assert.Equal(t, isEmpty, false)
ok := areFilesTheSame(t, lf.path, newLocation)
assert.Equal(t, ok, true)
}
func TestPrettyByteSize(t *testing.T) {
tests := map[string]struct {
input int
want string
}{
"KB": {input: 5675, want: "5.5KB"},
"MB": {input: 7060600, want: "6.7MB"},
"GB": {input: 8000000000, want: "7.5GB"},
"TB": {input: 1300007000000, want: "1.2TB"},
"EB": {input: 1300007000000000000, want: "1.1EB"},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
got := prettyByteSize(int64(tc.input))
assert.Equal(t, got, tc.want)
})
}
}
func TestCompressFile(t *testing.T) {
tmpDir := t.TempDir()
mkDirForTest(t, fmt.Sprintf("%s/tmp", tmpDir))
rulesJsonFp := "testData/app_over_size.log"
tmpLf := fmt.Sprintf("%s/tmp/app.log", tmpDir)
cpFileForTest(t, rulesJsonFp, tmpLf)
expected := fmt.Sprintf("%s/tmp/app.log.gz", tmpDir)
err := compressOldFile(tmpLf)
assert.Equal(t, err, nil)
exists := doesFileExist(tmpLf)
assert.Equal(t, exists, false)
exists = doesFileExist(expected)
assert.Equal(t, exists, true)
}
func TestRotate(t *testing.T) {
tmpDir := t.TempDir()
mkDirForTest(t, fmt.Sprintf("%s/tmp", tmpDir))
rulesJsonFp := "testData/app_over_size.log"
tmpLf := fmt.Sprintf("%s/tmp/app.log", tmpDir)
cpFileForTest(t, rulesJsonFp, tmpLf)
lf := &LogFile{
path: tmpLf,
}
fd, _ := os.Open(tmpLf)
lf.handle = fd
isEmpty := isFileEmpty(t, tmpLf)
assert.Equal(t, isEmpty, false)
err := lf.rotate(true)
assert.Equal(t, err, nil)
exists := doesFileExist(tmpLf)
assert.Equal(t, exists, true)
isEmpty = isFileEmpty(t, tmpLf)
assert.Equal(t, isEmpty, true)
expected := walkMatch(t, tmpDir, "*.gz")
assert.NotEmpty(t, expected)
t.Logf("%+v", expected)
assert.NotEqual(t, lf.handle, fd)
} }

View File

@ -1,11 +1,13 @@
package main package main
import ( import (
"bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"io" "io"
"io/fs" "io/fs"
"os" "os"
"path/filepath"
"testing" "testing"
) )
@ -81,6 +83,29 @@ func doesFileExist(name string) bool {
return err == nil return err == nil
} }
// Derived from here (https://stackoverflow.com/a/55300382)
func walkMatch(t *testing.T, root, pattern string) []string {
var matches []string
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if matched, err := filepath.Match(pattern, filepath.Base(path)); err != nil {
return err
} else if matched {
matches = append(matches, path)
}
return nil
})
if err != nil {
t.Fatal(err)
}
return matches
}
// func doesFileExist(name string) bool { // func doesFileExist(name string) bool {
// _, err := os.Stat(name) // _, err := os.Stat(name)
// return !errors.Is(err, fs.ErrNotExist) // return !errors.Is(err, fs.ErrNotExist)
@ -113,3 +138,72 @@ func isFileEmpty(t *testing.T, name string) bool {
} }
return finfo.Size() < 1 return finfo.Size() < 1
} }
// Derived from here (https://stackoverflow.com/a/73411967)
func areFilesTheSame(t *testing.T, fp_1 string, fp_2 string) bool {
chunkSize := 4 * 1024
// shortcuts: check file metadata
finfo_1, err := os.Stat(fp_1)
if err != nil {
t.Fatal(err)
}
finfo_2, err := os.Stat(fp_2)
if err != nil {
t.Fatal(err)
}
// are inputs are literally the same file?
if os.SameFile(finfo_1, finfo_2) {
return true
}
// do inputs at least have the same size?
if finfo_1.Size() != finfo_2.Size() {
return false
}
// long way: compare contents
fd_1, err := os.Open(fp_1)
if err != nil {
t.Fatal(err)
}
defer fd_1.Close()
fd_2, err := os.Open(fp_2)
if err != nil {
t.Fatal(err)
}
defer fd_2.Close()
bfd_1 := make([]byte, chunkSize)
bfd_2 := make([]byte, chunkSize)
for {
n1, err1 := io.ReadFull(fd_1, bfd_1)
n2, err2 := io.ReadFull(fd_2, bfd_2)
// https://pkg.go.dev/io#Reader
// > Callers should always process the n > 0 bytes returned
// > before considering the error err. Doing so correctly
// > handles I/O errors that happen after reading some bytes
// > and also both of the allowed EOF behaviors.
if !bytes.Equal(bfd_1[:n1], bfd_2[:n2]) {
return false
}
if (err1 == io.EOF && err2 == io.EOF) || (err1 == io.ErrUnexpectedEOF && err2 == io.ErrUnexpectedEOF) {
return true
}
// some other error, like a dropped network connection or a bad transfer
if err1 != nil {
t.Fatal(err1)
}
if err2 != nil {
t.Fatal(err2)
}
}
}