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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 */
20
21#include <boost/test/unit_test.hpp>
22
23#include <board.h>
24#include <footprint.h>
25#include <net_chain_bridging.h>
26#include <netinfo.h>
27#include <pad.h>
28#include <padstack.h>
29
30
31namespace
32{
33
34constexpr int PAD_SIZE_NM = 500'000;
35
36
37PAD* addPad( FOOTPRINT* aFp, NETINFO_ITEM* aNet, const VECTOR2I& aPos )
38{
39 PAD* pad = new PAD( aFp );
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
58FOOTPRINT* addFootprint( BOARD* aBoard )
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
89 startPad, endPad );
90
92 BOOST_CHECK_EQUAL( p.beforeStart.size(), 1u );
93 BOOST_CHECK_EQUAL( p.afterEnd.size(), 1u );
94 BOOST_CHECK( p.beforeStart.count( nA->GetNetCode() ) == 1 );
95 BOOST_CHECK( p.afterEnd.count( nC->GetNetCode() ) == 1 );
96}
97
98
99// Query net is the chain terminal: end pad has no bridge neighbor.
100BOOST_AUTO_TEST_CASE( TerminalQueryNetLeavesOneSideEmpty )
101{
102 BOARD board;
103
104 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
105 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxS( "SIG" ) );
106
107 FOOTPRINT* r1 = addFootprint( &board );
108 PAD* startPad = addPad( r1, nA, VECTOR2I( -1'000'000, 0 ) );
109 addPad( r1, nB, VECTOR2I( 1'000'000, 0 ) );
110
111 // Synthetic second pad on nA with no bridge partner so we have two distinct pads.
112 FOOTPRINT* loose = addFootprint( &board );
113 PAD* endPad = addPad( loose, nA, VECTOR2I( -10'000'000, 0 ) );
114
116 startPad, endPad );
117
119 BOOST_CHECK_EQUAL( p.beforeStart.size(), 1u );
120 BOOST_CHECK( p.beforeStart.count( nB->GetNetCode() ) == 1 );
121 BOOST_CHECK( p.afterEnd.empty() );
122}
123
124
125// 3-pin part: start pad shares its footprint with two distinct non-query nets.
126// Both must seed the BFS.
127BOOST_AUTO_TEST_CASE( MultiBridgePadSeedsAllNeighbors )
128{
129 BOARD board;
130
131 NETINFO_ITEM* nQ = addNet( &board, wxS( "/Q" ), 1, wxS( "SIG" ) );
132 NETINFO_ITEM* nN1 = addNet( &board, wxS( "/N1" ), 2, wxS( "SIG" ) );
133 NETINFO_ITEM* nN2 = addNet( &board, wxS( "/N2" ), 3, wxS( "SIG" ) );
134 NETINFO_ITEM* nE = addNet( &board, wxS( "/E" ), 4, wxS( "SIG" ) );
135
136 // 3-pin part: query pad + two distinct cross-net pads.
137 FOOTPRINT* tri = addFootprint( &board );
138 PAD* startPad = addPad( tri, nQ, VECTOR2I( 0, 0 ) );
139 addPad( tri, nN1, VECTOR2I( 1'000'000, 0 ) );
140 addPad( tri, nN2, VECTOR2I( 0, 1'000'000 ) );
141
142 // Endpoint pad on a regular 2-pin part with its own neighbor.
143 FOOTPRINT* r = addFootprint( &board );
144 PAD* endPad = addPad( r, nQ, VECTOR2I( 5'000'000, 0 ) );
145 addPad( r, nE, VECTOR2I( 6'000'000, 0 ) );
146
148 startPad, endPad );
149
151 BOOST_CHECK_EQUAL( p.beforeStart.size(), 2u );
152 BOOST_CHECK( p.beforeStart.count( nN1->GetNetCode() ) == 1 );
153 BOOST_CHECK( p.beforeStart.count( nN2->GetNetCode() ) == 1 );
154 BOOST_CHECK_EQUAL( p.afterEnd.size(), 1u );
155 BOOST_CHECK( p.afterEnd.count( nE->GetNetCode() ) == 1 );
156}
157
158
159// Parallel passives between NB and NC form a cycle that bypasses the query net.
160// Cutting at NB still leaves the two sides reachable from each other → ambiguous.
161BOOST_AUTO_TEST_CASE( CycleNotThroughQueryIsAmbiguous )
162{
163 BOARD board;
164
165 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
166 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxS( "SIG" ) );
167 NETINFO_ITEM* nC = addNet( &board, wxS( "/C" ), 3, wxS( "SIG" ) );
168
169 // R1: A -- B (start side)
170 FOOTPRINT* r1 = addFootprint( &board );
171 addPad( r1, nA, VECTOR2I( -2'000'000, 0 ) );
172 PAD* startPad = addPad( r1, nB, VECTOR2I( -1'000'000, 0 ) );
173
174 // R2: B -- C (end side)
175 FOOTPRINT* r2 = addFootprint( &board );
176 PAD* endPad = addPad( r2, nB, VECTOR2I( 1'000'000, 0 ) );
177 addPad( r2, nC, VECTOR2I( 2'000'000, 0 ) );
178
179 // R3: A -- C (parallel cycle, bypassing nB entirely)
180 FOOTPRINT* r3 = addFootprint( &board );
181 addPad( r3, nA, VECTOR2I( -2'000'000, 5'000'000 ) );
182 addPad( r3, nC, VECTOR2I( 2'000'000, 5'000'000 ) );
183
185 startPad, endPad );
186
188
189 // Both sides still populated for caller inspection.
190 BOOST_CHECK( !p.beforeStart.empty() );
191 BOOST_CHECK( !p.afterEnd.empty() );
192}
193
194
195// Start and end pads supplied on a net other than the query net → error.
196BOOST_AUTO_TEST_CASE( StartPadOnDifferentNetReportsError )
197{
198 BOARD board;
199
200 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
201 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxS( "SIG" ) );
202
203 FOOTPRINT* r1 = addFootprint( &board );
204 PAD* aPad = addPad( r1, nA, VECTOR2I( -1'000'000, 0 ) );
205 PAD* bPad = addPad( r1, nB, VECTOR2I( 1'000'000, 0 ) );
206
208 bPad, aPad );
209
211 BOOST_CHECK( p.beforeStart.empty() );
212 BOOST_CHECK( p.afterEnd.empty() );
213}
214
215
216// Same pad for start and end is an invalid query.
217BOOST_AUTO_TEST_CASE( EqualStartEndPadsAreInvalid )
218{
219 BOARD board;
220
221 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
222 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxS( "SIG" ) );
223
224 FOOTPRINT* r1 = addFootprint( &board );
225 PAD* startPad = addPad( r1, nA, VECTOR2I( -1'000'000, 0 ) );
226 addPad( r1, nB, VECTOR2I( 1'000'000, 0 ) );
227
229 startPad, startPad );
230
232}
233
234
235// Net not assigned to any chain → distinct status.
236BOOST_AUTO_TEST_CASE( QueryNetWithoutChainReportsMissing )
237{
238 BOARD board;
239
240 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxString() );
241 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxString() );
242
243 FOOTPRINT* fp = addFootprint( &board );
244 PAD* a1 = addPad( fp, nA, VECTOR2I( 0, 0 ) );
245 addPad( fp, nB, VECTOR2I( 1'000'000, 0 ) );
246
247 FOOTPRINT* fp2 = addFootprint( &board );
248 PAD* a2 = addPad( fp2, nA, VECTOR2I( 10'000'000, 0 ) );
249
250 NET_CHAIN_PARTITION p = PartitionNetChainAroundNet( &board, nA->GetNetCode(), a1, a2 );
251
253}
254
255
256// A chain with no bridges at all (only one net) cannot be partitioned.
257BOOST_AUTO_TEST_CASE( ChainWithNoBridgesReportsNoBridges )
258{
259 BOARD board;
260
261 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
262
263 FOOTPRINT* fp = addFootprint( &board );
264 PAD* p1 = addPad( fp, nA, VECTOR2I( 0, 0 ) );
265 PAD* p2 = addPad( fp, nA, VECTOR2I( 1'000'000, 0 ) );
266
267 NET_CHAIN_PARTITION p = PartitionNetChainAroundNet( &board, nA->GetNetCode(), p1, p2 );
268
270}
271
272
273// A chain has bridges between other nets, but no bridge is incident on the query net.
274// The query net was tagged as a chain member but is unreachable through the bridge graph.
275BOOST_AUTO_TEST_CASE( QueryNetWithoutOwnBridgeReportsNoBridges )
276{
277 BOARD board;
278
279 NETINFO_ITEM* nQ = addNet( &board, wxS( "/Q" ), 1, wxS( "SIG" ) );
280 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 2, wxS( "SIG" ) );
281 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 3, wxS( "SIG" ) );
282
283 // Bridge between nA and nB; nothing touches nQ.
284 FOOTPRINT* r = addFootprint( &board );
285 addPad( r, nA, VECTOR2I( -1'000'000, 0 ) );
286 addPad( r, nB, VECTOR2I( 1'000'000, 0 ) );
287
288 // Two distinct pads on nQ, neither on a bridge.
289 FOOTPRINT* fp1 = addFootprint( &board );
290 PAD* startPad = addPad( fp1, nQ, VECTOR2I( 10'000'000, 0 ) );
291
292 FOOTPRINT* fp2 = addFootprint( &board );
293 PAD* endPad = addPad( fp2, nQ, VECTOR2I( 20'000'000, 0 ) );
294
296 startPad, endPad );
297
299 BOOST_CHECK( p.beforeStart.empty() );
300 BOOST_CHECK( p.afterEnd.empty() );
301}
302
303
304// Cross-board pad reference must be rejected, since EnumerateChainBridges walks aBoard.
305BOOST_AUTO_TEST_CASE( CrossBoardPadIsInvalidInput )
306{
307 BOARD boardA;
308 BOARD boardB;
309
310 NETINFO_ITEM* nA = addNet( &boardA, wxS( "/A" ), 1, wxS( "SIG" ) );
311 NETINFO_ITEM* nB = addNet( &boardA, wxS( "/B" ), 2, wxS( "SIG" ) );
312
313 FOOTPRINT* r = addFootprint( &boardA );
314 PAD* startPad = addPad( r, nA, VECTOR2I( 0, 0 ) );
315 addPad( r, nB, VECTOR2I( 1'000'000, 0 ) );
316
317 // A pad on a different board.
318 NETINFO_ITEM* nA_b = addNet( &boardB, wxS( "/A" ), 1, wxS( "SIG" ) );
319 FOOTPRINT* r_b = addFootprint( &boardB );
320 PAD* foreignPad = addPad( r_b, nA_b, VECTOR2I( 0, 0 ) );
321
323 startPad, foreignPad );
324
326}
327
328
329// Caller-owned result struct starts empty; we don't depend on the caller pre-clearing.
330BOOST_AUTO_TEST_CASE( PrePopulatedCallerStateIsIgnored )
331{
332 BOARD board;
333
334 NETINFO_ITEM* nA = addNet( &board, wxS( "/A" ), 1, wxS( "SIG" ) );
335 NETINFO_ITEM* nB = addNet( &board, wxS( "/B" ), 2, wxS( "SIG" ) );
336 NETINFO_ITEM* nC = addNet( &board, wxS( "/C" ), 3, wxS( "SIG" ) );
337
338 FOOTPRINT* r1 = addFootprint( &board );
339 addPad( r1, nA, VECTOR2I( -2'000'000, 0 ) );
340 PAD* startPad = addPad( r1, nB, VECTOR2I( -1'000'000, 0 ) );
341
342 FOOTPRINT* r2 = addFootprint( &board );
343 PAD* endPad = addPad( r2, nB, VECTOR2I( 1'000'000, 0 ) );
344 addPad( r2, nC, VECTOR2I( 2'000'000, 0 ) );
345
347 startPad, endPad );
348
350
351 // Reassignment from another call must not leak prior state.
352 p = PartitionNetChainAroundNet( &board, nB->GetNetCode(), startPad, endPad );
353
354 BOOST_CHECK_EQUAL( p.beforeStart.size(), 1u );
355 BOOST_CHECK_EQUAL( p.afterEnd.size(), 1u );
356}
357
358
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1247
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:98
void SetNetChain(const wxString &aNetChain)
Definition netinfo.h:117
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:65
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:687