From 28a611563366998830b7117b3d4c494bbdd27b1b Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Sun, 18 Oct 2026 23:33:07 +0200 Subject: [PATCH 1/3] test: match probability precondition checks to debug builds --- ChangeLog | 9 +++++++++ tests/BasePairProbabilities_test.cpp | 7 +++++++ tests/StackSeedDomain_test.cpp | 16 ++++++++++------ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index b39ddb81..f83059ef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -68,6 +68,9 @@ ## Technical changes and Optimizations +- align probability API invalid-input tests with debug-only precondition checks + and avoid an unchecked null seed-mask call in release test runs (PR #258) + - use hashed base-pair keys for sparse stack-seed lookup, assert internal seed geometry in debug builds, and document probability computation steps and contributor conventions (PR #258 review) @@ -191,6 +194,12 @@ energies, restricted partition sums, and trackers. ################################################################################ ################################################################################ +261018 Alexander Mitrofanov + * BasePairProbabilities_test.cpp, StackSeedDomain_test.cpp + * exercise debug-only region/mask/handler/length checks only in debug builds + + restore the unsupported-handler test under the matching debug guard + * keep ordinary lifecycle, arithmetic and output checks in both configurations + 261013 Alexander Mitrofanov * StackSeedDomain, SeededPartitionFunction, PredictorMfeEns2dSeedExtension * use Interaction::BasePair coordinates and hashed sparse seed lookup tables diff --git a/tests/BasePairProbabilities_test.cpp b/tests/BasePairProbabilities_test.cpp index 9cffec2d..90852098 100644 --- a/tests/BasePairProbabilities_test.cpp +++ b/tests/BasePairProbabilities_test.cpp @@ -14,10 +14,13 @@ TEST_CASE("raw pair regions commit once and finalize only on success", "[BasePai REQUIRE(p(0,2)==.5);REQUIRE(p(3,3)==.5);REQUIRE(p(0,4)==0); BasePairProbabilities empty(1,1);empty.finalize(); REQUIRE(empty.status()==BasePairProbabilities::Status::empty); +#if INTARNA_IN_DEBUG_MODE + // Overlap is a caller precondition; the library checks it only in debug builds. BasePairProbabilities overlap(4,5);overlap.addRegion({0,1},{1,2},2,m); REQUIRE_THROWS(overlap.addRegion({1,2},{2,3},2,m)); REQUIRE(overlap.status()==BasePairProbabilities::Status::failed); REQUIRE(overlap.getZ()==2);REQUIRE_THROWS(overlap.finalize()); +#endif BasePairProbabilities cancelled(4,5);cancelled.addRegion({0,1},{1,2},2,m);cancelled.fail(); REQUIRE_THROWS(cancelled.finalize());REQUIRE_THROWS(cancelled.probabilities()); } @@ -82,10 +85,14 @@ TEST_CASE("seed annotations commit with regional masses and SVG requires success r.fail();std::ostringstream failed; REQUIRE_THROWS(BasePairProbabilityWriter::writeSvg(failed,r,energy,1,1)); REQUIRE(failed.str().empty()); +#if INTARNA_IN_DEBUG_MODE + // A collecting sink requires a regional mask. Do not dereference a missing + // mask in release builds, where this precondition is intentionally unchecked. BasePairProbabilities missing(4,5,true); REQUIRE_THROWS(missing.addRegion({0,1},{1,2},2,mass)); REQUIRE(missing.status()==BasePairProbabilities::Status::failed); REQUIRE(missing.getZ()==0);REQUIRE(missing.seedPairs()(0,2)==0); +#endif BasePairProbabilities plain(4,5); REQUIRE_FALSE(plain.collectsSeedPairs());REQUIRE(plain.seedPairs().size1()==0); plain.finalize();std::ostringstream empty; diff --git a/tests/StackSeedDomain_test.cpp b/tests/StackSeedDomain_test.cpp index be595ada..02fb50d4 100644 --- a/tests/StackSeedDomain_test.cpp +++ b/tests/StackSeedDomain_test.cpp @@ -20,11 +20,13 @@ TEST_CASE("stack capability and active occurrence domain", "[StackSeedDomain]") SeedHandlerNoBulge stack(energy,computed); REQUIRE(mfe.guaranteesStackOnlySeeds()); REQUIRE(stack.guaranteesStackOnlySeeds()); - // only in debug mode the following checks are enabled - // SeedConstraint bulged(2,1,1,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF,IndexRangeList(),IndexRangeList(),"",false,false,false); - // SeedHandlerMfe bulge(energy,bulged); - // REQUIRE_FALSE(bulge.guaranteesStackOnlySeeds()); - // REQUIRE_THROWS_AS(StackSeedDomain(bulge,6,6,6,6),std::invalid_argument); +#if INTARNA_IN_DEBUG_MODE + // Unsupported handlers are rejected only by the debug precondition check. + SeedConstraint bulged(2,1,1,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF,IndexRangeList(),IndexRangeList(),"",false,false,false); + SeedHandlerMfe bulge(energy,bulged); + REQUIRE_FALSE(bulge.guaranteesStackOnlySeeds()); + REQUIRE_THROWS_AS(StackSeedDomain(bulge,6,6,6,6),std::invalid_argument); +#endif SeedConstraint explicitC(1,0,0,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF, IndexRangeList(),IndexRangeList(),"1|&6|,2|||&3|||,5||&1||",false,false,false); SeedHandlerIdxOffset explicitH(new SeedHandlerExplicit(energy,explicitC)); @@ -72,8 +74,10 @@ TEST_CASE("stack-domain lookups and traversal preserve overlapping seed geometry domain.forEachStart([&](size_t i,size_t j){ starts.emplace_back(i,j); }); REQUIRE(starts==expected); } - // Conflicting externally supplied lengths remain an input error in release. +#if INTARNA_IN_DEBUG_MODE + // Conflicting lengths violate a debug-checked caller precondition. REQUIRE_THROWS_AS((StackSeedDomain({{1,1,1},{1,1,2}},4,4,4,4)),std::invalid_argument); +#endif } TEST_CASE("stack-domain admission preserves handler-specific threshold equality", "[StackSeedDomain]") { From 5ee0c0ace36e50cd9e467c106c1bb01d5f7caee8 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Sun, 18 Oct 2026 23:33:43 +0200 Subject: [PATCH 2/3] test: guard invalid probability prediction regions in debug builds --- ChangeLog | 5 +++-- tests/PredictorHeuristicProbability_test.cpp | 3 +++ tests/PredictorUnseededProbability_test.cpp | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index f83059ef..f40c8b25 100644 --- a/ChangeLog +++ b/ChangeLog @@ -195,8 +195,9 @@ energies, restricted partition sums, and trackers. ################################################################################ 261018 Alexander Mitrofanov - * BasePairProbabilities_test.cpp, StackSeedDomain_test.cpp - * exercise debug-only region/mask/handler/length checks only in debug builds + * BasePairProbabilities_test.cpp, StackSeedDomain_test.cpp, + PredictorUnseededProbability_test.cpp, PredictorHeuristicProbability_test.cpp + * exercise debug-only region/mask/handler/length preconditions only in debug builds + restore the unsupported-handler test under the matching debug guard * keep ordinary lifecycle, arithmetic and output checks in both configurations diff --git a/tests/PredictorHeuristicProbability_test.cpp b/tests/PredictorHeuristicProbability_test.cpp index d2805808..954defd7 100644 --- a/tests/PredictorHeuristicProbability_test.cpp +++ b/tests/PredictorHeuristicProbability_test.cpp @@ -202,10 +202,13 @@ TEST_CASE("heuristic pair owner merges disjoint regions and rejects failed resul REQUIRE_THROWS(failing.predict({3,5},{3,5})); REQUIRE(failed.status()==BasePairProbabilities::Status::failed); REQUIRE_THROWS(failed.finalize()); +#if INTARNA_IN_DEBUG_MODE + // Invalid prediction regions are a debug-checked caller precondition. BasePairProbabilities invalid(6,6);OutputHandlerInteractionList invalidOutput(constraint,1); FailingHeuristic badRange(energy,invalidOutput,nullptr,&invalid); REQUIRE_THROWS(badRange.predict({8,9},{0,2})); REQUIRE(invalid.status()==BasePairProbabilities::Status::failed); +#endif } TEST_CASE("heuristic probability collection preserves numerical-zero admission", "[BasePairProbabilities][PredictorHeuristicProbability]") { diff --git a/tests/PredictorUnseededProbability_test.cpp b/tests/PredictorUnseededProbability_test.cpp index c45858e4..af61a248 100644 --- a/tests/PredictorUnseededProbability_test.cpp +++ b/tests/PredictorUnseededProbability_test.cpp @@ -205,10 +205,13 @@ TEST_CASE("unseeded probability collection follows legacy numerical gates and fa ChangedObjectivePredictor changed(energy,output,nullptr,&unsupported); REQUIRE_THROWS(changed.predict()); REQUIRE(unsupported.status()==BasePairProbabilities::Status::failed); +#if INTARNA_IN_DEBUG_MODE + // Invalid prediction regions are a debug-checked caller precondition. BasePairProbabilities invalid(5,5); PredictorMfeEns2d predictor(energy,output,nullptr,&invalid); REQUIRE_THROWS(predictor.predict({5,6},{0,4})); REQUIRE(invalid.status()==BasePairProbabilities::Status::failed); +#endif BasePairProbabilities overflow(5,5); InteractionEnergyBasePair extreme(targetAcc,reverse,2,2,false,1,Ekcal_2_E(-1000)); PredictorMfeEns2d huge(extreme,output,nullptr,&overflow); From 47fd5af04ea99f4f390ad9abdd13cd69b3d3c2f3 Mon Sep 17 00:00:00 2001 From: AlexanderMitrofanov Date: Sun, 18 Oct 2026 23:38:43 +0200 Subject: [PATCH 3/3] test: respect conservative mixed explicit-seed capability --- tests/StackSeedDomain_test.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/StackSeedDomain_test.cpp b/tests/StackSeedDomain_test.cpp index 02fb50d4..23c45e04 100644 --- a/tests/StackSeedDomain_test.cpp +++ b/tests/StackSeedDomain_test.cpp @@ -102,7 +102,7 @@ TEST_CASE("stack-domain admission preserves handler-specific threshold equality" REQUIRE(StackSeedDomain(ex,3,3,3,3).maxSeedLength()==2); } -TEST_CASE("explicit stack capability inspects the retained minimum-energy pattern", "[StackSeedDomain]") { +TEST_CASE("explicit stack capability remains conservative for mixed encodings", "[StackSeedDomain]") { #include "testEasyLoggingSetup.icc" RnaSequence t("t","GGGG"),q("q","CCCC"); AccessibilityDisabled at(t,0,nullptr),aq(q,0,nullptr);ReverseAccessibility ar(aq); @@ -110,9 +110,13 @@ TEST_CASE("explicit stack capability inspects the retained minimum-energy patter SeedConstraint sc(2,0,0,0,E_INF,Accessibility::ED_UPPER_BOUND,E_INF, IndexRangeList(),IndexRangeList(),"1|.|&2|.|,1|||&2|||",false,false,false); SeedHandlerExplicit handler(e,sc); - REQUIRE(!handler.guaranteesStackOnlySeeds()); + // The retained minimum-energy pattern is a stack, but mixed input encodings + // conservatively disable the stack-only handler contract before dispatch. + REQUIRE_FALSE(handler.guaranteesStackOnlySeeds()); REQUIRE(handler.fillSeed(0,3,0,3)==1); - StackSeedDomain domain(handler,4,4,4,4); - REQUIRE(domain.seedLength(0,0)==3); - REQUIRE(domain.maxSeedLength()==3); // constraint's minimum BP is only two + REQUIRE(handler.getSeedLength1(0,0)==3); + REQUIRE(handler.getSeedLength2(0,0)==3); +#if INTARNA_IN_DEBUG_MODE + REQUIRE_THROWS_AS(StackSeedDomain(handler,4,4,4,4),std::invalid_argument); +#endif }