Skip to content

Add gcov/lcov coverage reporting and new test cases for payoffs, bonds, Heston, and cash flows#8

Open
tobydrinkall wants to merge 2 commits into
masterfrom
devin/1780676965-coverage-reporting
Open

Add gcov/lcov coverage reporting and new test cases for payoffs, bonds, Heston, and cash flows#8
tobydrinkall wants to merge 2 commits into
masterfrom
devin/1780676965-coverage-reporting

Conversation

@tobydrinkall
Copy link
Copy Markdown

@tobydrinkall tobydrinkall commented Jun 5, 2026

Summary

Adds CMake-based gcov/lcov coverage reporting infrastructure and 43 new Boost.Test cases targeting business-critical coverage gaps identified via baseline measurement.

Coverage infrastructure

New CMake option QL_ENABLE_COVERAGE=ON adds -fprofile-arcs -ftest-coverage flags (GCC/Clang only). Custom make coverage target runs: lcov --zerocountersctestlcov --capturelcov --extract .../ql/*genhtml.

cmake -B build-cov -DQL_ENABLE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug -DBOOST_ROOT=/usr -DQL_BUILD_EXAMPLES=OFF
cmake --build build-cov -j$(nproc)
make -C build-cov coverage
# → build-cov/coverage_report/index.html

Coverage results (before → after)

Metric Before After Delta
Lines 72.2% (62,418/86,443) 72.6% (62,747/86,456) +0.4% (+329 lines)
Functions 70.2% (11,083/15,785) 70.6% (11,140/15,788) +0.4% (+57 functions)

Targeted file improvements:

File Before After
instruments/payoffs.cpp 27% 66.9%
pricingengines/bond/bondfunctions.cpp 47% 84.9%
cashflows/duration.cpp 0% 80.0%
cashflows/cashflows.cpp 64% 81.7%
processes/hestonprocess.cpp 60% 72.0%

New test files (4 files, 43 test cases)

test-suite/payoffs.cppPayoffTests (9 cases): NullPayoff, PlainVanillaPayoff (call/put/boundary), CashOrNothingPayoff, AssetOrNothingPayoff, GapPayoff, SuperFundPayoff, SuperSharePayoff, PercentageStrikePayoff, FloatingTypePayoff. Covers all payoff operator()/accept()/description() paths.

test-suite/bondfunctions.cppBondFunctionsTests (12 cases): date inspectors (startDate, maturityDate, isTradable), cash flow inspectors (nextCashFlowDate/Amount, previousCouponRate), accrual inspectors, duration (Simple/Macaulay/Modified + relationship modified ≈ macaulay/(1+y)), convexity, bps, basisPointValue/yieldValueBasisPoint, clean/dirty price from yield, yield-from-price round-trip, atmRate, zSpread, zero-coupon bond, Duration::Type operator<<.

test-suite/hestonprocess.cppHestonProcessTests (10 cases): accessors, size()/factors() for QE vs Broadie-Kaya, initialValues(), drift() with positive/negative variance across PartialTruncation/FullTruncation/Reflection, diffusion() matrix structure, apply() (log-normal spot + additive variance), evolve() with QE and PartialTruncation, all 5 standard discretization schemes.

test-suite/cashflowsmore.cppCashFlowsMoreTests (12 cases): CashFlows::npv with InterestRate (monotonicity in rate), bps, duration (all 3 types), convexity, basisPointValue/yieldValueBasisPoint, zSpread round-trip, atmRate, yield-from-NPV round-trip, SimpleCashFlow leg, previous/next cash flow iteration, NPV with YieldTermStructure and z-spread consistency.

Local verification

100% build, 1373 test cases passed, 0 tests failed (285.94s)

No CI on this fork — all verification is local."

Link to Devin session: https://app.devin.ai/sessions/531a528a8c414c328b721bf32799c28b
Requested by: @tobydrinkall


Devin Review

Status Commit
⚪ Not started

Run Devin Review

Open in Devin Review (Staging)
Open in Devin Review

- Add QL_ENABLE_COVERAGE CMake option with gcov/lcov support
- Add 'make coverage' custom target for HTML report generation
- Add PayoffTests: NullPayoff, PlainVanilla, CashOrNothing,
  AssetOrNothing, Gap, SuperFund, SuperShare, PercentageStrike,
  FloatingType payoffs with boundary/edge cases
- Add BondFunctionsTests: date inspectors, accrual, duration
  (Simple/Macaulay/Modified), convexity, bps, basisPointValue,
  yieldValueBasisPoint, clean/dirty price, yield, ATM rate,
  z-spread, zero-coupon bond, Duration::Type streaming
- Add HestonProcessTests: accessors, size/factors, initial values,
  drift, drift with negative variance (PartialTruncation,
  FullTruncation, Reflection), diffusion matrix, apply, evolve
  with QE/PartialTruncation, multiple discretization schemes
- Add CashFlowsMoreTests: NPV with InterestRate/YieldTermStructure,
  bps, duration types, convexity, basisPointValue, z-spread,
  ATM rate, yield round-trip, SimpleCashFlow leg, previous/next
  cash flow iteration, z-spread NPV consistency

Local verification: 100% build, 1373 test cases passed, 0 failed

Co-Authored-By: Toby Drinkall <toby.drinkall@cognition.ai>
@devin-ai-integration
Copy link
Copy Markdown

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

View 4 additional findings in Devin Review.

Open in Devin Review

Comment thread test-suite/payoffs.cpp Outdated
Comment on lines +48 to +49
BOOST_CHECK_CLOSE(call(100.0), 0.0, 1e-10);
BOOST_CHECK_CLOSE(call(80.0), 0.0, 1e-10);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 BOOST_CHECK_CLOSE used with 0.0 expected value — wrong assertion for zero comparisons

Throughout test-suite/payoffs.cpp, BOOST_CHECK_CLOSE is used 17 times with an expected value of 0.0 (e.g., BOOST_CHECK_CLOSE(call(80.0), 0.0, 1e-10)). BOOST_CHECK_CLOSE performs a relative tolerance comparison, and Boost documentation explicitly states both arguments must be non-zero. While the tests happen to pass when both values are exactly 0.0 (due to safe_fpt_division internals), these assertions cannot properly catch bugs: if the payoff implementation were changed to return a tiny non-zero value (e.g. 1e-20) instead of exact 0.0, BOOST_CHECK_CLOSE would always fail regardless of the tolerance — because the relative difference against 0.0 is infinite. The existing test suite correctly uses BOOST_CHECK_SMALL or BOOST_CHECK_EQUAL for zero comparisons (e.g., test-suite/bondfunctions.cpp:314, test-suite/integrals.cpp:355). These 17 occurrences should use BOOST_CHECK_EQUAL(x, 0.0) (for exact zero) or BOOST_CHECK_SMALL(x, tol) (for approximate zero).

Prompt for agents
In test-suite/payoffs.cpp, all 17 uses of BOOST_CHECK_CLOSE with a 0.0 expected value should be replaced with BOOST_CHECK_EQUAL(x, 0.0) since the payoff results are exact zeros from std::max. The affected lines are: 48, 49, 55, 56, 76, 77, 82, 95, 96, 100, 112, 118, 133, 134, 148, 149, 164. For example, line 48 should change from BOOST_CHECK_CLOSE(call(100.0), 0.0, 1e-10) to BOOST_CHECK_EQUAL(call(100.0), 0.0). Similarly, in test-suite/hestonprocess.cpp line 177, BOOST_CHECK_CLOSE(diff[0][1], 0.0, 1e-10) should become BOOST_CHECK_EQUAL(diff[0][1], 0.0) since the diffusion matrix element is explicitly assigned 0.0.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fixed in 6b4bd0c — all 17 BOOST_CHECK_CLOSE(x, 0.0, tol) replaced with BOOST_CHECK_EQUAL(x, 0.0) since these payoff results are exact zeros from std::max. All 1373 tests still pass.

Comment thread test-suite/hestonprocess.cpp Outdated

Real vol = std::sqrt(0.04);
BOOST_CHECK_CLOSE(diff[0][0], vol, 1e-10);
BOOST_CHECK_CLOSE(diff[0][1], 0.0, 1e-10);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 BOOST_CHECK_CLOSE used with 0.0 expected value in diffusion matrix test

Same issue as in payoffs.cpp: BOOST_CHECK_CLOSE(diff[0][1], 0.0, 1e-10) uses relative comparison against zero. The diffusion matrix at ql/processes/hestonprocess.cpp:99 explicitly sets tmp[0][1] = 0.0, so the result is exact zero. BOOST_CHECK_EQUAL should be used instead, consistent with the existing test suite's patterns.

Suggested change
BOOST_CHECK_CLOSE(diff[0][1], 0.0, 1e-10);
BOOST_CHECK_EQUAL(diff[0][1], 0.0);
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fixed in the same commit (6b4bd0c) — changed to BOOST_CHECK_EQUAL(diff[0][1], 0.0) since the diffusion matrix element is explicitly assigned 0.0.

Replace BOOST_CHECK_CLOSE(x, 0.0, tol) with BOOST_CHECK_EQUAL(x, 0.0)
for exact-zero payoff/diffusion results. BOOST_CHECK_CLOSE uses relative
comparison which is undefined against zero.

Co-Authored-By: Toby Drinkall <toby.drinkall@cognition.ai>
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