KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_issue24409_cli_hierarchical_instances.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// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/24409
21
23
24#include <memory>
25#include <set>
26
27#include <connection_graph.h>
28#include <eeschema_helpers.h>
29#include <erc/erc.h>
30#include <erc/erc_item.h>
31#include <erc/erc_report.h>
32#include <erc/erc_settings.h>
33#include <locale_io.h>
34#include <sch_marker.h>
35#include <sch_pin.h>
36#include <sch_reference_list.h>
37#include <sch_screen.h>
38#include <sch_sheet.h>
39#include <sch_sheet_path.h>
40#include <sch_symbol.h>
41#include <schematic.h>
42
43
44// The reproduction case has three sheet instances of one sub-sheet with two
45// resistors R1/R2 (no wires). Per-instance annotations make this R1+R2,
46// R3+R4 and R5+R6. RunERC dedups markers that share a driver pin so each
47// pin produces exactly one pin_not_connected marker, but the dedup must
48// pick the marker on the *first* sheet path (by page number) so that
49// kicad-cli produces the same report a user sees in the GUI. Before the
50// fix the unordered iteration of SCH_ITEM::m_connection_map caused
51// subgraphs to be created in hash-bucket order, so the marker landed on
52// whichever instance happened to surface first (R5/R6 on this layout).
53BOOST_AUTO_TEST_CASE( Issue24409CliHierarchicalInstances )
54{
56
57 wxString schPath = wxString::FromUTF8( KI_TEST::GetEeschemaTestDataDir() ) +
58 wxS( "issue24409/issue24409.kicad_sch" );
59
60 // Use the CLI/headless loader directly so the regression covers the path
61 // exercised by kicad-cli sch erc / sch export bom / sch export netlist.
62 std::unique_ptr<SCHEMATIC> sch( EESCHEMA_HELPERS::LoadSchematic( schPath, true, false ) );
63 BOOST_REQUIRE( sch != nullptr );
64
65 SCH_SHEET_LIST sheets = sch->Hierarchy();
66
67 // Sanity: three sub-sheet instances + the root sheet.
68 BOOST_REQUIRE_EQUAL( sheets.size(), 4u );
69
70 // The two resistors get annotated R1..R6 once instances are resolved.
72 sheets.GetSymbols( refs, SYMBOL_FILTER_NON_POWER, false );
73 BOOST_REQUIRE_EQUAL( refs.GetCount(), 6u );
74
75 std::set<wxString> seenRefs;
76
77 for( size_t i = 0; i < refs.GetCount(); ++i )
78 seenRefs.insert( refs[i].GetRef() );
79
80 BOOST_CHECK( seenRefs.count( wxS( "R1" ) ) );
81 BOOST_CHECK( seenRefs.count( wxS( "R2" ) ) );
82 BOOST_CHECK( seenRefs.count( wxS( "R3" ) ) );
83 BOOST_CHECK( seenRefs.count( wxS( "R4" ) ) );
84 BOOST_CHECK( seenRefs.count( wxS( "R5" ) ) );
85 BOOST_CHECK( seenRefs.count( wxS( "R6" ) ) );
86
87 // Now run ERC against the loaded schematic and verify that every pin on
88 // every sheet instance has been reported. Disable the library-mismatch
89 // and sim-model checks because the reproduction project ships without
90 // installed libraries.
91 ERC_SETTINGS& settings = sch->ErcSettings();
95
96 sch->ConnectionGraph()->RunERC();
97
98 // Walk every sheet's screen and count pin_not_connected markers.
99 int unconnectedMarkers = 0;
100 std::set<wxString> markedRefs;
101
102 std::set<const SCH_SCREEN*> visitedScreens;
103
104 for( const SCH_SHEET_PATH& sheet : sheets )
105 {
106 SCH_SCREEN* screen = sheet.LastScreen();
107
108 // Each underlying screen carries its own markers; visit each only once.
109 if( !visitedScreens.insert( screen ).second )
110 continue;
111
112 for( SCH_ITEM* item : screen->Items().OfType( SCH_MARKER_T ) )
113 {
114 SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
115 std::shared_ptr<ERC_ITEM> ercItem =
116 std::dynamic_pointer_cast<ERC_ITEM>( marker->GetRCItem() );
117
118 if( !ercItem || ercItem->GetErrorCode() != ERCE_PIN_NOT_CONNECTED )
119 continue;
120
121 ++unconnectedMarkers;
122
123 const SCH_SHEET_PATH& markerSheet = ercItem->IsSheetSpecific()
124 ? ercItem->GetSpecificSheetPath()
125 : sheet;
126 EDA_ITEM* erred = sch->ResolveItem( ercItem->GetMainItemID(), nullptr, true );
127
128 if( SCH_PIN* pin = dynamic_cast<SCH_PIN*>( erred ) )
129 {
130 if( SCH_SYMBOL* parent = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() ) )
131 markedRefs.insert( parent->GetRef( &markerSheet ) );
132 }
133 }
134 }
135
136 // 2 resistors per sub-sheet * 2 pins per resistor = 4 logical pins; the
137 // RunERC dedup collapses the three instances of each pin down to one
138 // marker apiece so we expect 4 markers in total.
139 BOOST_CHECK_MESSAGE( unconnectedMarkers == 4,
140 "Expected 4 deduplicated pin_not_connected markers, got "
141 << unconnectedMarkers );
142
143 // The dedup must keep the marker on the first sheet instance by page
144 // number (Untitled Sheet, page 2, annotated R1/R2) so that kicad-cli
145 // matches the GUI. Without the fix this is R5/R6 because the unordered
146 // iteration of m_connection_map surfaces the last sheet first.
147 for( const wxString& ref : { wxS( "R1" ), wxS( "R2" ) } )
148 {
149 BOOST_CHECK_MESSAGE( markedRefs.count( ref ),
150 "Missing pin_not_connected marker for " << ref
151 << " on the first sheet instance" );
152 }
153
154 for( const wxString& ref : { wxS( "R3" ), wxS( "R4" ), wxS( "R5" ), wxS( "R6" ) } )
155 {
156 BOOST_CHECK_MESSAGE( !markedRefs.count( ref ),
157 "Unexpected pin_not_connected marker for " << ref
158 << "; dedup should keep only the first instance" );
159 }
160
161 // Also exercise the text report writer end-to-end so the regression catches
162 // the case where the marker is on the right sheet but the printed reference
163 // is wrong (i.e. the symbol's REFERENCE field text was left on the last
164 // sheet's annotation by the helpers' UpdateAllScreenReferences loop).
165 std::shared_ptr<SHEETLIST_ERC_ITEMS_PROVIDER> markersProvider =
166 std::make_shared<SHEETLIST_ERC_ITEMS_PROVIDER>( sch.get() );
167 markersProvider->SetSeverities( RPT_SEVERITY_ERROR | RPT_SEVERITY_WARNING );
168
169 ERC_REPORT reportWriter( sch.get(), EDA_UNITS::MM, markersProvider );
170 wxString report = reportWriter.GetTextReport();
171
172 for( const wxString& ref : { wxS( "R1" ), wxS( "R2" ) } )
173 {
174 BOOST_CHECK_MESSAGE( report.Contains( wxS( "Symbol " ) + ref + wxS( " " ) ),
175 "ERC text report missing pin_not_connected for "
176 << ref << "\n" << report );
177 }
178
179 for( const wxString& ref : { wxS( "R3" ), wxS( "R4" ), wxS( "R5" ), wxS( "R6" ) } )
180 {
181 BOOST_CHECK_MESSAGE( !report.Contains( wxS( "Symbol " ) + ref + wxS( " " ) ),
182 "ERC text report unexpectedly mentions "
183 << ref << "\n" << report );
184 }
185}
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
static SCHEMATIC * LoadSchematic(const wxString &aFileName, bool aSetActive, bool aForceDefaultProject, PROJECT *aProject=nullptr, bool aCalculateConnectivity=true)
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:221
wxString GetTextReport()
Returns the ERC report in "text" (human readable) format in the C-locale.
Container for ERC settings.
std::map< int, SEVERITY > m_ERCSeverities
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition locale_io.h:37
std::shared_ptr< RC_ITEM > GetRCItem() const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:162
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:115
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
void GetSymbols(SCH_REFERENCE_LIST &aReferences, SYMBOL_FILTER aSymbolFilter, bool aForceIncludeOrphanSymbols=false) const
Add a SCH_REFERENCE object to aReferences for each symbol in the list of sheets.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Schematic symbol object.
Definition sch_symbol.h:69
@ ERCE_PIN_NOT_CONNECTED
Pin not connected and not no connect symbol.
@ ERCE_SIMULATION_MODEL
An error was found in the simulation model.
@ ERCE_LIB_SYMBOL_MISMATCH
Symbol doesn't match copy in library.
@ ERCE_LIB_SYMBOL_ISSUES
Symbol not found in active libraries.
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_IGNORE
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
@ SYMBOL_FILTER_NON_POWER
std::vector< FAB_LAYER_COLOR > dummy
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_CASE(Issue24409CliHierarchicalInstances)
KIBIS_PIN * pin
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
@ SCH_MARKER_T
Definition typeinfo.h:155