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(
57 wxFileName::CreateTempFileName( wxString::Format( wxT( "kicad-%s-" ), aPrefix ) ) );
58
59 BOOST_REQUIRE( tempDir.FileExists() );
60 BOOST_REQUIRE( wxRemoveFile( tempDir.GetFullPath() ) );
61 BOOST_REQUIRE( tempDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
62
63 return tempDir;
64}
65
66
67// Save and restore the static ODB++ exporter formatting state so this regression
68// test does not change formatting defaults for later tests in the shared binary.
69struct ODB_EXPORT_STATE_GUARD
70{
71 ODB_EXPORT_STATE_GUARD() :
72 m_scale( PCB_IO_ODBPP::m_scale ),
73 m_symbolScale( PCB_IO_ODBPP::m_symbolScale ),
74 m_sigfig( PCB_IO_ODBPP::m_sigfig ),
75 m_unitsStr( PCB_IO_ODBPP::m_unitsStr )
76 {
77 }
78
79 ~ODB_EXPORT_STATE_GUARD()
80 {
81 PCB_IO_ODBPP::m_scale = m_scale;
82 PCB_IO_ODBPP::m_symbolScale = m_symbolScale;
83 PCB_IO_ODBPP::m_sigfig = m_sigfig;
84 PCB_IO_ODBPP::m_unitsStr = m_unitsStr;
85 }
86
87 double m_scale;
88 double m_symbolScale;
89 int m_sigfig;
90 std::string m_unitsStr;
91};
92
93
94wxString ReadFile( const wxFileName& aPath )
95{
96 wxFFile stream( aPath.GetFullPath(), wxT( "rb" ) );
97 wxString contents;
98 BOOST_REQUIRE( stream.ReadAll( &contents ) );
99 stream.Close();
100 return contents;
101}
102
103} // anonymous namespace
104
105
106BOOST_AUTO_TEST_CASE( ODBNetlistCoordinatesMatchFeatures )
107{
108 wxFileName tempDir = MakeTempDir( wxT( "odb-netlist" ) );
109
110 ODB_EXPORT_STATE_GUARD odbExportStateGuard;
111
112 BOARD board;
113 board.SetCopperLayerCount( 2 );
114
115 // Set a non-zero aux origin to trigger the bug
116 VECTOR2I auxOrigin( pcbIUScale.mmToIU( 50.0 ), pcbIUScale.mmToIU( 30.0 ) );
117 board.GetDesignSettings().SetAuxOrigin( auxOrigin );
118
119 // Add a footprint with a pad at a known position
120 FOOTPRINT* fp = new FOOTPRINT( &board );
121 fp->SetPosition( VECTOR2I( pcbIUScale.mmToIU( 100.0 ), pcbIUScale.mmToIU( 80.0 ) ) );
122 fp->SetReference( wxT( "U1" ) );
123
124 PAD* pad = new PAD( fp );
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->SetPosition( VECTOR2I( pcbIUScale.mmToIU( 120.0 ), pcbIUScale.mmToIU( 60.0 ) ) );
142 via->SetLayerPair( F_Cu, B_Cu );
143 via->SetDrill( pcbIUScale.mmToIU( 0.3 ) );
144 via->SetWidth( pcbIUScale.mmToIU( 0.6 ) );
145 via->SetNet( net1 );
146 board.Add( via );
147
148 // Export ODB++
149 wxFileName odbRoot( tempDir.GetFullPath(), wxEmptyString );
150 odbRoot.AppendDir( wxT( "odb_out" ) );
151 BOOST_REQUIRE( odbRoot.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
152
153 PCB_IO_ODBPP odbExporter;
154 std::map<std::string, UTF8> props;
155 props["units"] = "mm";
156 props["sigfig"] = "6";
157 BOOST_REQUIRE_NO_THROW( odbExporter.SaveBoard( odbRoot.GetFullPath(), &board, &props ) );
158
159 // Read the netlist file
160 wxFileName netlistFile( odbRoot.GetFullPath(), wxEmptyString );
161 netlistFile.AppendDir( wxT( "steps" ) );
162 netlistFile.AppendDir( wxT( "pcb" ) );
163 netlistFile.AppendDir( wxT( "netlists" ) );
164 netlistFile.AppendDir( wxT( "cadnet" ) );
165 netlistFile.SetFullName( wxT( "netlist" ) );
166 BOOST_REQUIRE( netlistFile.FileExists() );
167
168 wxString netlistContent = ReadFile( netlistFile );
169
170 // Read a features file that has the toeprint/component position to compare
171 wxFileName compFile( odbRoot.GetFullPath(), wxEmptyString );
172 compFile.AppendDir( wxT( "steps" ) );
173 compFile.AppendDir( wxT( "pcb" ) );
174 compFile.AppendDir( wxT( "layers" ) );
175 compFile.AppendDir( wxT( "comp_+_top" ) );
176 compFile.SetFullName( wxT( "components" ) );
177 BOOST_REQUIRE( compFile.FileExists() );
178
179 wxString compContent = ReadFile( compFile );
180
181 // The pad is at board position (100, 80) mm.
182 // ODB::AddXY applies m_scale and negates Y, giving (100.0, -80.0) in mm.
183 // The toeprint (TOP record) should have x=100.0, y=-80.0 if in mm.
184 // The netlist net-point should have the same coordinates.
185
186 // Verify the toeprint has the pad position (component file uses ODB::AddXY)
187 BOOST_CHECK_MESSAGE( compContent.Contains( wxT( "100" ) ),
188 "Component file should contain the pad X coordinate" );
189
190 // The critical check: the netlist should NOT be offset by the aux origin.
191 // With the bug, x would be (100 - 50) = 50 and y would be (30 - 80) = -50 in mm.
192 // With the fix, x should be 100 and y should be -80 in mm.
193 // We look for "100" and "-80" appearing in the netlist net-point data.
194 BOOST_CHECK_MESSAGE( !netlistContent.Contains( wxT( "50.0" ) ),
195 "Netlist should not contain aux-origin-shifted X coordinate 50.0" );
196 BOOST_CHECK_MESSAGE( netlistContent.Contains( wxT( "100.0" ) ),
197 "Netlist should contain the raw pad X coordinate 100.0" );
198
199 // Clean up
200 wxFileName::Rmdir( odbRoot.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
201 wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
202}
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: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 SetCopperLayerCount(int aCount)
Definition board.cpp:991
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1149
void SetPosition(const VECTOR2I &aPos) override
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.
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
Handle the data for a net.
Definition netinfo.h:46
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
static int m_sigfig
static double m_symbolScale
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
@ 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:99
@ RECTANGLE
Definition padstack.h:54
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_CASE(ODBNetlistCoordinatesMatchFeatures)
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683