KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_net_chain_rollback.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.TXT for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * https://www.gnu.org/licenses/gpl-3.0.en.html
19 */
20
21#include <boost/test/unit_test.hpp>
22
25
26#include <connection_graph.h>
27#include <sch_netchain.h>
28#include <schematic.h>
30#include <locale_io.h>
31
32#include <wx/debug.h>
33
34#include <functional>
35#include <stdexcept>
36
37
38// Test backdoor declared in connection_graph.h.
40 std::unique_ptr<SCH_NETCHAIN> aChain );
41
42
43// RAII swap of the wx assert handler. The eeschema test_module installs
44// KI_TEST::wxAssertThrower, which converts every wxFAIL_MSG into a thrown
45// WX_ASSERT_ERROR. RebuildNetChains()'s catch block opens with wxFAIL_MSG;
46// under the throwing handler that fires before the rollback statements run,
47// hiding the very behaviour this test is trying to validate. Restore the
48// default (silent) handler for the duration of the throw test, then put the
49// throwing handler back.
51{
53 {
54 wxSetDefaultAssertHandler();
55 }
56
58 {
59 wxSetAssertHandler( &KI_TEST::wxAssertThrower );
60 }
61};
62
63
65{
67
69 {
70 // Defensive cleanup so a failing test never leaks a hook into a sibling test.
72 }
73
75 std::unique_ptr<SCHEMATIC> m_schematic;
76};
77
78
79// Regression for H-12. RebuildNetChains() snapshots m_committedNetChains.size() and
80// m_netChainsBuilt at entry and rolls them back if any of the restore passes throws.
81// A regression that moved the snapshot inside the try, dropped the resize, or forgot to
82// restore the built flag would leak partial chains and report stale readiness. Inject a
83// throw via the test hook and verify the catch handler truncates the vector and restores
84// the flag.
85BOOST_FIXTURE_TEST_CASE( NetChain_RebuildRollback_TruncatesAndRestoresOnThrow,
87{
89 KI_TEST::LoadSchematic( m_settingsManager, wxString( "net_chains_four_nets" ),
90 m_schematic );
91
92 CONNECTION_GRAPH* graph = m_schematic->ConnectionGraph();
93 BOOST_REQUIRE( graph );
94
95 SCH_SHEET_LIST sheets = m_schematic->BuildSheetListSortedByPageNumbers();
96
97 // Initial pass leaves m_netChainsBuilt = true and the snapshot baseline at zero
98 // committed chains (the fixture has none persisted).
99 graph->Recalculate( sheets, /*aUnconditional=*/true );
100 BOOST_REQUIRE( graph->NetChainsBuilt() );
101
102 const std::size_t baselineCount = graph->GetCommittedNetChains().size();
103
104 // Note: Recalculate(true) -> Reset() flips m_netChainsBuilt to false BEFORE
105 // RebuildNetChains() runs, so the snapshot captured at RebuildNetChains entry is
106 // always false on the unconditional path. After the catch block restores the
107 // snapshot value, NetChainsBuilt() must therefore read false.
108 const bool expectedBuiltAfterRollback = false;
109
110 // Hook fires inside the try block, after the restore loops have run. It mimics a
111 // partial-rebuild failure: push a fresh chain onto m_committedNetChains so the
112 // container has grown beyond the snapshot, then throw. The catch block must
113 // truncate back to baselineCount and restore m_netChainsBuilt to baselineBuilt.
114 bool hookFired = false;
116 [&]( CONNECTION_GRAPH& aGraph )
117 {
118 hookFired = true;
119
120 auto stray = std::make_unique<SCH_NETCHAIN>();
121 stray->SetName( wxT( "ROLLBACK_PARTIAL" ) );
122 stray->AddNet( wxT( "/STRAY_NET" ) );
123 boost_test_inject_committed_net_chain( aGraph, std::move( stray ) );
124
125 BOOST_REQUIRE_GT( aGraph.GetCommittedNetChains().size(), 0u );
126
127 throw std::runtime_error( "rollback test injected throw" );
128 };
129
130 {
131 ASSERT_HANDLER_SCOPE silenceWxFail;
132 graph->Recalculate( sheets, /*aUnconditional=*/true );
133 }
134
136
137 BOOST_CHECK_MESSAGE( hookFired,
138 "Rollback hook never fired; snapshot/restore code path was not exercised" );
139
140 BOOST_CHECK_MESSAGE( graph->GetCommittedNetChains().size() == baselineCount,
141 "Catch block did not resize m_committedNetChains back to the snapshot; "
142 "partial chain leaked into the committed list" );
143
144 BOOST_CHECK_MESSAGE( graph->NetChainsBuilt() == expectedBuiltAfterRollback,
145 "Catch block did not restore m_netChainsBuilt; readiness flag is stale" );
146
147 // The stray name we injected must not survive the rollback. GetNetChainByName scans
148 // the live committed list; if rollback worked the lookup misses.
149 BOOST_CHECK_MESSAGE( graph->GetNetChainByName( wxT( "ROLLBACK_PARTIAL" ) ) == nullptr,
150 "Partially-built chain 'ROLLBACK_PARTIAL' survived the rollback" );
151}
152
153
154// Companion check. When RebuildNetChains() throws WITHOUT any growth in
155// m_committedNetChains, the catch block must still restore the built-flag snapshot
156// and clear m_potentialNetChains, leaving the graph in a clean pre-rebuild state.
157// Catches a regression that flipped m_netChainsBuilt to true before the hook call.
158BOOST_FIXTURE_TEST_CASE( NetChain_RebuildRollback_NoGrowthStillRestoresFlagAndPotentials,
160{
162 KI_TEST::LoadSchematic( m_settingsManager, wxString( "net_chains_four_nets" ),
163 m_schematic );
164
165 CONNECTION_GRAPH* graph = m_schematic->ConnectionGraph();
166 BOOST_REQUIRE( graph );
167
168 SCH_SHEET_LIST sheets = m_schematic->BuildSheetListSortedByPageNumbers();
169
170 // LoadSchematic already ran a Recalculate, so the graph has potentials and built==true.
171 // We re-run with throw to confirm rollback returns the built flag to false (the
172 // snapshot value captured AFTER Reset() runs at the top of Recalculate(true)).
173 BOOST_REQUIRE( graph->NetChainsBuilt() );
174 const std::size_t baselineCount = graph->GetCommittedNetChains().size();
175
176 bool hookFired = false;
178 [&]( CONNECTION_GRAPH& )
179 {
180 hookFired = true;
181 throw std::runtime_error( "no-growth rollback test throw" );
182 };
183
184 {
185 ASSERT_HANDLER_SCOPE silenceWxFail;
186 graph->Recalculate( sheets, /*aUnconditional=*/true );
187 }
188
190
191 BOOST_CHECK( hookFired );
192
193 // Reset() inside Recalculate(true) clears m_netChainsBuilt to false BEFORE
194 // RebuildNetChains() snapshots it; after rollback the flag must read false.
196 "Rollback left m_netChainsBuilt = true; the success-flag write "
197 "escaped the catch handler's snapshot restore" );
198
199 BOOST_CHECK_EQUAL( graph->GetCommittedNetChains().size(), baselineCount );
200
201 // The catch block also clears m_potentialNetChains so a partial pass cannot leak
202 // half-populated inferred groupings into the next consumer.
203 BOOST_CHECK( graph->GetPotentialNetChains().empty() );
204}
Calculate the connectivity of a schematic and generates netlists.
SCH_NETCHAIN * GetNetChainByName(const wxString &aName)
static std::function< void(CONNECTION_GRAPH &)> & RebuildNetChainsTestHook()
Test-only hook fired inside RebuildNetChains() after the restore passes have finished but before the ...
void Recalculate(const SCH_SHEET_LIST &aSheetList, bool aUnconditional=false, std::function< void(SCH_ITEM *)> *aChangedItemHandler=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr)
Update the connection graph for the given list of sheets.
const std::vector< std::unique_ptr< SCH_NETCHAIN > > & GetPotentialNetChains() const
Potential net chains are inferred groupings produced by RebuildNetChains() but not yet user-committed...
bool NetChainsBuilt() const
Returns true once RebuildNetChains() has completed at least once on this graph.
const std::vector< std::unique_ptr< SCH_NETCHAIN > > & GetCommittedNetChains() const
Return user-created (committed) net chains (legacy accessor retained under net-chain API).
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition locale_io.h:41
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
void LoadSchematic(SETTINGS_MANAGER &aSettingsManager, const wxString &aRelPath, std::unique_ptr< SCHEMATIC > &aSchematic)
void wxAssertThrower(const wxString &aFile, int aLine, const wxString &aFunc, const wxString &aCond, const wxString &aMsg)
Definition wx_assert.h:71
std::vector< FAB_LAYER_COLOR > dummy
std::unique_ptr< SCHEMATIC > m_schematic
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
void boost_test_inject_committed_net_chain(CONNECTION_GRAPH &aGraph, std::unique_ptr< SCH_NETCHAIN > aChain)
BOOST_FIXTURE_TEST_CASE(NetChain_RebuildRollback_TruncatesAndRestoresOnThrow, NETCHAIN_ROLLBACK_FIXTURE)
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
BOOST_CHECK_EQUAL(result, "25.4")