KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_odbpp_netlist.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
31
32#include <boost/test/unit_test.hpp>
33
34#include <board.h>
36#include <footprint.h>
37#include <pad.h>
38#include <pcb_track.h>
39#include <base_units.h>
40#include <core/utf8.h>
41
43
44#include <wx/dir.h>
45#include <wx/ffile.h>
46#include <wx/filename.h>
47#include <wx/utils.h>
48#include <wx/regex.h>
49
50
51namespace
52{
53
54wxFileName MakeTempDir( const wxString& aPrefix )
55{
56 wxFileName tempDir( wxFileName::CreateTempFileName( wxString::Format( wxT( "kicad-%s-" ), aPrefix ) ) );
57
58 BOOST_REQUIRE( tempDir.FileExists() );
59 BOOST_REQUIRE( wxRemoveFile( tempDir.GetFullPath() ) );
60 BOOST_REQUIRE( tempDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
61
62 return tempDir;
63}
64
65
66// Save and restore the static ODB++ exporter formatting state so this regression
67// test does not change formatting defaults for later tests in the shared binary.
68struct ODB_EXPORT_STATE_GUARD
69{
71 m_scale( PCB_IO_ODBPP::m_scale ),
72 m_symbolScale( PCB_IO_ODBPP::m_symbolScale ),
73 m_sigfig( PCB_IO_ODBPP::m_sigfig ),
74 m_unitsStr( PCB_IO_ODBPP::m_unitsStr )
75 {
76 }
77
79 {
80 PCB_IO_ODBPP::m_scale = m_scale;
81 PCB_IO_ODBPP::m_symbolScale = m_symbolScale;
82 PCB_IO_ODBPP::m_sigfig = m_sigfig;
83 PCB_IO_ODBPP::m_unitsStr = m_unitsStr;
84 }
85
86 double m_scale;
87 double m_symbolScale;
88 int m_sigfig;
89 std::string m_unitsStr;
90};
91
92
93wxString ReadFile( const wxFileName& aPath )
94{
95 wxFFile stream( aPath.GetFullPath(), wxT( "rb" ) );
96 wxString contents;
97 BOOST_REQUIRE( stream.ReadAll( &contents ) );
98 stream.Close();
99 return contents;
100}
101
102} // anonymous namespace
103
104
105BOOST_AUTO_TEST_CASE( ODBNetlistCoordinatesMatchFeatures )
106{
107 wxFileName tempDir = MakeTempDir( wxT( "odb-netlist" ) );
108
109 ODB_EXPORT_STATE_GUARD odbExportStateGuard;
110
111 BOARD board;
112 board.SetCopperLayerCount( 2 );
113
114 // Set a non-zero aux origin to trigger the bug
115 VECTOR2I auxOrigin( pcbIUScale.mmToIU( 50.0 ), pcbIUScale.mmToIU( 30.0 ) );
116 board.GetDesignSettings().SetAuxOrigin( auxOrigin );
117
118 // Add a footprint with a pad at a known position
119 FOOTPRINT* fp = new FOOTPRINT( &board );
120 fp->SetPosition( VECTOR2I( pcbIUScale.mmToIU( 100.0 ), pcbIUScale.mmToIU( 80.0 ) ) );
121 fp->SetReference( wxT( "U1" ) );
122
123 PAD* pad = new PAD( fp );
124 pad->SetPadstackMode( PADSTACK::MODE::NORMAL );
125 pad->SetAttribute( PAD_ATTRIB::SMD );
127 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 0.5 ) ) );
128 pad->SetPosition( VECTOR2I( pcbIUScale.mmToIU( 100.0 ), pcbIUScale.mmToIU( 80.0 ) ) );
129 pad->SetLayerSet( LSET( { F_Cu, F_Mask, F_Paste } ) );
130 pad->SetNumber( wxT( "1" ) );
131
132 NETINFO_ITEM* net1 = new NETINFO_ITEM( &board, wxT( "TestNet" ), 1 );
133 board.Add( net1 );
134 pad->SetNet( net1 );
135
136 fp->Add( pad );
137 board.Add( fp );
138
139 // Add a via at a known position
140 PCB_VIA* via = new PCB_VIA( &board );
141 via->SetPadstackMode( PADSTACK::MODE::NORMAL );
142 via->SetPosition( VECTOR2I( pcbIUScale.mmToIU( 120.0 ), pcbIUScale.mmToIU( 60.0 ) ) );
143 via->SetLayerPair( F_Cu, B_Cu );
144 via->SetDrill( pcbIUScale.mmToIU( 0.3 ) );
145 via->SetWidth( PADSTACK::ALL_LAYERS, pcbIUScale.mmToIU( 0.6 ) );
146 via->SetNet( net1 );
147 board.Add( via );
148
149 // Export ODB++
150 wxFileName odbRoot( tempDir.GetFullPath(), wxEmptyString );
151 odbRoot.AppendDir( wxT( "odb_out" ) );
152 BOOST_REQUIRE( odbRoot.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
153
154 PCB_IO_ODBPP odbExporter;
155 std::map<std::string, UTF8> props;
156 props["units"] = "mm";
157 props["sigfig"] = "6";
158 BOOST_REQUIRE_NO_THROW( odbExporter.SaveBoard( odbRoot.GetFullPath(), board, &props ) );
159
160 // Read the netlist file
161 wxFileName netlistFile( odbRoot.GetFullPath(), wxEmptyString );
162 netlistFile.AppendDir( wxT( "steps" ) );
163 netlistFile.AppendDir( wxT( "pcb" ) );
164 netlistFile.AppendDir( wxT( "netlists" ) );
165 netlistFile.AppendDir( wxT( "cadnet" ) );
166 netlistFile.SetFullName( wxT( "netlist" ) );
167 BOOST_REQUIRE( netlistFile.FileExists() );
168
169 wxString netlistContent = ReadFile( netlistFile );
170
171 // Read a features file that has the toeprint/component position to compare
172 wxFileName compFile( odbRoot.GetFullPath(), wxEmptyString );
173 compFile.AppendDir( wxT( "steps" ) );
174 compFile.AppendDir( wxT( "pcb" ) );
175 compFile.AppendDir( wxT( "layers" ) );
176 compFile.AppendDir( wxT( "comp_+_top" ) );
177 compFile.SetFullName( wxT( "components" ) );
178 BOOST_REQUIRE( compFile.FileExists() );
179
180 wxString compContent = ReadFile( compFile );
181
182 // The pad is at board position (100, 80) mm.
183 // ODB::AddXY applies m_scale and negates Y, giving (100.0, -80.0) in mm.
184 // The toeprint (TOP record) should have x=100.0, y=-80.0 if in mm.
185 // The netlist net-point should have the same coordinates.
186
187 // Verify the toeprint has the pad position (component file uses ODB::AddXY)
188 BOOST_CHECK_MESSAGE( compContent.Contains( wxT( "100" ) ),
189 "Component file should contain the pad X coordinate" );
190
191 // The critical check: the netlist should NOT be offset by the aux origin.
192 // With the bug, x would be (100 - 50) = 50 and y would be (30 - 80) = -50 in mm.
193 // With the fix, x should be 100 and y should be -80 in mm.
194 // We look for "100" and "-80" appearing in the netlist net-point data.
195 BOOST_CHECK_MESSAGE( !netlistContent.Contains( wxT( "50.0" ) ),
196 "Netlist should not contain aux-origin-shifted X coordinate 50.0" );
197 BOOST_CHECK_MESSAGE( netlistContent.Contains( wxT( "100.0" ) ),
198 "Netlist should contain the raw pad X coordinate 100.0" );
199
200 // Clean up
201 wxFileName::Rmdir( odbRoot.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
202 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
203}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
void SetAuxOrigin(const VECTOR2I &aOrigin)
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 SetCopperLayerCount(int aCount)
Definition board.cpp:1137
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1299
void SetPosition(const VECTOR2I &aPos) override
void SetReference(const wxString &aReference)
Definition footprint.h:907
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
Handle the data for a net.
Definition netinfo.h:50
@ 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
static int m_sigfig
static double m_symbolScale
Internal units to thousandths of the output unit, the scale ODB++ symbol names use.
void SaveBoard(const wxString &aFileName, BOARD &aBoard, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aBoard to a storage file in a format that this PCB_IO implementation knows about or it can be u...
static std::string m_unitsStr
static double m_scale
Internal units to the output units named by m_unitsStr, for values written as data.
@ F_Paste
Definition layer_ids.h:100
@ B_Cu
Definition layer_ids.h:61
@ F_Mask
Definition layer_ids.h:93
@ F_Cu
Definition layer_ids.h:60
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:98
@ RECTANGLE
Definition padstack.h:53
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_CASE(ODBNetlistCoordinatesMatchFeatures)
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683