Skip to content

fix: support iperf3 without --json-stream#205

Merged
s0up4200 merged 3 commits into
developfrom
codex/fix-iperf-import-156
Mar 8, 2026
Merged

fix: support iperf3 without --json-stream#205
s0up4200 merged 3 commits into
developfrom
codex/fix-iperf-import-156

Conversation

@s0up4200
Copy link
Copy Markdown
Collaborator

@s0up4200 s0up4200 commented Feb 18, 2026

Root Cause

Netronome always invoked iperf3 with --json-stream. On environments like Ubuntu 22.04 with iperf3 3.9, that flag is not supported, so iperf exits with status 1. We also were not surfacing stderr, which hid the real failure reason.

Summary

  • detect --json-stream support from iperf3 --help and fall back to -J when unavailable
  • include stderr in iperf command failure messages (stdout/stderr are now surfaced)
  • parse streamed end metrics with fallback to monolithic -J JSON output
  • expand scanner buffers to handle larger monolithic JSON payloads
  • keep jitter handling fixes for parsed iperf metrics and zero-value rendering in UI

Validation

  • go test ./internal/speedtest/...
  • go test ./...
  • pnpm -C web build

Closes #156

Summary by CodeRabbit

  • Bug Fixes

    • Fixed jitter metric handling to treat 0 as a valid measurement for averages.
    • Improved jitter display to properly handle missing values on desktop and mobile.
    • Enhanced failure messages to include more diagnostic output when a speed test times out or fails.
  • New Features

    • More robust parsing of speed test results, improving final speed and jitter reporting across streaming and non-streaming modes.
  • Tests

    • Added comprehensive unit tests for result parsing and failure-output formatting.
  • Documentation

    • Added guidance to review screenshots/logs for issue-driven fixes.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 18, 2026

📝 Walkthrough

Walkthrough

Centralizes iperf3 final-metrics parsing in the backend (streaming and monolithic JSON), adds streaming-mode detection and richer failure output, and updates frontend jitter handling to treat 0 as a valid value for display and averaging.

Changes

Cohort / File(s) Summary
Iperf parsing & runtime
internal/speedtest/iperf.go
Add iperfEndData, parseIperfFinalMetrics, direction helpers, supportsIperfJSONStream, streaming vs monolithic JSON selection, stderr streaming/formatting, and integrate into runSingleIperfTest with improved error context.
Iperf tests
internal/speedtest/iperf_test.go
New unit tests for parsing and failure formatting covering streaming events, monolithic JSON fallback, zero-jitter handling, invalid/large payloads, and formatted failure messages.
Frontend jitter rendering
web/src/components/speedtest/DashboardTab.tsx, web/src/components/speedtest/columns.tsx
Treat jitter==0 as valid for averaging; change conditional rendering to explicit null/undefined checks so zero displays and is included in averages.
Docs guideline
AGENTS.md
Add guideline to review attached screenshots/log images for issue-driven fixes.

Sequence Diagram(s)

sequenceDiagram
    participant Runner as runSingleIperfTest
    participant StdoutProc as stdout processor
    participant StderrProc as stderr streamer
    participant Detector as supportsIperfJSONStream
    participant Parser as parseIperfFinalMetrics
    participant Selector as selectIperfDirectionMetrics / selectIperfDirectionJitter

    Runner->>Detector: Check JSON streaming support
    Runner->>StdoutProc: Start iperf3 (stdout stream)
    Runner->>StderrProc: Start capturing/stashing stderr
    StdoutProc->>Parser: Stream lines / full output
    alt Stream contains "end" event
        Parser->>Selector: Extract direction metrics & jitter from streaming end
    else No streaming end event
        Parser->>Parser: Parse monolithic -J JSON
        Parser->>Selector: Extract direction metrics & jitter from monolithic end
    end
    Selector-->>Parser: Return Mbps and optional jitter
    Parser-->>Runner: Return final metrics or error (includes formatted stdout/stderr)
Loading
sequenceDiagram
    participant Dashboard as DashboardTab
    participant Data as CollectedResults
    participant Columns as columns.tsx

    Data->>Dashboard: Provide results (speed, jitter which may be 0)
    Dashboard->>Dashboard: calculateAverage("jitter") when jitter !== null && jitter !== undefined
    Dashboard->>Columns: Send metrics
    Columns->>Columns: Render jitter when !== null && !==undefined, else "—"
    Columns-->>Dashboard: UI displays jitter (0 shown and averaged)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰
I parsed the streams where iperf roamed,
Found end events where numbers combed,
Zero jitter now hops in view,
Speed and sound both counted true,
Hooray — the metrics bounce anew!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive All changes are scoped to iperf3 parsing robustness and jitter rendering. The AGENTS.md documentation update about reviewing issue screenshots is tangentially related to the development process but not directly related to the code fix. Consider removing the AGENTS.md guideline addition as it is unrelated to the bug fix, or clarify its necessity as part of this change.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is specific and clearly describes the main change: adding support for iperf3 when --json-stream is not available.
Description check ✅ Passed The PR description covers Root Cause, Summary, and Validation sections, providing clear context on the issue and solution, though the template's Testing section is not explicitly filled.
Linked Issues check ✅ Passed Changes directly address issue #156: iperf3 command execution now detects --json-stream support with fallback to -J, stderr is surfaced in failures, and jitter rendering handles zero values correctly.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/fix-iperf-import-156

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.5.0)

Error: unknown linters: 'modernize', run 'golangci-lint help linters' to see the list of supported linters
The command is terminated due to an error: unknown linters: 'modernize', run 'golangci-lint help linters' to see the list of supported linters


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
internal/speedtest/iperf_test.go (1)

13-64: Prefer table-driven tests for these parsing cases.

Consider consolidating the streaming, fallback, zero-jitter, and invalid-input cases into a single table-driven test to reduce duplication and make future cases easier to add.

As per coding guidelines, “Prefer table-driven tests for new backend logic in Go.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/speedtest/iperf_test.go` around lines 13 - 64, Consolidate the four
separate tests into a single table-driven test that iterates over test cases
(identify cases by name like "streaming JSON", "monolithic JSON", "zero jitter",
"invalid input"), each case providing input string, the boolean isDownload flag,
expected speed (or nil), expected jitter pointer (or nil), and whether an error
is expected; inside the loop call parseIperfFinalMetrics and assert results
per-case (use require.NoError/require.Error and assert.InDelta/assert.Nil as
appropriate). Keep references to parseIperfFinalMetrics and the existing
expectations (download vs upload) so assertions for download/upload mapping
remain correct, and ensure each case has a descriptive name to make failures
readable. Ensure imports and test helpers (require/assert) remain available and
maintain equivalent assertions from the original TestParseIperfFinalMetrics_*
tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/speedtest/iperf.go`:
- Around line 44-52: The jitter fields in iperfEndData (currently non-pointer
floats) and the helper selectIperfDirectionJitter are dropping explicit 0.0
values by returning nil for jitter <= 0; change the SumSent.JitterMs and
SumReceived.JitterMs types to *float64 so JSON-unset vs explicit 0.0 can be
distinguished, update selectIperfDirectionJitter to return *float64 (return a
pointer to 0.0 when the parsed value is exactly 0.0 and only return nil when the
JSON field is absent/unset), and adjust any call sites and tests (e.g.,
TestParseIperfFinalMetrics_ZeroJitterReturnsNilPointer) to assert pointer
semantics rather than treating 0.0 as missing.

---

Nitpick comments:
In `@internal/speedtest/iperf_test.go`:
- Around line 13-64: Consolidate the four separate tests into a single
table-driven test that iterates over test cases (identify cases by name like
"streaming JSON", "monolithic JSON", "zero jitter", "invalid input"), each case
providing input string, the boolean isDownload flag, expected speed (or nil),
expected jitter pointer (or nil), and whether an error is expected; inside the
loop call parseIperfFinalMetrics and assert results per-case (use
require.NoError/require.Error and assert.InDelta/assert.Nil as appropriate).
Keep references to parseIperfFinalMetrics and the existing expectations
(download vs upload) so assertions for download/upload mapping remain correct,
and ensure each case has a descriptive name to make failures readable. Ensure
imports and test helpers (require/assert) remain available and maintain
equivalent assertions from the original TestParseIperfFinalMetrics_* tests.

Comment thread internal/speedtest/iperf.go
@s0up4200 s0up4200 changed the title fix: make iperf result parsing more robust fix: support iperf3 without --json-stream Feb 18, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
internal/speedtest/iperf.go (1)

453-464: Consider caching the --json-stream capability check.

supportsIperfJSONStream runs iperf3 --help on every test invocation. Since the iperf3 binary doesn't change during runtime, the result could be cached to avoid repeated subprocess spawns.

♻️ Optional: cache capability detection
+var (
+	iperfStreamOnce   sync.Once
+	iperfStreamSupport bool
+)
+
 func supportsIperfJSONStream(ctx context.Context) bool {
+	iperfStreamOnce.Do(func() {
 	helpCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
 	defer cancel()

 	output, err := exec.CommandContext(helpCtx, "iperf3", "--help").CombinedOutput()
 	if err != nil {
 		log.Debug().Err(err).Msg("Could not detect iperf3 --json-stream support; falling back to -J")
-		return false
+		iperfStreamSupport = false
+		return
 	}

-	return strings.Contains(string(output), "--json-stream")
+	iperfStreamSupport = strings.Contains(string(output), "--json-stream")
+	})
+	return iperfStreamSupport
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/speedtest/iperf.go` around lines 453 - 464, supportsIperfJSONStream
currently spawns `iperf3 --help` on every call; add a package-level cached
result (e.g., a bool `iperfJSONStreamSupported` with a `sync.Once` like
`detectIperfJSONStreamOnce`) and change supportsIperfJSONStream to run the
subprocess only once inside the once.Do initialization (preserving the existing
timeout, error logging and strings.Contains check) and thereafter return the
cached boolean, so repeated invocations avoid repeated exec.CommandContext
calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@internal/speedtest/iperf.go`:
- Around line 438-451: selectIperfDirectionJitter currently treats jitter == 0.0
as missing; change the function so zero is preserved: either (preferred) make
SumReceived.JitterMs and SumSent.JitterMs *float64 and return that pointer
directly from selectIperfDirectionJitter (handle nil pointers as missing), or
(minimal) keep the current float64 fields but change the nil-check from jitter
<= 0 to jitter < 0 so only negative jitter yields nil and 0.0 is returned as a
valid value.
- Around line 47-56: The JitterMs fields in the iperfEndData struct (inside
SumSent and SumReceived) must be changed from float64 to *float64 so JSON
unmarshalling can distinguish a missing jitter_ms (nil) from an explicit 0.0;
update the struct definitions for iperfEndData -> SumSent and SumReceived to use
*float64 for JitterMs and then update any consumers (notably
selectIperfDirectionJitter) to check for nil versus non-nil and handle a nil as
"missing" while treating a non-nil 0.0 as a real zero value.

---

Nitpick comments:
In `@internal/speedtest/iperf.go`:
- Around line 453-464: supportsIperfJSONStream currently spawns `iperf3 --help`
on every call; add a package-level cached result (e.g., a bool
`iperfJSONStreamSupported` with a `sync.Once` like `detectIperfJSONStreamOnce`)
and change supportsIperfJSONStream to run the subprocess only once inside the
once.Do initialization (preserving the existing timeout, error logging and
strings.Contains check) and thereafter return the cached boolean, so repeated
invocations avoid repeated exec.CommandContext calls.

@s0up4200 s0up4200 merged commit 2be9b51 into develop Mar 8, 2026
12 checks passed
@s0up4200 s0up4200 deleted the codex/fix-iperf-import-156 branch March 8, 2026 15:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error executing and importing IPerf3 data (from Brazil)

1 participant