110 std::unique_ptr<SCHEMATIC> schematic;
112 std::vector<SCH_SHEET_PATH> paths;
116 if(
path.LastScreen()->GetFileName().EndsWith(
"LEDs.kicad_sch" ) )
117 paths.push_back(
path );
120 BOOST_REQUIRE_EQUAL( paths.size(), 2 );
124 std::unique_ptr<SCHEMATIC> source;
132 auto* symbol =
static_cast<SCH_SYMBOL*
>( item );
134 if( !symbol->IsMultiUnit() )
137 if( !first && symbol->
GetUnit() == 1 )
139 first =
static_cast<SCH_SYMBOL*
>( symbol->Duplicate(
false ) );
142 else if( !second && symbol->
GetUnit() == 2 )
144 second =
static_cast<SCH_SYMBOL*
>( symbol->Duplicate(
false ) );
152 first->
SetRef( &root,
"ERC_FP" );
163 for(
bool backend : {
true,
false } )
166 schematic->RebuildConnectivity();
170 BOOST_TEST_CONTEXT(
"backend=" << backend <<
"; displayed=" << displayed.GetPageNumber() )
172 schematic->SetCurrentSheet( displayed );
175 std::vector<SCH_MARKER*> markers;
176 std::set<KIID_PATH> seen;
180 auto* marker =
static_cast<SCH_MARKER*
>( item );
181 const auto error = std::static_pointer_cast<ERC_ITEM>( marker->GetRCItem() );
184 || error->GetMainItemID() != first->
m_Uuid || error->GetAuxItemID() != second->
m_Uuid )
189 markers.push_back( marker );
190 BOOST_CHECK( marker->GetPosition() == second->
GetPosition() );
191 BOOST_CHECK( error->IsSheetSpecific() );
192 BOOST_CHECK( error->MainItemHasSheetPath() );
193 BOOST_CHECK( error->AuxItemHasSheetPath() );
195 if( error->MainItemHasSheetPath() )
196 BOOST_CHECK( error->GetMainItemSheetPath().PathRef() == root.
PathRef() );
198 if( error->IsSheetSpecific() && error->AuxItemHasSheetPath() )
200 BOOST_CHECK( error->GetAuxItemSheetPath().PathRef()
201 == error->GetSpecificSheetPath().PathRef() );
202 BOOST_CHECK( seen.insert( error->GetSpecificSheetPath().PathRef() ).second );
207 const std::set<KIID_PATH>
expected{ paths[0].PathRef(), paths[1].PathRef() };
216 const auto targetMarkers = [&]
218 std::vector<SCH_MARKER*>
result;
222 auto* marker =
static_cast<SCH_MARKER*
>( item );
226 && error->GetMainItemID() == first->
m_Uuid && error->GetAuxItemID() == second->
m_Uuid )
228 result.push_back( marker );
234 auto& exclusions = schematic->ErcSettings().m_ErcExclusions;
236 for(
bool legacy : {
false,
true } )
241 BOOST_REQUIRE_EQUAL( targetMarkers().size(), 2 );
245 const auto error = std::static_pointer_cast<ERC_ITEM>( marker->GetRCItem() );
248 if( error->GetSpecificSheetPath().PathRef() != paths[0].PathRef() )
255 proto.mutable_marker()->clear_sheet_specific_path();
256 proto.mutable_marker()->clear_main_item_sheet_path();
257 proto.mutable_marker()->clear_aux_item_sheet_path();
261 exclusion.SetComment(
"Retained multi-unit footprint exclusion" );
262 const nlohmann::json saved = exclusion;
266 BOOST_REQUIRE_EQUAL( exclusions.size(), 1 );
267 const auto savedExclusions = exclusions;
272 for(
bool backend : {
true,
false } )
277 exclusions = savedExclusions;
278 schematic->RebuildConnectivity();
281 schematic->ResolveERCExclusionsPostUpdate();
282 std::vector<SCH_MARKER*> rootMarkers;
286 auto* marker =
static_cast<SCH_MARKER*
>( item );
290 && error->GetMainItemID() == first->
m_Uuid && error->GetAuxItemID() == second->
m_Uuid )
292 rootMarkers.push_back( marker );
296 BOOST_CHECK( rootMarkers.empty() );
306 const auto error = std::static_pointer_cast<ERC_ITEM>( marker->GetRCItem() );
307 BOOST_CHECK( error->IsSheetSpecific() );
309 if( !error->IsSheetSpecific() )
312 const bool expected = legacy || error->GetSpecificSheetPath().PathRef() == paths[0].PathRef();
315 ? wxString(
"Retained multi-unit footprint exclusion" ) : wxString() );
316 excluded += marker->IsExcluded();
329 screen->
Append( first,
false );
331 second->
SetRef( &root,
"ERC_FP" );
341 const KIID_PATH canonical = std::min( paths[0].PathRef(), paths[1].PathRef() );
343 for(
bool variable : {
false,
true } )
347 for(
bool backend : {
true,
false } )
351 nlohmann::json saved;
353 for(
bool reverse : {
false,
true } )
355 BOOST_TEST_CONTEXT(
"variable=" << variable <<
"; backend=" << backend <<
"; reverse=" << reverse )
357 paths[0].SetPageNumber( reverse ?
"3" :
"2" );
358 paths[1].SetPageNumber( reverse ?
"2" :
"3" );
360 if( backend && reverse )
361 schematic->Connectivity().Recalculate( *schematic );
363 schematic->RebuildConnectivity();
368 if( !variable && reverse )
371 schematic->ResolveERCExclusionsPostUpdate();
378 auto* marker =
static_cast<SCH_MARKER*
>( item );
379 const auto error = std::static_pointer_cast<ERC_ITEM>( marker->GetRCItem() );
382 || error->GetMainItemID() != first->
m_Uuid || error->GetAuxItemID() != second->
m_Uuid )
398 BOOST_CHECK( witness.
PathRef() == canonical );
402 BOOST_CHECK( marker->IsExcluded() );
408 exclusion.SetComment(
"Page order retained" );
418 std::vector<SCH_MARKER*> markers;
421 markers.push_back(
static_cast<SCH_MARKER*
>( item ) );
424 current->DeleteItem( marker );
435 struct FOOTPRINT_FACE :
KIFACE
439 void Reset()
override {}
441 void GetActions( std::vector<TOOL_ACTION*>& )
const override {}
443 static int Check(
const wxString& aFootprint,
PROJECT* )
445 if( aFootprint ==
"NoLibrary:2" )
448 if( aFootprint ==
"Disabled:2" )
451 if( aFootprint ==
"Missing:2" )
460 return reinterpret_cast<void*
>( &Check );
468 std::unique_ptr<SCHEMATIC> schematic;
470 std::vector<SCH_SHEET_PATH> paths;
474 if(
path.LastScreen()->GetFileName().EndsWith(
"ampli_ht.kicad_sch" ) )
475 paths.push_back(
path );
478 BOOST_REQUIRE_EQUAL( paths.size(), 2 );
481 paths[0].SetPageNumber(
"2" );
482 paths[1].SetPageNumber(
"3" );
487 auto* candidate =
static_cast<SCH_SYMBOL*
>( item );
489 if( candidate->GetLibSymbolRef() && !candidate->GetLibSymbolRef()->IsPower() )
498 for(
const wxString& footprint : { wxString(
"NoLibrary:2" ), wxString(
"NoLibrary:${#}" ),
499 wxString(
"Disabled:${#}" ), wxString(
"Missing:${#}" ),
500 wxString(
"Present:${#}" ) } )
503 const bool bothInstances = footprint ==
"NoLibrary:2";
504 const size_t expected = bothInstances ? 2 : footprint ==
"Present:${#}" ? 0 : 1;
506 for(
bool backend : {
true,
false } )
509 schematic->RebuildConnectivity();
514 <<
"; displayed=" << displayed.GetPageNumber() )
516 schematic->SetCurrentSheet( displayed );
520 std::set<KIID_PATH> seen;
521 std::vector<SCH_MARKER*> markers;
525 auto* marker =
static_cast<SCH_MARKER*
>( item );
526 markers.push_back( marker );
527 const auto error = std::static_pointer_cast<ERC_ITEM>( marker->GetRCItem() );
530 || error->GetMainItemID() != symbol->
m_Uuid )
536 BOOST_CHECK( marker->GetPosition() == symbol->
GetPosition() );
537 BOOST_CHECK( error->IsSheetSpecific() );
538 BOOST_CHECK( error->MainItemHasSheetPath() );
539 BOOST_CHECK( !error->AuxItemHasSheetPath() );
541 if( error->IsSheetSpecific() && error->MainItemHasSheetPath() )
543 const KIID_PATH&
path = error->GetSpecificSheetPath().PathRef();
544 BOOST_CHECK( error->GetMainItemSheetPath().PathRef() ==
path );
545 BOOST_CHECK( seen.insert(
path ).second );
546 BOOST_CHECK(
path == paths[0].PathRef()
547 || ( bothInstances &&
path == paths[1].PathRef() ) );
562 const auto targetMarkers = [&]
564 std::vector<SCH_MARKER*>
result;
568 auto* marker =
static_cast<SCH_MARKER*
>( item );
571 && marker->GetRCItem()->GetMainItemID() == symbol->
m_Uuid )
573 result.push_back( marker );
579 auto& exclusions = schematic->ErcSettings().m_ErcExclusions;
583 for(
bool pathlessExclusion : {
false,
true } )
587 schematic->RebuildConnectivity();
590 BOOST_REQUIRE_EQUAL( targetMarkers().size(), 2 );
594 const auto error = std::static_pointer_cast<ERC_ITEM>( marker->GetRCItem() );
597 if( error->GetSpecificSheetPath().PathRef() != target.PathRef() )
602 if( pathlessExclusion )
604 proto.mutable_marker()->clear_sheet_specific_path();
605 proto.mutable_marker()->clear_main_item_sheet_path();
609 exclusion.SetComment(
"Retained footprint link exclusion" );
610 const nlohmann::json saved = exclusion;
614 BOOST_REQUIRE_EQUAL( exclusions.size(), 1 );
615 const auto savedExclusions = exclusions;
620 for(
bool backend : {
true,
false } )
622 BOOST_TEST_CONTEXT(
"pathless exclusion=" << pathlessExclusion <<
"; backend=" << backend
623 <<
"; target=" << target.GetPageNumber() )
626 exclusions = savedExclusions;
627 schematic->RebuildConnectivity();
630 schematic->ResolveERCExclusionsPostUpdate();
636 const auto error = std::static_pointer_cast<ERC_ITEM>( marker->GetRCItem() );
637 BOOST_CHECK( error->IsSheetSpecific() );
638 BOOST_CHECK( error->MainItemHasSheetPath() );
640 if( !error->IsSheetSpecific() )
643 const bool expected = pathlessExclusion
644 || error->GetSpecificSheetPath().PathRef() == target.PathRef();
647 ? wxString(
"Retained footprint link exclusion" ) : wxString() );
648 excluded += marker->IsExcluded();
662 for(
const auto& [backend, legacyString] : { std::pair{
true,
false }, std::pair{
false,
false },
663 std::pair{
true,
true }, std::pair{
false,
true } } )
665 BOOST_TEST_CONTEXT(
"deleted exclusion instance; backend=" << backend <<
"; legacy=" << legacyString )
669 schematic->SetCurrentSheet( schematic->Hierarchy().front() );
670 schematic->RebuildConnectivity();
673 BOOST_REQUIRE_EQUAL( targetMarkers().size(), 1 );
674 SCH_MARKER* original = targetMarkers().front();
675 const auto error = std::static_pointer_cast<ERC_ITEM>( original->
GetRCItem() );
677 BOOST_REQUIRE( error->GetSpecificSheetPath().PathRef() == paths[0].PathRef() );
678 original->
SetExcluded(
true,
"Keep the removed instance exclusion" );
679 schematic->RecordERCExclusions();
680 BOOST_REQUIRE_EQUAL( exclusions.size(), 1 );
681 const nlohmann::json saved = *exclusions.begin();
682 const wxString originalComment = original->
GetComment();
683 const wxString legacyData = wxString::Format( wxS(
"%s|%d|%d|%s|%s|%s|%s|" ),
685 error->GetMainItemID().AsString(),
niluuid.AsString(),
686 error->GetSpecificSheetPath().PathRef().AsString(),
687 error->GetMainItemSheetPath().PathRef().AsString() );
692 schematic->ErcSettings().m_ErcExclusionsLegacy.emplace( legacyData, originalComment );
697 schematic->RootScreen()->Remove( removed );
698 std::unique_ptr<SCH_SHEET> undoOwnedSheet( removed );
699 schematic->RefreshHierarchy();
700 BOOST_REQUIRE( !schematic->Hierarchy().GetSheetPathByKIIDPath( paths[0].PathRef() ) );
701 BOOST_REQUIRE( schematic->Hierarchy().GetSheetPathByKIIDPath( paths[1].PathRef() ) );
704 schematic->SetCurrentSheet( paths[1] );
705 schematic->ResolveERCExclusionsPostUpdate();
706 BOOST_CHECK( schematic->ErcSettings().m_ErcExclusionsLegacy.empty() );
710 const auto retained = std::static_pointer_cast<ERC_ITEM>( marker->GetRCItem() );
711 BOOST_CHECK( retained->IsSheetSpecific() );
713 if( retained->IsSheetSpecific() )
714 BOOST_CHECK( retained->GetSpecificSheetPath().PathRef() == paths[0].PathRef() );
719 if( exclusions.size() == 1 )
720 BOOST_CHECK( nlohmann::json( *exclusions.begin() ) == saved );
728 schematic->ClearUnresolvedERCExclusions();
729 schematic->RecordERCExclusions();
730 schematic->ResolveERCExclusionsPostUpdate();
731 BOOST_CHECK( exclusions.empty() );
734 schematic->ResolveERCExclusionsPostUpdate();
737 schematic->RootScreen()->Append( undoOwnedSheet.release() );
738 schematic->RefreshHierarchy();
739 schematic->SetCurrentSheet( schematic->Hierarchy().front() );
740 schematic->RecordERCExclusions();
743 if( exclusions.size() == 1 )
744 BOOST_CHECK( nlohmann::json( *exclusions.begin() ) == saved );
749 schematic->RebuildConnectivity();
753 schematic->ResolveERCExclusionsPostUpdate();
759 const auto restoredError = std::static_pointer_cast<ERC_ITEM>( marker->GetRCItem() );
760 BOOST_CHECK( marker->IsExcluded() );
761 BOOST_CHECK_EQUAL( marker->GetComment(), wxString(
"Keep the removed instance exclusion" ) );
762 BOOST_CHECK( restoredError->IsSheetSpecific() );
764 if( restoredError->IsSheetSpecific() )
765 BOOST_CHECK( restoredError->GetSpecificSheetPath().PathRef() == paths[0].PathRef() );
767 marker->SetExcluded(
false );
768 schematic->RecordERCExclusions();
769 BOOST_CHECK( exclusions.empty() );
784 std::unique_ptr<SCHEMATIC> schematic;
786 std::vector<SCH_SHEET_PATH> paths;
790 if(
path.LastScreen()->GetFileName().EndsWith(
"ampli_ht.kicad_sch" ) )
791 paths.push_back(
path );
794 BOOST_REQUIRE_EQUAL( paths.size(), 2 );
797 BOOST_REQUIRE( paths[0].GetPageNumber() != paths[1].GetPageNumber() );
802 auto* candidate =
static_cast<SCH_SYMBOL*
>( item );
804 if( candidate->GetLibSymbolRef() && !candidate->GetLibSymbolRef()->IsPower() )
814 const wxString firstPage = paths[0].GetPageNumber();
815 const wxString secondPage = paths[1].GetPageNumber();
816 wxArrayString pageFilters;
817 pageFilters.Add( wxString(
"Acceptance:" ) + firstPage );
819 auto collectPaths = [&]()
823 std::set<KIID_PATH> found;
824 std::vector<SCH_MARKER*> markers;
828 auto* marker =
static_cast<SCH_MARKER*
>( item );
829 const auto error = std::static_pointer_cast<ERC_ITEM>( marker->GetRCItem() );
834 BOOST_CHECK( error->MainItemHasSheetPath() );
835 BOOST_CHECK( error->GetMainItemSheetPath().PathRef() == error->GetSpecificSheetPath().PathRef() );
836 BOOST_CHECK( found.insert( error->GetSpecificSheetPath().PathRef() ).second );
839 markers.push_back( marker );
848 for(
bool backend : {
false,
true } )
853 schematic->RebuildConnectivity();
854 const auto beforePages = collectPaths();
855 BOOST_REQUIRE( beforePages == std::set<KIID_PATH>{ paths[1].PathRef() } );
856 paths[0].SetPageNumber( secondPage );
857 paths[1].SetPageNumber( firstPage );
861 schematic->Connectivity().Recalculate( *schematic );
863 const auto afterPages = collectPaths();
864 BOOST_REQUIRE( afterPages == std::set<KIID_PATH>{ paths[0].PathRef() } );
865 schematic->RebuildConnectivity();
866 BOOST_CHECK( collectPaths() == afterPages );
867 paths[0].SetPageNumber( firstPage );
868 paths[1].SetPageNumber( secondPage );
871 schematic->Connectivity().Recalculate( *schematic );
873 BOOST_CHECK( collectPaths() == beforePages );
874 schematic->SetCurrentSheet( schematic->Hierarchy().front() );
876 schematic->RootScreen()->Remove( removed );
877 std::unique_ptr<SCH_SHEET> undoOwnedSheet( removed );
878 schematic->RefreshHierarchy();
879 BOOST_CHECK( collectPaths().
empty() );
880 schematic->RootScreen()->Append( undoOwnedSheet.release() );
881 schematic->RefreshHierarchy();
882 BOOST_CHECK( collectPaths() == beforePages );