From 2cb3cb950becdc67c6cc5ef5db3bf986fb0244b9 Mon Sep 17 00:00:00 2001 From: nagayon-935 <92247537+nagayon-935@users.noreply.github.com> Date: Fri, 17 Apr 2026 09:50:03 +0900 Subject: [PATCH] fix: resolve all go-review issues (CRITICAL/HIGH/MEDIUM) CRITICAL: - portchecker: handle SetDeadline/Write errors in checkUDP to prevent goroutine leak - traceroute: check SetControlMessage error on IPv6 send socket (non-fatal) HIGH: - mping: protect portChecker with pMu mutex to eliminate data race across goroutines - mping: create real doneCh when cfg.count > 0 so TUI shows "Finished" footer - mping: use errors.Is for pflag.ErrHelp comparison - ui: replace 16-parameter Run() with RunOptions struct - mping/traceroute: wrap errors with context in socket open paths and getInterfaceIP MEDIUM: - pinger: replace raw sync/atomic usage with atomic.Uint32 for traceCounter - pinger/portchecker: use errors.As instead of direct type assertions for net.Error - tui_helpers: return false (not silent zero) on ParseFloat error in filter - tui_helpers: appendErrorLog uses incremental fmt.Fprintf (O(1)) instead of O(n) join on every call - pinger: add godoc to NewPinger, NewPingerWithOptions, Reply --- cmd/main/mping.go | 99 +++++++++++++++++++++------------- cmd/main/mping_test.go | 47 ++++++++-------- internal/pinger/pinger.go | 11 ++-- internal/pinger/portchecker.go | 15 +++--- internal/pinger/traceroute.go | 12 +++-- internal/ui/tui.go | 37 ++++++++++++- internal/ui/tui_helpers.go | 16 ++++-- internal/ui/tui_test.go | 60 ++++++++++----------- 8 files changed, 188 insertions(+), 109 deletions(-) diff --git a/cmd/main/mping.go b/cmd/main/mping.go index ba8c459..30f22b2 100644 --- a/cmd/main/mping.go +++ b/cmd/main/mping.go @@ -3,6 +3,7 @@ package main import ( "bytes" "context" + "errors" "fmt" "io" "net" @@ -12,7 +13,7 @@ import ( "github.com/nagayon-935/mping/internal/pinger" "github.com/nagayon-935/mping/internal/stats" - "github.com/nagayon-935/mping/internal/ui" + ui "github.com/nagayon-935/mping/internal/ui" "github.com/spf13/pflag" "gopkg.in/yaml.v3" ) @@ -78,7 +79,7 @@ var newPinger = func(targets []*stats.TargetStats, opts pinger.Options) pingerCo return &pingerAdapter{Pinger: pinger.NewPingerWithOptions(targets, opts)} } -var uiRun = ui.Run +var uiRun = func(opts ui.RunOptions) error { return ui.Run(opts) } var ( interfaceByName = net.InterfaceByName @@ -88,11 +89,11 @@ var ( func getInterfaceIP(ifaceName string, wantIPv6 bool) (string, error) { iface, err := interfaceByName(ifaceName) if err != nil { - return "", err + return "", fmt.Errorf("lookup interface %q: %w", ifaceName, err) } addrs, err := iface.Addrs() if err != nil { - return "", err + return "", fmt.Errorf("get addresses for interface %q: %w", ifaceName, err) } for _, addr := range addrs { if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { @@ -478,7 +479,7 @@ func setupPortChecker(targets []*stats.TargetStats, portSpecs []pinger.PortSpec, func run(args []string, out io.Writer, errOut io.Writer) int { cfg, hosts, fs, usage, err := parseArgs(args) if err != nil { - if err == pflag.ErrHelp { + if errors.Is(err, pflag.ErrHelp) { fmt.Fprint(out, usage) return 0 } @@ -552,6 +553,7 @@ func run(args []string, out io.Writer, errOut io.Writer) int { p pingerController traceCtx context.Context traceCancel context.CancelFunc + portChecker *pinger.PortChecker ) startPinger := func() error { @@ -559,16 +561,14 @@ func run(args []string, out io.Writer, errOut io.Writer) int { if err := next.Start(interval, timeout); err != nil { return err } + pMu.Lock() if cfg.trace { - pMu.Lock() if traceCancel != nil { traceCancel() } traceCtx, traceCancel = context.WithCancel(context.Background()) go runTraceroutes(traceCtx, next, targets) - pMu.Unlock() } - pMu.Lock() p = next pMu.Unlock() return nil @@ -603,16 +603,17 @@ func run(args []string, out io.Writer, errOut io.Writer) int { } portSpecs = append(portSpecs, spec) } - portChecker := setupPortChecker(targets, portSpecs, interval, timeout) - - if cfg.trace { - // Traceroute info can be added to logs if needed - } + pMu.Lock() + portChecker = setupPortChecker(targets, portSpecs, interval, timeout) + pMu.Unlock() stopAll := func() { stopPinger() - if portChecker != nil { - portChecker.Stop() + pMu.Lock() + cur := portChecker + pMu.Unlock() + if cur != nil { + cur.Stop() } } @@ -632,40 +633,62 @@ func run(args []string, out io.Writer, errOut io.Writer) int { var resetPort func() if len(portSpecs) > 0 { resetPort = func() { - if portChecker != nil { - portChecker.Stop() + pMu.Lock() + cur := portChecker + portChecker = nil + pMu.Unlock() + if cur != nil { + cur.Stop() } - portChecker = setupPortChecker(targets, portSpecs, interval, timeout) + next := setupPortChecker(targets, portSpecs, interval, timeout) + pMu.Lock() + portChecker = next + pMu.Unlock() } } + // doneCh is closed when the pinger finishes (count-limited mode). + var doneCh chan struct{} + if cfg.count > 0 { + doneCh = make(chan struct{}) + go func() { + pMu.Lock() + cur := p + pMu.Unlock() + if cur != nil { + cur.Wait() + } + close(doneCh) + }() + } + // Start TUI - if err := uiRun( - targets, - interval, - timeout, - nil, - displaySourceIPv4, - displaySourceIPv6, - packetSizeToUse, - preLogs, - cfg.trace, - len(portSpecs) > 0, - cfg.asnEnabled, - stopAll, - func() error { + if err := uiRun(ui.RunOptions{ + Targets: targets, + Interval: interval, + Timeout: timeout, + DoneCh: doneCh, + SourceIPv4: displaySourceIPv4, + SourceIPv6: displaySourceIPv6, + PacketSize: packetSizeToUse, + InitialLogs: preLogs, + TraceEnabled: cfg.trace, + PortEnabled: len(portSpecs) > 0, + ASNEnabled: cfg.asnEnabled, + OnStop: stopAll, + OnRestart: func() error { stopAll() if err := startPinger(); err != nil { return err } - if portChecker != nil { - portChecker = setupPortChecker(targets, portSpecs, interval, timeout) - } + pMu.Lock() + portChecker = setupPortChecker(targets, portSpecs, interval, timeout) + pMu.Unlock() return nil }, - resetTrace, - resetPort, - ); err != nil { + OnResetTrace: resetTrace, + OnResetPort: resetPort, + }); err != nil { fmt.Fprintf(errOut, "Error running application: %v\n", err) stopAll() return 1 diff --git a/cmd/main/mping_test.go b/cmd/main/mping_test.go index 58e2437..c4ffcc8 100644 --- a/cmd/main/mping_test.go +++ b/cmd/main/mping_test.go @@ -14,6 +14,7 @@ import ( "github.com/nagayon-935/mping/internal/pinger" "github.com/nagayon-935/mping/internal/stats" + "github.com/nagayon-935/mping/internal/ui" "github.com/spf13/pflag" ) @@ -209,9 +210,9 @@ func TestRunStopRestart(t *testing.T) { newPinger = func(targets []*stats.TargetStats, opts pinger.Options) pingerController { return fp } - uiRun = func(targets []*stats.TargetStats, interval, timeout time.Duration, doneCh chan struct{}, sourceIPv4, sourceIPv6 string, packetSize int, initialLogs []string, traceEnabled bool, portEnabled bool, asnEnabled bool, onStop func(), onRestart func() error, onResetTrace func(), onResetPort func()) error { - onStop() - if err := onRestart(); err != nil { + uiRun = func(opts ui.RunOptions) error { + opts.OnStop() + if err := opts.OnRestart(); err != nil { t.Fatalf("restart failed: %v", err) } return nil @@ -239,7 +240,7 @@ func TestRunStartError(t *testing.T) { newPinger = func(targets []*stats.TargetStats, opts pinger.Options) pingerController { return fp } - uiRun = func(targets []*stats.TargetStats, interval, timeout time.Duration, doneCh chan struct{}, sourceIPv4, sourceIPv6 string, packetSize int, initialLogs []string, traceEnabled bool, portEnabled bool, asnEnabled bool, onStop func(), onRestart func() error, onResetTrace func(), onResetPort func()) error { + uiRun = func(opts ui.RunOptions) error { return nil } @@ -592,7 +593,7 @@ func TestRunInvalidPortSpec(t *testing.T) { newPinger = func(targets []*stats.TargetStats, opts pinger.Options) pingerController { return &fakePinger{} } - uiRun = func(targets []*stats.TargetStats, interval, timeout time.Duration, doneCh chan struct{}, sourceIPv4, sourceIPv6 string, packetSize int, initialLogs []string, traceEnabled bool, portEnabled bool, asnEnabled bool, onStop func(), onRestart func() error, onResetTrace func(), onResetPort func()) error { + uiRun = func(opts ui.RunOptions) error { return nil } var out, errOut bytes.Buffer @@ -616,7 +617,7 @@ func TestRunWithPortSpec(t *testing.T) { newPinger = func(targets []*stats.TargetStats, opts pinger.Options) pingerController { return fp } - uiRun = func(targets []*stats.TargetStats, interval, timeout time.Duration, doneCh chan struct{}, sourceIPv4, sourceIPv6 string, packetSize int, initialLogs []string, traceEnabled bool, portEnabled bool, asnEnabled bool, onStop func(), onRestart func() error, onResetTrace func(), onResetPort func()) error { + uiRun = func(opts ui.RunOptions) error { return nil } var out, errOut bytes.Buffer @@ -637,7 +638,7 @@ func TestRunWithTrace(t *testing.T) { newPinger = func(targets []*stats.TargetStats, opts pinger.Options) pingerController { return fp } - uiRun = func(targets []*stats.TargetStats, interval, timeout time.Duration, doneCh chan struct{}, sourceIPv4, sourceIPv6 string, packetSize int, initialLogs []string, traceEnabled bool, portEnabled bool, asnEnabled bool, onStop func(), onRestart func() error, onResetTrace func(), onResetPort func()) error { + uiRun = func(opts ui.RunOptions) error { return nil } var out, errOut bytes.Buffer @@ -662,26 +663,26 @@ func TestRunResetTrace(t *testing.T) { return fp } - uiRun = func(targets []*stats.TargetStats, interval, timeout time.Duration, doneCh chan struct{}, sourceIPv4, sourceIPv6 string, packetSize int, initialLogs []string, traceEnabled bool, portEnabled bool, asnEnabled bool, onStop func(), onRestart func() error, onResetTrace func(), onResetPort func()) error { - if onResetTrace == nil { - t.Error("onResetTrace must not be nil when traceEnabled=true") + uiRun = func(opts ui.RunOptions) error { + if opts.OnResetTrace == nil { + t.Error("OnResetTrace must not be nil when TraceEnabled=true") return nil } - // Simulate the UI clearing TraceHops before calling onResetTrace (as 'R' does). - for _, tg := range targets { + // Simulate the UI clearing TraceHops before calling OnResetTrace (as 'R' does). + for _, tg := range opts.Targets { tg.SetTraceHops(nil) } - onResetTrace() + opts.OnResetTrace() // Wait for the re-run to populate TraceHops. deadline := time.Now().Add(2 * time.Second) for time.Now().Before(deadline) { - if len(targets[0].GetView().TraceHops) > 0 { + if len(opts.Targets[0].GetView().TraceHops) > 0 { break } time.Sleep(10 * time.Millisecond) } - if hops := targets[0].GetView().TraceHops; len(hops) == 0 { - t.Error("TraceHops not repopulated after onResetTrace()") + if hops := opts.Targets[0].GetView().TraceHops; len(hops) == 0 { + t.Error("TraceHops not repopulated after OnResetTrace()") } return nil } @@ -708,17 +709,17 @@ func TestRunResetPort(t *testing.T) { return fp } - uiRun = func(targets []*stats.TargetStats, interval, timeout time.Duration, doneCh chan struct{}, sourceIPv4, sourceIPv6 string, packetSize int, initialLogs []string, traceEnabled bool, portEnabled bool, asnEnabled bool, onStop func(), onRestart func() error, onResetTrace func(), onResetPort func()) error { - if !portEnabled { - t.Error("portEnabled should be true when port specs given") + uiRun = func(opts ui.RunOptions) error { + if !opts.PortEnabled { + t.Error("PortEnabled should be true when port specs given") return nil } - if onResetPort == nil { - t.Error("onResetPort must not be nil when port specs are provided") + if opts.OnResetPort == nil { + t.Error("OnResetPort must not be nil when port specs are provided") return nil } // Calling it must not panic - onResetPort() + opts.OnResetPort() return nil } @@ -739,7 +740,7 @@ func TestRunWithMTUIPv6Warning(t *testing.T) { newPinger = func(targets []*stats.TargetStats, opts pinger.Options) pingerController { return &fakePinger{} } - uiRun = func(targets []*stats.TargetStats, interval, timeout time.Duration, doneCh chan struct{}, sourceIPv4, sourceIPv6 string, packetSize int, initialLogs []string, traceEnabled bool, portEnabled bool, asnEnabled bool, onStop func(), onRestart func() error, onResetTrace func(), onResetPort func()) error { + uiRun = func(opts ui.RunOptions) error { return nil } var out, errOut bytes.Buffer diff --git a/internal/pinger/pinger.go b/internal/pinger/pinger.go index 6d3ff98..6969673 100644 --- a/internal/pinger/pinger.go +++ b/internal/pinger/pinger.go @@ -1,12 +1,14 @@ package pinger import ( + "errors" "fmt" "io" "net" "os" "strings" "sync" + "sync/atomic" "time" "github.com/nagayon-935/mping/internal/stats" @@ -44,7 +46,7 @@ type PacketConnV6 interface { SetControlMessage(cf ipv6.ControlFlags, on bool) error } -// Reply represents a received ICMP echo reply. +// Reply represents a single received ICMP echo reply or error from the receiver loop. type Reply struct { RTT time.Duration TTL int @@ -79,7 +81,7 @@ type Pinger struct { traceChans []chan traceMsg // one per concurrent TraceRoute call traceChansMu sync.RWMutex - traceCounter uint32 // atomic counter for unique traceID per concurrent call + traceCounter atomic.Uint32 // unique traceID per concurrent call LogWriter io.Writer // Optional logger @@ -104,10 +106,12 @@ type Options struct { AsnEnabled bool } +// NewPinger creates a Pinger with default options for the given targets. func NewPinger(targets []*stats.TargetStats) *Pinger { return NewPingerWithOptions(targets, Options{}) } +// NewPingerWithOptions creates a Pinger with the provided options. func NewPingerWithOptions(targets []*stats.TargetStats, opts Options) *Pinger { resolve := opts.ResolveIPAddr if resolve == nil { @@ -357,7 +361,8 @@ func (p *Pinger) runReceiver( setDeadline(time.Now().Add(receiverReadTimeout)) n, ttl, src, err := readFrom(buf) if err != nil { - if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() { + var opErr *net.OpError + if errors.As(err, &opErr) && opErr.Timeout() { continue } return diff --git a/internal/pinger/portchecker.go b/internal/pinger/portchecker.go index d1132ec..cb1570c 100644 --- a/internal/pinger/portchecker.go +++ b/internal/pinger/portchecker.go @@ -1,6 +1,7 @@ package pinger import ( + "errors" "fmt" "net" "strings" @@ -143,8 +144,12 @@ func checkUDP(addr string, timeout time.Duration) (string, time.Duration) { defer conn.Close() start := time.Now() - conn.SetDeadline(time.Now().Add(timeout)) - conn.Write([]byte{}) + if err := conn.SetDeadline(time.Now().Add(timeout)); err != nil { + return "Error", 0 + } + if _, err := conn.Write([]byte{}); err != nil { + return "Error", 0 + } buf := make([]byte, 1) _, err = conn.Read(buf) @@ -161,8 +166,6 @@ func checkUDP(addr string, timeout time.Duration) (string, time.Duration) { } func isTimeout(err error) bool { - if netErr, ok := err.(net.Error); ok { - return netErr.Timeout() - } - return false + var netErr net.Error + return errors.As(err, &netErr) && netErr.Timeout() } diff --git a/internal/pinger/traceroute.go b/internal/pinger/traceroute.go index 6a8b2f0..b7cef5b 100644 --- a/internal/pinger/traceroute.go +++ b/internal/pinger/traceroute.go @@ -3,7 +3,6 @@ package pinger import ( "fmt" "net" - "sync/atomic" "time" "golang.org/x/net/icmp" @@ -63,7 +62,7 @@ func (p *Pinger) TraceRoute(dest string, maxHops int, timeout time.Duration) ([] } c, err := p.listenPacket("ip4:icmp", bindAddr) if err != nil { - return nil, err + return nil, fmt.Errorf("open traceroute send socket (v4): %w", err) } sendConn = c sendV4 = ipv4.NewPacketConn(c) @@ -74,15 +73,18 @@ func (p *Pinger) TraceRoute(dest string, maxHops int, timeout time.Duration) ([] } c, err := p.listenPacket("ip6:ipv6-icmp", bindAddr) if err != nil { - return nil, err + return nil, fmt.Errorf("open traceroute send socket (v6): %w", err) } sendConn = c sendV6 = ipv6.NewPacketConn(c) - sendV6.SetControlMessage(ipv6.FlagHopLimit, true) + if err := sendV6.SetControlMessage(ipv6.FlagHopLimit, true); err != nil { + // Non-fatal: hop limit control message may not be available on all platforms. + _ = err + } } defer sendConn.Close() - traceID := (p.baseID + 0x1234 + int(atomic.AddUint32(&p.traceCounter, 1))) & 0xffff + traceID := (p.baseID + 0x1234 + int(p.traceCounter.Add(1))) & 0xffff hops := make([]string, 0, maxHops) // acceptPacket checks whether a received message is a valid reply to the diff --git a/internal/ui/tui.go b/internal/ui/tui.go index c2226ee..cea231a 100644 --- a/internal/ui/tui.go +++ b/internal/ui/tui.go @@ -20,7 +20,42 @@ const ( var newApplication = tview.NewApplication -func Run(targets []*stats.TargetStats, interval, timeout time.Duration, doneCh chan struct{}, sourceIPv4, sourceIPv6 string, packetSize int, initialLogs []string, traceEnabled bool, portEnabled bool, asnEnabled bool, onStop func(), onRestart func() error, onResetTrace func(), onResetPort func()) error { +// RunOptions contains all parameters for the Run function. +type RunOptions struct { + Targets []*stats.TargetStats + Interval time.Duration + Timeout time.Duration + DoneCh chan struct{} // closed when pinger finishes (count-limited mode); nil means unlimited + SourceIPv4 string + SourceIPv6 string + PacketSize int + InitialLogs []string + TraceEnabled bool + PortEnabled bool + ASNEnabled bool + OnStop func() + OnRestart func() error + OnResetTrace func() + OnResetPort func() +} + +// Run starts the TUI application with the given options. +func Run(opts RunOptions) error { + targets := opts.Targets + interval := opts.Interval + doneCh := opts.DoneCh + sourceIPv4 := opts.SourceIPv4 + sourceIPv6 := opts.SourceIPv6 + packetSize := opts.PacketSize + initialLogs := opts.InitialLogs + traceEnabled := opts.TraceEnabled + portEnabled := opts.PortEnabled + asnEnabled := opts.ASNEnabled + onStop := opts.OnStop + onRestart := opts.OnRestart + onResetTrace := opts.OnResetTrace + onResetPort := opts.OnResetPort + app := newApplication() table := tview.NewTable(). SetBorders(true). diff --git a/internal/ui/tui_helpers.go b/internal/ui/tui_helpers.go index 4078e1d..6d11308 100644 --- a/internal/ui/tui_helpers.go +++ b/internal/ui/tui_helpers.go @@ -131,7 +131,10 @@ func matchesFilter(view stats.TargetView, filter string) bool { if len(parts) == 2 { key := strings.TrimSpace(parts[0]) valStr := strings.TrimSpace(parts[1]) - val, _ := strconv.ParseFloat(valStr, 64) + val, err := strconv.ParseFloat(valStr, 64) + if err != nil { + return false + } switch key { case "loss": return calcLossRate(view) > val @@ -148,7 +151,10 @@ func matchesFilter(view stats.TargetView, filter string) bool { if len(parts) == 2 { key := strings.TrimSpace(parts[0]) valStr := strings.TrimSpace(parts[1]) - val, _ := strconv.ParseFloat(valStr, 64) + val, err := strconv.ParseFloat(valStr, 64) + if err != nil { + return false + } switch key { case "loss": return calcLossRate(view) < val @@ -689,9 +695,13 @@ func buildCompactRowCells(values []string, widths []int, aligns []int, rowColor func appendErrorLog(errorLogs *[]string, errorView *tview.TextView, msg string) { *errorLogs = append(*errorLogs, msg) if len(*errorLogs) > errorLogMaxSize { + // Rebuild once on eviction instead of every call. *errorLogs = (*errorLogs)[1:] + errorView.SetText(strings.Join(*errorLogs, "\n") + "\n") + errorView.ScrollToEnd() + return } - errorView.SetText(strings.Join(*errorLogs, "\n") + "\n") + fmt.Fprintf(errorView, "%s\n", msg) errorView.ScrollToEnd() } diff --git a/internal/ui/tui_test.go b/internal/ui/tui_test.go index 06df0a6..e7d416c 100644 --- a/internal/ui/tui_test.go +++ b/internal/ui/tui_test.go @@ -609,7 +609,7 @@ func TestRunWithSimulationScreen(t *testing.T) { close(done) }() - err := Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, done, "", "", 56, nil, false, false, false, nil, nil, nil, nil) + err := Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, DoneCh: done, PacketSize: 56}) if err != nil { t.Fatalf("Run returned error: %v", err) } @@ -1041,7 +1041,7 @@ func TestRunWithTraceEnabled(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "10.0.0.1", "", 56, nil, true, false, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, SourceIPv4: "10.0.0.1", PacketSize: 56, TraceEnabled: true}) }() screen := <-screenCh @@ -1073,7 +1073,7 @@ func TestRunWithPortEnabled(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "10.0.0.1", "", 56, nil, false, true, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, SourceIPv4: "10.0.0.1", PacketSize: 56, PortEnabled: true}) }() screen := <-screenCh @@ -1105,7 +1105,7 @@ func TestRunWithBothTracAndPort(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, true, true, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56, TraceEnabled: true, PortEnabled: true}) }() screen := <-screenCh @@ -1134,7 +1134,7 @@ func TestRunWithInitialLogs(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, logs, false, false, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56, InitialLogs: logs}) }() screen := <-screenCh @@ -1163,7 +1163,7 @@ func TestRunWithDoneChClosed(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, doneCh, "", "", 56, nil, false, false, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, DoneCh: doneCh, PacketSize: 56}) }() screen := <-screenCh @@ -1196,7 +1196,7 @@ func TestRunKeyboardStop(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, false, false, false, onStop, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56, OnStop: onStop}) }() screen := <-screenCh @@ -1230,7 +1230,7 @@ func TestRunKeyboardReset(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, false, false, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56}) }() screen := <-screenCh @@ -1267,7 +1267,7 @@ func TestRunKeyboardRestart(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, false, false, false, onStop, onRestart, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56, OnStop: onStop, OnRestart: onRestart}) }() screen := <-screenCh @@ -1299,7 +1299,7 @@ func TestRunKeyboardTab(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, false, false, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56}) }() screen := <-screenCh @@ -1414,7 +1414,7 @@ func TestRunTabWithTraceAndPort(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, true, true, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56, TraceEnabled: true, PortEnabled: true}) }() screen := <-screenCh @@ -1450,7 +1450,7 @@ func TestRunKeyboardStopNoCallback(t *testing.T) { errCh := make(chan error, 1) go func() { // onStop=nil, onRestart=nil - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, false, false, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56}) }() screen := <-screenCh @@ -1487,7 +1487,7 @@ func TestRunWithLossTarget(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "10.0.0.1", "", 56, nil, false, false, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, SourceIPv4: "10.0.0.1", PacketSize: 56}) }() screen := <-screenCh @@ -1554,7 +1554,7 @@ func TestRunTableScrollKeys(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run(targets, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, false, false, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: targets, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56}) }() screen := <-screenCh @@ -1595,7 +1595,7 @@ func TestRunRestartError(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, false, false, false, onStop, func() error { return restartErr }, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56, OnStop: onStop, OnRestart: func() error { return restartErr }}) }() screen := <-screenCh @@ -1688,7 +1688,7 @@ func TestRunTabTraceOnly(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, true, false, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56, TraceEnabled: true}) }() screen := <-screenCh @@ -1725,7 +1725,7 @@ func TestRunTabPortOnly(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, false, true, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56, PortEnabled: true}) }() screen := <-screenCh @@ -1780,7 +1780,7 @@ func TestRunNarrowScreenCompactLayout(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run(targets, 50*time.Millisecond, 50*time.Millisecond, nil, "10.0.0.1", "", 56, nil, false, false, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: targets, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, SourceIPv4: "10.0.0.1", PacketSize: 56}) }() screen := <-screenCh @@ -1867,7 +1867,7 @@ func TestRunPortEnabledNoResults(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, false, true, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56, PortEnabled: true}) }() screen := <-screenCh @@ -1903,7 +1903,7 @@ func TestRunWithHighRTTAndJitter(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "10.0.0.1", "", 56, nil, false, false, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, SourceIPv4: "10.0.0.1", PacketSize: 56}) }() screen := <-screenCh @@ -1942,7 +1942,7 @@ func TestRunWithHighLossAlert(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "10.0.0.1", "", 56, nil, false, false, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, SourceIPv4: "10.0.0.1", PacketSize: 56}) }() screen := <-screenCh @@ -1973,7 +1973,7 @@ func TestRunWithTraceAndTicker(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "10.0.0.1", "", 56, nil, true, false, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, SourceIPv4: "10.0.0.1", PacketSize: 56, TraceEnabled: true}) }() screen := <-screenCh @@ -2008,7 +2008,7 @@ func TestRunWithPortAndTicker(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, false, true, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56, PortEnabled: true}) }() screen := <-screenCh @@ -2051,7 +2051,7 @@ func TestRunWithBothAndTicker(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target, target2}, 50*time.Millisecond, 50*time.Millisecond, nil, "10.0.0.1", "2001::1", 56, nil, true, true, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target, target2}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, SourceIPv4: "10.0.0.1", SourceIPv6: "2001::1", PacketSize: 56, TraceEnabled: true, PortEnabled: true}) }() screen := <-screenCh @@ -2138,7 +2138,7 @@ func TestRunScrollKeyWithFewTargets(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "10.0.0.1", "", 56, nil, false, false, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, SourceIPv4: "10.0.0.1", PacketSize: 56}) }() screen := <-screenCh @@ -2179,7 +2179,7 @@ func TestRunWithIPAsHost(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, false, false, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56}) }() screen := <-screenCh @@ -2213,7 +2213,7 @@ func TestRunResetTraceKey(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, true, false, false, nil, nil, onResetTrace, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56, TraceEnabled: true, OnResetTrace: onResetTrace}) }() screen := <-screenCh @@ -2263,7 +2263,7 @@ func TestRunResetPortKey(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, false, true, false, nil, nil, nil, onResetPort) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56, PortEnabled: true, OnResetPort: onResetPort}) }() screen := <-screenCh @@ -2304,7 +2304,7 @@ func TestRunPortStatusChangeLogged(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, false, true, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56, PortEnabled: true}) }() screen := <-screenCh @@ -2345,7 +2345,7 @@ func TestRunWithPortLastChange(t *testing.T) { errCh := make(chan error, 1) go func() { - errCh <- Run([]*stats.TargetStats{target}, 50*time.Millisecond, 50*time.Millisecond, nil, "", "", 56, nil, false, true, false, nil, nil, nil, nil) + errCh <- Run(RunOptions{Targets: []*stats.TargetStats{target}, Interval: 50*time.Millisecond, Timeout: 50*time.Millisecond, PacketSize: 56, PortEnabled: true}) }() screen := <-screenCh