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 );
40 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( PAD_SIZE_NM, PAD_SIZE_NM ) );
41 pad->SetPosition( aPos );
42 pad->SetNet( aNet );
43 aFp->Add( pad );
44 return pad;
45}
46
47
48NETINFO_ITEM* addNet( BOARD* aBoard, const wxString& aName, int aCode, const wxString& aChain )
49{
50 NETINFO_ITEM* n = new NETINFO_ITEM( aBoard, aName, aCode );
51 n->SetNetChain( aChain );
52 aBoard->Add( n );
53 return n;
54}
55
56
57FOOTPRINT* addFootprint( BOARD* aBoard )
58{
59 FOOTPRINT* fp = new FOOTPRINT( aBoard );
60 aBoard->Add( fp );
61 return fp;
62}
63
64} // namespace
65
66
67BOOST_AUTO_TEST_SUITE( NetChainPartition )
68
69
70// Linear chain NA -- R1 -- NB -- R2 -- NC with query net NB. Expect before={NA}, after={NC}.
71BOOST_AUTO_TEST_CASE( LinearChainSplitsCleanly )
72{
73 BOARD board;
74
75 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
76 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxS( "SIG" ) );
77 NETINFO_ITEM* nC = addNet( &board, wxS( "/C" ), 3, wxS( "SIG" ) );
78
79 FOOTPRINT* r1 = addFootprint( &board );
80 addPad( r1, nA, VECTOR2I( -2'000'000, 0 ) );
81 PAD* startPad = addPad( r1, nB, VECTOR2I( -1'000'000, 0 ) );
82
83 FOOTPRINT* r2 = addFootprint( &board );
84 PAD* endPad = addPad( r2, nB, VECTOR2I( 1'000'000, 0 ) );
85 addPad( r2, nC, VECTOR2I( 2'000'000, 0 ) );
86
88 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
115 startPad, endPad );
116
118 BOOST_CHECK_EQUAL( p.beforeStart.size(), 1u );
119 BOOST_CHECK( p.beforeStart.count( nB->GetNetCode() ) == 1 );
120 BOOST_CHECK( p.afterEnd.empty() );
121}
122
123
124// 3-pin part: start pad shares its footprint with two distinct non-query nets.
125// Both must seed the BFS.
126BOOST_AUTO_TEST_CASE( MultiBridgePadSeedsAllNeighbors )
127{
128 BOARD board;
129
130 NETINFO_ITEM* nQ = addNet( &board, wxS( "/Q" ), 1, wxS( "SIG" ) );
131 NETINFO_ITEM* nN1 = addNet( &board, wxS( "/N1" ), 2, wxS( "SIG" ) );
132 NETINFO_ITEM* nN2 = addNet( &board, wxS( "/N2" ), 3, wxS( "SIG" ) );
133 NETINFO_ITEM* nE = addNet( &board, wxS( "/E" ), 4, wxS( "SIG" ) );
134
135 // 3-pin part: query pad + two distinct cross-net pads.
136 FOOTPRINT* tri = addFootprint( &board );
137 PAD* startPad = addPad( tri, nQ, VECTOR2I( 0, 0 ) );
138 addPad( tri, nN1, VECTOR2I( 1'000'000, 0 ) );
139 addPad( tri, nN2, VECTOR2I( 0, 1'000'000 ) );
140
141 // Endpoint pad on a regular 2-pin part with its own neighbor.
142 FOOTPRINT* r = addFootprint( &board );
143 PAD* endPad = addPad( r, nQ, VECTOR2I( 5'000'000, 0 ) );
144 addPad( r, nE, VECTOR2I( 6'000'000, 0 ) );
145
147 startPad, endPad );
148
150 BOOST_CHECK_EQUAL( p.beforeStart.size(), 2u );
151 BOOST_CHECK( p.beforeStart.count( nN1->GetNetCode() ) == 1 );
152 BOOST_CHECK( p.beforeStart.count( nN2->GetNetCode() ) == 1 );
153 BOOST_CHECK_EQUAL( p.afterEnd.size(), 1u );
154 BOOST_CHECK( p.afterEnd.count( nE->GetNetCode() ) == 1 );
155}
156
157
158// Parallel passives between NB and NC form a cycle that bypasses the query net.
159// Cutting at NB still leaves the two sides reachable from each other → ambiguous.
160BOOST_AUTO_TEST_CASE( CycleNotThroughQueryIsAmbiguous )
161{
162 BOARD board;
163
164 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
165 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxS( "SIG" ) );
166 NETINFO_ITEM* nC = addNet( &board, wxS( "/C" ), 3, wxS( "SIG" ) );
167
168 // R1: A -- B (start side)
169 FOOTPRINT* r1 = addFootprint( &board );
170 addPad( r1, nA, VECTOR2I( -2'000'000, 0 ) );
171 PAD* startPad = addPad( r1, nB, VECTOR2I( -1'000'000, 0 ) );
172
173 // R2: B -- C (end side)
174 FOOTPRINT* r2 = addFootprint( &board );
175 PAD* endPad = addPad( r2, nB, VECTOR2I( 1'000'000, 0 ) );
176 addPad( r2, nC, VECTOR2I( 2'000'000, 0 ) );
177
178 // R3: A -- C (parallel cycle, bypassing nB entirely)
179 FOOTPRINT* r3 = addFootprint( &board );
180 addPad( r3, nA, VECTOR2I( -2'000'000, 5'000'000 ) );
181 addPad( r3, nC, VECTOR2I( 2'000'000, 5'000'000 ) );
182
184 startPad, endPad );
185
187
188 // Both sides still populated for caller inspection.
189 BOOST_CHECK( !p.beforeStart.empty() );
190 BOOST_CHECK( !p.afterEnd.empty() );
191}
192
193
194// Start and end pads supplied on a net other than the query net → error.
195BOOST_AUTO_TEST_CASE( StartPadOnDifferentNetReportsError )
196{
197 BOARD board;
198
199 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
200 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxS( "SIG" ) );
201
202 FOOTPRINT* r1 = addFootprint( &board );
203 PAD* aPad = addPad( r1, nA, VECTOR2I( -1'000'000, 0 ) );
204 PAD* bPad = addPad( r1, nB, VECTOR2I( 1'000'000, 0 ) );
205
207 bPad, aPad );
208
210 BOOST_CHECK( p.beforeStart.empty() );
211 BOOST_CHECK( p.afterEnd.empty() );
212}
213
214
215// Same pad for start and end is an invalid query.
216BOOST_AUTO_TEST_CASE( EqualStartEndPadsAreInvalid )
217{
218 BOARD board;
219
220 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
221 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxS( "SIG" ) );
222
223 FOOTPRINT* r1 = addFootprint( &board );
224 PAD* startPad = addPad( r1, nA, VECTOR2I( -1'000'000, 0 ) );
225 addPad( r1, nB, VECTOR2I( 1'000'000, 0 ) );
226
228 startPad, startPad );
229
231}
232
233
234// Net not assigned to any chain → distinct status.
235BOOST_AUTO_TEST_CASE( QueryNetWithoutChainReportsMissing )
236{
237 BOARD board;
238
239 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxString() );
240 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxString() );
241
242 FOOTPRINT* fp = addFootprint( &board );
243 PAD* a1 = addPad( fp, nA, VECTOR2I( 0, 0 ) );
244 addPad( fp, nB, VECTOR2I( 1'000'000, 0 ) );
245
246 FOOTPRINT* fp2 = addFootprint( &board );
247 PAD* a2 = addPad( fp2, nA, VECTOR2I( 10'000'000, 0 ) );
248
249 NET_CHAIN_PARTITION p = PartitionNetChainAroundNet( &board, nA->GetNetCode(), a1, a2 );
250
252}
253
254
255// A chain with no bridges at all (only one net) cannot be partitioned.
256BOOST_AUTO_TEST_CASE( ChainWithNoBridgesReportsNoBridges )
257{
258 BOARD board;
259
260 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
261
262 FOOTPRINT* fp = addFootprint( &board );
263 PAD* p1 = addPad( fp, nA, VECTOR2I( 0, 0 ) );
264 PAD* p2 = addPad( fp, nA, VECTOR2I( 1'000'000, 0 ) );
265
266 NET_CHAIN_PARTITION p = PartitionNetChainAroundNet( &board, nA->GetNetCode(), p1, p2 );
267
269}
270
271
272// A chain has bridges between other nets, but no bridge is incident on the query net.
273// The query net was tagged as a chain member but is unreachable through the bridge graph.
274BOOST_AUTO_TEST_CASE( QueryNetWithoutOwnBridgeReportsNoBridges )
275{
276 BOARD board;
277
278 NETINFO_ITEM* nQ = addNet( &board, wxS( "/Q" ), 1, wxS( "SIG" ) );
279 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 2, wxS( "SIG" ) );
280 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 3, wxS( "SIG" ) );
281
282 // Bridge between nA and nB; nothing touches nQ.
283 FOOTPRINT* r = addFootprint( &board );
284 addPad( r, nA, VECTOR2I( -1'000'000, 0 ) );
285 addPad( r, nB, VECTOR2I( 1'000'000, 0 ) );
286
287 // Two distinct pads on nQ, neither on a bridge.
288 FOOTPRINT* fp1 = addFootprint( &board );
289 PAD* startPad = addPad( fp1, nQ, VECTOR2I( 10'000'000, 0 ) );
290
291 FOOTPRINT* fp2 = addFootprint( &board );
292 PAD* endPad = addPad( fp2, nQ, VECTOR2I( 20'000'000, 0 ) );
293
295 startPad, endPad );
296
298 BOOST_CHECK( p.beforeStart.empty() );
299 BOOST_CHECK( p.afterEnd.empty() );
300}
301
302
303// Cross-board pad reference must be rejected, since EnumerateChainBridges walks aBoard.
304BOOST_AUTO_TEST_CASE( CrossBoardPadIsInvalidInput )
305{
306 BOARD boardA;
307 BOARD boardB;
308
309 NETINFO_ITEM* nA = addNet( &boardA, wxS( "/A" ), 1, wxS( "SIG" ) );
310 NETINFO_ITEM* nB = addNet( &boardA, wxS( "/B" ), 2, wxS( "SIG" ) );
311
312 FOOTPRINT* r = addFootprint( &boardA );
313 PAD* startPad = addPad( r, nA, VECTOR2I( 0, 0 ) );
314 addPad( r, nB, VECTOR2I( 1'000'000, 0 ) );
315
316 // A pad on a different board.
317 NETINFO_ITEM* nA_b = addNet( &boardB, wxS( "/A" ), 1, wxS( "SIG" ) );
318 FOOTPRINT* r_b = addFootprint( &boardB );
319 PAD* foreignPad = addPad( r_b, nA_b, VECTOR2I( 0, 0 ) );
320
322 startPad, foreignPad );
323
325}
326
327
328// Caller-owned result struct starts empty; we don't depend on the caller pre-clearing.
329BOOST_AUTO_TEST_CASE( PrePopulatedCallerStateIsIgnored )
330{
331 BOARD board;
332
333 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
334 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxS( "SIG" ) );
335 NETINFO_ITEM* nC = addNet( &board, wxS( "/C" ), 3, wxS( "SIG" ) );
336
337 FOOTPRINT* r1 = addFootprint( &board );
338 addPad( r1, nA, VECTOR2I( -2'000'000, 0 ) );
339 PAD* startPad = addPad( r1, nB, VECTOR2I( -1'000'000, 0 ) );
340
341 FOOTPRINT* r2 = addFootprint( &board );
342 PAD* endPad = addPad( r2, nB, VECTOR2I( 1'000'000, 0 ) );
343 addPad( r2, nC, VECTOR2I( 2'000'000, 0 ) );
344
346 startPad, endPad );
347
349
350 // Reassignment from another call must not leak prior state.
351 p = PartitionNetChainAroundNet( &board, nB->GetNetCode(), startPad, endPad );
352
353 BOOST_CHECK_EQUAL( p.beforeStart.size(), 1u );
354 BOOST_CHECK_EQUAL( p.afterEnd.size(), 1u );
355}
356
357
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:372
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1295
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:46
int GetNetCode() const
Definition netinfo.h:94
void SetNetChain(const wxString &aNetChain)
Definition netinfo.h:113
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
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()
BOOST_AUTO_TEST_CASE(LinearChainSplitsCleanly)
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683