mirror of
https://blitiri.com.ar/repos/chasquid
synced 2025-12-18 14:47:03 +00:00
test: Improve layout of helper binaries
This patch moves the test helper binaries to a "one directory per helper" layout, and also makes them to be ignored in the coverage build instead of all builds. With this change, "go build ./..." will build all binaries including the test helpers, which helps make sure that module manage automation also considers them. In particular, this makes "go mod tidy" work fine.
This commit is contained in:
208
test/util/loadgen/loadgen.go
Normal file
208
test/util/loadgen/loadgen.go
Normal file
@@ -0,0 +1,208 @@
|
||||
//go:build !coverage
|
||||
// +build !coverage
|
||||
|
||||
// SMTP load generator, for testing purposes.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
_ "net/http/pprof"
|
||||
|
||||
"golang.org/x/net/trace"
|
||||
|
||||
"blitiri.com.ar/go/chasquid/internal/smtp"
|
||||
"blitiri.com.ar/go/log"
|
||||
)
|
||||
|
||||
var (
|
||||
addr = flag.String("addr", "",
|
||||
"server address")
|
||||
httpAddr = flag.String("http_addr", "localhost:8011",
|
||||
"monitoring HTTP server listening address")
|
||||
parallel = flag.Int("parallel", 0,
|
||||
"how many sending loops to run in parallel")
|
||||
runFor = flag.Duration("run_for", 0,
|
||||
"how long to run for (0 = forever)")
|
||||
wait = flag.Bool("wait", false,
|
||||
"don't exit after --run_for has lapsed")
|
||||
noop = flag.Bool("noop", false,
|
||||
"don't send an email, just connect and run a NOOP")
|
||||
)
|
||||
|
||||
var (
|
||||
host string
|
||||
exit bool
|
||||
|
||||
globalCount int64 = 0
|
||||
globalRuntime time.Duration
|
||||
globalMu = &sync.Mutex{}
|
||||
)
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
|
||||
flag.Parse()
|
||||
log.Init()
|
||||
|
||||
host, _, err = net.SplitHostPort(*addr)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to split --addr=%q: %v", *addr, err)
|
||||
}
|
||||
|
||||
if *wait {
|
||||
go http.ListenAndServe(*httpAddr, nil)
|
||||
log.Infof("monitoring address: http://%v/debug/requests?fam=one&b=11",
|
||||
*httpAddr)
|
||||
}
|
||||
|
||||
if *parallel == 0 {
|
||||
*parallel = runtime.GOMAXPROCS(0)
|
||||
}
|
||||
|
||||
lt := "full"
|
||||
if *noop {
|
||||
lt = "noop"
|
||||
}
|
||||
|
||||
log.Infof("launching %d %s sending loops in parallel", *parallel, lt)
|
||||
for i := 0; i < *parallel; i++ {
|
||||
go serial(i)
|
||||
}
|
||||
|
||||
var totalCount int64
|
||||
var totalRuntime time.Duration
|
||||
start := time.Now()
|
||||
for range time.Tick(1 * time.Second) {
|
||||
globalMu.Lock()
|
||||
totalCount += globalCount
|
||||
totalRuntime += globalRuntime
|
||||
count := globalCount
|
||||
runtime := globalRuntime
|
||||
globalCount = 0
|
||||
globalRuntime = 0
|
||||
globalMu.Unlock()
|
||||
|
||||
if count == 0 {
|
||||
log.Infof("0 ops")
|
||||
} else {
|
||||
log.Infof("%d ops, %v /op", count,
|
||||
time.Duration(runtime.Nanoseconds()/count).Truncate(time.Microsecond))
|
||||
}
|
||||
|
||||
if *runFor > 0 && time.Since(start) > *runFor {
|
||||
exit = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
end := time.Now()
|
||||
window := end.Sub(start)
|
||||
log.Infof("total: %d ops, %v wall, %v run",
|
||||
totalCount,
|
||||
window.Truncate(time.Millisecond),
|
||||
totalRuntime.Truncate(time.Millisecond))
|
||||
|
||||
avgLat := time.Duration(totalRuntime.Nanoseconds() / totalCount)
|
||||
log.Infof("avg: %v /op, %.0f ops/s",
|
||||
avgLat.Truncate(time.Microsecond),
|
||||
float64(totalCount)/window.Seconds(),
|
||||
)
|
||||
|
||||
if *wait {
|
||||
for {
|
||||
time.Sleep(24 * time.Hour)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func serial(id int) {
|
||||
var count int64
|
||||
start := time.Now()
|
||||
for {
|
||||
count += 1
|
||||
err := one()
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
if count == 5 {
|
||||
globalMu.Lock()
|
||||
globalCount += count
|
||||
globalRuntime += time.Since(start)
|
||||
globalMu.Unlock()
|
||||
count = 0
|
||||
start = time.Now()
|
||||
|
||||
if exit {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func one() error {
|
||||
tr := trace.New("one", *addr)
|
||||
defer tr.Finish()
|
||||
|
||||
conn, err := net.Dial("tcp", *addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
client, err := smtp.NewClient(conn, host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
if *noop {
|
||||
err = client.Noop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = client.MailAndRcpt("test@test", "null@testserver")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
retry:
|
||||
w, err := client.Data()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
// If we are sending too fast we might hit chasquid's queue size
|
||||
// limit. In that case, wait and try again.
|
||||
// We detect it with error code 451 which is used for this
|
||||
// situation.
|
||||
if terr, ok := err.(*textproto.Error); ok {
|
||||
if terr.Code == 451 {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
goto retry
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var body = []byte(`Subject: Load test
|
||||
|
||||
This is the body of the load test email.
|
||||
`)
|
||||
Reference in New Issue
Block a user