138 if( ::geteuid() == 0 )
148 const std::filesystem::path libPath = tmpLib.
CreateChildDir(
"kicad_qa_adapter_save_readonly.pretty" );
151 TEST_FOOTPRINT_LIBRARY_ADAPTER adapter( manager );
153 const wxString nickname = wxS(
"scratch" );
154 adapter.SeedLoadedLibrary( nickname, libPath.string() );
156 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
164 std::filesystem::path savedFile = libPath /
"readonly_fp.kicad_mod";
169 std::filesystem::permissions( savedFile,
170 std::filesystem::perms::owner_write | std::filesystem::perms::group_write
171 | std::filesystem::perms::others_write,
172 std::filesystem::perm_options::remove );
174 BOOST_CHECK_THROW( adapter.SaveFootprint( nickname, fp ),
IO_ERROR );
184 const std::filesystem::path libPath = tmpLib.
CreateChildDir(
"kicad_qa_adapter_concurrent.pretty" );
186 for(
const auto& entry : std::filesystem::directory_iterator(
187 std::filesystem::path( getResistorLibPath().
ToStdString() ) ) )
189 if( entry.is_regular_file() )
190 std::filesystem::copy_file( entry.path(), libPath / entry.path().filename() );
194 TEST_FOOTPRINT_LIBRARY_ADAPTER adapter( manager );
196 const wxString nickname = wxS(
"Resistor_SMD" );
197 adapter.SeedLoadedLibrary( nickname, libPath.string() );
200 const wxString stableFp = wxS(
"R_0603_1608Metric" );
202 BOOST_REQUIRE( adapter.FootprintExists( nickname, stableFp ) );
204 constexpr int readerCount = 6;
205 constexpr int iterations = 40;
207 std::atomic<bool> sawMissing{
false };
208 std::atomic<bool> sawNullLoad{
false };
209 std::atomic<int> savedCount{ 0 };
212 std::atomic<int> ready{ 0 };
213 std::atomic<bool> go{
false };
215 auto waitForStart = [&]()
217 ready.fetch_add( 1 );
220 std::this_thread::yield();
223 std::vector<std::thread> workers;
224 workers.reserve( readerCount + 1 );
226 for(
int t = 0; t < readerCount; ++t )
228 workers.emplace_back(
233 for(
int i = 0; i < iterations; ++i )
236 adapter.IsWritable( nickname );
238 if( !adapter.FootprintExists( nickname, stableFp ) )
241 std::unique_ptr<FOOTPRINT> fp{ adapter.LoadFootprint( nickname, stableFp,
false ) };
249 workers.emplace_back(
252 std::unique_ptr<FOOTPRINT> seed{ adapter.LoadFootprint( nickname, stableFp,
false ) };
262 for(
int i = 0; i < iterations; ++i )
265 seed->SetFPID(
LIB_ID( nickname, wxString::Format( wxS(
"scratch_%d" ), i % 4 ) ) );
270 savedCount.fetch_add( 1 );
279 while( ready.load() < readerCount + 1 )
280 std::this_thread::yield();
284 for( std::thread& worker : workers )
287 BOOST_CHECK( !sawMissing.load() );
288 BOOST_CHECK( !sawNullLoad.load() );
291 BOOST_CHECK( savedCount.load() > 0 );
300 const std::filesystem::path libPath = tmpLib.
CreateChildDir(
"kicad_qa_adapter_stale.pretty" );
302 const std::filesystem::path source =
303 std::filesystem::path( getResistorLibPath().
ToStdString() ) /
"R_0402_1005Metric.kicad_mod";
305 std::filesystem::copy_file( source, libPath /
"R_0402_1005Metric.kicad_mod" );
307 const wxString nickname = wxS(
"StaleCheck" );
310 std::ofstream
table( tmpTable.
Path() /
"fp-lib-table" );
311 table <<
"(fp_lib_table\n (version 7)\n";
312 table <<
" (lib (name \"" << nickname.ToStdString() <<
"\")(type \"KiCad\")(uri \""
313 << libPath.string() <<
"\")(options \"\")(descr \"\"))\n)\n";
319 TEST_FOOTPRINT_LIBRARY_ADAPTER adapter( manager );
320 adapter.SeedLoadedLibrary( nickname, libPath.string() );
322 adapter.RefreshLibraryIfChanged( nickname );
323 BOOST_REQUIRE_EQUAL( adapter.GetFootprints( nickname,
true ).size(), 1u );
325 std::filesystem::copy_file( source, libPath /
"ZZ_PulledFootprint.kicad_mod" );
327 adapter.RefreshChangedLibraries();
331 for(
FOOTPRINT* fp : adapter.GetFootprints( nickname,
true ) )
333 if( fp && fp->GetFPID().GetLibItemName().wx_str() == wxS(
"ZZ_PulledFootprint" ) )
337 BOOST_CHECK_MESSAGE( found,
"a footprint added to the library on disk is missing from the listing" );
347 const std::filesystem::path libPathA = tmpA.
CreateChildDir(
"kicad_qa_adapter_skip_a.pretty" );
348 const std::filesystem::path libPathB = tmpB.
CreateChildDir(
"kicad_qa_adapter_skip_b.pretty" );
350 const std::filesystem::path source =
351 std::filesystem::path( getResistorLibPath().
ToStdString() ) /
"R_0402_1005Metric.kicad_mod";
353 std::filesystem::copy_file( source, libPathA /
"R_0402_1005Metric.kicad_mod" );
355 for(
int i = 0; i < 8; ++i )
357 std::filesystem::copy_file( source, libPathB / (
"R_" + std::to_string( i ) +
".kicad_mod" ) );
360 const wxString nickA = wxS(
"SkipCheckA" );
361 const wxString nickB = wxS(
"SkipCheckB" );
363 const std::filesystem::path tablePath = tmpTable.
Path() /
"fp-lib-table";
366 std::ofstream
table( tablePath );
367 table <<
"(fp_lib_table\n (version 7)\n";
368 table <<
" (lib (name \"" << nickA.ToStdString() <<
"\")(type \"KiCad\")(uri \"" << libPathA.string()
369 <<
"\")(options \"\")(descr \"\"))\n";
370 table <<
" (lib (name \"" << nickB.ToStdString() <<
"\")(type \"KiCad\")(uri \"" << libPathB.string()
371 <<
"\")(options \"\")(descr \"\"))\n)\n";
377 TEST_FOOTPRINT_LIBRARY_ADAPTER adapter( manager );
378 adapter.SeedLoadedLibrary( nickA, libPathA.string() );
379 adapter.SeedLoadedLibrary( nickB, libPathB.string() );
381 adapter.RefreshLibraryIfChanged( nickA );
382 adapter.RefreshLibraryIfChanged( nickB );
384 BOOST_REQUIRE_EQUAL( adapter.GetFootprints( nickA,
true ).size(), 1u );
385 BOOST_REQUIRE_EQUAL( adapter.GetFootprints( nickB,
true ).size(), 8u );
389 std::vector<FOOTPRINT*> untouched = adapter.GetFootprints( nickB,
true );
391 std::filesystem::copy_file( source, libPathA /
"ZZ_Added.kicad_mod" );
393 adapter.RefreshChangedLibraries();
396 BOOST_CHECK_MESSAGE( adapter.GetFootprints( nickB,
true ) == untouched,
"an unchanged library was re-enumerated" );
410 const std::filesystem::path libPath = tmpLib.
CreateChildDir(
"kicad_qa_adapter_rt.pretty" );
412 const std::filesystem::path source =
413 std::filesystem::path( getResistorLibPath().
ToStdString() ) /
"R_0402_1005Metric.kicad_mod";
414 const std::filesystem::path target = libPath /
"R_0402_1005Metric.kicad_mod";
416 std::filesystem::copy_file( source, target );
418 const wxString nickname = wxS(
"RoundTrip" );
419 const wxString fpName = wxS(
"R_0402_1005Metric" );
422 std::ofstream
table( tmpTable.
Path() /
"fp-lib-table" );
423 table <<
"(fp_lib_table\n (version 7)\n";
424 table <<
" (lib (name \"" << nickname.ToStdString() <<
"\")(type \"KiCad\")(uri \""
425 << libPath.string() <<
"\")(options \"\")(descr \"\"))\n)\n";
429 [](
const std::filesystem::path& aPath )
431 std::set<std::string> ids;
432 std::ifstream in( aPath );
435 while( std::getline( in, line ) )
437 size_t pos = line.find(
"(uuid \"" );
439 if( pos != std::string::npos )
440 ids.insert( line.substr( pos + 7, 36 ) );
446 const std::set<std::string> before = fileUuids( target );
451 TEST_FOOTPRINT_LIBRARY_ADAPTER adapter( manager );
452 adapter.SeedLoadedLibrary( nickname, libPath.string() );
455 adapter.RefreshLibraryIfChanged( nickname );
457 std::unique_ptr<FOOTPRINT> edited( adapter.LoadFootprint( nickname, fpName,
true ) );
462 const std::set<std::string> after = fileUuids( target );
464 std::vector<std::string> common;
465 std::set_intersection( before.begin(), before.end(), after.begin(), after.end(),
466 std::back_inserter( common ) );
471 BOOST_CHECK_MESSAGE( common.size() == after.size(),
472 "a load/save round trip through the editor path rewrote UUIDs in the .kicad_mod" );
BOOST_CHECK_EQUAL(result, "25.4")