KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_connectivity_dump.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
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21#include <boost/test/data/test_case.hpp>
23
28#include <connection_graph.h>
29#include <advanced_config.h>
30#include <sch_commit.h>
31#include <sch_label.h>
32#include <sch_line.h>
33#include <sch_pin.h>
34#include <sch_screen.h>
35#include <sch_sheet.h>
36#include <sch_symbol.h>
37#include <schematic.h>
39#include <tool/tool_manager.h>
40
41#include <json_common.h>
42#include <algorithm>
43#include <map>
44#include <set>
45#include <scoped_set_reset.h>
46
47
48namespace
49{
50// Keep this exhaustive corpus small because every item edit compares two full rebuilds
51// Native fixtures from the disabled entries in test_incremental_netlister.cpp's RemoveAddItems
52const char* const ORACLE_FIXTURES[] = {
53 "incremental_test", "issue10430", "issue10926_1", "issue11926", "issue12505", "issue12814",
54 "issue13112", "issue13162", "issue13212", "issue13431", "issue13591", "issue16223", "issue6588", "issue9367"
55};
56
57const char* const PARITY_FIXTURES[] = {
58 "incremental_test", "issue10430", "issue10926_1", "issue11926", "issue12505", "issue12814",
59 "issue13112", "issue13162", "issue13212", "issue13431", "issue13591", "issue16223", "issue6588", "issue9367",
60 "issue18346", "unconnected_bus_entry_qa", "netlist_exporter_unit_metadata_per_unit",
61 "netlists/multinetclasses/multinetclasses", "issue22286/bugtest", "issue7203", "netlists/jumpers/jumpers",
62 "issue23058/issue23058", "netlists/bus_junctions/bus_junctions",
63 "netlists/prefix_bus_alias/prefix_bus_alias",
64 "issue9673/issue9673", "erc_directive_label_not_connected", "netlists/legacy_power/legacy_power",
65 "same_local_global_power", "issue1768/issue1768", "issue18092/issue18092", "issue22864/Test_Move_Grid",
66 "issue23840/BusAndVectors"
67};
68
69// Legacy records each island's own driver while the engine records the whole net's winner
70void EraseDrivers( nlohmann::json& aValue )
71{
72 if( aValue.is_object() )
73 {
74 aValue.erase( "driver" );
75 aValue.erase( "driver_path" );
76 }
77
78 if( aValue.is_structured() )
79 {
80 for( auto& child : aValue )
81 EraseDrivers( child );
82 }
83}
84}
85
86
88{
90 std::unique_ptr<SCHEMATIC> schematic;
91};
92
93
94BOOST_FIXTURE_TEST_SUITE( ConnectivityDump, CONNECTIVITY_DUMP_FIXTURE )
95
96BOOST_AUTO_TEST_CASE( ShadowDumpResolvesClassesWithoutLegacyAssignments )
97{
98 KI_TEST::LoadSchematic( settings, "netlists/multinetclasses/multinetclasses", schematic );
99 auto& facade = schematic->Connectivity();
100 facade.Update( *schematic );
101 const auto legacy = SCH_CONNECTIVITY::Dump( *schematic );
102 const auto engine = SCH_CONNECTIVITY::Dump( *schematic, facade );
103 auto classes = schematic->Project().GetProjectFile().NetSettings();
104 classes->SetNetclassLabelAssignment( "/NET_1", { "CORRUPTED" } );
105 classes->ClearCacheForNet( "/NET_1" );
106
107 // Parity would be vacuous if the engine Dump read the cached assignments legacy rows use
108 BOOST_REQUIRE_NE( SCH_CONNECTIVITY::Dump( *schematic ), legacy );
109 BOOST_CHECK_EQUAL( SCH_CONNECTIVITY::Dump( *schematic, facade ), engine );
110}
111
113 LegacyAndEngineDumpsMatch,
114 boost::unit_test::data::make( PARITY_FIXTURES ), fixture )
115{
116 auto& enabled = const_cast<ADVANCED_CFG&>( ADVANCED_CFG::GetCfg() ).m_ConnectivityEngine;
117 SCOPED_SET_RESET restore( enabled, false );
118
119 BOOST_TEST_CONTEXT( fixture )
120 {
121 KI_TEST::LoadSchematic( settings, fixture, schematic );
122 auto& facade = schematic->Connectivity();
123 facade.Update( *schematic );
124 auto legacy = nlohmann::json::parse( SCH_CONNECTIVITY::Dump( *schematic ) );
125 auto engine = nlohmann::json::parse( SCH_CONNECTIVITY::Dump( *schematic, facade ) );
126 EraseDrivers( legacy );
127 EraseDrivers( engine );
128 BOOST_CHECK_MESSAGE( legacy == engine, nlohmann::json::diff( legacy, engine ).dump( 2 ) );
129 }
130}
131
132BOOST_AUTO_TEST_CASE( NativeBusNamesPreserveMembership )
133{
134 using namespace SCH_CONNECTIVITY;
135 using KEY = std::pair<std::string, std::string>;
136 KI_TEST::LoadSchematic( settings, "issue9673/issue9673", schematic );
137 auto& facade = schematic->Connectivity();
138 facade.Update( *schematic );
139 const auto& keys = facade.Keys();
140 const auto legacy = nlohmann::json::parse( SCH_CONNECTIVITY::Dump( *schematic ) );
141 const auto shadow = nlohmann::json::parse( SCH_CONNECTIVITY::Dump( *schematic, facade ) );
142 std::map<KEY, std::set<KEY>> legacyBuses;
143 std::map<int, std::set<KEY>> legacyNets;
144 std::map<KEY, const nlohmann::json*> shadowRows;
145
146 for( const auto& row : shadow )
147 shadowRows.emplace( KEY{ row.at( "path" ), row.at( "item" ) }, &row );
148
149 for( const auto& row : legacy )
150 {
151 const KEY key{ row.at( "path" ), row.at( "item" ) };
152 const auto path = schematic->Hierarchy().GetSheetPathByKIIDPath(
153 KIID_PATH( wxString::FromUTF8( key.first ) ) );
155 const auto* item = path->LastScreen()->GetConnectivityItem( KIID( key.second ) );
156 BOOST_REQUIRE( item );
157 const auto* connection = item->Connection( &*path );
158
159 if( !connection || !connection->Driver() )
160 continue;
161
162 if( connection->IsBus() )
163 legacyBuses[KEY{ row.at( "driver_path" ), row.at( "driver" ) }].insert( key );
164 else if( connection->IsNet() )
165 {
166 BOOST_REQUIRE_GT( connection->NetCode(), 0 );
167 legacyNets[connection->NetCode()].insert( key );
168 const auto after = shadowRows.find( key );
169 BOOST_REQUIRE( after != shadowRows.end() );
170 BOOST_CHECK( row.at( "name" ) == after->second->at( "name" ) );
171 BOOST_CHECK( row.at( "type" ) == after->second->at( "type" ) );
172 }
173 }
174
175 const auto itemKeys = [&]( const auto& items )
176 {
177 std::set<KEY> result;
178
179 for( const ITEM_KEY& item : items )
180 result.emplace( keys.Instance( item.inst ).AsString().ToStdString(),
181 item.item.AsString().ToStdString() );
182
183 return result;
184 };
185 const auto checkFields = []( const auto& before, const auto& after )
186 {
187 for( const char* field : { "name", "local_name", "type", "netclasses" } )
188 BOOST_CHECK_MESSAGE( before.at( field ) == after.at( field ), field );
189 };
190 const auto checkMembers = [&]( const auto& self, const auto& before, const auto& after ) -> void
191 {
192 BOOST_REQUIRE_EQUAL( before.size(), after.size() );
193
194 for( size_t i = 0; i < before.size(); ++i )
195 {
196 checkFields( before[i], after[i] );
197 self( self, before[i].at( "members" ), after[i].at( "members" ) );
198 }
199 };
200
201 for( const char* name : { "/{FOO BAR HAM EGGS}", "/{MIXED_BUS}", "/A[0..4]", "/D[0..7]" } )
202 {
203 const auto row = std::find_if( legacy.begin(), legacy.end(),
204 [&]( const auto& value ) { return value.at( "name" ) == name; } );
205 BOOST_REQUIRE( row != legacy.end() );
206 const auto path = schematic->Hierarchy().GetSheetPathByKIIDPath(
207 KIID_PATH( wxString::FromUTF8( row->at( "path" ).get<std::string>() ) ) );
209 const KIID itemId( row->at( "item" ).get<std::string>() );
210 const auto instance = keys.FindInstance( path->Path() );
211 BOOST_REQUIRE( instance );
213 {
214 const auto result = facade.Published().Rows().find( ITEM_KEY{ itemId, *instance } );
215 BOOST_REQUIRE( result != facade.Published().Rows().end() );
216 const auto& component = facade.Published().Components().at( result->second.component );
217 BOOST_REQUIRE( component.name != INVALID_ID );
218 const auto actual = itemKeys( component.content->items );
219 const auto& expectedItems = legacyBuses.at( KEY{ row->at( "driver_path" ), row->at( "driver" ) } );
220 BOOST_CHECK_MESSAGE( actual == expectedItems, nlohmann::json( actual ).dump()
221 << " expected " << nlohmann::json( expectedItems ).dump() );
222 BOOST_CHECK_EQUAL( keys.Name( component.name ).ToStdString( wxConvUTF8 ), name );
223
224 for( const auto& before : legacy )
225 {
226 const KEY key{ before.at( "path" ), before.at( "item" ) };
227
228 if( !expectedItems.contains( key ) )
229 continue;
230
231 const auto after = shadowRows.find( key );
232 BOOST_REQUIRE( after != shadowRows.end() );
233 BOOST_REQUIRE( !before.at( "members" ).empty() );
234 checkFields( before, *after->second );
235 checkMembers( checkMembers, before.at( "members" ), after->second->at( "members" ) );
236 }
237 }
238 }
239
240 for( const auto& [code, items] : legacyNets )
241 {
242 const KEY& first = *items.begin();
243 const auto instance = keys.FindInstance( KIID_PATH( wxString::FromUTF8( first.first ) ) );
244 BOOST_REQUIRE( instance );
245 BOOST_TEST_CONTEXT( code )
246 {
247 const auto row = facade.Published().Rows().find( ITEM_KEY{ KIID( first.second ), *instance } );
248 BOOST_REQUIRE( row != facade.Published().Rows().end() );
249 const auto& component = facade.Published().Components().at( row->second.component );
250 BOOST_CHECK( itemKeys( component.content->items ) == items );
251 }
252 }
253
254 BOOST_CHECK_GT( legacyNets.size(), 0u );
255}
256
257BOOST_AUTO_TEST_CASE( NativeMemberPriorityPreservesScalarPartitions )
258{
259 using namespace SCH_CONNECTIVITY;
260 using KEY = std::pair<std::string, std::string>;
261
262 for( const char* fixture : { "netlists/hierarchy_aliases/hierarchy_aliases",
263 "netlists/multinetclasses/multinetclasses" } )
264 {
265 BOOST_TEST_CONTEXT( fixture )
266 {
267 KI_TEST::LoadSchematic( settings, fixture, schematic );
268 auto& facade = schematic->Connectivity();
269 facade.Update( *schematic );
270 const auto& keys = facade.Keys();
271 const auto legacy = nlohmann::json::parse( SCH_CONNECTIVITY::Dump( *schematic ) );
272 const auto shadow = nlohmann::json::parse( SCH_CONNECTIVITY::Dump( *schematic, facade ) );
273 BOOST_REQUIRE_EQUAL( legacy.size(), shadow.size() );
274
275 for( size_t i = 0; i < legacy.size(); ++i )
276 {
277 BOOST_REQUIRE( legacy[i].at( "path" ) == shadow[i].at( "path" ) );
278 BOOST_REQUIRE( legacy[i].at( "item" ) == shadow[i].at( "item" ) );
279 }
280
281 const auto paths = schematic->Hierarchy();
282 std::map<int, std::set<KEY>> nets;
283 std::map<int, std::string> names;
284
285 for( const auto& row : legacy )
286 {
287 const KEY key{ row.at( "path" ), row.at( "item" ) };
288 const auto path = paths.GetSheetPathByKIIDPath( KIID_PATH( wxString::FromUTF8( key.first ) ) );
290 const auto* item = path->LastScreen()->GetConnectivityItem( KIID( key.second ) );
291 BOOST_REQUIRE( item );
292 const auto* connection = item->Connection( &*path );
293
294 if( connection && connection->IsNet() && connection->Driver() )
295 {
296 BOOST_REQUIRE_GT( connection->NetCode(), 0 );
297 nets[connection->NetCode()].insert( key );
298 names[connection->NetCode()] = connection->Name().ToStdString( wxConvUTF8 );
299 }
300 }
301
302 for( const auto& [code, items] : nets )
303 {
304 const auto& first = *items.begin();
305 const auto instance = keys.FindInstance( KIID_PATH( wxString::FromUTF8( first.first ) ) );
306 BOOST_REQUIRE( instance );
307 const auto row = facade.Published().Rows().find( ITEM_KEY{ KIID( first.second ), *instance } );
308 BOOST_REQUIRE( row != facade.Published().Rows().end() );
309 const auto& component = facade.Published().Components().at( row->second.component );
310 std::set<KEY> actual;
311
312 for( const ITEM_KEY& item : component.content->items )
313 actual.emplace( keys.Instance( item.inst ).AsString().ToStdString(),
314 item.item.AsString().ToStdString() );
315
316 BOOST_CHECK_MESSAGE( actual == items, "Net code " << code );
317 BOOST_REQUIRE( component.name != INVALID_ID );
318 BOOST_CHECK_EQUAL( keys.Name( component.name ).ToStdString( wxConvUTF8 ), names.at( code ) );
319 }
320 }
321 }
322}
323
324BOOST_AUTO_TEST_CASE( HeadlessCommitPublishesCompletedCleanup )
325{
326 auto& config = const_cast<ADVANCED_CFG&>( ADVANCED_CFG::GetCfg() );
327 SCOPED_SET_RESET restoreEngine( config.m_ConnectivityEngine, config.m_ConnectivityEngine );
328
329 for( bool engine : { false, true } )
330 {
331 BOOST_TEST_CONTEXT( "new engine " << engine )
332 {
333 config.m_ConnectivityEngine = engine;
334 KI_TEST::LoadSchematic( settings, "issue12505", schematic );
335 schematic->RebuildConnectivity();
336 SCH_SCREEN* screen = schematic->GetCurrentScreen();
337 const KIID junction( "2934c2df-d750-4949-9bb5-2d5234f61386" );
338 auto* wire = dynamic_cast<SCH_LINE*>(
339 screen->GetConnectivityItem( KIID( "06af46a6-6f60-4f17-95ae-2a85a89f02a6" ) ) );
340 auto* upper = dynamic_cast<SCH_LINE*>(
341 screen->GetConnectivityItem( KIID( "ac6a6b32-a43c-4495-b132-b2f68491aae2" ) ) );
342 auto* lower = dynamic_cast<SCH_LINE*>(
343 screen->GetConnectivityItem( KIID( "f145de0f-02ed-4631-85ea-7126ccf81633" ) ) );
344 BOOST_REQUIRE( wire && upper && lower );
345 SCH_ITEM* dot = screen->GetConnectivityItem( junction );
346 BOOST_REQUIRE( dot );
347 const VECTOR2I position = dot->GetPosition();
348 BOOST_REQUIRE( wire->IsEndPoint( position ) && upper->IsEndPoint( position )
349 && lower->IsEndPoint( position ) );
350 BOOST_REQUIRE_EQUAL( screen->GetBusesAndWires( position, false ).size(), 3u );
351 const std::set<int> directions{ wire->GetAngleFrom( position ), upper->GetAngleFrom( position ),
352 lower->GetAngleFrom( position ) };
353 BOOST_REQUIRE_EQUAL( directions.size(), 3u );
354 const VECTOR2I start = upper->GetStartPoint();
355 const VECTOR2I end = lower->GetEndPoint();
356 int notifications = 0;
357 auto subscription = schematic->Connectivity().Subscribe(
358 [&]( const SCH_CONNECTIVITY::CHANGE_SET& )
359 {
361 BOOST_CHECK( !screen->GetConnectivityItem( junction ) );
362 } );
363 TOOL_MANAGER manager;
364 manager.SetEnvironment( schematic.get(), nullptr, nullptr, nullptr, nullptr );
365 SCH_COMMIT move( &manager );
366 schematic->CleanUp( &move, screen );
367 BOOST_REQUIRE( move.Empty() );
368 BOOST_REQUIRE( screen->GetConnectivityItem( junction ) == dot );
369 BOOST_CHECK( dot->GetPosition() == position );
370 move.Modify( wire, screen );
371 wire->Move( VECTOR2I( 10000000, 10000000 ) );
372 BOOST_REQUIRE( !wire->IsEndPoint( position ) );
373 BOOST_REQUIRE( upper->IsEndPoint( position ) && lower->IsEndPoint( position ) );
374 BOOST_REQUIRE_NE( upper->GetAngleFrom( position ), lower->GetAngleFrom( position ) );
375 BOOST_REQUIRE_EQUAL( screen->GetBusesAndWires( position, false ).size(), 2u );
376 move.Push( "Disconnect native junction", SKIP_UNDO );
377 BOOST_CHECK( !screen->GetConnectivityItem( junction ) );
378 BOOST_CHECK_EQUAL( notifications, engine ? 1 : 0 );
379 size_t merged = 0;
380
381 for( SCH_ITEM* item : screen->Items() )
382 {
383 BOOST_CHECK( !item->HasFlag( STRUCT_DELETED ) );
384
385 if( auto* line = dynamic_cast<SCH_LINE*>( item );
386 line && line->IsEndPoint( start ) && line->IsEndPoint( end ) )
387 ++merged;
388 }
389
390 BOOST_CHECK_EQUAL( merged, 1u );
391 const auto dump = [&]()
392 {
393 return engine ? SCH_CONNECTIVITY::Dump( *schematic, schematic->Connectivity() )
394 : SCH_CONNECTIVITY::Dump( *schematic );
395 };
396 const std::string incremental = dump();
397 schematic->RebuildConnectivity();
398 BOOST_CHECK_EQUAL( incremental, dump() );
399 }
400 }
401}
402
403
405 PreviouslyDisabledFixturesMatchFullAndFreshRebuildsAfterCommits,
406 boost::unit_test::data::make( ORACLE_FIXTURES ), fixture )
407{
408 auto& config = const_cast<ADVANCED_CFG&>( ADVANCED_CFG::GetCfg() );
409 SCOPED_SET_RESET restoreEngine( config.m_ConnectivityEngine, config.m_ConnectivityEngine );
410 SCOPED_SET_RESET restoreIncremental( config.m_IncrementalConnectivity, config.m_IncrementalConnectivity );
411 config.m_ConnectivityEngine = true;
412 config.m_IncrementalConnectivity = true;
413
414 KI_TEST::LoadSchematic( settings, fixture, schematic );
415 TOOL_MANAGER manager;
416 manager.SetEnvironment( schematic.get(), nullptr, nullptr, nullptr, nullptr );
417 SCH_COMMIT preparation( &manager );
418 schematic->CleanUpConnections( &preparation, GLOBAL_CLEANUP );
419 preparation.Push( "Prepare native oracle geometry", SKIP_UNDO | SKIP_CONNECTIVITY | DELETE_REMOVED_ITEMS );
420 schematic->RebuildConnectivity();
421 SCH_SYMBOL* symbol = nullptr;
422
423 for( const SCH_SHEET_PATH& path : schematic->Hierarchy() )
424 {
425 for( SCH_ITEM* item : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
426 {
427 auto* candidate = static_cast<SCH_SYMBOL*>( item );
428
429 if( !candidate->IsPower() && !candidate->GetPins( &path ).empty() )
430 {
431 symbol = candidate;
432 break;
433 }
434 }
435
436 if( symbol )
437 break;
438 }
439
440 BOOST_REQUIRE( symbol );
441 BOOST_REQUIRE( !symbol->GetParentGroup() );
442 std::set<SCH_SCREEN*> seenScreens;
443 const auto isGeometry = []( const SCH_ITEM* item )
444 {
445 return item->IsConnectable()
446 && ( item->Type() == SCH_LINE_T || item->Type() == SCH_BUS_WIRE_ENTRY_T
447 || item->Type() == SCH_BUS_BUS_ENTRY_T || item->Type() == SCH_JUNCTION_T
448 || item->Type() == SCH_NO_CONNECT_T );
449 };
450 std::vector<std::pair<SCH_SCREEN*, std::unique_ptr<SCH_ITEM>>> geometry;
451
452 for( const SCH_SHEET_PATH& path : schematic->Hierarchy() )
453 {
454 if( !seenScreens.insert( path.LastScreen() ).second )
455 continue;
456
457 for( SCH_ITEM* item : path.LastScreen()->Items() )
458 {
459 if( isGeometry( item ) )
460 {
461 BOOST_REQUIRE( !item->GetParentGroup() );
462 geometry.emplace_back( path.LastScreen(), static_cast<SCH_ITEM*>( item->Clone() ) );
463 }
464 }
465 }
466
467 std::sort( geometry.begin(), geometry.end(),
468 []( const auto& a, const auto& b ) { return a.second->m_Uuid < b.second->m_Uuid; } );
469 const VECTOR2I offset( 10000000, 10000000 );
471 rebuilt.Update( *schematic, true );
472 const auto verify = [&]()
473 {
474 for( SCH_SCREEN* screen : seenScreens )
475 {
476 for( SCH_ITEM* item : screen->Items() )
477 {
478 BOOST_REQUIRE_MESSAGE( !item->HasFlag( STRUCT_DELETED ),
479 "Cleanup left a deleted item on the screen: "
480 << item->m_Uuid.AsString().ToStdString() );
481 }
482 }
483
484 const std::string incremental = SCH_CONNECTIVITY::Dump( *schematic, schematic->Connectivity() );
485 rebuilt.Update( *schematic, true );
486 BOOST_CHECK_EQUAL( incremental, SCH_CONNECTIVITY::Dump( *schematic, rebuilt ) );
487 // A separate fresh engine also detects stale content-keyed caches retained by a full rebuild
489 fresh.Update( *schematic, true );
490 BOOST_CHECK_EQUAL( incremental, SCH_CONNECTIVITY::Dump( *schematic, fresh ) );
491 std::map<int, int> toFresh;
492 std::map<int, int> fromFresh;
493 const auto& current = schematic->Connectivity();
494
495 for( const auto& [key, row] : current.Published().Rows() )
496 {
497 const auto& path = current.Keys().Instance( key.inst );
498 const auto actual = current.Connection( key.item, path );
499 const auto full = rebuilt.Connection( key.item, path );
500 const auto empty = fresh.Connection( key.item, path );
501 BOOST_REQUIRE( actual && full && empty );
502 BOOST_CHECK_EQUAL( actual->NetCode(), full->NetCode() );
503 BOOST_CHECK_EQUAL( actual->NetCode() == 0, empty->NetCode() == 0 );
504 const auto forward = toFresh.emplace( actual->NetCode(), empty->NetCode() ).first;
505 const auto reverse = fromFresh.emplace( empty->NetCode(), actual->NetCode() ).first;
506 BOOST_CHECK_EQUAL( forward->second, empty->NetCode() );
507 BOOST_CHECK_EQUAL( reverse->second, actual->NetCode() );
508 }
509 };
510
511 const std::string original = SCH_CONNECTIVITY::Dump( *schematic, schematic->Connectivity() );
512 const auto restoreGeometry = [&]()
513 {
514 // A wire edit can remove junctions and merge other wires. Restore all native geometry
515 // through a commit, preserving engine history while recovering the exact source state
516 SCH_COMMIT restore( &manager );
517 std::vector<SCH_ITEM*> removed;
518 std::vector<std::pair<SCH_SCREEN*, std::unique_ptr<SCH_ITEM>>> added;
519
520 for( SCH_SCREEN* screen : seenScreens )
521 {
522 for( SCH_ITEM* item : screen->Items() )
523 {
524 if( isGeometry( item ) )
525 {
526 restore.Remove( item, screen );
527 removed.push_back( item );
528 }
529 }
530 }
531
532 for( const auto& [screen, source] : geometry )
533 {
534 auto copy = std::unique_ptr<SCH_ITEM>( static_cast<SCH_ITEM*>( source->Clone() ) );
535 BOOST_REQUIRE( copy && copy->m_Uuid == source->m_Uuid );
536 restore.Add( copy.get(), screen );
537 added.emplace_back( screen, std::move( copy ) );
538 }
539
540 restore.Push( "Restore native oracle geometry", SKIP_UNDO );
541
542 for( auto& [screen, item] : added )
543 {
544 BOOST_REQUIRE( screen->CheckIfOnDrawList( item.get() ) );
545 item.release();
546 }
547
548 for( SCH_ITEM* item : removed )
549 delete item;
550
551 verify();
552 BOOST_CHECK_EQUAL( original, SCH_CONNECTIVITY::Dump( *schematic, schematic->Connectivity() ) );
553 };
554
555 for( const VECTOR2I& movement : { offset, VECTOR2I( -offset.x, -offset.y ) } )
556 {
557 SCH_SCREEN* screen = symbol->GetParentScreen();
558 SCH_COMMIT commit( &manager );
559 commit.Modify( symbol, screen );
560 symbol->Move( movement );
561 commit.Push( "Move native oracle symbol", SKIP_UNDO );
562 BOOST_CHECK( !schematic->Connectivity().Published().Changes().Empty() );
563 verify();
564 }
565
566 restoreGeometry();
567 SCH_SCREEN* symbolScreen = symbol->GetParentScreen();
568 // Restore the same item and pin identities after destroying the original object
569 auto symbolReplacement = std::make_unique<SCH_SYMBOL>( *symbol );
570 SCH_COMMIT removal( &manager );
571 removal.Remove( symbol, symbolScreen );
572 removal.Push( "Remove native oracle symbol", SKIP_UNDO );
573 BOOST_REQUIRE( !symbolScreen->CheckIfOnDrawList( symbol ) );
574 delete symbol;
575 verify();
576 BOOST_CHECK_NE( original, SCH_CONNECTIVITY::Dump( *schematic, schematic->Connectivity() ) );
577
578 SCH_COMMIT addition( &manager );
579 addition.Add( symbolReplacement.get(), symbolScreen );
580 addition.Push( "Restore native oracle symbol", SKIP_UNDO );
581 BOOST_REQUIRE( symbolScreen->CheckIfOnDrawList( symbolReplacement.get() ) );
582 symbolReplacement.release();
583 verify();
584 restoreGeometry();
585
586 std::vector<SCH_LABEL_BASE*> labels;
587
588 for( SCH_SCREEN* screen : seenScreens )
589 {
590 for( SCH_ITEM* item : screen->Items() )
591 {
592 if( item->Type() == SCH_LABEL_T || item->Type() == SCH_GLOBAL_LABEL_T
593 || item->Type() == SCH_HIER_LABEL_T )
594 labels.push_back( static_cast<SCH_LABEL_BASE*>( item ) );
595 }
596 }
597
598 std::sort( labels.begin(), labels.end(),
599 []( const auto* a, const auto* b ) { return a->m_Uuid < b->m_Uuid; } );
600
601 for( SCH_LABEL_BASE* label : labels )
602 {
603 const wxString originalText = label->GetText();
604
605 for( const wxString& text : { wxString( "ORACLE_" ) + originalText, originalText } )
606 {
607 SCH_COMMIT rename( &manager );
608 rename.Modify( label, label->GetParentScreen() );
609 label->SetText( text );
610 rename.Push( "Rename native oracle label", SKIP_UNDO );
611 verify();
612 }
613 }
614
615 for( const auto& [screen, source] : geometry )
616 {
617 const KIID& id = source->m_Uuid;
618
619 BOOST_TEST_CONTEXT( "geometry item " << id.AsString().ToStdString() )
620 {
621 SCH_ITEM* item = screen->GetConnectivityItem( id );
622 BOOST_REQUIRE( item );
623 BOOST_REQUIRE( !item->GetParentGroup() );
624 const VECTOR2I expectedPosition = item->GetPosition() + offset;
625 SCH_COMMIT move( &manager );
626 move.Modify( item, screen );
627 item->Move( offset );
628 BOOST_CHECK( item->GetPosition() == expectedPosition );
629 move.Push( "Move native oracle geometry", SKIP_UNDO );
630 BOOST_CHECK( !schematic->Connectivity().Published().Changes().Empty() );
631 verify();
632 restoreGeometry();
633
634 item = screen->GetConnectivityItem( id );
635 BOOST_REQUIRE( item );
636 SCH_COMMIT removal( &manager );
637 removal.Remove( item, screen );
638 removal.Push( "Remove native oracle geometry", SKIP_UNDO );
639 BOOST_REQUIRE( !screen->CheckIfOnDrawList( item ) );
640 delete item;
641
642 verify();
643 BOOST_CHECK_NE( original, SCH_CONNECTIVITY::Dump( *schematic, schematic->Connectivity() ) );
644
645 restoreGeometry();
646 }
647 }
648
649 BOOST_TEST_MESSAGE( fixture << ": checked " << labels.size() << " native label renames and restores, and "
650 << geometry.size() << " native geometry move/remove/restore sequences" );
651 const std::string incremental = SCH_CONNECTIVITY::Dump( *schematic, schematic->Connectivity() );
652 BOOST_CHECK_EQUAL( original, incremental );
653 schematic->RebuildConnectivity();
654 BOOST_CHECK_EQUAL( incremental, SCH_CONNECTIVITY::Dump( *schematic, schematic->Connectivity() ) );
655}
656
657
658BOOST_DATA_TEST_CASE_F( CONNECTIVITY_DUMP_FIXTURE, NativeLabelBatchMatchesFullRebuildAndRestores,
659 boost::unit_test::data::make( { "issue7203", "netlists/hierarchy_aliases/hierarchy_aliases" } ),
660 fixture )
661{
662 KI_TEST::LoadSchematic( settings, fixture, schematic );
663 const auto hierarchy = schematic->Hierarchy();
664 SCH_SCREEN* screen = hierarchy.front().LastScreen();
665
666 for( const auto& path : hierarchy )
667 {
668 SCH_SCREEN* candidate = path.LastScreen();
669 auto labels = candidate->Items().OfType( SCH_LABEL_T );
670 const auto instances = std::ranges::count_if( hierarchy, [&]( const auto& other )
671 {
672 return other.LastScreen() == candidate;
673 } );
674
675 if( instances > 1 && labels.begin() != labels.end() )
676 screen = candidate;
677 }
678
679 std::vector<SCH_SHEET_PATH> affected;
680
681 for( const auto& path : hierarchy )
682 {
683 if( path.LastScreen() == screen )
684 affected.push_back( path );
685 }
686
687 BOOST_REQUIRE_EQUAL( affected.size(), hierarchy.size() == 1 ? 1u : 2u );
688 std::vector<SCH_ITEM*> sources;
689
690 for( SCH_ITEM* item : screen->Items().OfType( SCH_LABEL_T ) )
691 sources.push_back( item );
692
693 BOOST_REQUIRE( !sources.empty() );
694 std::ranges::sort( sources, []( const SCH_ITEM* a, const SCH_ITEM* b ) { return a->m_Uuid < b->m_Uuid; } );
695 SCH_CONNECTIVITY::FACADE incremental;
696 incremental.Update( *schematic );
697 const std::string baseline = SCH_CONNECTIVITY::Dump( *schematic, incremental );
698 struct BATCH
699 {
700 SCH_SCREEN* screen;
701 std::vector<std::unique_ptr<SCH_ITEM>> items;
702
703 ~BATCH()
704 {
705 for( const auto& item : items )
706 {
707 if( screen->CheckIfOnDrawList( item.get() ) )
708 screen->Remove( item.get(), false );
709 }
710 }
711 } batch{ screen, {} };
712 batch.items.reserve( 500 );
713
714 for( size_t i = 0; i < 500; ++i )
715 {
716 std::unique_ptr<SCH_ITEM> copy( sources[i % sources.size()]->Duplicate( false ) );
717 copy->SetParentGroup( nullptr );
718 batch.items.push_back( std::move( copy ) );
719 }
720
722
723 for( int cycle = 0; cycle < 2; ++cycle )
724 {
725 for( const auto& item : batch.items )
726 screen->Append( item.get(), false );
727
728 incremental.Update( *schematic );
729
730 for( const auto& path : affected )
731 {
732 for( const auto& item : batch.items )
733 BOOST_REQUIRE( incremental.Connection( item->m_Uuid, path.Path() ) );
734 }
735
736 const std::string edited = SCH_CONNECTIVITY::Dump( *schematic, incremental );
737 BOOST_CHECK_NE( edited, baseline );
738 rebuilt.Update( *schematic, true );
739 BOOST_CHECK_EQUAL( edited, SCH_CONNECTIVITY::Dump( *schematic, rebuilt ) );
740
741 for( const auto& item : batch.items )
742 BOOST_REQUIRE( screen->Remove( item.get(), false ) );
743
744 incremental.Update( *schematic );
745 BOOST_CHECK_EQUAL( baseline, SCH_CONNECTIVITY::Dump( *schematic, incremental ) );
746
747 for( const auto& path : affected )
748 {
749 for( const auto& item : batch.items )
750 BOOST_CHECK( !incremental.Connection( item->m_Uuid, path.Path() ) );
751 }
752
753 rebuilt.Update( *schematic, true );
754 BOOST_CHECK_EQUAL( baseline, SCH_CONNECTIVITY::Dump( *schematic, rebuilt ) );
755 }
756}
757
const char * name
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:86
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:102
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:74
virtual VECTOR2I GetPosition() const
Definition eda_item.h:348
const KIID m_Uuid
Definition eda_item.h:597
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:116
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:248
Definition kiid.h:46
wxString AsString() const
Definition kiid.cpp:264
Commit for the schematic and symbol editors.
Definition sch_commit.h:54
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Execute the changes.
Model-owned source resolver.
void Update(SCHEMATIC &aSchematic, bool aRebuild=false)
std::optional< ITEM_VIEW > Connection(const KIID &aItem, const KIID_PATH &aPath) const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
SCH_SCREEN * GetParentScreen() const
Definition sch_item.cpp:303
virtual void Move(const VECTOR2I &aMoveVector)
Move the item by aMoveVector to a new position.
Definition sch_item.h:416
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:39
bool IsEndPoint(const VECTOR2I &aPoint) const override
Test if aPt is an end point of this schematic object.
Definition sch_line.h:88
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
std::vector< SCH_LINE * > GetBusesAndWires(const VECTOR2I &aPosition, bool aIgnoreEndpoints=false) const
Return buses and wires passing through aPosition.
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:122
bool Remove(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Remove aItem from the schematic associated with this screen.
SCH_ITEM * GetConnectivityItem(const KIID &aId) const
Resolve a drawing item or a connectable child on this screen; ambiguous IDs return null.
bool CheckIfOnDrawList(const SCH_ITEM *aItem) const
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:74
void Move(const VECTOR2I &aMoveVector) override
Move the item by aMoveVector to a new position.
Definition sch_symbol.h:792
RAII class that sets an value at construction and resets it to the original value at destruction.
Master controller class:
void SetEnvironment(EDA_ITEM *aModel, KIGFX::VIEW *aView, KIGFX::VIEW_CONTROLS *aViewControls, APP_SETTINGS_BASE *aSettings, TOOLS_HOLDER *aFrame)
Set the work environment (model, view, view controls and the parent window).
static bool empty(const wxTextEntryBase *aCtrl)
static std::string ToStdString(const wxString &aStr)
#define STRUCT_DELETED
flag indication structures to be erased
void LoadSchematic(SETTINGS_MANAGER &aSettingsManager, const wxString &aRelPath, std::unique_ptr< SCHEMATIC > &aSchematic)
Value keys and the key session of the schematic connectivity engine.
std::string Dump(SCHEMATIC &aSchematic)
Canonical, pointer-free connectivity rows for migration and rebuild comparisons.
constexpr uint32_t INVALID_ID
Marks an unset handle.
Definition conn_keys.h:47
#define SKIP_CONNECTIVITY
Definition sch_commit.h:41
#define DELETE_REMOVED_ITEMS
Definition sch_commit.h:43
#define SKIP_UNDO
Definition sch_commit.h:38
@ GLOBAL_CLEANUP
Definition schematic.h:95
std::unique_ptr< SCHEMATIC > schematic
Difference between two publications.
One item or pin in one sheet instance.
Definition conn_keys.h:77
KIID item
The item or pin KIID.
Definition conn_keys.h:78
INST_ID inst
The sheet instance that shows the item.
Definition conn_keys.h:79
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_CASE(ShadowDumpResolvesClassesWithoutLegacyAssignments)
BOOST_DATA_TEST_CASE_F(CONNECTIVITY_DUMP_FIXTURE, LegacyAndEngineDumpsMatch, boost::unit_test::data::make(PARITY_FIXTURES), fixture)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
std::string path
VECTOR2I end
BOOST_TEST_CONTEXT("Test Clearance")
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
int actual
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")
@ BATCH
Tool that runs in the background without any user intervention.
Definition tool_base.h:48
wxString dump(const wxArrayString &aArray)
Debug helper for printing wxArrayString contents.
@ SCH_LINE_T
Definition typeinfo.h:159
@ SCH_NO_CONNECT_T
Definition typeinfo.h:156
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_LABEL_T
Definition typeinfo.h:163
@ SCH_HIER_LABEL_T
Definition typeinfo.h:165
@ SCH_BUS_BUS_ENTRY_T
Definition typeinfo.h:158
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:157
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:164
@ SCH_JUNCTION_T
Definition typeinfo.h:155
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683