mirror of
https://github.com/aykhans/dodo.git
synced 2025-09-01 00:53:34 +00:00
251 lines
5.8 KiB
Go
251 lines
5.8 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|