KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_library_tables.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 <filesystem>
22#include <fstream>
23
24#include <mock_pgm_base.h>
25#include <richio.h>
29#include <pegtl/contrib/analyze.hpp>
30
35
36
37BOOST_AUTO_TEST_SUITE( LibraryTables )
38
39
41{
42 BOOST_REQUIRE( tao::pegtl::analyze< LIBRARY_TABLE_GRAMMAR::LIB_TABLE_FILE >( 1 ) == 0 );
43}
44
45
47{
49 tl::expected<LIBRARY_TABLE_IR, LIBRARY_PARSE_ERROR> result = parser.ParseBuffer( "" );
50 BOOST_REQUIRE( !result.has_value() );
51}
52
53
54BOOST_AUTO_TEST_CASE( ParseFromFile )
55{
56 std::vector<std::string> cases = {
57 "sym-lib-table",
58 "fp-lib-table"
59 };
60
61 std::filesystem::path p( KI_TEST::GetTestDataRootDir() );
62 p.append( "libraries/" );
63
65
66 for( const std::string& path : cases )
67 {
68 p.remove_filename();
69 p.append( path );
70
71 auto result = parser.Parse( p );
72
73 BOOST_REQUIRE( result.has_value() );
74 }
75}
76
77
78BOOST_AUTO_TEST_CASE( ParseAndConstruct )
79{
80 struct TESTCASE
81 {
82 wxString filename;
83 wxString expected_error;
84 size_t expected_rows;
85 bool check_formatted = true;
86 };
87
88 std::vector<TESTCASE> cases = {
89 { .filename = "sym-lib-table", .expected_rows = 224 },
90 { .filename = "fp-lib-table", .expected_rows = 146 },
91 { .filename = "nested-symbols", .expected_rows = 6 },
92 { .filename = "nested-disabled", .expected_rows = 4 },
93 { .filename = "nested-hidden", .expected_rows = 4 },
94 { .filename = "cycle", .expected_rows = 2 },
95 { .filename = "sym-hand-edited", .expected_rows = 2, .check_formatted = false },
96 { .filename = "corrupted", .expected_error = "Syntax error at line 6, column 9" },
97 { .filename = "truncated", .expected_error = "Syntax error at line 11, column 1" }
98 };
99
100 wxFileName fn( KI_TEST::GetTestDataRootDir(), wxEmptyString );
101 fn.AppendDir( "libraries" );
102
103 for( const auto& [filename, expected_error, expected_rows, check_formatted] : cases )
104 {
105 BOOST_TEST_CONTEXT( filename )
106 {
107 fn.SetName( filename );
109
110 BOOST_REQUIRE( table.IsOk() == ( expected_error.IsEmpty() ) );
111
112 BOOST_REQUIRE_MESSAGE( table.Rows().size() == expected_rows,
113 wxString::Format( "Expected %zu rows but got %zu",
114 expected_rows, table.Rows().size() ) );
115
116 BOOST_REQUIRE_MESSAGE( table.ErrorDescription() == expected_error,
117 wxString::Format( "Expected error '%s' but got '%s'",
118 expected_error, table.ErrorDescription() ) );
119
120 // Non-parsed tables can't be formatted
121 if( !table.IsOk() || !check_formatted )
122 continue;
123
124 std::ifstream inFp;
125 inFp.open( fn.GetFullPath().fn_str() );
126 BOOST_REQUIRE( inFp.is_open() );
127
128 std::stringstream inBuf;
129 inBuf << inFp.rdbuf();
130 std::string inData = inBuf.str();
131
132 STRING_FORMATTER formatter;
133 table.Format( &formatter );
135 KICAD_FORMAT::FORMAT_MODE::LIBRARY_TABLE );
136
137 if( formatter.GetString().compare( inData ) != 0 )
138 {
139 BOOST_TEST_MESSAGE( "--- original ---" );
140 BOOST_TEST_MESSAGE( inData );
141 BOOST_TEST_MESSAGE( "--- formatted ---" );
142 BOOST_TEST_MESSAGE( formatter.GetString() );
143 }
144
145 BOOST_REQUIRE( formatter.GetString().compare( inData ) == 0 );
146 }
147 }
148}
149
151{
152 LIBRARY_MANAGER manager;
153 manager.LoadGlobalTables();
154
155 BOOST_REQUIRE( manager.Rows( LIBRARY_TABLE_TYPE::SYMBOL ).size() == 3 );
156 BOOST_REQUIRE( manager.Rows( LIBRARY_TABLE_TYPE::FOOTPRINT ).size() == 146 );
157}
158
159
160BOOST_AUTO_TEST_CASE( NestedTablesDisabledHidden )
161{
162 // Test that disabled and hidden nested library table rows are parsed correctly
163 // This is a regression test for https://gitlab.com/kicad/code/kicad/-/issues/22784
164 // Note that full end-to-end testing requires the library manager to process the tables,
165 // but the parse test verifies the flag is correctly read from disk.
166
167 wxFileName fn( KI_TEST::GetTestDataRootDir(), wxEmptyString );
168 fn.AppendDir( "libraries" );
169
170 // Test with the disabled nested table
171 fn.SetName( "nested-disabled" );
172 LIBRARY_TABLE disabledTable( fn, LIBRARY_TABLE_SCOPE::GLOBAL );
173 BOOST_REQUIRE( disabledTable.IsOk() );
174 BOOST_REQUIRE_MESSAGE( disabledTable.Rows().size() == 4,
175 wxString::Format( "Expected 4 rows but got %zu",
176 disabledTable.Rows().size() ) );
177
178 // Verify the disabled flag is parsed correctly on the nested table row
179 bool foundDisabledRow = false;
180
181 for( const LIBRARY_TABLE_ROW& row : disabledTable.Rows() )
182 {
183 if( row.Type() == LIBRARY_TABLE_ROW::TABLE_TYPE_NAME )
184 {
185 BOOST_REQUIRE_MESSAGE( row.Disabled(),
186 "Nested table row should have disabled flag set" );
187 foundDisabledRow = true;
188 }
189 }
190
191 BOOST_REQUIRE_MESSAGE( foundDisabledRow,
192 "Disabled nested table row not found in parsed table" );
193
194 // Test hidden nested table has same behavior
195 fn.SetName( "nested-hidden" );
197 BOOST_REQUIRE( hiddenTable.IsOk() );
198 BOOST_REQUIRE_MESSAGE( hiddenTable.Rows().size() == 4,
199 wxString::Format( "Expected 4 rows but got %zu",
200 hiddenTable.Rows().size() ) );
201
202 bool foundHiddenRow = false;
203
204 for( const LIBRARY_TABLE_ROW& row : hiddenTable.Rows() )
205 {
206 if( row.Type() == LIBRARY_TABLE_ROW::TABLE_TYPE_NAME )
207 {
208 BOOST_REQUIRE_MESSAGE( row.Hidden(),
209 "Nested table row should have hidden flag set" );
210 foundHiddenRow = true;
211 }
212 }
213
214 BOOST_REQUIRE_MESSAGE( foundHiddenRow,
215 "Hidden nested table row not found in parsed table" );
216}
217
218
228BOOST_AUTO_TEST_CASE( InsertRowPreservesExistingRowPointers )
229{
230 LIBRARY_TABLE table( true, wxEmptyString, LIBRARY_TABLE_SCOPE::PROJECT );
232
233 // Seed with a few rows and snapshot pointers plus the expected URIs.
234 std::vector<const LIBRARY_TABLE_ROW*> seededPointers;
235 std::vector<wxString> seededUris;
236
237 for( int i = 0; i < 4; ++i )
238 {
239 LIBRARY_TABLE_ROW& row = table.InsertRow();
240 row.SetNickname( wxString::Format( wxS( "seed_%d" ), i ) );
241 row.SetURI( wxString::Format( wxS( "${KIPRJMOD}/libs/seed_%d.kicad_sym" ), i ) );
242 row.SetType( wxS( "KiCad" ) );
243
244 seededPointers.push_back( &row );
245 seededUris.push_back( row.URI() );
246 }
247
248 // Insert additional rows to force container growth that would reallocate
249 // a std::vector, and verify the seeded pointers continue to resolve to the
250 // same logical rows (same nickname and URI).
251 for( int i = 0; i < 64; ++i )
252 {
253 LIBRARY_TABLE_ROW& row = table.InsertRow();
254 row.SetNickname( wxString::Format( wxS( "extra_%d" ), i ) );
255 row.SetURI( wxString::Format( wxS( "${KIPRJMOD}/libs/extra_%d.kicad_sym" ), i ) );
256 row.SetType( wxS( "KiCad" ) );
257
258 for( size_t j = 0; j < seededPointers.size(); ++j )
259 {
260 BOOST_REQUIRE_MESSAGE(
261 seededPointers[j]->URI() == seededUris[j],
262 wxString::Format(
263 wxS( "Seed row %zu pointer was invalidated after inserting %d rows: "
264 "expected URI '%s', got '%s'" ),
265 j, i + 1, seededUris[j], seededPointers[j]->URI() ) );
266 }
267 }
268}
269
270
284BOOST_AUTO_TEST_CASE( IsPcmManagedRow_URITemplateMatching )
285{
286 struct CASE
287 {
288 wxString uri;
289 bool expectedPcmManaged;
290 wxString description;
291 };
292
293 std::vector<CASE> cases = {
294 { wxS( "${KICAD10_3RD_PARTY}/symbols/foo/foo.kicad_sym" ), true,
295 wxS( "Versioned 3RD_PARTY template should be recognised as PCM-managed" ) },
296 { wxS( "${KICAD9_3RD_PARTY}/symbols/legacy/legacy.kicad_sym" ), true,
297 wxS( "Legacy versioned 3RD_PARTY template should still match the wildcard" ) },
298 { wxS( "${KICAD10_3RD_PARTY}/footprints/bar/bar.pretty" ), true,
299 wxS( "Footprint library using 3RD_PARTY template should match" ) },
300 { wxS( "${KICAD_USER_LIB}/symbols/test.kicad_sym" ), false,
301 wxS( "Row using a different env var must not be flagged as PCM-managed" ) },
302 { wxS( "${KIPRJMOD}/libs/local.kicad_sym" ), false,
303 wxS( "Project-relative row must not be flagged as PCM-managed" ) },
304 { wxS( "/abs/path/to/lib.kicad_sym" ), false,
305 wxS( "Absolute path row must not be flagged as PCM-managed" ) },
306 { wxS( "${}" ), false,
307 wxS( "Malformed empty var name must not match" ) },
308 { wxS( "${KICAD10_3RD_PARTY_EXTRA}/foo" ), false,
309 wxS( "Similar-but-different var name must not match" ) },
310 };
311
312 for( const CASE& c : cases )
313 {
315 row.SetURI( c.uri );
316
318
320 actual == c.expectedPcmManaged,
321 wxString::Format( wxS( "%s: URI='%s' expected=%d actual=%d" ),
322 c.description, c.uri, c.expectedPcmManaged ? 1 : 0,
323 actual ? 1 : 0 ) );
324 }
325}
326
327
static bool IsPcmManagedRow(const LIBRARY_TABLE_ROW &aRow)
Return true if a library table row was added by the Plugin and Content Manager.
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
tl::expected< LIBRARY_TABLE_IR, LIBRARY_PARSE_ERROR > ParseBuffer(const std::string &aBuffer)
tl::expected< LIBRARY_TABLE_IR, LIBRARY_PARSE_ERROR > Parse(const std::filesystem::path &aPath)
void SetNickname(const wxString &aNickname)
void SetType(const wxString &aType)
static const wxString TABLE_TYPE_NAME
void SetURI(const wxString &aUri)
const wxString & URI() const
const std::deque< LIBRARY_TABLE_ROW > & Rows() const
bool IsOk() const
Implement an OUTPUTFORMATTER to a memory buffer.
Definition richio.h:422
std::string & MutableString()
Definition richio.h:450
const std::string & GetString()
Definition richio.h:445
void Prettify(std::string &aSource, FORMAT_MODE aMode)
Pretty-prints s-expression text according to KiCad format rules.
std::string GetTestDataRootDir()
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
std::string path
BOOST_AUTO_TEST_CASE(Grammar)
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
BOOST_TEST_MESSAGE("\n=== Real-World Polygon PIP Benchmark ===\n"<< formatTable(table))
std::vector< std::vector< std::string > > table
BOOST_TEST_CONTEXT("Test Clearance")
int actual
wxString result
Test unit parsing edge cases and error handling.