mirror of
https://github.com/lunchcat/sif.git
synced 2026-06-12 19:11:25 -07:00
094f1e7806
concurrent workers (-threads 40) all hit the same milestone bucket on increment, spamming ~10 duplicate [25%] lines. track the last printed bucket under p.mu and only print when it advances.
97 lines
2.7 KiB
Go
97 lines
2.7 KiB
Go
/*
|
|
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
|
: :
|
|
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
|
: ▄█ █ █▀ · BSD 3-Clause License :
|
|
: :
|
|
: (c) 2022-2026 vmfunc, xyzeva, :
|
|
: lunchcat alumni & contributors :
|
|
: :
|
|
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
|
*/
|
|
|
|
package output
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
// the non-tty milestone path divides current*100/total, so a zero-total bar
|
|
// used to panic with integer divide-by-zero when piped or redirected.
|
|
func TestProgressZeroTotalNoPanic(t *testing.T) {
|
|
p := NewProgress(0, "scanning")
|
|
p.Increment("item")
|
|
p.Set(0, "item")
|
|
p.Done()
|
|
}
|
|
|
|
func TestProgressCounts(t *testing.T) {
|
|
p := NewProgress(4, "scanning")
|
|
for i := 0; i < 4; i++ {
|
|
p.Increment("x")
|
|
}
|
|
if p.current != 4 {
|
|
t.Errorf("current = %d, want 4", p.current)
|
|
}
|
|
}
|
|
|
|
// many concurrent workers used to spam the same milestone bucket (e.g. ten
|
|
// "[25%] .../1000" lines). each bucket must now print at most once.
|
|
func TestProgressNonTTYDedupesMilestones(t *testing.T) {
|
|
savedTTY, savedAPI := IsTTY, apiMode
|
|
IsTTY, apiMode = false, false
|
|
defer func() { IsTTY, apiMode = savedTTY, savedAPI }()
|
|
|
|
out := captureStdout(t, func() {
|
|
p := NewProgress(1000, "scanning")
|
|
var wg sync.WaitGroup
|
|
for i := 0; i < 40; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for j := 0; j < 25; j++ {
|
|
p.Increment("x")
|
|
}
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
})
|
|
|
|
lines := strings.Count(out, "\n")
|
|
if lines > 5 {
|
|
t.Errorf("printed %d milestone lines, want <=5:\n%s", lines, out)
|
|
}
|
|
}
|
|
|
|
func captureStdout(t *testing.T, fn func()) string {
|
|
t.Helper()
|
|
r, w, err := os.Pipe()
|
|
if err != nil {
|
|
t.Fatalf("pipe: %v", err)
|
|
}
|
|
saved := os.Stdout
|
|
os.Stdout = w
|
|
|
|
done := make(chan string, 1)
|
|
go func() {
|
|
buf := make([]byte, 0, 4096)
|
|
tmp := make([]byte, 1024)
|
|
for {
|
|
n, rerr := r.Read(tmp)
|
|
buf = append(buf, tmp[:n]...)
|
|
if rerr != nil {
|
|
break
|
|
}
|
|
}
|
|
done <- string(buf)
|
|
}()
|
|
|
|
fn()
|
|
os.Stdout = saved
|
|
w.Close()
|
|
return <-done
|
|
}
|