KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_issue23020_deep_hierarchy.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 modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
21#include "eeschema_test_utils.h"
22
23#include <core/profile.h>
24#include <sch_sheet_path.h>
25#include <sch_symbol.h>
27
28
30{
31protected:
32 wxFileName SchematicQAPath( const wxString& aRelativePath ) override
33 {
34 wxFileName fn( KI_TEST::GetEeschemaTestDataDir() );
35 fn.SetName( aRelativePath );
37
38 return fn;
39 }
40
43 static const SCH_SYMBOL_INSTANCE* NaiveFindInstance( const SCH_SYMBOL* aSymbol,
44 const KIID_PATH& aPath )
45 {
46 for( const SCH_SYMBOL_INSTANCE& instance : aSymbol->GetInstances() )
47 {
48 if( instance.m_Path == aPath )
49 return &instance;
50 }
51
52 return nullptr;
53 }
54};
55
56
57BOOST_FIXTURE_TEST_SUITE( Issue23020DeepHierarchy, TEST_ISSUE23020_FIXTURE )
58
59
60// The optimization for issue #23020 replaces the per-lookup linear scan of a symbol's
61// instance list with a path-keyed index. The index must return exactly what the linear
62// scan did for every symbol on every sheet path; this test pins that equivalence against
63// the real (deep, duplicate-sheet) hierarchy from the issue.
64BOOST_AUTO_TEST_CASE( InstanceLookupMatchesLinearScan )
65{
66 wxFileName fn( KI_TEST::GetEeschemaTestDataDir() );
67 fn.AppendDir( "issue23020" );
68 fn.SetName( "issue23020" );
70
71 if( !fn.FileExists() )
72 {
73 BOOST_TEST_MESSAGE( "Skipping test: issue23020 test data not found at " << fn.GetFullPath() );
74 return;
75 }
76
77 PROF_TIMER loadTimer;
78
79 LoadSchematic( fn );
80
81 loadTimer.Stop();
82 BOOST_TEST_MESSAGE( "Deep hierarchy load time: " << loadTimer.msecs() << " ms" );
83
84 SCH_SHEET_LIST sheets = m_schematic->BuildSheetListSortedByPageNumbers();
85
86 BOOST_TEST_MESSAGE( "Total sheet paths: " << sheets.size() );
87
88 // The point of the issue is a deep hierarchy with many duplicated sheets.
89 BOOST_CHECK( sheets.size() > 100 );
90
91 size_t symbolsChecked = 0;
92 PROF_TIMER lookupTimer;
93
94 for( const SCH_SHEET_PATH& sheet : sheets )
95 {
96 KIID_PATH path = sheet.Path();
97
98 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
99 {
100 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
101
102 const SCH_SYMBOL_INSTANCE* expected = NaiveFindInstance( symbol, path );
103
104 SCH_SYMBOL_INSTANCE indexed;
105 bool found = symbol->GetInstance( indexed, path );
106
107 BOOST_CHECK_EQUAL( found, expected != nullptr );
108
109 if( expected )
110 {
111 BOOST_CHECK( indexed.m_Path == expected->m_Path );
112 BOOST_CHECK_EQUAL( indexed.m_Reference, expected->m_Reference );
113 BOOST_CHECK_EQUAL( indexed.m_Unit, expected->m_Unit );
114
115 // GetRef()/GetUnitSelection() must agree with the raw instance data.
116 BOOST_CHECK_EQUAL( symbol->GetRef( &sheet, false ), expected->m_Reference );
117 BOOST_CHECK_EQUAL( symbol->GetUnitSelection( &sheet ), expected->m_Unit );
118 }
119
120 symbolsChecked++;
121 }
122 }
123
124 lookupTimer.Stop();
125
126 BOOST_TEST_MESSAGE( "Verified " << symbolsChecked << " indexed lookups in "
127 << lookupTimer.msecs() << " ms" );
128
129 BOOST_CHECK( symbolsChecked > 0 );
130}
131
132
133// Guards the index-maintenance code paths (add/remove/rebuild) directly: after a sequence
134// of mutations the index must still resolve every surviving path to the correct instance
135// and must not resolve a removed path. A desync (e.g. a missed rebuild after erase) breaks
136// this even though the underlying data is intact.
137BOOST_AUTO_TEST_CASE( IndexStaysConsistentThroughMutations )
138{
139 SCH_SYMBOL symbol;
140
141 auto makePath = []( int aTag ) -> KIID_PATH
142 {
144 path.push_back( KIID() );
145 path.push_back( KIID() );
146 path.push_back( KIID() );
147
148 // Tag the last element deterministically so lookups are reproducible.
149 path.back() = KIID( wxString::Format( "abcd1234-0000-0000-0000-%012d", aTag ) );
150
151 return path;
152 };
153
154 std::vector<KIID_PATH> paths;
155
156 for( int ii = 0; ii < 8; ++ii )
157 {
158 KIID_PATH path = makePath( ii );
159 paths.push_back( path );
160 symbol.AddHierarchicalReference( path, wxString::Format( "R%d", ii ), ii % 3 + 1 );
161 }
162
163 auto checkAll = [&]( const std::vector<size_t>& aLive )
164 {
165 for( size_t idx : aLive )
166 {
167 SCH_SYMBOL_INSTANCE instance;
168 BOOST_CHECK( symbol.GetInstance( instance, paths[idx] ) );
169 BOOST_CHECK( instance.m_Path == paths[idx] );
170 BOOST_CHECK_EQUAL( instance.m_Reference, wxString::Format( "R%zu", idx ) );
171 BOOST_CHECK_EQUAL( instance.m_Unit, (int) ( idx % 3 + 1 ) );
172 }
173 };
174
175 checkAll( { 0, 1, 2, 3, 4, 5, 6, 7 } );
176
177 // Remove a couple of interior entries; the index must be rebuilt so the survivors still
178 // map to their (now shifted) positions.
179 symbol.RemoveInstance( paths[2] );
180 symbol.RemoveInstance( paths[5] );
181
183 BOOST_CHECK( !symbol.GetInstance( gone, paths[2] ) );
184 BOOST_CHECK( !symbol.GetInstance( gone, paths[5] ) );
185
186 checkAll( { 0, 1, 3, 4, 6, 7 } );
187
188 // Re-add a removed path with a new reference; it must resolve to the new value.
189 symbol.AddHierarchicalReference( paths[2], "R2b", 2 );
190
191 SCH_SYMBOL_INSTANCE readded;
192 BOOST_CHECK( symbol.GetInstance( readded, paths[2] ) );
193 BOOST_CHECK_EQUAL( readded.m_Reference, "R2b" );
194
195 checkAll( { 0, 1, 3, 4, 6, 7 } );
196}
197
198
Definition kiid.h:44
A generic fixture for loading schematics and associated settings for qa tests.
A small class to help profiling.
Definition profile.h:46
void Stop()
Save the time when this function was called, and set the counter stane to stop.
Definition profile.h:86
double msecs(bool aSinceLast=false)
Definition profile.h:147
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:162
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
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
const std::vector< SCH_SYMBOL_INSTANCE > & GetInstances() const
Definition sch_symbol.h:128
void RemoveInstance(const SCH_SHEET_PATH &aInstancePath)
void AddHierarchicalReference(const KIID_PATH &aPath, const wxString &aRef, int aUnit)
Add a full hierarchical reference to this symbol.
bool GetInstance(SCH_SYMBOL_INSTANCE &aInstance, const KIID_PATH &aSheetPath, bool aTestFromEnd=false) const
int GetUnitSelection(const SCH_SHEET_PATH *aSheet) const
Return the instance-specific unit selection for the given sheet path.
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
wxFileName SchematicQAPath(const wxString &aRelativePath) override
static const SCH_SYMBOL_INSTANCE * NaiveFindInstance(const SCH_SYMBOL *aSymbol, const KIID_PATH &aPath)
Reference implementation: resolve a symbol instance by a linear scan over the raw instance vector,...
static const std::string KiCadSchematicFileExtension
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
static void LoadSchematic(SCHEMATIC *aSchematic, SCH_SHEET *aRootSheet, const wxString &aFileName)
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
A simple container for schematic symbol instance information.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(InstanceLookupMatchesLinearScan)
std::string path
VECTOR3I expected(15, 30, 45)
BOOST_TEST_MESSAGE("\n=== Real-World Polygon PIP Benchmark ===\n"<< formatTable(table))
BOOST_CHECK_EQUAL(result, "25.4")
@ SCH_SYMBOL_T
Definition typeinfo.h:169
Definition of file extensions used in Kicad.