KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_net_chain_partition.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 2
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, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <boost/test/unit_test.hpp>
21
22#include <board.h>
23#include <footprint.h>
24#include <net_chain_bridging.h>
25#include <netinfo.h>
26#include <pad.h>
27#include <padstack.h>
28
29
30namespace
31{
32
33constexpr int PAD_SIZE_NM = 500'000;
34
35
36PAD* addPad( FOOTPRINT* aFp, NETINFO_ITEM* aNet, const VECTOR2I& aPos )
37{
38 PAD* pad = new PAD( aFp );
39 pad->SetPadstackMode( PADSTACK::MODE::NORMAL );
41 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( PAD_SIZE_NM, PAD_SIZE_NM ) );
42 pad->SetPosition( aPos );
43 pad->SetNet( aNet );
44 aFp->Add( pad );
45 return pad;
46}
47
48
49NETINFO_ITEM* addNet( BOARD* aBoard, const wxString& aName, int aCode, const wxString& aChain )
50{
51 NETINFO_ITEM* n = new NETINFO_ITEM( aBoard, aName, aCode );
52 n->SetNetChain( aChain );
53 aBoard->Add( n );
54 return n;
55}
56
57
59{
60 FOOTPRINT* fp = new FOOTPRINT( aBoard );
61 aBoard->Add( fp );
62 return fp;
63}
64
65} // namespace
66
67
68BOOST_AUTO_TEST_SUITE( NetChainPartition )
69
70
71// Linear chain NA -- R1 -- NB -- R2 -- NC with query net NB. Expect before={NA}, after={NC}.
72BOOST_AUTO_TEST_CASE( LinearChainSplitsCleanly )
73{
74 BOARD board;
75
76 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
77 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxS( "SIG" ) );
78 NETINFO_ITEM* nC = addNet( &board, wxS( "/C" ), 3, wxS( "SIG" ) );
79
80 FOOTPRINT* r1 = addFootprint( &board );
81 addPad( r1, nA, VECTOR2I( -2'000'000, 0 ) );
82 PAD* startPad = addPad( r1, nB, VECTOR2I( -1'000'000, 0 ) );
83
84 FOOTPRINT* r2 = addFootprint( &board );
85 PAD* endPad = addPad( r2, nB, VECTOR2I( 1'000'000, 0 ) );
86 addPad( r2, nC, VECTOR2I( 2'000'000, 0 ) );
87
88 NET_CHAIN_PARTITION p = PartitionNetChainAroundNet( &board, nB->GetNetCode(), startPad, endPad );
89
91 BOOST_CHECK_EQUAL( p.beforeStart.size(), 1u );
92 BOOST_CHECK_EQUAL( p.afterEnd.size(), 1u );
93 BOOST_CHECK( p.beforeStart.count( nA->GetNetCode() ) == 1 );
94 BOOST_CHECK( p.afterEnd.count( nC->GetNetCode() ) == 1 );
95}
96
97
98// Query net is the chain terminal: end pad has no bridge neighbor.
99BOOST_AUTO_TEST_CASE( TerminalQueryNetLeavesOneSideEmpty )
100{
101 BOARD board;
102
103 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
104 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxS( "SIG" ) );
105
106 FOOTPRINT* r1 = addFootprint( &board );
107 PAD* startPad = addPad( r1, nA, VECTOR2I( -1'000'000, 0 ) );
108 addPad( r1, nB, VECTOR2I( 1'000'000, 0 ) );
109
110 // Synthetic second pad on nA with no bridge partner so we have two distinct pads.
111 FOOTPRINT* loose = addFootprint( &board );
112 PAD* endPad = addPad( loose, nA, VECTOR2I( -10'000'000, 0 ) );
113
114 NET_CHAIN_PARTITION p = PartitionNetChainAroundNet( &board, nA->GetNetCode(), startPad, endPad );
115
117 BOOST_CHECK_EQUAL( p.beforeStart.size(), 1u );
118 BOOST_CHECK( p.beforeStart.count( nB->GetNetCode() ) == 1 );
119 BOOST_CHECK( p.afterEnd.empty() );
120}
121
122
123// 3-pin part: start pad shares its footprint with two distinct non-query nets.
124// Both must seed the BFS.
125BOOST_AUTO_TEST_CASE( MultiBridgePadSeedsAllNeighbors )
126{
127 BOARD board;
128
129 NETINFO_ITEM* nQ = addNet( &board, wxS( "/Q" ), 1, wxS( "SIG" ) );
130 NETINFO_ITEM* nN1 = addNet( &board, wxS( "/N1" ), 2, wxS( "SIG" ) );
131 NETINFO_ITEM* nN2 = addNet( &board, wxS( "/N2" ), 3, wxS( "SIG" ) );
132 NETINFO_ITEM* nE = addNet( &board, wxS( "/E" ), 4, wxS( "SIG" ) );
133
134 // 3-pin part: query pad + two distinct cross-net pads.
135 FOOTPRINT* tri = addFootprint( &board );
136 PAD* startPad = addPad( tri, nQ, VECTOR2I( 0, 0 ) );
137 addPad( tri, nN1, VECTOR2I( 1'000'000, 0 ) );
138 addPad( tri, nN2, VECTOR2I( 0, 1'000'000 ) );
139
140 // Endpoint pad on a regular 2-pin part with its own neighbor.
141 FOOTPRINT* r = addFootprint( &board );
142 PAD* endPad = addPad( r, nQ, VECTOR2I( 5'000'000, 0 ) );
143 addPad( r, nE, VECTOR2I( 6'000'000, 0 ) );
144
145 NET_CHAIN_PARTITION p = PartitionNetChainAroundNet( &board, nQ->GetNetCode(), startPad, endPad );
146
148 BOOST_CHECK_EQUAL( p.beforeStart.size(), 2u );
149 BOOST_CHECK( p.beforeStart.count( nN1->GetNetCode() ) == 1 );
150 BOOST_CHECK( p.beforeStart.count( nN2->GetNetCode() ) == 1 );
151 BOOST_CHECK_EQUAL( p.afterEnd.size(), 1u );
152 BOOST_CHECK( p.afterEnd.count( nE->GetNetCode() ) == 1 );
153}
154
155
156// Parallel passives between NB and NC form a cycle that bypasses the query net.
157// Cutting at NB still leaves the two sides reachable from each other → ambiguous.
158BOOST_AUTO_TEST_CASE( CycleNotThroughQueryIsAmbiguous )
159{
160 BOARD board;
161
162 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
163 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxS( "SIG" ) );
164 NETINFO_ITEM* nC = addNet( &board, wxS( "/C" ), 3, wxS( "SIG" ) );
165
166 // R1: A -- B (start side)
167 FOOTPRINT* r1 = addFootprint( &board );
168 addPad( r1, nA, VECTOR2I( -2'000'000, 0 ) );
169 PAD* startPad = addPad( r1, nB, VECTOR2I( -1'000'000, 0 ) );
170
171 // R2: B -- C (end side)
172 FOOTPRINT* r2 = addFootprint( &board );
173 PAD* endPad = addPad( r2, nB, VECTOR2I( 1'000'000, 0 ) );
174 addPad( r2, nC, VECTOR2I( 2'000'000, 0 ) );
175
176 // R3: A -- C (parallel cycle, bypassing nB entirely)
177 FOOTPRINT* r3 = addFootprint( &board );
178 addPad( r3, nA, VECTOR2I( -2'000'000, 5'000'000 ) );
179 addPad( r3, nC, VECTOR2I( 2'000'000, 5'000'000 ) );
180
181 NET_CHAIN_PARTITION p = PartitionNetChainAroundNet( &board, nB->GetNetCode(), startPad, endPad );
182
184
185 // Both sides still populated for caller inspection.
186 BOOST_CHECK( !p.beforeStart.empty() );
187 BOOST_CHECK( !p.afterEnd.empty() );
188}
189
190
191// Start and end pads supplied on a net other than the query net → error.
192BOOST_AUTO_TEST_CASE( StartPadOnDifferentNetReportsError )
193{
194 BOARD board;
195
196 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
197 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxS( "SIG" ) );
198
199 FOOTPRINT* r1 = addFootprint( &board );
200 PAD* aPad = addPad( r1, nA, VECTOR2I( -1'000'000, 0 ) );
201 PAD* bPad = addPad( r1, nB, VECTOR2I( 1'000'000, 0 ) );
202
203 NET_CHAIN_PARTITION p = PartitionNetChainAroundNet( &board, nA->GetNetCode(), bPad, aPad );
204
206 BOOST_CHECK( p.beforeStart.empty() );
207 BOOST_CHECK( p.afterEnd.empty() );
208}
209
210
211// Same pad for start and end is an invalid query.
212BOOST_AUTO_TEST_CASE( EqualStartEndPadsAreInvalid )
213{
214 BOARD board;
215
216 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
217 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxS( "SIG" ) );
218
219 FOOTPRINT* r1 = addFootprint( &board );
220 PAD* startPad = addPad( r1, nA, VECTOR2I( -1'000'000, 0 ) );
221 addPad( r1, nB, VECTOR2I( 1'000'000, 0 ) );
222
223 NET_CHAIN_PARTITION p = PartitionNetChainAroundNet( &board, nA->GetNetCode(), startPad, startPad );
224
226}
227
228
229// Net not assigned to any chain → distinct status.
230BOOST_AUTO_TEST_CASE( QueryNetWithoutChainReportsMissing )
231{
232 BOARD board;
233
234 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxString() );
235 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxString() );
236
237 FOOTPRINT* fp = addFootprint( &board );
238 PAD* a1 = addPad( fp, nA, VECTOR2I( 0, 0 ) );
239 addPad( fp, nB, VECTOR2I( 1'000'000, 0 ) );
240
241 FOOTPRINT* fp2 = addFootprint( &board );
242 PAD* a2 = addPad( fp2, nA, VECTOR2I( 10'000'000, 0 ) );
243
244 NET_CHAIN_PARTITION p = PartitionNetChainAroundNet( &board, nA->GetNetCode(), a1, a2 );
245
247}
248
249
250// A chain with no bridges at all (only one net) cannot be partitioned.
251BOOST_AUTO_TEST_CASE( ChainWithNoBridgesReportsNoBridges )
252{
253 BOARD board;
254
255 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
256
257 FOOTPRINT* fp = addFootprint( &board );
258 PAD* p1 = addPad( fp, nA, VECTOR2I( 0, 0 ) );
259 PAD* p2 = addPad( fp, nA, VECTOR2I( 1'000'000, 0 ) );
260
261 NET_CHAIN_PARTITION p = PartitionNetChainAroundNet( &board, nA->GetNetCode(), p1, p2 );
262
264}
265
266
267// A chain has bridges between other nets, but no bridge is incident on the query net.
268// The query net was tagged as a chain member but is unreachable through the bridge graph.
269BOOST_AUTO_TEST_CASE( QueryNetWithoutOwnBridgeReportsNoBridges )
270{
271 BOARD board;
272
273 NETINFO_ITEM* nQ = addNet( &board, wxS( "/Q" ), 1, wxS( "SIG" ) );
274 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 2, wxS( "SIG" ) );
275 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 3, wxS( "SIG" ) );
276
277 // Bridge between nA and nB; nothing touches nQ.
278 FOOTPRINT* r = addFootprint( &board );
279 addPad( r, nA, VECTOR2I( -1'000'000, 0 ) );
280 addPad( r, nB, VECTOR2I( 1'000'000, 0 ) );
281
282 // Two distinct pads on nQ, neither on a bridge.
283 FOOTPRINT* fp1 = addFootprint( &board );
284 PAD* startPad = addPad( fp1, nQ, VECTOR2I( 10'000'000, 0 ) );
285
286 FOOTPRINT* fp2 = addFootprint( &board );
287 PAD* endPad = addPad( fp2, nQ, VECTOR2I( 20'000'000, 0 ) );
288
289 NET_CHAIN_PARTITION p = PartitionNetChainAroundNet( &board, nQ->GetNetCode(), startPad, endPad );
290
292 BOOST_CHECK( p.beforeStart.empty() );
293 BOOST_CHECK( p.afterEnd.empty() );
294}
295
296
297// Cross-board pad reference must be rejected, since EnumerateChainBridges walks aBoard.
298BOOST_AUTO_TEST_CASE( CrossBoardPadIsInvalidInput )
299{
300 BOARD boardA;
301 BOARD boardB;
302
303 NETINFO_ITEM* nA = addNet( &boardA, wxS( "/A" ), 1, wxS( "SIG" ) );
304 NETINFO_ITEM* nB = addNet( &boardA, wxS( "/B" ), 2, wxS( "SIG" ) );
305
306 FOOTPRINT* r = addFootprint( &boardA );
307 PAD* startPad = addPad( r, nA, VECTOR2I( 0, 0 ) );
308 addPad( r, nB, VECTOR2I( 1'000'000, 0 ) );
309
310 // A pad on a different board.
311 NETINFO_ITEM* nA_b = addNet( &boardB, wxS( "/A" ), 1, wxS( "SIG" ) );
312 FOOTPRINT* r_b = addFootprint( &boardB );
313 PAD* foreignPad = addPad( r_b, nA_b, VECTOR2I( 0, 0 ) );
314
315 NET_CHAIN_PARTITION p = PartitionNetChainAroundNet( &boardA, nA->GetNetCode(), startPad, foreignPad );
316
318}
319
320
321// Caller-owned result struct starts empty; we don't depend on the caller pre-clearing.
322BOOST_AUTO_TEST_CASE( PrePopulatedCallerStateIsIgnored )
323{
324 BOARD board;
325
326 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
327 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxS( "SIG" ) );
328 NETINFO_ITEM* nC = addNet( &board, wxS( "/C" ), 3, wxS( "SIG" ) );
329
330 FOOTPRINT* r1 = addFootprint( &board );
331 addPad( r1, nA, VECTOR2I( -2'000'000, 0 ) );
332 PAD* startPad = addPad( r1, nB, VECTOR2I( -1'000'000, 0 ) );
333
334 FOOTPRINT* r2 = addFootprint( &board );
335 PAD* endPad = addPad( r2, nB, VECTOR2I( 1'000'000, 0 ) );
336 addPad( r2, nC, VECTOR2I( 2'000'000, 0 ) );
337
338 NET_CHAIN_PARTITION p = PartitionNetChainAroundNet( &board, nB->GetNetCode(), startPad, endPad );
339
341
342 // Reassignment from another call must not leak prior state.
343 p = PartitionNetChainAroundNet( &board, nB->GetNetCode(), startPad, endPad );
344
345 BOOST_CHECK_EQUAL( p.beforeStart.size(), 1u );
346 BOOST_CHECK_EQUAL( p.afterEnd.size(), 1u );
347}
348
349
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:409
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1497
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Handle the data for a net.
Definition netinfo.h:50
int GetNetCode() const
Definition netinfo.h:104
void SetNetChain(const wxString &aNetChain)
Definition netinfo.h:123
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:170
static constexpr PCB_LAYER_ID ALL_LAYERS
! The layer identifier to use for the single defintion on normal padstacks
Definition padstack.h:179
Definition pad.h:61
NET_CHAIN_PARTITION PartitionNetChainAroundNet(const BOARD *aBoard, int aQueryNet, const PAD *aStartPad, const PAD *aEndPad)
Partition the chain containing aQueryNet around it, cut at the bridges incident on aStartPad and aEnd...
Result of PartitionNetChainAroundNet().
std::set< int > beforeStart
NET_CHAIN_PARTITION_STATUS status
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
static FOOTPRINT * addFootprint(BOARD &aBoard, const wxString &aReference)
BOOST_AUTO_TEST_CASE(LinearChainSplitsCleanly)
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683