KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_symbol_library.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 * @author Jon Evans <[email protected]>
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <fmt/format.h>
22#include <mock_pgm_base.h>
24#include <pgm_base.h>
25#include "eeschema_test_utils.h"
26
28
29// NOTE: this is for the new symbol library manager adapter and
30// library manager system. There is also test_symbol_library_manager.cpp
31// which is for the old code; that one can be removed once the old
32// library manager doesn't exist anymore
33
34
38
39BOOST_FIXTURE_TEST_SUITE( SymbolLibrary, TEST_SYMBOL_LIBRARY_FIXTURE )
40
41BOOST_AUTO_TEST_CASE( ProjectLibraryTable )
42{
43 if( !wxGetEnv( wxT( "KICAD_CONFIG_HOME_IS_QA" ), nullptr ) )
44 {
45 BOOST_TEST_MESSAGE( "QA test is running using unknown config home; skipping" );
46 return;
47 }
48
49 LIBRARY_MANAGER manager;
50 manager.LoadGlobalTables();
51
52 wxFileName fn( KI_TEST::GetTestDataRootDir(), "test_project.kicad_sch" );
53 fn.AppendDir( "libraries" );
54 fn.AppendDir( "test_project" );
55
56 std::vector<LIBRARY_TABLE_ROW*> rows = manager.Rows( LIBRARY_TABLE_TYPE::SYMBOL );
57
58 BOOST_REQUIRE( rows.size() == 3 );
59 BOOST_REQUIRE( rows[0]->Nickname() == "Device" );
60
61 LoadSchematic( fn.GetFullPath() );
62 PROJECT& project = SettingsManager().Prj();
63 manager.LoadProjectTables( project.GetProjectDirectory() );
64
65 rows = manager.Rows( LIBRARY_TABLE_TYPE::SYMBOL );
66
67 BOOST_REQUIRE( rows.size() == 4 );
68 BOOST_REQUIRE( rows[0]->Nickname() == "Device" );
69 BOOST_REQUIRE( rows[0]->URI() == "${KIPRJMOD}/Device.kicad_sym" );
70
71 SYMBOL_LIBRARY_ADAPTER adapter( manager );
72 adapter.LoadOne( "Device" );
73
74 std::vector<LIB_SYMBOL*> symbols = adapter.GetSymbols( "Device" );
75
76 // Check that the project's copy of Device is the one being returned
77 BOOST_REQUIRE( symbols.size() == 3 );
78
79 symbols = adapter.GetSymbols( "power" );
80 BOOST_TEST_MESSAGE( symbols.size() );
81
82 BOOST_REQUIRE( adapter.LoadSymbol( "Device", "R" ) );
83}
84
85
86// If you want to do benchmarking, it can be useful to run this testcase standalone with
87// the KICAD_CONFIG_HOME and KICAD9_SYMBOL_DIR environment variables set to an actual KiCad
88// installation so that you can benchmark a typical load
90{
91 LIBRARY_MANAGER manager;
92 manager.LoadGlobalTables();
93
95 SYMBOL_LIBRARY_ADAPTER adapter( manager );
96
97 auto tstart = std::chrono::high_resolution_clock::now();
98 adapter.AsyncLoad();
99
100 constexpr static int interval = 250;
101 constexpr static int timeLimit = 20000;
102 int elapsed = 0;
103
104 while( true )
105 {
106 std::this_thread::sleep_for( std::chrono::milliseconds( interval ) );
107
108 if( std::optional<float> loadStatus = adapter.AsyncLoadProgress(); loadStatus.has_value() )
109 {
110 BOOST_TEST_MESSAGE( fmt::format( "Loading libraries: ({:.1f}%)", *loadStatus * 100 ) );
111
112 if( loadStatus >= 1 )
113 break;
114 }
115 else
116 {
117 // Just informational; this could be fine when running in a QA context
118 BOOST_TEST_MESSAGE( "Async load not in progress" );
119 break;
120 }
121
122 elapsed += interval;
123
124 if( elapsed > timeLimit )
125 {
126 BOOST_TEST_FAIL( "Exceeded timeout" );
127 break;
128 }
129 }
130
131 auto duration = std::chrono::high_resolution_clock::now() - tstart;
132
134 fmt::format( "took {}ms",
135 std::chrono::duration_cast<std::chrono::milliseconds>( duration ).count() ) );
136
137 adapter.BlockUntilLoaded();
138
139 std::vector<LIBRARY_TABLE_ROW*> rows = manager.Rows( LIBRARY_TABLE_TYPE::SYMBOL );
140
141 BOOST_REQUIRE_GE( rows.size(), 2 );
142
143 for( auto& [nickname, status] : adapter.GetLibraryStatuses() )
144 {
145 wxString msg = nickname;
146 BOOST_REQUIRE( status.load_status != LOAD_STATUS::LOADING );
147
148 switch( status.load_status )
149 {
150 default:
152 msg << ": loaded OK";
153 BOOST_REQUIRE( !status.error.has_value() );
154 break;
155
157 BOOST_REQUIRE( status.error.has_value() );
158 msg << ": error: " << status.error->message;
159 break;
160 }
161
162 BOOST_TEST_MESSAGE( msg );
163 }
164}
165
166// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/23480
167// Verifies that reloading project tables during an async library load does not
168// crash due to dangling LIBRARY_TABLE_ROW pointers held by background workers.
169BOOST_AUTO_TEST_CASE( ProjectChangeDuringAsyncLoad )
170{
171 if( !wxGetEnv( wxT( "KICAD_CONFIG_HOME_IS_QA" ), nullptr ) )
172 {
173 BOOST_TEST_MESSAGE( "QA test is running using unknown config home; skipping" );
174 return;
175 }
176
177 LIBRARY_MANAGER manager;
178 manager.LoadGlobalTables();
179
180 wxFileName fn( KI_TEST::GetTestDataRootDir(), "test_project.kicad_sch" );
181 fn.AppendDir( "libraries" );
182 fn.AppendDir( "test_project" );
183
184 LoadSchematic( fn.GetFullPath() );
185 PROJECT& project = SettingsManager().Prj();
186 manager.LoadProjectTables( project.GetProjectDirectory() );
187
189 std::make_unique<SYMBOL_LIBRARY_ADAPTER>( manager ) );
190
191 auto adapterOpt = manager.Adapter( LIBRARY_TABLE_TYPE::SYMBOL );
192 BOOST_REQUIRE( adapterOpt.has_value() );
193 SYMBOL_LIBRARY_ADAPTER* adapter = static_cast<SYMBOL_LIBRARY_ADAPTER*>( *adapterOpt );
194
195 adapter->AsyncLoad();
196
197 // Immediately trigger a project table reload while async workers are running.
198 // Before the fix, this destroyed table rows that workers were still using.
199 manager.ProjectChanged();
200
201 adapter->BlockUntilLoaded();
202
203 BOOST_TEST_MESSAGE( "ProjectChanged during async load completed without crash" );
204}
205
206
A generic fixture for loading schematics and associated settings for qa tests.
std::optional< float > AsyncLoadProgress() const
Returns async load progress between 0.0 and 1.0, or nullopt if load is not in progress.
void AsyncLoad()
Loads all available libraries for this adapter type in the background.
std::vector< std::pair< wxString, LIB_STATUS > > GetLibraryStatuses() const
Returns a list of all library nicknames and their status (even if they failed to load)
std::optional< LIBRARY_MANAGER_ADAPTER * > Adapter(LIBRARY_TABLE_TYPE aType) const
void RegisterAdapter(LIBRARY_TABLE_TYPE aType, std::unique_ptr< LIBRARY_MANAGER_ADAPTER > &&aAdapter)
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
std::vector< LIBRARY_TABLE_ROW * > Rows(LIBRARY_TABLE_TYPE aType, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH, bool aIncludeInvalid=false) const
Returns a flattened list of libraries of the given type.
void LoadGlobalTables(std::initializer_list< LIBRARY_TABLE_TYPE > aTablesToLoad={})
(Re)loads the global library tables in the given list, or all tables if no list is given
void ProjectChanged()
Notify all adapters that the project has changed.
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:130
Container for project specific data.
Definition project.h:66
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
An interface to the global shared library manager that is schematic-specific and linked to one projec...
std::optional< LIB_STATUS > LoadOne(LIB_DATA *aLib) override
Loads or reloads the given library, if it exists.
LIB_SYMBOL * LoadSymbol(const wxString &aNickname, const wxString &aName)
Load a LIB_SYMBOL having aName from the library given by aNickname.
std::vector< LIB_SYMBOL * > GetSymbols(const wxString &aNickname, SYMBOL_TYPE aType=SYMBOL_TYPE::ALL_SYMBOLS)
std::string GetTestDataRootDir()
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
static void LoadSchematic(SCHEMATIC *aSchematic, SCH_SHEET *aRootSheet, const wxString &aFileName)
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_TEST_MESSAGE("\n=== Real-World Polygon PIP Benchmark ===\n"<< formatTable(table))
BOOST_AUTO_TEST_CASE(ProjectLibraryTable)