Files
runc/libcontainer/logs/logs_linux_test.go
Kir Kolyshkin 67840cce4b Enable gofumpt extra rules
Commit b2f8a74d "clothed" the naked return as inflicted by gofumpt
v0.9.0. Since gofumpt v0.9.2 this rule was moved to "extra" category,
not enabled by default. The only other "extra" rule is to group adjacent
parameters with the same type, which also makes sense.

Enable gofumpt "extra" rules, and reformat the code accordingly.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2025-11-10 13:18:45 -08:00

167 lines
3.3 KiB
Go

package logs
import (
"bytes"
"io"
"os"
"testing"
"time"
"github.com/sirupsen/logrus"
)
const msgErr = `"level":"error"`
func TestLoggingToFile(t *testing.T) {
l := runLogForwarding(t)
msg := `"level":"info","msg":"kitten"`
logToLogWriter(t, l, msg)
finish(t, l)
check(t, l, msg, msgErr)
}
func TestLogForwardingDoesNotStopOnJsonDecodeErr(t *testing.T) {
l := runLogForwarding(t)
logToLogWriter(t, l, `"invalid-json-with-kitten"`)
checkWait(t, l, msgErr, "")
truncateLogFile(t, l.file)
msg := `"level":"info","msg":"puppy"`
logToLogWriter(t, l, msg)
finish(t, l)
check(t, l, msg, msgErr)
}
func TestLogForwardingDoesNotStopOnLogLevelParsingErr(t *testing.T) {
l := runLogForwarding(t)
msg := `"level":"alert","msg":"puppy"`
logToLogWriter(t, l, msg)
checkWait(t, l, msgErr, msg)
truncateLogFile(t, l.file)
msg = `"level":"info","msg":"puppy"`
logToLogWriter(t, l, msg)
finish(t, l)
check(t, l, msg, msgErr)
}
func TestLogForwardingStopsAfterClosingTheWriter(t *testing.T) {
l := runLogForwarding(t)
msg := `"level":"info","msg":"sync"`
logToLogWriter(t, l, msg)
// Do not use finish() here as we check done pipe ourselves.
l.w.Close()
select {
case <-l.done:
case <-time.After(10 * time.Second):
t.Fatal("log forwarding did not stop after closing the pipe")
}
check(t, l, msg, msgErr)
}
func logToLogWriter(t *testing.T, l *log, message string) {
t.Helper()
_, err := l.w.Write([]byte("{" + message + "}\n"))
if err != nil {
t.Fatalf("failed to write %q to log writer: %v", message, err)
}
}
type log struct {
w io.WriteCloser
file *os.File
done chan error
}
func runLogForwarding(t *testing.T) *log {
t.Helper()
logR, logW, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
logR.Close()
logW.Close()
})
tempFile, err := os.CreateTemp("", "")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
tempFile.Close()
os.Remove(tempFile.Name())
})
logrus.SetOutput(tempFile)
logrus.SetFormatter(&logrus.JSONFormatter{})
doneForwarding := ForwardLogs(logR)
return &log{w: logW, done: doneForwarding, file: tempFile}
}
func finish(t *testing.T, l *log) {
t.Helper()
l.w.Close()
if err := <-l.done; err != nil {
t.Fatalf("ForwardLogs: %v", err)
}
}
func truncateLogFile(t *testing.T, file *os.File) {
t.Helper()
err := file.Truncate(0)
if err != nil {
t.Fatalf("failed to truncate log file: %v", err)
}
}
// check checks that the file contains txt and does not contain notxt.
func check(t *testing.T, l *log, txt, notxt string) {
t.Helper()
contents, err := os.ReadFile(l.file.Name())
if err != nil {
t.Fatal(err)
}
if txt != "" && !bytes.Contains(contents, []byte(txt)) {
t.Fatalf("%s does not contain %s", contents, txt)
}
if notxt != "" && bytes.Contains(contents, []byte(notxt)) {
t.Fatalf("%s does contain %s", contents, notxt)
}
}
// checkWait is like check, but if the file is empty,
// it waits until it's not.
func checkWait(t *testing.T, l *log, txt, notxt string) {
t.Helper()
const (
delay = 100 * time.Millisecond
iter = 3
)
for i := 0; ; i++ {
st, err := l.file.Stat()
if err != nil {
t.Fatal(err)
}
if st.Size() > 0 {
break
}
if i == iter {
t.Fatalf("waited %s for file %s to be non-empty but it still is", iter*delay, l.file.Name())
}
time.Sleep(delay)
}
check(t, l, txt, notxt)
}