198 lines
4.4 KiB
Go
198 lines
4.4 KiB
Go
package tee_test
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
tee "git.wntrmute.dev/kyle/goutils/tee"
|
|
)
|
|
|
|
// captureStdout redirects os.Stdout for the duration of fn and returns what was written.
|
|
func captureStdout(t *testing.T, fn func()) string {
|
|
t.Helper()
|
|
r, w, err := os.Pipe()
|
|
if err != nil {
|
|
t.Fatalf("pipe: %v", err)
|
|
}
|
|
old := os.Stdout
|
|
os.Stdout = w
|
|
defer func() { os.Stdout = old }()
|
|
|
|
fn()
|
|
|
|
// Close writer to unblock reader and restore stdout
|
|
_ = w.Close()
|
|
b, _ := io.ReadAll(r)
|
|
_ = r.Close()
|
|
return string(b)
|
|
}
|
|
|
|
func TestNewOutEmpty_WritesToStdoutOnly(t *testing.T) {
|
|
teeInst, err := tee.NewOut("")
|
|
if err != nil {
|
|
t.Fatalf("NewOut: %v", err)
|
|
}
|
|
|
|
out := captureStdout(t, func() {
|
|
var n int
|
|
if n, err = teeInst.Write([]byte("abc")); err != nil || n != 3 {
|
|
t.Fatalf("Write got n=%d err=%v", n, err)
|
|
}
|
|
|
|
if n, err = teeInst.Printf("-%d-", 7); err != nil || n != len("-7-") {
|
|
t.Fatalf("Printf got n=%d err=%v", n, err)
|
|
}
|
|
})
|
|
|
|
if out != "abc-7-" {
|
|
t.Fatalf("stdout = %q, want %q", out, "abc-7-")
|
|
}
|
|
}
|
|
|
|
func TestNewOutWithFile_WritesToBoth(t *testing.T) {
|
|
dir := t.TempDir()
|
|
logPath := filepath.Join(dir, "log.txt")
|
|
|
|
teeInst, err := tee.NewOut(logPath)
|
|
if err != nil {
|
|
t.Fatalf("NewOut: %v", err)
|
|
}
|
|
defer func() { _ = teeInst.Close() }()
|
|
|
|
out := captureStdout(t, func() {
|
|
if _, err = teeInst.Write([]byte("x")); err != nil {
|
|
t.Fatalf("Write: %v", err)
|
|
}
|
|
if _, err = teeInst.Printf("%s", "y"); err != nil {
|
|
t.Fatalf("Printf: %v", err)
|
|
}
|
|
})
|
|
|
|
if out != "xy" {
|
|
t.Fatalf("stdout = %q, want %q", out, "xy")
|
|
}
|
|
|
|
// Close to flush and release the file before reading
|
|
if err = teeInst.Close(); err != nil {
|
|
t.Fatalf("Close: %v", err)
|
|
}
|
|
|
|
data, err := os.ReadFile(logPath)
|
|
if err != nil {
|
|
t.Fatalf("ReadFile: %v", err)
|
|
}
|
|
if string(data) != "xy" {
|
|
t.Fatalf("file content = %q, want %q", string(data), "xy")
|
|
}
|
|
}
|
|
|
|
func TestVPrintf_VerboseToggle(t *testing.T) {
|
|
teeInst := &tee.Tee{} // stdout only
|
|
|
|
out := captureStdout(t, func() {
|
|
if n, err := teeInst.VPrintf("hello"); err != nil || n != 0 {
|
|
t.Fatalf("VPrintf (quiet) got n=%d err=%v", n, err)
|
|
}
|
|
})
|
|
if out != "" {
|
|
t.Fatalf("stdout = %q, want empty when not verbose", out)
|
|
}
|
|
|
|
teeInst.Verbose = true
|
|
out = captureStdout(t, func() {
|
|
if n, err := teeInst.VPrintf("%s", "hello"); err != nil || n != len("hello") {
|
|
t.Fatalf("VPrintf (verbose) got n=%d err=%v", n, err)
|
|
}
|
|
})
|
|
if out != "hello" {
|
|
t.Fatalf("stdout = %q, want %q", out, "hello")
|
|
}
|
|
}
|
|
|
|
func TestWrite_StdoutErrorDoesNotWriteToFile(t *testing.T) {
|
|
dir := t.TempDir()
|
|
logPath := filepath.Join(dir, "log.txt")
|
|
teeInst, err := tee.NewOut(logPath)
|
|
if err != nil {
|
|
t.Fatalf("NewOut: %v", err)
|
|
}
|
|
defer func() { _ = teeInst.Close() }()
|
|
|
|
// Replace stdout with a closed pipe writer to force write error.
|
|
r, w, err := os.Pipe()
|
|
if err != nil {
|
|
t.Fatalf("pipe: %v", err)
|
|
}
|
|
old := os.Stdout
|
|
os.Stdout = w
|
|
_ = w.Close() // immediately close to cause EPIPE on write
|
|
defer func() {
|
|
os.Stdout = old
|
|
_ = r.Close()
|
|
}()
|
|
|
|
var n int
|
|
if n, err = teeInst.Write([]byte("abc")); err == nil {
|
|
t.Fatalf("expected error writing to closed stdout, got n=%d err=nil", n)
|
|
}
|
|
|
|
// Ensure file remained empty because stdout write failed first.
|
|
_ = teeInst.Close()
|
|
data, err := os.ReadFile(logPath)
|
|
if err != nil {
|
|
t.Fatalf("ReadFile: %v", err)
|
|
}
|
|
if len(data) != 0 {
|
|
t.Fatalf("file content = %q, want empty due to stdout failure", string(data))
|
|
}
|
|
}
|
|
|
|
func TestGlobal_OpenPrintfVPrintfClose(t *testing.T) {
|
|
// Ensure a clean slate for global tee
|
|
_ = tee.Close()
|
|
tee.SetVerbose(false)
|
|
|
|
dir := t.TempDir()
|
|
logPath := filepath.Join(dir, "glog.txt")
|
|
|
|
if err := tee.Open(logPath); err != nil {
|
|
t.Fatalf("Open: %v", err)
|
|
}
|
|
|
|
out := captureStdout(t, func() {
|
|
if _, err := tee.Printf("A"); err != nil {
|
|
t.Fatalf("Printf: %v", err)
|
|
}
|
|
// Not verbose yet, should not print
|
|
if n, err := tee.VPrintf("B"); err != nil || n != 0 {
|
|
t.Fatalf("VPrintf (quiet) n=%d err=%v", n, err)
|
|
}
|
|
tee.SetVerbose(true)
|
|
if _, err := tee.VPrintf("C%d", 1); err != nil {
|
|
t.Fatalf("VPrintf (verbose): %v", err)
|
|
}
|
|
})
|
|
|
|
if out != "AC1" {
|
|
t.Fatalf("stdout = %q, want %q", out, "AC1")
|
|
}
|
|
|
|
if err := tee.Close(); err != nil {
|
|
t.Fatalf("Close: %v", err)
|
|
}
|
|
|
|
data, err := os.ReadFile(logPath)
|
|
if err != nil {
|
|
t.Fatalf("ReadFile: %v", err)
|
|
}
|
|
if string(data) != "AC1" {
|
|
t.Fatalf("file content = %q, want %q", string(data), "AC1")
|
|
}
|
|
|
|
// Reset global tee for other tests/packages
|
|
_ = tee.Close()
|
|
tee.SetVerbose(false)
|
|
}
|