Skip to content

test+ci: qvalue-parser edge coverage + gcov coverage job#84

Merged
eilandert merged 2 commits into
masterfrom
test/coverage-qvalue-edge-cases
Jul 17, 2026
Merged

test+ci: qvalue-parser edge coverage + gcov coverage job#84
eilandert merged 2 commits into
masterfrom
test/coverage-qvalue-edge-cases

Conversation

@eilandert

@eilandert eilandert commented Jul 17, 2026

Copy link
Copy Markdown
Member

Summary

  • 5 new TAP tests (T80-84) covering previously-uncovered branches in the Accept-Encoding qvalue parser: repeated q, q with no value, leading digit not 0/1, escaped quoted-pair in a non-q param, unterminated quoted-string running to end of field.
  • New coverage CI job: gcov-instrumented nginx build, full TAP+python suite run, lcov capture, HTML report artifact, 80% line-coverage floor gate.

Line coverage 79.2% -> 80.2%, branch coverage 68.1% -> 70.8% (function coverage already 100%). Remaining gaps are mostly alloc-failure/ZSTD-API-error defensive paths, not worth fault-injecting for marginal ROI.

Test plan

  • Local: gcov-instrumented build, prove t/*.t (1322 tests) all pass
  • Local: full python tool suite (compression matrix, cctx isolation, proxy truncation, terminal frame, window cap, LDM) all pass
  • Local: lcov summary confirms 80.2% line / 70.8% branch / 100% function
  • actionlint + yaml parse clean on modified workflow
  • Remote CI green

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of malformed and edge-case Accept-Encoding: zstd negotiation parameters.
    • Prevented invalid or incomplete quoted parameters from causing incorrect behavior.
  • Tests

    • Added broader coverage for repeated, missing, out-of-range, and malformed quality values.
    • Added automated coverage reporting with an 80% minimum line-coverage threshold.

Accept-Encoding qvalue parser (ngx_http_zstd_eval_qvalue /
ngx_http_zstd_skip_quoted) had untested branches: repeated q param,
q with no value, leading digit not 0/1, escaped quoted-pair inside a
non-q param value, and an unterminated quoted-string running to end
of field. Adds TAP TEST 80-84 covering each.

New gcov/lcov coverage job builds an instrumented nginx, runs the
full TAP+python suite, and enforces an 80% line-coverage floor
(measured 80.2% after these tests, up from 79.2%) so future changes
can't silently drop tested surface. Remaining gaps are mostly
alloc-failure/ZSTD-API-error defensive paths not worth fault-injecting.
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The pull request adds Accept-Encoding zstd negotiation edge-case tests and a GitHub Actions coverage job that builds instrumented nginx, runs Perl and Python tests, enforces an 80% line coverage floor, and uploads HTML and raw coverage reports.

Changes

Coverage and negotiation validation

Layer / File(s) Summary
Accept-Encoding negotiation edge cases
t/00-filter.t
Adds tests for repeated or valueless q parameters, out-of-range quality values, escaped quoted-pairs, and unterminated quoted strings.
Instrumented coverage pipeline
.github/workflows/build-test.yml
Builds nginx with coverage instrumentation, runs the test suites, enforces the line coverage threshold, generates HTML output, and uploads coverage artifacts.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GitHubActions
  participant nginx
  participant TestSuite
  participant lcov
  participant CoverageArtifact
  GitHubActions->>nginx: build instrumented nginx with zstd module
  GitHubActions->>TestSuite: run TAP and Python regression tests
  TestSuite->>nginx: exercise zstd negotiation and module paths
  GitHubActions->>lcov: capture and summarize line coverage
  lcov->>GitHubActions: return coverage percentage
  GitHubActions->>CoverageArtifact: upload coverage data and HTML report
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main changes: qvalue-parser edge-case tests and a gcov coverage CI job.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/coverage-qvalue-edge-cases

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

01-static.t asserts a fixed ETag derived from t/suite/test{,.zst} mtime.
The tests/tests-asan jobs already pin it with touch -d before running;
the new coverage job forgot this step, so the checkout's fresh mtime
produced a different ETag and failed 45/321 static subtests on CI
(passed locally only because the local git checkout's mtime happened
to already be old). Same touch -d, same epoch, matching the existing
jobs.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/build-test.yml:
- Around line 946-947: Update the “Checkout module” actions/checkout step to
disable persisted checkout credentials by setting its persist-credentials option
to false, while preserving the existing pinned action version and other checkout
behavior.
- Around line 1003-1010: Update the “Run TAP suite” step to configure the
required servroot before invoking prove, matching the ASAN job’s setup so the
relative root ../../t/suite resolves to the t/suite fixtures. Preserve the
existing timestamp pinning and prove t/*.t execution.

In `@t/00-filter.t`:
- Around line 2025-2129: Replace the no_plan setup in the filter test suite with
the fixed repeat_each() * (blocks() * 3) + N assertion plan, recalculating N to
include tests 80–84 and match the suite’s total assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f3b6ee04-b42a-4aa2-8070-0ff72e45b851

📥 Commits

Reviewing files that changed from the base of the PR and between 9fa2cb0 and a676bb8.

📒 Files selected for processing (2)
  • .github/workflows/build-test.yml
  • t/00-filter.t

Comment on lines +946 to +947
- name: Checkout module
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Disable persisted checkout credentials.

Repository-controlled tests run after checkout and can read the persisted token from Git configuration, which is especially risky on the self-hosted runner.

Proposed fix
       - name: Checkout module
         uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
+        with:
+          persist-credentials: false
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Checkout module
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: Checkout module
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
persist-credentials: false
🧰 Tools
🪛 zizmor (1.26.1)

[warning] 946-947: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/build-test.yml around lines 946 - 947, Update the
“Checkout module” actions/checkout step to disable persisted checkout
credentials by setting its persist-credentials option to false, while preserving
the existing pinned action version and other checkout behavior.

Source: Linters/SAST tools

Comment on lines +1003 to +1010
- name: Run TAP suite (drives filter/static/conf-warn paths)
run: |
export PERL5LIB="$HOME/perl5/lib/perl5:${PERL5LIB:-}"
cd "$GITHUB_WORKSPACE"
# 01-static.t asserts on a fixed ETag derived from fixture mtime;
# pin it or every run gets a fresh checkout mtime and fails.
touch -d @1541504307 "$GITHUB_WORKSPACE/t/suite/test" "$GITHUB_WORKSPACE/t/suite/test.zst"
prove t/*.t

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Configure the servroot required by the static TAP suite.

prove t/*.t includes t/01-static.t, but this step only pins timestamps. Arrange the servroot so its root ../../t/suite fixture path resolves, as the ASAN job already does.

Proposed fix
           export PERL5LIB="$HOME/perl5/lib/perl5:${PERL5LIB:-}"
           cd "$GITHUB_WORKSPACE"
+          export TEST_NGINX_SERVROOT="$GITHUB_WORKSPACE/t/servroot-static"
+          mkdir -p "$TEST_NGINX_SERVROOT"
           # 01-static.t asserts on a fixed ETag derived from fixture mtime;

As per coding guidelines, static-suite tests using relative roots must arrange the servroot so root ../../t/suite resolves correctly.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Run TAP suite (drives filter/static/conf-warn paths)
run: |
export PERL5LIB="$HOME/perl5/lib/perl5:${PERL5LIB:-}"
cd "$GITHUB_WORKSPACE"
# 01-static.t asserts on a fixed ETag derived from fixture mtime;
# pin it or every run gets a fresh checkout mtime and fails.
touch -d @1541504307 "$GITHUB_WORKSPACE/t/suite/test" "$GITHUB_WORKSPACE/t/suite/test.zst"
prove t/*.t
- name: Run TAP suite (drives filter/static/conf-warn paths)
run: |
export PERL5LIB="$HOME/perl5/lib/perl5:${PERL5LIB:-}"
cd "$GITHUB_WORKSPACE"
export TEST_NGINX_SERVROOT="$GITHUB_WORKSPACE/t/servroot-static"
mkdir -p "$TEST_NGINX_SERVROOT"
# 01-static.t asserts on a fixed ETag derived from fixture mtime;
# pin it or every run gets a fresh checkout mtime and fails.
touch -d `@1541504307` "$GITHUB_WORKSPACE/t/suite/test" "$GITHUB_WORKSPACE/t/suite/test.zst"
prove t/*.t
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/build-test.yml around lines 1003 - 1010, Update the “Run
TAP suite” step to configure the required servroot before invoking prove,
matching the ASAN job’s setup so the relative root ../../t/suite resolves to the
t/suite fixtures. Preserve the existing timestamp pinning and prove t/*.t
execution.

Source: Coding guidelines

Comment thread t/00-filter.t
Comment on lines +2025 to +2129
=== TEST 80: repeated "q" parameter is malformed (RFC 9110 permits at most one)
--- config
location /filter {
zstd on;
zstd_types text/plain;
proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/test;
}
location /test {
root $TEST_NGINX_PERL_PATH/suite/;
}
--- request
GET /filter
--- more_headers
Accept-Encoding: zstd;q=0.5;q=0.9
--- response_headers
Content-Length: 59738
!Content-Encoding
--- no_error_log
[error]



=== TEST 81: "q" parameter present without "=value" is malformed
--- config
location /filter {
zstd on;
zstd_types text/plain;
proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/test;
}
location /test {
root $TEST_NGINX_PERL_PATH/suite/;
}
--- request
GET /filter
--- more_headers
Accept-Encoding: zstd;q
--- response_headers
Content-Length: 59738
!Content-Encoding
--- no_error_log
[error]



=== TEST 82: qvalue leading digit not 0 or 1 is malformed (q=2)
--- config
location /filter {
zstd on;
zstd_types text/plain;
proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/test;
}
location /test {
root $TEST_NGINX_PERL_PATH/suite/;
}
--- request
GET /filter
--- more_headers
Accept-Encoding: zstd;q=2
--- response_headers
Content-Length: 59738
!Content-Encoding
--- no_error_log
[error]



=== TEST 83: non-"q" parameter with an escaped quoted-pair does not confuse the delimiter scan
--- config
location /filter {
zstd on;
zstd_types text/plain;
proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/test;
}
location /test {
root $TEST_NGINX_PERL_PATH/suite/;
}
--- request
GET /filter
--- more_headers
Accept-Encoding: zstd;foo="a\"b,c";q=0.8, gzip;q=0.1
--- response_headers_like
Content-Encoding: zstd
--- no_error_log
[error]



=== TEST 84: unterminated quoted-string in a non-"q" parameter runs to end without OOB read, defaults q=1
--- config
location /filter {
zstd on;
zstd_types text/plain;
proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/test;
}
location /test {
root $TEST_NGINX_PERL_PATH/suite/;
}
--- request
GET /filter
--- more_headers
Accept-Encoding: zstd;foo="unterminated
--- response_headers_like
Content-Encoding: zstd
--- no_error_log
[error]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Restore the suite’s fixed assertion plan.

Adding T80–84 while Line 28 remains plan 'no_plan' removes protection against premature test termination. Restore the required repeat_each() * (blocks() * 3) + N plan and recalculate N.

As per coding guidelines, t/00-filter.t: “Keep the filter test plan constant (plan tests => repeat_each() * (blocks() * 3) + N;) in sync with the actual number of assertions.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@t/00-filter.t` around lines 2025 - 2129, Replace the no_plan setup in the
filter test suite with the fixed repeat_each() * (blocks() * 3) + N assertion
plan, recalculating N to include tests 80–84 and match the suite’s total
assertions.

Source: Coding guidelines

@eilandert
eilandert merged commit 13b6eb2 into master Jul 17, 2026
13 of 14 checks passed
@eilandert
eilandert deleted the test/coverage-qvalue-edge-cases branch July 17, 2026 20:34
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.

1 participant