KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_net_chain_terminal_pads.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 * http://www.gnu.org/licenses/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <boost/test/unit_test.hpp>
26#include <board.h>
27#include <footprint.h>
28#include <pad.h>
29#include <pcb_track.h>
33#include <wx/filename.h>
34#include <filesystem>
35
40
41BOOST_FIXTURE_TEST_SUITE( RegressionSaveLoadTests, SIGNAL_PAD_FIXTURE )
42
43BOOST_AUTO_TEST_CASE( SignalTerminalPadsRoundTrip )
44{
45 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
46
47 // Create two footprints each with two pads; connect pads with tracks into two nets sharing a signal
48 FOOTPRINT* fp1 = new FOOTPRINT( board.get() );
49 fp1->SetReference( wxS( "U1" ) );
50 FOOTPRINT* fp2 = new FOOTPRINT( board.get() );
51 fp2->SetReference( wxS( "U2" ) );
52 board->Add( fp1 );
53 board->Add( fp2 );
54
55 PAD* p1a = new PAD( fp1 ); p1a->SetFrontShape( PAD_SHAPE::CIRCLE ); p1a->SetSize( F_Cu, VECTOR2I( 1000000,1000000 ) ); p1a->SetPosition( VECTOR2I( 0,0 ) ); p1a->SetNumber( wxS( "1" ) ); fp1->Add( p1a );
56 PAD* p1b = new PAD( fp1 ); p1b->SetFrontShape( PAD_SHAPE::CIRCLE ); p1b->SetSize( F_Cu, VECTOR2I( 1000000,1000000 ) ); p1b->SetPosition( VECTOR2I( 3000000,0 ) ); p1b->SetNumber( wxS( "2" ) ); fp1->Add( p1b );
57 PAD* p2a = new PAD( fp2 ); p2a->SetFrontShape( PAD_SHAPE::CIRCLE ); p2a->SetSize( F_Cu, VECTOR2I( 1000000,1000000 ) ); p2a->SetPosition( VECTOR2I( 10000000,0 ) ); p2a->SetNumber( wxS( "1" ) ); fp2->Add( p2a );
58 PAD* p2b = new PAD( fp2 ); p2b->SetFrontShape( PAD_SHAPE::CIRCLE ); p2b->SetSize( F_Cu, VECTOR2I( 1000000,1000000 ) ); p2b->SetPosition( VECTOR2I( 13000000,0 ) ); p2b->SetNumber( wxS( "2" ) ); fp2->Add( p2b );
59
60 // Add nets first so pads referencing them are valid
61 NETINFO_ITEM* n1 = new NETINFO_ITEM( board.get(), wxS( "Net-(1)" ), 1 ); board->Add( n1 );
62 NETINFO_ITEM* n2 = new NETINFO_ITEM( board.get(), wxS( "Net-(2)" ), 2 ); board->Add( n2 );
63
64 n1->SetNetChain( wxS( "SIG_A" ) );
65 n2->SetNetChain( wxS( "SIG_A" ) );
66
67 // Assign pads to nets for resolver (only first pad of each footprint for simplicity)
68 p1a->SetNet( n1 );
69 p2a->SetNet( n2 );
70
71 // Assign terminal pads (simulate import heuristic outcome)
72 n1->SetTerminalPad( 0, p1a ); n1->SetTerminalPad( 1, p2a );
73 n2->SetTerminalPad( 0, p1a ); n2->SetTerminalPad( 1, p2a );
74
75 // Store original UUIDs for debugging comparison after reload
76 wxString orig_p1a = p1a->m_Uuid.AsString();
77 wxString orig_p2a = p2a->m_Uuid.AsString();
78
79 // Create simple tracks to ensure nets are serialized
80 PCB_TRACK* t1 = new PCB_TRACK( board.get() ); t1->SetNetCode( 1 ); t1->SetStart( p1a->GetPosition() ); t1->SetEnd( p2a->GetPosition() ); board->Add( t1 );
81 PCB_TRACK* t2 = new PCB_TRACK( board.get() ); t2->SetNetCode( 2 ); t2->SetStart( p1a->GetPosition() ); t2->SetEnd( p2a->GetPosition() ); board->Add( t2 );
82
83 auto tmpFile = std::filesystem::temp_directory_path() / "net_chain_terminal_pads_roundtrip.kicad_pcb";
84 plugin.SaveBoard( tmpFile.string(), board.get() );
85
86 std::unique_ptr<BOARD> loaded = std::make_unique<BOARD>();
87 plugin.LoadBoard( tmpFile.string(), loaded.get() );
88
89 NETINFO_ITEM* ln1 = loaded->FindNet( 1 );
90 NETINFO_ITEM* ln2 = loaded->FindNet( 2 );
91
92 // Ensure terminal pads resolved (parser should do this but call defensively for test stability)
93 for( NETINFO_ITEM* n : loaded->GetNetInfo() )
94 n->ResolveTerminalPads( loaded.get() );
95
96 BOOST_REQUIRE( ln1 );
97 BOOST_REQUIRE( ln2 );
98 BOOST_CHECK_EQUAL( ln1->GetNetChain(), wxS( "SIG_A" ) );
99 BOOST_CHECK_EQUAL( ln2->GetNetChain(), wxS( "SIG_A" ) );
100
101 // Both nets should have terminal pad UUIDs resolved
102 BOOST_REQUIRE( ln1->GetTerminalPad( 0 ) );
103 BOOST_REQUIRE( ln1->GetTerminalPad( 1 ) );
104 BOOST_REQUIRE( ln2->GetTerminalPad( 0 ) );
105 BOOST_REQUIRE( ln2->GetTerminalPad( 1 ) );
106
107 BOOST_TEST_MESSAGE( wxString::Format( wxS("orig p1a=%s p2a=%s"), orig_p1a, orig_p2a ) );
108 BOOST_TEST_MESSAGE( wxString::Format( wxS("loaded terminal A net1=%s net2=%s"), ln1->GetTerminalPad(0)->m_Uuid.AsString(), ln2->GetTerminalPad(0)->m_Uuid.AsString() ) );
109 BOOST_TEST_MESSAGE( wxString::Format( wxS("loaded terminal B net1=%s net2=%s"), ln1->GetTerminalPad(1)->m_Uuid.AsString(), ln2->GetTerminalPad(1)->m_Uuid.AsString() ) );
110
111 // UUIDs can be regenerated on load (timestamp -> UUID or duplicate resolution). Validate
112 // that both nets agree on terminal pads and that those pads exist in the loaded board.
113 PAD* termA1 = ln1->GetTerminalPad( 0 );
114 PAD* termB1 = ln1->GetTerminalPad( 1 );
115 BOOST_CHECK( termA1 == ln2->GetTerminalPad( 0 ) );
116 BOOST_CHECK( termB1 == ln2->GetTerminalPad( 1 ) );
117
118 bool foundA = false, foundB = false;
119 for( FOOTPRINT* fp : loaded->Footprints() )
120 {
121 for( PAD* pad : fp->Pads() )
122 {
123 if( pad == termA1 ) foundA = true;
124 if( pad == termB1 ) foundB = true;
125 }
126 }
127 BOOST_CHECK( foundA );
128 BOOST_CHECK( foundB );
129}
130
131BOOST_AUTO_TEST_CASE( SingleNetSignalNamePersists )
132{
133 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
134 // Single net with explicit signal name, no terminal pads
135 NETINFO_ITEM* n1 = new NETINFO_ITEM( board.get(), wxS( "Net-(X)" ), 1 ); board->Add( n1 );
136 n1->SetNetChain( wxS( "CLK_REF" ) );
137 // Add trivial track so net is serialized
138 PCB_TRACK* t = new PCB_TRACK( board.get() ); t->SetNetCode( 1 ); t->SetStart( VECTOR2I(0,0) ); t->SetEnd( VECTOR2I(1000000,0) ); board->Add( t );
139
140 auto tmpFile = std::filesystem::temp_directory_path() / "single_net_chain_roundtrip.kicad_pcb";
141 plugin.SaveBoard( tmpFile.string(), board.get() );
142
143 std::unique_ptr<BOARD> loaded = std::make_unique<BOARD>();
144 plugin.LoadBoard( tmpFile.string(), loaded.get() );
145 NETINFO_ITEM* ln1 = loaded->FindNet( 1 );
146 BOOST_REQUIRE( ln1 );
147 BOOST_CHECK_EQUAL( ln1->GetNetChain(), wxS( "CLK_REF" ) );
148}
149
150BOOST_AUTO_TEST_CASE( SignalsSurviveBuildListOfNets )
151{
152 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
153
154 // Create 4 nets
155 NETINFO_ITEM* n1 = new NETINFO_ITEM( board.get(), wxS( "Net-(R1-Pad1)" ), 1 ); board->Add( n1 );
156 NETINFO_ITEM* n2 = new NETINFO_ITEM( board.get(), wxS( "Net-(R1-Pad2)" ), 2 ); board->Add( n2 );
157 NETINFO_ITEM* n3 = new NETINFO_ITEM( board.get(), wxS( "Net-(R2-Pad2)" ), 3 ); board->Add( n3 );
158 NETINFO_ITEM* n4 = new NETINFO_ITEM( board.get(), wxS( "Net-(R3-Pad2)" ), 4 ); board->Add( n4 );
159
160 for( NETINFO_ITEM* n : { n1, n2, n3, n4 } )
161 n->SetNetChain( wxS( "Signal1" ) );
162
163 // Add trivial tracks so that each net is considered used and serialized
164 int x = 0;
165 for( int code : { 1, 2, 3, 4 } )
166 {
167 PCB_TRACK* t = new PCB_TRACK( board.get() );
168 t->SetNetCode( code );
169 t->SetStart( VECTOR2I( x, 0 ) );
170 t->SetEnd( VECTOR2I( x + 1000000, 0 ) );
171 board->Add( t );
172 x += 2000000;
173 }
174
175 auto tmpFile = std::filesystem::temp_directory_path() / "net_chains_survive_buildlist.kicad_pcb";
176 plugin.SaveBoard( tmpFile.string(), board.get() );
177
178 std::unique_ptr<BOARD> loaded = std::make_unique<BOARD>();
179 plugin.LoadBoard( tmpFile.string(), loaded.get() );
180
181 // Simulate GUI post-load rebuild that previously cleared signals
182 loaded->BuildListOfNets();
183
184 int signalCount = 0;
185 std::set<wxString> expectedNames = { wxS("Net-(R1-Pad1)"), wxS("Net-(R1-Pad2)"), wxS("Net-(R2-Pad2)"), wxS("Net-(R3-Pad2)") };
186 std::set<wxString> seenNames;
187 for( NETINFO_ITEM* net : loaded->GetNetInfo() )
188 {
189 if( net->GetNetChain() == wxS( "Signal1" ) )
190 {
191 signalCount++;
192 seenNames.insert( net->GetNetname() );
193 }
194 }
195 BOOST_CHECK_EQUAL( signalCount, 4 );
196 BOOST_CHECK_EQUAL( seenNames.size(), expectedNames.size() );
197 for( const wxString& name : expectedNames )
198 BOOST_CHECK( seenNames.count( name ) );
199}
200
const char * name
General utilities for PCB file IO for QA programs.
virtual bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
virtual void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
const KIID m_Uuid
Definition eda_item.h:528
void SetReference(const wxString &aReference)
Definition footprint.h:835
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
wxString AsString() const
Definition kiid.cpp:244
Handle the data for a net.
Definition netinfo.h:50
const wxString & GetNetChain() const
Definition netinfo.h:115
PAD * GetTerminalPad(int aIndex) const
Definition netinfo.h:118
void SetNetChain(const wxString &aNetChain)
Definition netinfo.h:116
void SetTerminalPad(int aIndex, PAD *aPad)
Definition netinfo.h:119
Definition pad.h:55
void SetFrontShape(PAD_SHAPE aShape)
Definition pad.cpp:1424
VECTOR2I GetPosition() const override
Definition pad.h:209
void SetNumber(const wxString &aNumber)
Set the pad number (note that it can be alphanumeric, such as the array reference "AA12").
Definition pad.h:136
void SetPosition(const VECTOR2I &aPos) override
Definition pad.h:203
void SetSize(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
Definition pad.h:259
A #PLUGIN derivation for saving and loading Pcbnew s-expression formatted files.
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:93
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:96
@ F_Cu
Definition layer_ids.h:64
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(SignalTerminalPadsRoundTrip)
BOOST_TEST_MESSAGE("\n=== Real-World Polygon PIP Benchmark ===\n"<< formatTable(table))
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687