fix(sdcard): IsNonResultLine no longer false-positives on filenames starting with 'error' (#190)#195
Conversation
…tarting with 'error' (#190) Pre-fix, IsNonResultLine returned true for any line starting with the bare substring "ERROR" (case-insensitive). That made GetSdCardFilesAsync silently drop legit SD filenames whose basename starts with "error" — e.g. "error_log.csv", "Errors_summary.bin" — because the permissive classifier treated them as non-result error/status text. Tightened to require ERROR followed by one of: - nothing (the bare "ERROR" line, length 5) - `:` (the SCPI ERROR: prefix) - ` ` or `\t` (firmware "Error !!" / "Error " text) - `!` (firmware "Error!!") The "**ERROR" prefix path is unchanged. IsScpiErrorLine is also unchanged — it's already strict (requires ** or :). New test GetSdCardFilesAsync_FilenamesStartingWithErrorAreNotMisclassified canned three filenames into the LIST? response: error_log.csv, Errors_summary.bin, normal.bin. Asserts all three round-trip into the parsed file list (was: only normal.bin pre-fix). Mirrors the equivalent fix already merged in daqifi-python-core PR #102 (test_sdcard_typed_exceptions.py). 891 tests pass on net9.0 + net10.0 (was 890).
Review Summary by QodoFix IsNonResultLine false-positives on error-prefixed filenames
WalkthroughsDescription• Fix IsNonResultLine false-positives on SD filenames starting with "error" • Tighten ERROR prefix matching to require specific delimiters (:, space, !, tab) • Add regression test for filenames like error_log.csv and Errors_summary.bin • Prevent legitimate SD card files from being silently dropped during parsing Diagramflowchart LR
A["IsNonResultLine<br/>bare ERROR check"] -->|"Tighten to require<br/>delimiter after ERROR"| B["ERROR followed by<br/>: space ! or tab"]
B -->|"Prevents matching"| C["Filenames like<br/>error_log.csv"]
C -->|"Now included in"| D["GetSdCardFilesAsync<br/>results"]
File Changes1. src/Daqifi.Core/Device/DaqifiStreamingDevice.cs
|
Code Review by Qodo
1.
|
Bug 1 (Parser still skips bare ERROR): IsNonResultLine in
DaqifiStreamingDevice was tightened in pass 0 but
SdCardFileListParser.ParseFileList ALSO had the same bare
StartsWith("ERROR") check on the per-line classifier — and that
classifier runs on the directory-stripped path, so bare filenames
like "error_log.csv" emitted without the "Daqifi/" prefix would
still slip through the LIST? throw-on-error gate but get dropped
during file-list parsing. Tightened with the same prefix logic
factored into IsErrorResponseLine.
Bug 3 (IsNonResultLine comment mismatched): pass 0 comment said
ERROR is tightened to be followed by ":", " " (firmware "Error !!"),
or "*" (the "**ERROR" SCPI marker). Actual code: ":", " ", "!", tab,
or end-of-line. Documentation drift; rewrote comment to enumerate
the three cases (canonical "**ERROR" marker / "ERROR" exact / "ERROR"
followed by :/space/!/tab) accurately.
Requirement gap (Missing edge-case sdcard test): pass 0 test used
"Daqifi/error_log.csv" path shapes that don't actually start with
"error" after TrimStart — the prior bug never fired on those paths.
Strengthened the regression test:
- Added BARE filenames "error_log.csv", "Errors_summary.bin",
"ERROR_archive.bin" (no Daqifi/ prefix) which DO trigger the
prior bug.
- New test GetSdCardFilesAsync_OnlyErrorPrefixedFilenames_AllSurvive
for the all-error-only listing edge case explicitly called out
by Qodo (no normal.bin sanity anchor).
- New Theory GetSdCardFilesAsync_RealErrorLinesStillSkipped (6
parameterized cases) confirms the tightening didn't go too far —
real **ERROR / ERROR: / Error !! / bare ERROR / "error\t..." lines
are all still classified as non-result.
898 tests pass on net9.0 + net10.0 (was 891; added 7 net cases).
|
/improve |
|
/agentic_review |
|
Persistent review updated to latest commit c7d18d5 |
PR Code Suggestions ✨Latest suggestions up to 96dd1cb Warning
Previous suggestions✅ Suggestions up to commit 52a53ac
Suggestions up to commit dfb3e22
✅ Suggestions up to commit 89ed903
✅ Suggestions up to commit 73cb29e
Suggestions
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
…h code Qodo flagged that two comments described the bare-ERROR delimiter rule as "followed by a non-letter", but the actual helpers (IsErrorResponseLine in SdCardFileListParser and IsNonResultLine in DaqifiStreamingDevice) only treat ':', ' ', '!', '\t', and end of line as SCPI delimiters. Updating the comments to match prevents a future edit from broadening the check to "any non-letter" and reintroducing the #190 filename false-positive. Comment-only — no behavioral change.
|
/improve |
|
/agentic_review |
|
Persistent review updated to latest commit b9ccac2 |
PR Code Suggestions ✨Warning
|
|||||||||||
…or detection Qodo flagged that IsNonResultLine used TrimStart() while device responses typically end in CRLF. With TrimStart(), a bare "ERROR\r" leaves the '\r' as trimmed[5], failing the SCPI delimiter switch and silently classifying the error line as a regular result. Switched to Trim() to strip trailing CR/LF as well. IsErrorResponseLine in SdCardFileListParser gets the same defensive Trim() — current callers pre-trim the input, but mirroring the helper prevents future reuse from reintroducing the same blind spot.
|
/improve |
|
/agentic_review |
|
Persistent review updated to latest commit 73cb29e |
|
Persistent suggestions updated to latest commit 73cb29e |
…or-line helper Both Qodo surfaces (review + suggestions) flagged the same maintenance risk: IsNonResultLine in DaqifiStreamingDevice and IsErrorResponseLine in SdCardFileListParser implement the same predicate, so a future delimiter / trim refinement applied to one would silently miss the other and re-introduce the #190 filename false-positive. Promoted IsErrorResponseLine to internal so DaqifiStreamingDevice can call it; replaced IsNonResultLine's body with a one-liner delegation. The two SD-response classification paths now share a single source of truth for the rule.
|
/improve |
|
/agentic_review |
|
Persistent review updated to latest commit 89ed903 |
|
Persistent suggestions updated to latest commit 89ed903 |
…imiter rule Qodo flagged that **ERROR matched on prefix alone while ERROR required a SCPI delimiter follower — a theoretical inconsistency: a filename beginning with **ERROR would be classified as a SCPI error even without the delimiter. FAT/exFAT reserves '*' so this can't actually happen in SD card listings, but consistency between the two prefix checks is cheap and removes the inconsistency for future readers / non-FAT consumers. Refactored to a shared MatchesErrorPrefix helper that both prefixes call with the same delimiter rule.
|
/improve |
|
/agentic_review |
|
Persistent review updated to latest commit 5af7218 |
PR Code Suggestions ✨Warning
No code suggestions found for the PR. |
Bug 2 (error! filename misclassified): MatchesErrorPrefix treated a single '!' after "ERROR" as a delimiter, so legitimate filenames like "error!log.bin" were dropped from SD listings. Tightened to require '!!' (firmware always sends "Error !!" with space; the bang-only delimiter was purely defensive against a no-space "Error!!" variant — '!!' still catches that case while letting single-bang filenames pass.) Bug 6 (SCPI-only doc misleading): rewrote IsErrorResponseLine summary to accurately describe its dual role as a SCPI error detector AND a permissive non-result classifier for firmware status text — prevents future maintainers from "tightening" to SCPI-only and re-breaking SD listing classification. Tests: added Theory case for "Error!! No space" (still classifies), added Theory GetSdCardFilesAsync_FilenamesWithSingleBangSurvive (3 cases) for the regression. 216/216 pass.
|
/improve |
|
/agentic_review |
|
Persistent review updated to latest commit dfb3e22 |
|
Persistent suggestions updated to latest commit dfb3e22 |
Mirror the production parser's case-insensitive Daqifi/ prefix strip (StringComparison.OrdinalIgnoreCase) in the FilenamesWithSingleBangSurvive theory so future test cases with "daqifi/" / "DAQIFI/" don't false-fail.
|
/improve |
|
/agentic_review |
|
Persistent review updated to latest commit 52a53ac |
|
Persistent suggestions updated to latest commit 52a53ac |
Mirror the production parser's full normalization (Daqifi/ strip THEN Path.GetFileName) when computing the expected filename in the FilenamesWithSingleBangSurvive theory. Stripping only the prefix would diverge from production on nested-path entries like "Daqifi/sub/file.bin".
|
/improve |
|
/agentic_review |
|
Persistent review updated to latest commit 96dd1cb |
|
Persistent suggestions updated to latest commit 96dd1cb |
Decouple DaqifiStreamingDevice from SdCardFileListParser by relocating
the shared IsErrorResponseLine/MatchesErrorPrefix helpers into a neutral
internal ScpiResponseClassifier class. Previously the general SCPI
response classifier reached into the SD-card-specific module, coupling
unrelated layers.
Also fix an OS-dependent test expectation: the FilenamesWithSingleBangSurvive
theory used Path.GetFileName, which treats '\\' as a separator on Windows
but not on Linux/macOS. Replaced with an explicit LastIndexOf('/') split
since the device protocol uses forward slashes.
No behavior change. All 902 tests pass on net9.0 + net10.0. Verified
on real device (Nq1, FW 3.4.4) via --sd-list — normal listings parse
correctly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Live device verificationTested against a real DAQiFi Nq1 (FW 3.4.4) over serial at Discovery: SD list (the path the fix touches): What this proves
CaveatThe bug-fix half (filenames starting with |
Additional live-device sanity checksRan a broader set of checks against the same DAQiFi Nq1 (FW 3.4.4, serial at
What this coversBoth SCPI text-response paths that route through the new The bug-fix scenario itself (filenames starting with |
Summary
Pre-fix,
IsNonResultLinereturned true for any line starting with the bare substring "ERROR" (case-insensitive).GetSdCardFilesAsyncsilently dropped legit SD filenames whose basename starts with "error" (e.g.error_log.csv,Errors_summary.bin) because the permissive classifier treated them as non-result error/status text.Tightened to require ERROR followed by
:,/\t,!, or end-of-line. The strict**ERRORprefix path andIsScpiErrorLineare unchanged.Test plan
GetSdCardFilesAsync_FilenamesStartingWithErrorAreNotMisclassifiedcans three filenames (error_log.csv,Errors_summary.bin,normal.bin) into the LIST? response and asserts all three round-trip into the parsed file list.Refs