KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_footprint_import_reconciler.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, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <memory>
21#include <set>
22#include <vector>
23
26
27#include <wx/dir.h>
28#include <wx/filename.h>
29#include <wx/stdpaths.h>
30
31#include <board.h>
32#include <footprint.h>
33#include <lib_id.h>
34#include <pgm_base.h>
35#include <project.h>
36#include <project_pcb.h>
37#include <reporter.h>
46#include <tool/tool_manager.h>
47
49
50
51namespace
52{
54wxString stageProject( const wxString& aStem )
55{
56 wxString sep = wxFileName::GetPathSeparator();
57 wxString dir = wxStandardPaths::Get().GetTempDir() + sep + aStem + wxT( "-fpreconcile-qa" );
58
59 if( wxDirExists( dir ) )
60 wxFileName::Rmdir( dir, wxPATH_RMDIR_RECURSIVE );
61
62 wxFileName::Mkdir( dir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
63
64 wxString projectPath = dir + sep + aStem + wxT( ".kicad_pro" );
65
66 Pgm().GetSettingsManager().LoadProject( projectPath );
68 Pgm().GetSettingsManager().Prj().GetProjectDirectory() );
69
71}
72} // namespace
73
74
75BOOST_AUTO_TEST_SUITE( FootprintImportReconciler )
76
77
78// Eagle board reconciles to a generated cache: .pretty published, row added, every FPID resolves
79// fails on revert, since Eagle FPIDs keep empty nicknames
80BOOST_AUTO_TEST_CASE( EagleBoardResolvesToGeneratedCache )
81{
82 wxString projectPath = stageProject( wxS( "eagle_fpreconcile" ) );
84
85 wxFileName brdFn( KI_TEST::GetEeschemaTestDataDir() );
86 brdFn.AppendDir( wxS( "io" ) );
87 brdFn.AppendDir( wxS( "eagle" ) );
88 brdFn.SetFullName( wxS( "eagle-import-testfile.brd" ) );
89 BOOST_REQUIRE_MESSAGE( brdFn.FileExists(), "Missing Eagle board fixture" );
90
91 PCB_IO_EAGLE plugin;
92 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
93 board->SetProject( &project );
94 plugin.LoadBoard( brdFn.GetFullPath(), board.get(), nullptr, &project );
95
96 BOOST_REQUIRE_GT( board->Footprints().size(), 0 );
97
98 std::vector<FOOTPRINT*> raw = plugin.GetImportedCachedLibraryFootprints();
99 std::vector<std::unique_ptr<FOOTPRINT>> defs;
100
101 for( FOOTPRINT* fp : raw )
102 defs.emplace_back( fp );
103
105 BOOST_REQUIRE( adapter );
106
107 const wxString cacheNick = wxS( "eagle_test-import-fps" );
108 FOOTPRINT_IMPORT_RECONCILER reconciler( *adapter, project.GetProjectPath() );
109
111 reconciler.Reconcile( board.get(), std::move( defs ), cacheNick, {} );
112
113 // cache library published
114 BOOST_CHECK_EQUAL( result.m_cacheNickname, cacheNick );
115 BOOST_CHECK_GT( result.m_savedToCache, 0 );
116
117 wxFileName prettyDir( project.GetProjectPath(), cacheNick,
119 BOOST_CHECK_MESSAGE( wxDir::Exists( prettyDir.GetFullPath() ),
120 "Generated .pretty was not published" );
121
122 // project fp-lib table gained the row
123 LIBRARY_TABLE* projectTable = adapter->ProjectTable().value_or( nullptr );
124 BOOST_REQUIRE( projectTable );
125 BOOST_CHECK( projectTable->HasRow( cacheNick ) );
126
127 // every board FPID resolves via the adapter
128 int resolved = 0;
129
130 for( FOOTPRINT* fp : board->Footprints() )
131 {
132 wxString name = fp->GetFPID().GetUniStringLibItemName();
133
134 if( name.IsEmpty() )
135 continue;
136
137 wxString nick = fp->GetFPID().GetUniStringLibNickname();
138
139 BOOST_CHECK_MESSAGE( !nick.IsEmpty(),
140 wxString::Format( "Board footprint '%s' left with empty nickname",
141 name ) );
142 BOOST_CHECK_MESSAGE( adapter->FootprintExists( nick, name ),
143 wxString::Format( "FPID '%s:%s' does not resolve after reconciliation",
144 nick, name ) );
145 resolved++;
146 }
147
148 BOOST_CHECK_GT( resolved, 0 );
149 BOOST_CHECK_EQUAL( result.m_unresolved, 0 );
150}
151
152
153// Altium footprints carry a source-.PcbLib nick not registered here, so all fall back to the cache
154// and resolve through the adapter
155BOOST_AUTO_TEST_CASE( AltiumBoardResolvesToGeneratedCache )
156{
157 stageProject( wxS( "altium_fpreconcile" ) );
159
160 std::string dataPath =
161 KI_TEST::GetPcbnewTestDataDir() + "plugins/altium/HiFive/HiFive1.B01.PcbDoc";
162
164 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
165 board->SetProject( &project );
166 plugin.LoadBoard( dataPath, board.get(), nullptr, &project );
167
168 BOOST_REQUIRE_GT( board->Footprints().size(), 0 );
169
170 std::vector<FOOTPRINT*> raw = plugin.GetImportedCachedLibraryFootprints();
171 std::vector<std::unique_ptr<FOOTPRINT>> defs;
172
173 for( FOOTPRINT* fp : raw )
174 defs.emplace_back( fp );
175
177 BOOST_REQUIRE( adapter );
178
179 const wxString cacheNick = wxS( "hifive-import-fps" );
180 FOOTPRINT_IMPORT_RECONCILER reconciler( *adapter, project.GetProjectPath() );
181
183 reconciler.Reconcile( board.get(), std::move( defs ), cacheNick, {} );
184
185 BOOST_CHECK_EQUAL( result.m_cacheNickname, cacheNick );
186 BOOST_CHECK_EQUAL( result.m_unresolved, 0 );
187
188 int resolved = 0;
189
190 for( FOOTPRINT* fp : board->Footprints() )
191 {
192 wxString name = fp->GetFPID().GetUniStringLibItemName();
193
194 if( name.IsEmpty() )
195 continue;
196
197 wxString nick = fp->GetFPID().GetUniStringLibNickname();
198
199 BOOST_CHECK_MESSAGE( adapter->FootprintExists( nick, name ),
200 wxString::Format( "FPID '%s:%s' does not resolve after reconciliation",
201 nick, name ) );
202 resolved++;
203 }
204
205 BOOST_CHECK_GT( resolved, 0 );
206}
207
208
209// regression gate for a netlist of reconciled FPIDs applying via BOARD_NETLIST_UPDATER with 0 errors
210// and no not-found, over the same adapter path Update PCB from Schematic uses
211BOOST_AUTO_TEST_CASE( ReconciledFootprintsResolveViaNetlistUpdater )
212{
213 stageProject( wxS( "eagle_netlist_roundtrip" ) );
215
216 wxFileName brdFn( KI_TEST::GetEeschemaTestDataDir() );
217 brdFn.AppendDir( wxS( "io" ) );
218 brdFn.AppendDir( wxS( "eagle" ) );
219 brdFn.SetFullName( wxS( "eagle-import-testfile.brd" ) );
220 BOOST_REQUIRE( brdFn.FileExists() );
221
222 PCB_IO_EAGLE plugin;
223 std::unique_ptr<BOARD> imported = std::make_unique<BOARD>();
224 imported->SetProject( &project );
225 plugin.LoadBoard( brdFn.GetFullPath(), imported.get(), nullptr, &project );
226
227 std::vector<FOOTPRINT*> raw = plugin.GetImportedCachedLibraryFootprints();
228 std::vector<std::unique_ptr<FOOTPRINT>> defs;
229
230 for( FOOTPRINT* fp : raw )
231 defs.emplace_back( fp );
232
234 BOOST_REQUIRE( adapter );
235
236 const wxString cacheNick = wxS( "eagle_test-import-fps" );
237 FOOTPRINT_IMPORT_RECONCILER reconciler( *adapter, project.GetProjectPath() );
238 reconciler.Reconcile( imported.get(), std::move( defs ), cacheNick, {} );
239
240 // netlist of distinct reconciled FPIDs, one fresh component each
242 std::set<wxString> seen;
243 int expectedComponents = 0;
244
245 for( FOOTPRINT* fp : imported->Footprints() )
246 {
247 LIB_ID fpid = fp->GetFPID();
248
249 if( fpid.GetUniStringLibItemName().IsEmpty() || !seen.insert( fpid.GetUniStringLibId() ).second )
250 continue;
251
252 wxString ref = wxString::Format( wxS( "U%d" ), ++expectedComponents );
253 netlist.AddComponent(
254 new COMPONENT( fpid, ref, ref, KIID_PATH(), std::vector<KIID>{ KIID() } ) );
255 }
256
257 BOOST_REQUIRE_GT( expectedComponents, 0 );
258
259 // updater must load each footprint from the reconciled lib onto a fresh board
260 std::unique_ptr<BOARD> target = std::make_unique<BOARD>();
261 target->SetProject( &project );
262
263 TOOL_MANAGER toolMgr;
264 toolMgr.SetEnvironment( target.get(), nullptr, nullptr, nullptr, nullptr );
265 toolMgr.RegisterTool( new KI_TEST::DUMMY_TOOL() );
266
268 BOARD_NETLIST_UPDATER updater( &toolMgr, target.get() );
269 updater.SetReporter( &reporter );
270 updater.SetReplaceFootprints( false );
271 updater.SetDeleteUnusedFootprints( false );
272
273 BOOST_REQUIRE( updater.UpdateNetlist( netlist ) );
274
275 BOOST_CHECK_EQUAL( updater.GetErrorCount(), 0 );
276 BOOST_CHECK_MESSAGE( !reporter.GetMessages().Lower().Contains( wxS( "not found" ) ),
277 "Netlist updater reported a footprint not found after reconciliation" );
278 BOOST_CHECK_EQUAL( static_cast<int>( target->Footprints().size() ), expectedComponents );
279}
280
281
const char * name
Update the BOARD with a new netlist.
void SetReporter(REPORTER *aReporter)
Enable dry run mode (just report, no changes to PCB).
bool UpdateNetlist(NETLIST &aNetlist)
Update the board's components according to the new netlist.
void SetDeleteUnusedFootprints(bool aEnabled)
void SetReplaceFootprints(bool aEnabled)
Frame-independent, non-interactive service that reconciles the footprint-library references of a fres...
FOOTPRINT_IMPORT_RECONCILE_RESULT Reconcile(BOARD *aBoard, std::vector< std::unique_ptr< FOOTPRINT > > aDefinitions, const wxString &aCacheNickname, const std::vector< wxString > &aSourceLibNicknames)
Reconcile aBoard against the importer definitions and the provenance source libraries.
An interface to the global shared library manager that is schematic-specific and linked to one projec...
bool FootprintExists(const wxString &aNickname, const wxString &aName)
Definition kiid.h:44
std::optional< LIBRARY_TABLE * > ProjectTable() const
Retrieves the project library table for this adapter type, or nullopt if one doesn't exist.
void LoadProjectTables(std::initializer_list< LIBRARY_TABLE_TYPE > aTablesToLoad={})
(Re)loads the project library tables in the given list, or all tables if no list is given
bool HasRow(const wxString &aNickname) const
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
wxString GetUniStringLibId() const
Definition lib_id.h:144
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Definition lib_id.h:108
Store information read from a netlist along with the flags used to update the NETLIST in the BOARD.
std::vector< FOOTPRINT * > GetImportedCachedLibraryFootprints() override
Return a container with the cached library footprints generated in the last call to Load.
BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties, PROJECT *aProject=nullptr) override
Load information from some input file format that this PCB_IO implementation knows about into either ...
Works with Eagle 6.x XML board files and footprints to implement the Pcbnew #PLUGIN API or a portion ...
std::vector< FOOTPRINT * > GetImportedCachedLibraryFootprints() override
Return a container with the cached library footprints generated in the last call to Load.
BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr) override
Load information from some input file format that this PCB_IO implementation knows about into either ...
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:124
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:126
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
Container for project specific data.
Definition project.h:63
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:183
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
Master controller class:
void RegisterTool(TOOL_BASE *aTool)
Add a tool to the manager set and sets it up.
void SetEnvironment(EDA_ITEM *aModel, KIGFX::VIEW *aView, KIGFX::VIEW_CONTROLS *aViewControls, APP_SETTINGS_BASE *aSettings, TOOLS_HOLDER *aFrame)
Set the work environment (model, view, view controls and the parent window).
A wrapper for reporting to a wxString object.
Definition reporter.h:225
static const std::string KiCadFootprintLibPathExtension
PROJECT & Prj()
Definition kicad.cpp:728
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
Outcome of a post-import footprint-library reconciliation pass.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_CASE(EagleBoardResolvesToGeneratedCache)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
std::string netlist
IbisParser parser & reporter
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")