Claude suggested this to cover the else if (!accepted && (!sc->m_found || sc->m_state.IsValid())) branch:
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 5de893768d..7e98992c82 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -384,3 +384,48 @@ BOOST_AUTO_TEST_CASE(witness_commitment_index)
BOOST_CHECK_EQUAL(GetWitnessCommitmentIndex(pblock), 2);
}
+
+// submitBlock() reports "inconclusive" when ProcessNewBlock() fails for a
+// non-validity reason (e.g. an activation/system error) rather than because the
+// submitted block is invalid. This exercises the
+// `!accepted && !sc->m_found` branch in SubmitBlock(), which has no other
+// coverage.
+BOOST_AUTO_TEST_CASE(submit_block_inconclusive_on_activation_failure)
+{
+ bool ignored;
+ BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(std::make_shared<CBlock>(Params().GenesisBlock()), true, true, &ignored));
+
+ auto mining{interfaces::MakeMining(m_node)};
+ const uint256 genesis{Params().GenesisBlock().GetHash()};
+ std::string reason, debug;
+
+ // Block A extends genesis and becomes the active tip.
+ const auto block_a{GoodBlock(genesis)};
+ BOOST_REQUIRE(mining->submitBlock(*block_a, reason, debug));
+
+ // Block B1 is a valid sibling of A: accepted and stored, but not connected
+ // (equal work). This is the existing "accepted but not connected" path.
+ const auto block_b1{GoodBlock(genesis)};
+ BOOST_REQUIRE(block_b1->GetHash() != block_a->GetHash());
+ BOOST_REQUIRE(!mining->submitBlock(*block_b1, reason, debug));
+ BOOST_CHECK_EQUAL(reason, "inconclusive");
+
+ // Corrupt B1's stored location so it can no longer be read from disk.
+ WITH_LOCK(::cs_main, {
+ CBlockIndex* index{m_node.chainman->m_blockman.LookupBlockIndex(block_b1->GetHash())};
+ BOOST_REQUIRE(index);
+ index->nDataPos = 1 << 30; // past end of file
+ });
+
+ // Block B2 builds on B1, so the B-branch now has more work than A.
+ // Submitting it triggers a reorg that must read B1 back from disk to connect
+ // it; the read fails with a fatal (non-invalid) error, so ProcessNewBlock()
+ // returns false without ever emitting BlockChecked for B2. The result is
+ // accepted == false and m_found == false: branch 2's !sc->m_found case.
+ const auto block_b2{GoodBlock(block_b1->GetHash())};
+ reason = "stale";
+ debug = "stale";
+ BOOST_CHECK(!mining->submitBlock(*block_b2, reason, debug));
+ BOOST_CHECK_EQUAL(reason, "inconclusive");
+ BOOST_CHECK_EQUAL(debug, "");
+}
BOOST_AUTO_TEST_SUITE_END()
(wording needs some tweaking; it's referring to what the functional tests are currently doing)