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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 */
20
21// Regression test for https://gitlab.com/kicad/code/kicad/-/issues/24409
22
24
25#include <memory>
26#include <set>
27
28#include <connection_graph.h>
29#include <eeschema_helpers.h>
30#include <erc/erc.h>
31#include <erc/erc_item.h>
32#include <erc/erc_report.h>
33#include <erc/erc_settings.h>
34#include <locale_io.h>
35#include <sch_marker.h>
36#include <sch_pin.h>
37#include <sch_reference_list.h>
38#include <sch_screen.h>
39#include <sch_sheet.h>
40#include <sch_sheet_path.h>
41#include <sch_symbol.h>
42#include <schematic.h>
43
44
45// The reproduction case has three sheet instances of one sub-sheet with two
46// resistors R1/R2 (no wires). Per-instance annotations make this R1+R2,
47// R3+R4 and R5+R6. RunERC dedups markers that share a driver pin so each
48// pin produces exactly one pin_not_connected marker, but the dedup must
49// pick the marker on the *first* sheet path (by page number) so that
50// kicad-cli produces the same report a user sees in the GUI. Before the
51// fix the unordered iteration of SCH_ITEM::m_connection_map caused
52// subgraphs to be created in hash-bucket order, so the marker landed on
53// whichever instance happened to surface first (R5/R6 on this layout).
54BOOST_AUTO_TEST_CASE( Issue24409CliHierarchicalInstances )
55{
57
58 wxString schPath = wxString::FromUTF8( KI_TEST::GetEeschemaTestDataDir() ) +
59 wxS( "issue24409/issue24409.kicad_sch" );
60
61 // Use the CLI/headless loader directly so the regression covers the path
62 // exercised by kicad-cli sch erc / sch export bom / sch export netlist.
63 std::unique_ptr<SCHEMATIC> sch( EESCHEMA_HELPERS::LoadSchematic( schPath, true, false ) );
64 BOOST_REQUIRE( sch != nullptr );
65
66 SCH_SHEET_LIST sheets = sch->Hierarchy();
67
68 // Sanity: three sub-sheet instances + the root sheet.
69 BOOST_REQUIRE_EQUAL( sheets.size(), 4u );
70
71 // The two resistors get annotated R1..R6 once instances are resolved.
73 sheets.GetSymbols( refs, SYMBOL_FILTER_NON_POWER, false );
74 BOOST_REQUIRE_EQUAL( refs.GetCount(), 6u );
75
76 std::set<wxString> seenRefs;
77
78 for( size_t i = 0; i < refs.GetCount(); ++i )
79 seenRefs.insert( refs[i].GetRef() );
80
81 BOOST_CHECK( seenRefs.count( wxS( "R1" ) ) );
82 BOOST_CHECK( seenRefs.count( wxS( "R2" ) ) );
83 BOOST_CHECK( seenRefs.count( wxS( "R3" ) ) );
84 BOOST_CHECK( seenRefs.count( wxS( "R4" ) ) );
85 BOOST_CHECK( seenRefs.count( wxS( "R5" ) ) );
86 BOOST_CHECK( seenRefs.count( wxS( "R6" ) ) );
87
88 // Now run ERC against the loaded schematic and verify that every pin on
89 // every sheet instance has been reported. Disable the library-mismatch
90 // and sim-model checks because the reproduction project ships without
91 // installed libraries.
92 ERC_SETTINGS& settings = sch->ErcSettings();
96
97 sch->ConnectionGraph()->RunERC();
98
99 // Walk every sheet's screen and count pin_not_connected markers.
100 int unconnectedMarkers = 0;
101 std::set<wxString> markedRefs;
102
103 std::set<const SCH_SCREEN*> visitedScreens;
104
105 for( const SCH_SHEET_PATH& sheet : sheets )
106 {
107 SCH_SCREEN* screen = sheet.LastScreen();
108
109 // Each underlying screen carries its own markers; visit each only once.
110 if( !visitedScreens.insert( screen ).second )
111 continue;
112
113 for( SCH_ITEM* item : screen->Items().OfType( SCH_MARKER_T ) )
114 {
115 SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
116 std::shared_ptr<ERC_ITEM> ercItem =
117 std::dynamic_pointer_cast<ERC_ITEM>( marker->GetRCItem() );
118
119 if( !ercItem || ercItem->GetErrorCode() != ERCE_PIN_NOT_CONNECTED )
120 continue;
121
122 ++unconnectedMarkers;
123
124 const SCH_SHEET_PATH& markerSheet = ercItem->IsSheetSpecific()
125 ? ercItem->GetSpecificSheetPath()
126 : sheet;
127 EDA_ITEM* erred = sch->ResolveItem( ercItem->GetMainItemID(), nullptr, true );
128
129 if( SCH_PIN* pin = dynamic_cast<SCH_PIN*>( erred ) )
130 {
131 if( SCH_SYMBOL* parent = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() ) )
132 markedRefs.insert( parent->GetRef( &markerSheet ) );
133 }
134 }
135 }
136
137 // 2 resistors per sub-sheet * 2 pins per resistor = 4 logical pins; the
138 // RunERC dedup collapses the three instances of each pin down to one
139 // marker apiece so we expect 4 markers in total.
140 BOOST_CHECK_MESSAGE( unconnectedMarkers == 4,
141 "Expected 4 deduplicated pin_not_connected markers, got "
142 << unconnectedMarkers );
143
144 // The dedup must keep the marker on the first sheet instance by page
145 // number (Untitled Sheet, page 2, annotated R1/R2) so that kicad-cli
146 // matches the GUI. Without the fix this is R5/R6 because the unordered
147 // iteration of m_connection_map surfaces the last sheet first.
148 for( const wxString& ref : { wxS( "R1" ), wxS( "R2" ) } )
149 {
150 BOOST_CHECK_MESSAGE( markedRefs.count( ref ),
151 "Missing pin_not_connected marker for " << ref
152 << " on the first sheet instance" );
153 }
154
155 for( const wxString& ref : { wxS( "R3" ), wxS( "R4" ), wxS( "R5" ), wxS( "R6" ) } )
156 {
157 BOOST_CHECK_MESSAGE( !markedRefs.count( ref ),
158 "Unexpected pin_not_connected marker for " << ref
159 << "; dedup should keep only the first instance" );
160 }
161
162 // Also exercise the text report writer end-to-end so the regression catches
163 // the case where the marker is on the right sheet but the printed reference
164 // is wrong (i.e. the symbol's REFERENCE field text was left on the last
165 // sheet's annotation by the helpers' UpdateAllScreenReferences loop).
166 std::shared_ptr<SHEETLIST_ERC_ITEMS_PROVIDER> markersProvider =
167 std::make_shared<SHEETLIST_ERC_ITEMS_PROVIDER>( sch.get() );
168 markersProvider->SetSeverities( RPT_SEVERITY_ERROR | RPT_SEVERITY_WARNING );
169
170 ERC_REPORT reportWriter( sch.get(), EDA_UNITS::MM, markersProvider );
171 wxString report = reportWriter.GetTextReport();
172
173 for( const wxString& ref : { wxS( "R1" ), wxS( "R2" ) } )
174 {
175 BOOST_CHECK_MESSAGE( report.Contains( wxS( "Symbol " ) + ref + wxS( " " ) ),
176 "ERC text report missing pin_not_connected for "
177 << ref << "\n" << report );
178 }
179
180 for( const wxString& ref : { wxS( "R3" ), wxS( "R4" ), wxS( "R5" ), wxS( "R6" ) } )
181 {
182 BOOST_CHECK_MESSAGE( !report.Contains( wxS( "Symbol " ) + ref + wxS( " " ) ),
183 "ERC text report unexpectedly mentions "
184 << ref << "\n" << report );
185 }
186}
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:100
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:225
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:41
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:166
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:119
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:73
@ 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:159