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, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <boost/test/unit_test.hpp>
22#include <board.h>
23#include <footprint.h>
24#include <pad.h>
25#include <pcb_track.h>
29#include <wx/filename.h>
30#include <filesystem>
31
36
37BOOST_FIXTURE_TEST_SUITE( RegressionSaveLoadTests, SIGNAL_PAD_FIXTURE )
38
39BOOST_AUTO_TEST_CASE( SignalTerminalPadsRoundTrip )
40{
41 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
42
43 // Create two footprints each with two pads; connect pads with tracks into two nets sharing a signal
44 FOOTPRINT* fp1 = new FOOTPRINT( board.get() );
45 fp1->SetReference( wxS( "U1" ) );
46 FOOTPRINT* fp2 = new FOOTPRINT( board.get() );
47 fp2->SetReference( wxS( "U2" ) );
48 board->Add( fp1 );
49 board->Add( fp2 );
50
51 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 );
52 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 );
53 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 );
54 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 );
55
56 // Add nets first so pads referencing them are valid
57 NETINFO_ITEM* n1 = new NETINFO_ITEM( board.get(), wxS( "Net-(1)" ), 1 ); board->Add( n1 );
58 NETINFO_ITEM* n2 = new NETINFO_ITEM( board.get(), wxS( "Net-(2)" ), 2 ); board->Add( n2 );
59
60 n1->SetNetChain( wxS( "SIG_A" ) );
61 n2->SetNetChain( wxS( "SIG_A" ) );
62
63 // Assign pads to nets for resolver (only first pad of each footprint for simplicity)
64 p1a->SetNet( n1 );
65 p2a->SetNet( n2 );
66
67 // Assign terminal pads (simulate import heuristic outcome)
68 n1->SetTerminalPad( 0, p1a ); n1->SetTerminalPad( 1, p2a );
69 n2->SetTerminalPad( 0, p1a ); n2->SetTerminalPad( 1, p2a );
70
71 // Store original UUIDs for debugging comparison after reload
72 wxString orig_p1a = p1a->m_Uuid.AsString();
73 wxString orig_p2a = p2a->m_Uuid.AsString();
74
75 // Create simple tracks to ensure nets are serialized
76 PCB_TRACK* t1 = new PCB_TRACK( board.get() ); t1->SetNetCode( 1 ); t1->SetStart( p1a->GetPosition() ); t1->SetEnd( p2a->GetPosition() ); board->Add( t1 );
77 PCB_TRACK* t2 = new PCB_TRACK( board.get() ); t2->SetNetCode( 2 ); t2->SetStart( p1a->GetPosition() ); t2->SetEnd( p2a->GetPosition() ); board->Add( t2 );
78
79 auto tmpFile = std::filesystem::temp_directory_path() / "net_chain_terminal_pads_roundtrip.kicad_pcb";
80 plugin.SaveBoard( tmpFile.string(), board.get() );
81
82 std::unique_ptr<BOARD> loaded = std::make_unique<BOARD>();
83 plugin.LoadBoard( tmpFile.string(), loaded.get() );
84
85 NETINFO_ITEM* ln1 = loaded->FindNet( 1 );
86 NETINFO_ITEM* ln2 = loaded->FindNet( 2 );
87
88 // Ensure terminal pads resolved (parser should do this but call defensively for test stability)
89 for( NETINFO_ITEM* n : loaded->GetNetInfo() )
90 n->ResolveTerminalPads( loaded.get() );
91
92 BOOST_REQUIRE( ln1 );
93 BOOST_REQUIRE( ln2 );
94 BOOST_CHECK_EQUAL( ln1->GetNetChain(), wxS( "SIG_A" ) );
95 BOOST_CHECK_EQUAL( ln2->GetNetChain(), wxS( "SIG_A" ) );
96
97 // Both nets should have terminal pad UUIDs resolved
98 BOOST_REQUIRE( ln1->GetTerminalPad( 0 ) );
99 BOOST_REQUIRE( ln1->GetTerminalPad( 1 ) );
100 BOOST_REQUIRE( ln2->GetTerminalPad( 0 ) );
101 BOOST_REQUIRE( ln2->GetTerminalPad( 1 ) );
102
103 BOOST_TEST_MESSAGE( wxString::Format( wxS("orig p1a=%s p2a=%s"), orig_p1a, orig_p2a ) );
104 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() ) );
105 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() ) );
106
107 // UUIDs can be regenerated on load (timestamp -> UUID or duplicate resolution). Validate
108 // that both nets agree on terminal pads and that those pads exist in the loaded board.
109 PAD* termA1 = ln1->GetTerminalPad( 0 );
110 PAD* termB1 = ln1->GetTerminalPad( 1 );
111 BOOST_CHECK( termA1 == ln2->GetTerminalPad( 0 ) );
112 BOOST_CHECK( termB1 == ln2->GetTerminalPad( 1 ) );
113
114 bool foundA = false, foundB = false;
115 for( FOOTPRINT* fp : loaded->Footprints() )
116 {
117 for( PAD* pad : fp->Pads() )
118 {
119 if( pad == termA1 ) foundA = true;
120 if( pad == termB1 ) foundB = true;
121 }
122 }
123 BOOST_CHECK( foundA );
124 BOOST_CHECK( foundB );
125}
126
127BOOST_AUTO_TEST_CASE( SingleNetSignalNamePersists )
128{
129 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
130 // Single net with explicit signal name, no terminal pads
131 NETINFO_ITEM* n1 = new NETINFO_ITEM( board.get(), wxS( "Net-(X)" ), 1 ); board->Add( n1 );
132 n1->SetNetChain( wxS( "CLK_REF" ) );
133 // Add trivial track so net is serialized
134 PCB_TRACK* t = new PCB_TRACK( board.get() ); t->SetNetCode( 1 ); t->SetStart( VECTOR2I(0,0) ); t->SetEnd( VECTOR2I(1000000,0) ); board->Add( t );
135
136 auto tmpFile = std::filesystem::temp_directory_path() / "single_net_chain_roundtrip.kicad_pcb";
137 plugin.SaveBoard( tmpFile.string(), board.get() );
138
139 std::unique_ptr<BOARD> loaded = std::make_unique<BOARD>();
140 plugin.LoadBoard( tmpFile.string(), loaded.get() );
141 NETINFO_ITEM* ln1 = loaded->FindNet( 1 );
142 BOOST_REQUIRE( ln1 );
143 BOOST_CHECK_EQUAL( ln1->GetNetChain(), wxS( "CLK_REF" ) );
144}
145
146BOOST_AUTO_TEST_CASE( SignalsSurviveBuildListOfNets )
147{
148 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
149
150 // Create 4 nets
151 NETINFO_ITEM* n1 = new NETINFO_ITEM( board.get(), wxS( "Net-(R1-Pad1)" ), 1 ); board->Add( n1 );
152 NETINFO_ITEM* n2 = new NETINFO_ITEM( board.get(), wxS( "Net-(R1-Pad2)" ), 2 ); board->Add( n2 );
153 NETINFO_ITEM* n3 = new NETINFO_ITEM( board.get(), wxS( "Net-(R2-Pad2)" ), 3 ); board->Add( n3 );
154 NETINFO_ITEM* n4 = new NETINFO_ITEM( board.get(), wxS( "Net-(R3-Pad2)" ), 4 ); board->Add( n4 );
155
156 for( NETINFO_ITEM* n : { n1, n2, n3, n4 } )
157 n->SetNetChain( wxS( "Signal1" ) );
158
159 // Add trivial tracks so that each net is considered used and serialized
160 int x = 0;
161 for( int code : { 1, 2, 3, 4 } )
162 {
163 PCB_TRACK* t = new PCB_TRACK( board.get() );
164 t->SetNetCode( code );
165 t->SetStart( VECTOR2I( x, 0 ) );
166 t->SetEnd( VECTOR2I( x + 1000000, 0 ) );
167 board->Add( t );
168 x += 2000000;
169 }
170
171 auto tmpFile = std::filesystem::temp_directory_path() / "net_chains_survive_buildlist.kicad_pcb";
172 plugin.SaveBoard( tmpFile.string(), board.get() );
173
174 std::unique_ptr<BOARD> loaded = std::make_unique<BOARD>();
175 plugin.LoadBoard( tmpFile.string(), loaded.get() );
176
177 // Simulate GUI post-load rebuild that previously cleared signals
178 loaded->BuildListOfNets();
179
180 int signalCount = 0;
181 std::set<wxString> expectedNames = { wxS("Net-(R1-Pad1)"), wxS("Net-(R1-Pad2)"), wxS("Net-(R2-Pad2)"), wxS("Net-(R3-Pad2)") };
182 std::set<wxString> seenNames;
183 for( NETINFO_ITEM* net : loaded->GetNetInfo() )
184 {
185 if( net->GetNetChain() == wxS( "Signal1" ) )
186 {
187 signalCount++;
188 seenNames.insert( net->GetNetname() );
189 }
190 }
191 BOOST_CHECK_EQUAL( signalCount, 4 );
192 BOOST_CHECK_EQUAL( seenNames.size(), expectedNames.size() );
193 for( const wxString& name : expectedNames )
194 BOOST_CHECK( seenNames.count( name ) );
195}
196
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:531
void SetReference(const wxString &aReference)
Definition footprint.h:847
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:242
Handle the data for a net.
Definition netinfo.h:46
const wxString & GetNetChain() const
Definition netinfo.h:112
PAD * GetTerminalPad(int aIndex) const
Definition netinfo.h:115
void SetNetChain(const wxString &aNetChain)
Definition netinfo.h:113
void SetTerminalPad(int aIndex, PAD *aPad)
Definition netinfo.h:116
Definition pad.h:61
void SetFrontShape(PAD_SHAPE aShape)
Definition pad.cpp:1669
VECTOR2I GetPosition() const override
Definition pad.cpp:245
void SetNumber(const wxString &aNumber)
Set the pad number (note that it can be alphanumeric, such as the array reference "AA12").
Definition pad.h:142
void SetPosition(const VECTOR2I &aPos) override
Definition pad.cpp:234
void SetSize(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
Definition pad.cpp:254
A #PLUGIN derivation for saving and loading Pcbnew s-expression formatted files.
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:89
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:92
@ F_Cu
Definition layer_ids.h:60
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:683