mirror of
https://github.com/aykhans/dodo.git
synced 2025-09-05 10:43:37 +00:00
Here we go again...
This commit is contained in:
250
pkg/utils/print_test.go
Normal file
250
pkg/utils/print_test.go
Normal file
@@ -0,0 +1,250 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/text"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPrintErr(t *testing.T) {
|
||||
t.Run("PrintErr writes to stderr with color", func(t *testing.T) {
|
||||
// Capture stderr
|
||||
oldStderr := os.Stderr
|
||||
reader, writer, _ := os.Pipe()
|
||||
os.Stderr = writer
|
||||
|
||||
// Call PrintErr
|
||||
PrintErr(text.FgRed, "Error: %s", "test error")
|
||||
|
||||
// Restore stderr and read output
|
||||
writer.Close()
|
||||
os.Stderr = oldStderr
|
||||
var buf bytes.Buffer
|
||||
io.Copy(&buf, reader)
|
||||
output := buf.String()
|
||||
|
||||
// The output should contain the message (color codes are included)
|
||||
assert.Contains(t, output, "test error")
|
||||
assert.Contains(t, output, "Error:")
|
||||
assert.True(t, strings.HasSuffix(output, "\n"))
|
||||
})
|
||||
|
||||
t.Run("PrintErr with multiple format arguments", func(t *testing.T) {
|
||||
oldStderr := os.Stderr
|
||||
reader, writer, _ := os.Pipe()
|
||||
os.Stderr = writer
|
||||
|
||||
PrintErr(text.FgYellow, "Warning: %s at line %d", "issue", 42)
|
||||
|
||||
writer.Close()
|
||||
os.Stderr = oldStderr
|
||||
var buf bytes.Buffer
|
||||
io.Copy(&buf, reader)
|
||||
output := buf.String()
|
||||
|
||||
assert.Contains(t, output, "Warning: issue at line 42")
|
||||
})
|
||||
|
||||
t.Run("PrintErr with no format arguments", func(t *testing.T) {
|
||||
oldStderr := os.Stderr
|
||||
reader, writer, _ := os.Pipe()
|
||||
os.Stderr = writer
|
||||
|
||||
PrintErr(text.FgGreen, "Simple message")
|
||||
|
||||
writer.Close()
|
||||
os.Stderr = oldStderr
|
||||
var buf bytes.Buffer
|
||||
io.Copy(&buf, reader)
|
||||
output := buf.String()
|
||||
|
||||
assert.Contains(t, output, "Simple message")
|
||||
assert.True(t, strings.HasSuffix(output, "\n"))
|
||||
})
|
||||
|
||||
t.Run("PrintErr with different colors", func(t *testing.T) {
|
||||
colors := []text.Color{
|
||||
text.FgRed,
|
||||
text.FgGreen,
|
||||
text.FgYellow,
|
||||
text.FgBlue,
|
||||
text.FgMagenta,
|
||||
text.FgCyan,
|
||||
}
|
||||
|
||||
for _, color := range colors {
|
||||
oldStderr := os.Stderr
|
||||
reader, writer, _ := os.Pipe()
|
||||
os.Stderr = writer
|
||||
|
||||
PrintErr(color, "Message with color")
|
||||
|
||||
writer.Close()
|
||||
os.Stderr = oldStderr
|
||||
var buf bytes.Buffer
|
||||
io.Copy(&buf, reader)
|
||||
output := buf.String()
|
||||
|
||||
assert.Contains(t, output, "Message with color")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("PrintErr with empty string", func(t *testing.T) {
|
||||
oldStderr := os.Stderr
|
||||
reader, writer, _ := os.Pipe()
|
||||
os.Stderr = writer
|
||||
|
||||
PrintErr(text.FgRed, "")
|
||||
|
||||
writer.Close()
|
||||
os.Stderr = oldStderr
|
||||
var buf bytes.Buffer
|
||||
io.Copy(&buf, reader)
|
||||
output := buf.String()
|
||||
|
||||
assert.Equal(t, "\n", strings.TrimPrefix(output, "\x1b[31m\x1b[0m")) // Just newline after color codes
|
||||
})
|
||||
|
||||
t.Run("PrintErr with special characters", func(t *testing.T) {
|
||||
oldStderr := os.Stderr
|
||||
reader, writer, _ := os.Pipe()
|
||||
os.Stderr = writer
|
||||
|
||||
PrintErr(text.FgRed, "Special chars: %s", "!@#$%^&*()")
|
||||
|
||||
writer.Close()
|
||||
os.Stderr = oldStderr
|
||||
var buf bytes.Buffer
|
||||
io.Copy(&buf, reader)
|
||||
output := buf.String()
|
||||
|
||||
assert.Contains(t, output, "Special chars: !@#$%^&*()")
|
||||
})
|
||||
|
||||
t.Run("PrintErr with percent sign in message", func(t *testing.T) {
|
||||
oldStderr := os.Stderr
|
||||
reader, writer, _ := os.Pipe()
|
||||
os.Stderr = writer
|
||||
|
||||
PrintErr(text.FgRed, "Progress: 100%% complete")
|
||||
|
||||
writer.Close()
|
||||
os.Stderr = oldStderr
|
||||
var buf bytes.Buffer
|
||||
io.Copy(&buf, reader)
|
||||
output := buf.String()
|
||||
|
||||
assert.Contains(t, output, "Progress: 100% complete")
|
||||
})
|
||||
}
|
||||
|
||||
func TestPrintErrAndExit(t *testing.T) {
|
||||
if os.Getenv("BE_CRASHER") == "1" {
|
||||
// This is the subprocess that will actually call PrintErrAndExit
|
||||
exitCode := 1
|
||||
if code := os.Getenv("EXIT_CODE"); code != "" {
|
||||
switch code {
|
||||
case "0":
|
||||
exitCode = 0
|
||||
case "1":
|
||||
exitCode = 1
|
||||
case "2":
|
||||
exitCode = 2
|
||||
}
|
||||
}
|
||||
PrintErrAndExit(text.FgRed, exitCode, "Error: %s", "fatal error")
|
||||
return
|
||||
}
|
||||
|
||||
t.Run("PrintErrAndExit calls os.Exit with correct code", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
exitCode int
|
||||
}{
|
||||
{"Exit with code 0", 0},
|
||||
{"Exit with code 1", 1},
|
||||
{"Exit with code 2", 2},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
cmd := exec.CommandContext(ctx, os.Args[0], "-test.run=TestPrintErrAndExit")
|
||||
cmd.Env = append(os.Environ(),
|
||||
"BE_CRASHER=1",
|
||||
"EXIT_CODE="+string(rune('0'+testCase.exitCode)))
|
||||
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
err := cmd.Run()
|
||||
|
||||
if testCase.exitCode == 0 {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
assert.Equal(t, testCase.exitCode, exitErr.ExitCode())
|
||||
}
|
||||
}
|
||||
|
||||
// Check that error message was printed to stderr
|
||||
assert.Contains(t, stderr.String(), "Error: fatal error")
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("PrintErrAndExit prints before exiting", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
cmd := exec.CommandContext(ctx, os.Args[0], "-test.run=TestPrintErrAndExit")
|
||||
cmd.Env = append(os.Environ(), "BE_CRASHER=1", "EXIT_CODE=1")
|
||||
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
cmd.Run() // Ignore error since we expect non-zero exit
|
||||
|
||||
output := stderr.String()
|
||||
assert.Contains(t, output, "Error: fatal error")
|
||||
assert.True(t, strings.HasSuffix(output, "\n"))
|
||||
})
|
||||
}
|
||||
|
||||
// Benchmarks for performance testing
|
||||
func BenchmarkPrintErr(b *testing.B) {
|
||||
// Redirect stderr to /dev/null for benchmarking
|
||||
oldStderr := os.Stderr
|
||||
devNull, _ := os.Open(os.DevNull)
|
||||
os.Stderr = devNull
|
||||
defer func() {
|
||||
os.Stderr = oldStderr
|
||||
devNull.Close()
|
||||
}()
|
||||
|
||||
b.Run("Simple message", func(b *testing.B) {
|
||||
for range b.N {
|
||||
PrintErr(text.FgRed, "Error message")
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("Formatted message", func(b *testing.B) {
|
||||
for range b.N {
|
||||
PrintErr(text.FgRed, "Error: %s at line %d", "issue", 42)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("Different colors", func(b *testing.B) {
|
||||
colors := []text.Color{text.FgRed, text.FgGreen, text.FgYellow}
|
||||
for idx := range b.N {
|
||||
PrintErr(colors[idx%len(colors)], "Message %d", idx)
|
||||
}
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user