KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_sch_sheet_path.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
24
27#include "eeschema_test_utils.h"
28
29// Code under test
30#include <sch_sheet_path.h>
31
33#include <eeschema_helpers.h>
34#include <sch_screen.h>
35#include <sch_sheet.h>
36#include <schematic.h>
37
38#include <atomic>
39#include <sstream>
40#include <thread>
41
43{
44public:
46 {
47 for( unsigned i = 0; i < 4; ++i )
48 {
49 m_sheets.emplace_back( nullptr, VECTOR2I( i, i ) );
50
51 std::ostringstream ss;
52 ss << "Sheet" << i;
53 m_sheets[i].GetField( FIELD_T::SHEET_NAME )->SetText( ss.str() );
54 m_sheets[i].SetParent( &m_schematic );
55 }
56
57 // 0->1->2
58 m_linear.push_back( &m_sheets[0] );
59 m_linear.push_back( &m_sheets[1] );
60 m_linear.push_back( &m_sheets[2] );
61 }
62
65
71
73 std::vector<SCH_SHEET> m_sheets;
74};
75
76
80BOOST_FIXTURE_TEST_SUITE( SchSheetPath, TEST_SCH_SHEET_PATH_FIXTURE )
81
82
83
87{
88 BOOST_CHECK_EQUAL( m_empty_path.size(), 0 );
89
90 BOOST_CHECK_THROW( m_empty_path.at( 0 ), std::out_of_range );
91
92 // Sheet paths with no SCH_SCHEET object are illegal.
93 // CHECK_WX_ASSERT( m_empty_path.GetPageNumber() );
94
95 // These accessors return nullptr when empty (i.e. they don't crash)
96 BOOST_CHECK_EQUAL( m_empty_path.Last(), nullptr );
97 BOOST_CHECK_EQUAL( m_empty_path.LastScreen(), nullptr );
98
99 BOOST_CHECK_EQUAL( m_empty_path.PathAsString(), "/" );
100 BOOST_CHECK_EQUAL( m_empty_path.PathHumanReadable(), "/" );
101}
102
103
108{
109 BOOST_CHECK_EQUAL( m_linear.size(), 3 );
110
111 BOOST_CHECK_EQUAL( m_linear.at( 0 ), &m_sheets[0] );
112 BOOST_CHECK_EQUAL( m_linear.at( 1 ), &m_sheets[1] );
113 BOOST_CHECK_EQUAL( m_linear.at( 2 ), &m_sheets[2] );
114
115 BOOST_CHECK_EQUAL( m_linear.Last(), &m_sheets[2] );
116 BOOST_CHECK_EQUAL( m_linear.LastScreen(), nullptr );
117
118 // don't know what the uuids will be, but we know the format: /<8-4-4-4-12>/<8-4-4-4-12>/
120 KI_TEST::IsUUIDPathWithLevels, ( m_linear.PathAsString().ToStdString() )( 2 ) );
121
122 // Sheet0 is the root sheet and isn't in the path
123 BOOST_CHECK_EQUAL( m_linear.PathHumanReadable(), "/Sheet1/Sheet2/" );
124}
125
126
128{
129 SCH_SHEET_PATH otherEmpty;
130
131 BOOST_CHECK( m_empty_path == otherEmpty );
132
133 BOOST_CHECK( m_empty_path != m_linear );
134}
135
136
137BOOST_AUTO_TEST_CASE( SheetListGetOrdinalPath )
138{
139 // The "complex_hierarchy" test project has a root sheet with two sheets that reference the
140 // same file.
141 std::unique_ptr<SCHEMATIC> schematic;
142 wxFileName fn( wxString::Format( wxS( "%snetlists/complex_hierarchy" ),
144 wxS( "complex_hierarchy" ), FILEEXT::KiCadSchematicFileExtension );
145
146 schematic.reset( EESCHEMA_HELPERS::LoadSchematic( fn.GetFullPath(), false, false, nullptr ) );
147 BOOST_REQUIRE( schematic );
148
149 SCH_SHEET_LIST hierarchy = schematic->Hierarchy();
150 BOOST_CHECK_EQUAL( hierarchy.size(), 3 );
151
152 // A null pointer should always result in an empty return value.
153 BOOST_CHECK( !hierarchy.GetOrdinalPath( nullptr ) );
154
155 // The root sheet is a single instance. It's always ordinal.
156 BOOST_CHECK( hierarchy.GetOrdinalPath( schematic->RootScreen() ).value() == hierarchy.at( 0 ) );
157
158 // The shared schematic with the lowest page number is the ordinal sheet path.
159 SCH_SHEET* sheet = hierarchy.at( 1 ).Last();
160 BOOST_CHECK( hierarchy.GetOrdinalPath( sheet->GetScreen() ).value() == hierarchy.at( 1 ) );
161
162 // The shared sheet with a higher page number is not the ordinal sheet path.
163 sheet = hierarchy.at( 2 ).Last();
164 BOOST_CHECK( hierarchy.GetOrdinalPath( sheet->GetScreen() ).value() == hierarchy.at( 1 ) );
165}
166
167
171BOOST_AUTO_TEST_CASE( SheetPathPageProperties )
172{
173 // BOOST_CHECK_EQUAL( m_linear.GetPageNumber(), wxEmptyString );
174
175 // Add new instance to sheet object.
176 // BOOST_CHECK( m_linear.Last()->AddInstance( m_linear.Path() ) );
177 // m_linear.SetPageNumber( "1" );
178 // BOOST_CHECK_EQUAL( m_linear.GetPageNumber(), "1" );
179 // m_linear.SetPageNumber( "i" );
180 // BOOST_CHECK_EQUAL( m_linear.GetPageNumber(), "i" );
181}
182
183
189BOOST_AUTO_TEST_CASE( PathHumanReadableWithSlashes )
190{
191 SCH_SHEET_PATH pathWithSlash;
192 SCHEMATIC schematicLocal( nullptr );
193 std::vector<SCH_SHEET> sheets;
194
195 for( unsigned i = 0; i < 3; ++i )
196 {
197 sheets.emplace_back( nullptr, VECTOR2I( i, i ) );
198 sheets[i].SetParent( &schematicLocal );
199 }
200
201 sheets[0].GetField( FIELD_T::SHEET_NAME )->SetText( "Root" );
202 sheets[1].GetField( FIELD_T::SHEET_NAME )->SetText( "Power/Supply" );
203 sheets[2].GetField( FIELD_T::SHEET_NAME )->SetText( "SubSheet" );
204
205 pathWithSlash.push_back( &sheets[0] );
206 pathWithSlash.push_back( &sheets[1] );
207 pathWithSlash.push_back( &sheets[2] );
208
209 // Without escaping, the path contains the literal '/' in the sheet name
210 wxString unescaped = pathWithSlash.PathHumanReadable( true, false, false );
211 BOOST_CHECK_EQUAL( unescaped, "/Power/Supply/SubSheet/" );
212
213 // With escaping, the '/' in the sheet name becomes "{slash}"
214 wxString escaped = pathWithSlash.PathHumanReadable( true, false, true );
215 BOOST_CHECK_EQUAL( escaped, "/Power{slash}Supply/SubSheet/" );
216
217 // The escaped version should be unambiguous since '/' only means path separator
218 // and "{slash}" means a literal slash character in the sheet name.
219
220 // Test with stripping trailing separator
221 wxString escapedNoTrail = pathWithSlash.PathHumanReadable( true, true, true );
222 BOOST_CHECK_EQUAL( escapedNoTrail, "/Power{slash}Supply/SubSheet" );
223}
224
225
229BOOST_AUTO_TEST_CASE( PathFollowsSheetListChanges )
230{
231 const KIID_PATH before = m_linear.Path();
232
233 BOOST_REQUIRE_EQUAL( before.size(), 3u );
234
235 m_linear.push_back( &m_sheets[3] );
236
237 const KIID_PATH grown = m_linear.Path();
238
239 BOOST_CHECK_EQUAL( grown.size(), 4u );
240 BOOST_CHECK( grown != before );
241
242 m_linear.pop_back();
243
244 BOOST_CHECK( m_linear.Path() == before );
245}
246
247
253BOOST_AUTO_TEST_CASE( PathIsSafeOnSharedPaths )
254{
255 // Copy before asking m_linear for its path: a lazily filled cache would otherwise come
256 // across already populated and the threads would never race to fill it
257 std::vector<SCH_SHEET_PATH> shared( 4000, m_linear );
258
259 const KIID_PATH expected = m_linear.Path();
260
261 unsigned threadCount = std::max( 4u, std::min( 8u, std::thread::hardware_concurrency() ) );
262
263 std::atomic<unsigned> live( 0 );
264 std::atomic<unsigned> mismatches( 0 );
265 std::vector<std::thread> threads;
266
267 for( unsigned ii = 0; ii < threadCount; ++ii )
268 {
269 threads.emplace_back(
270 [&]()
271 {
272 // Walk in step with the other threads so they collide on the same path
273 live.fetch_add( 1 );
274
275 while( live.load() < threadCount )
276 std::this_thread::yield();
277
278 for( const SCH_SHEET_PATH& path : shared )
279 {
280 if( path.Path() != expected )
281 mismatches.fetch_add( 1 );
282 }
283 } );
284 }
285
286 for( std::thread& thread : threads )
287 thread.join();
288
289 BOOST_CHECK_EQUAL( mismatches.load(), 0u );
290}
291
292
static SCHEMATIC * LoadSchematic(const wxString &aFileName, bool aSetActive, bool aForceDefaultProject, PROJECT *aProject=nullptr, bool aCalculateConnectivity=true, REPORTER *aRootReporter=nullptr)
Holds all the data relating to one schematic.
Definition schematic.h:148
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
std::optional< SCH_SHEET_PATH > GetOrdinalPath(const SCH_SCREEN *aScreen) const
Return the ordinal sheet path of aScreen.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
wxString PathHumanReadable(bool aUseShortRootName=true, bool aStripTrailingSeparator=false, bool aEscapeSheetNames=false) const
Return the sheet path in a human readable form made from the sheet names.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:145
std::vector< SCH_SHEET > m_sheets
handy store of SCH_SHEET objects
SCH_SHEET_PATH m_linear
We look at sheet 2 in the hierarchy: Sheets: 0 -> 1 -> 2.
static const std::string KiCadSchematicFileExtension
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
bool IsUUIDPathWithLevels(const std::string &aStr, unsigned aLevels)
Predicate to check a string is a UUID path format.
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
std::string path
VECTOR3I expected(15, 30, 45)
BOOST_AUTO_TEST_CASE(Empty)
Declare the test suite.
BOOST_CHECK_PREDICATE(ArePolylineEndPointsNearCircle,(chain)(c.m_geom.m_center_point)(radius)(accuracy+epsilon))
BOOST_CHECK_EQUAL(result, "25.4")
Test utilities for timestamps.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
Definition of file extensions used in Kicad.