KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_symbol_editor_tabs.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
23
28
29#include <lib_symbol.h>
30#include <sch_screen.h>
31#include <undo_redo_container.h>
32
34
35#include <nlohmann/json.hpp>
36
37
40{
41public:
42 INSTRUMENTED_LIB_SYMBOL( const wxString& aName, int* aDtorCounter ) :
43 LIB_SYMBOL( aName ),
44 m_dtorCounter( aDtorCounter )
45 {
46 }
47
49 {
50 if( m_dtorCounter )
51 ( *m_dtorCounter )++;
52 }
53
54private:
56};
57
58
60{
62
64 std::unique_ptr<SYMBOL_BUFFER> makeBuffer( const wxString& aName )
65 {
66 auto symbol = std::make_unique<LIB_SYMBOL>( aName );
67 auto screen = std::make_unique<SCH_SCREEN>();
68
69 return std::make_unique<SYMBOL_BUFFER>( std::move( symbol ), std::move( screen ) );
70 }
71
74 {
75 aTransient->SetFlags( UR_TRANSIENT );
76
78 cmd->PushItem( ITEM_PICKER( nullptr, aTransient, UNDO_REDO::LIBEDIT ) );
79 aList.PushCommand( cmd );
80 }
81
83 static void freeTransientUndoCommands( UNDO_REDO_CONTAINER& aList, const LIB_SYMBOL* aLiveSymbol )
84 {
86 }
87};
88
89
90BOOST_FIXTURE_TEST_SUITE( SymbolEditorTabs, SYMBOL_EDITOR_TABS_TEST_FIXTURE )
91
92
93
95BOOST_AUTO_TEST_CASE( SymbolTabContextOwnsWorkingCopy )
96{
97 std::unique_ptr<SYMBOL_BUFFER> buf = makeBuffer( wxS( "R" ) );
98
99 SYMBOL_EDITOR_TAB_CONTEXT ctx( wxS( "Device" ), wxS( "R" ), buf.get() );
100
101 BOOST_CHECK_EQUAL( ctx.GetTabKey(), wxS( "Device:R" ) );
102 BOOST_CHECK_EQUAL( ctx.GetDisplayName(), wxS( "R" ) );
103
104 BOOST_REQUIRE( ctx.GetSymbol() != nullptr );
105 BOOST_REQUIRE( ctx.GetScreen() != nullptr );
106 BOOST_CHECK( ctx.GetSymbol() != &buf->GetSymbol() );
107 BOOST_CHECK( ctx.GetScreen() != buf->GetScreen() );
108 BOOST_CHECK_EQUAL( ctx.GetSymbol()->GetName(), wxS( "R" ) );
109
110 BOOST_CHECK( !ctx.IsModified() );
112 BOOST_CHECK( ctx.IsModified() );
113
114 // Marking the buffer's screen must not affect the owned context.
115 std::unique_ptr<SYMBOL_BUFFER> clean = makeBuffer( wxS( "C" ) );
116 SYMBOL_EDITOR_TAB_CONTEXT cleanCtx( wxS( "Device" ), wxS( "C" ), clean.get() );
117 clean->GetScreen()->SetContentModified();
118 BOOST_CHECK( !cleanCtx.IsModified() );
119
120 BOOST_CHECK_EQUAL( ctx.GetUnit(), 1 );
122
123 ctx.SetUnit( 2 );
124 ctx.SetBodyStyle( 2 );
125 BOOST_CHECK_EQUAL( ctx.GetUnit(), 2 );
127}
128
129
132BOOST_AUTO_TEST_CASE( InstanceTabContextIsTransientAndKeyedByUuid )
133{
134 KIID sourceUuid;
135 const wxString reference = wxS( "R5" );
136
137 LIB_SYMBOL* symbol = new LIB_SYMBOL( wxS( "R" ) );
138 SCH_SCREEN* screen = new SCH_SCREEN();
139
140 SYMBOL_EDITOR_TAB_CONTEXT ctx( symbol, screen, sourceUuid, reference );
141
142 BOOST_CHECK( ctx.IsTransient() );
143 BOOST_CHECK( ctx.IsFromSchematic() );
144 BOOST_CHECK_EQUAL( ctx.GetReference(), reference );
146 BOOST_CHECK_EQUAL( ctx.GetDisplayName(), reference );
147
150
151 // The instance key cannot collide with any library:name key.
152 BOOST_CHECK( ctx.GetTabKey() != SYMBOL_EDITOR_TAB_CONTEXT::MakeTabKey( wxS( "Device" ),
153 wxS( "R" ) ) );
154 BOOST_CHECK( ctx.GetTabKey().StartsWith( wxString( wxT( "\x01" ) ) ) );
155
156 BOOST_CHECK( ctx.GetSymbol() == symbol );
157 BOOST_CHECK( ctx.GetScreen() == screen );
158}
159
160
162BOOST_AUTO_TEST_CASE( SymbolTabContextSeedsDirtyFromBuffer )
163{
164 std::unique_ptr<SYMBOL_BUFFER> buf = makeBuffer( wxS( "R" ) );
165 buf->GetScreen()->SetContentModified();
166
167 SYMBOL_EDITOR_TAB_CONTEXT ctx( wxS( "Device" ), wxS( "R" ), buf.get() );
168 BOOST_CHECK( ctx.IsModified() );
169}
170
171
173BOOST_AUTO_TEST_CASE( OpenTabsJsonRoundTrip )
174{
176
177 source.m_OpenTabs.push_back( { wxS( "Device" ), wxS( "R" ), 1, 1 } );
178 source.m_OpenTabs.push_back( { wxS( "Connector" ), wxS( "Conn_01x02" ), 2, 1 } );
179 source.m_ActiveTabKey = wxS( "Connector:Conn_01x02" );
180
181 BOOST_REQUIRE( source.Store() );
182
183 std::string serialized = source.FormatAsString();
184 nlohmann::json reparsed = nlohmann::json::parse( serialized );
185
186 BOOST_REQUIRE( reparsed.contains( "open_tabs" ) );
187 BOOST_REQUIRE( reparsed["open_tabs"].is_array() );
188 BOOST_CHECK_EQUAL( reparsed["open_tabs"].size(), 2u );
189
191
192 // Seed a stale entry to catch a reader that fails to clear first.
193 sink.m_OpenTabs.push_back( { wxS( "STALE" ), wxS( "X" ), 1, 1 } );
194
195 JSON_SETTINGS_INTERNALS reparsedInternals;
196 static_cast<nlohmann::json&>( reparsedInternals ) = reparsed;
197 sink.Internals()->CloneFrom( reparsedInternals );
198 sink.Load();
199
200 BOOST_REQUIRE_EQUAL( sink.m_OpenTabs.size(), 2u );
201
202 BOOST_CHECK_EQUAL( sink.m_OpenTabs[0].lib, wxS( "Device" ) );
203 BOOST_CHECK_EQUAL( sink.m_OpenTabs[0].name, wxS( "R" ) );
204 BOOST_CHECK_EQUAL( sink.m_OpenTabs[0].unit, 1 );
205
206 BOOST_CHECK_EQUAL( sink.m_OpenTabs[1].lib, wxS( "Connector" ) );
207 BOOST_CHECK_EQUAL( sink.m_OpenTabs[1].name, wxS( "Conn_01x02" ) );
208 BOOST_CHECK_EQUAL( sink.m_OpenTabs[1].unit, 2 );
209
210 BOOST_CHECK_EQUAL( sink.m_ActiveTabKey, wxS( "Connector:Conn_01x02" ) );
211}
212
213
217BOOST_AUTO_TEST_CASE( FreeTransientUndoCommandsDeletesCopies )
218{
219 int dtorCount = 0;
220
223
224 pushTransientCommand( undo, new INSTRUMENTED_LIB_SYMBOL( wxS( "u1" ), &dtorCount ) );
225 pushTransientCommand( undo, new INSTRUMENTED_LIB_SYMBOL( wxS( "u2" ), &dtorCount ) );
226 pushTransientCommand( redo, new INSTRUMENTED_LIB_SYMBOL( wxS( "r1" ), &dtorCount ) );
227
228 BOOST_REQUIRE_EQUAL( undo.m_CommandsList.size(), 2u );
229 BOOST_REQUIRE_EQUAL( redo.m_CommandsList.size(), 1u );
230
231 freeTransientUndoCommands( undo, nullptr );
232 freeTransientUndoCommands( redo, nullptr );
233
234 BOOST_CHECK_EQUAL( dtorCount, 3 );
235 BOOST_CHECK_EQUAL( undo.m_CommandsList.size(), 0u );
236 BOOST_CHECK_EQUAL( redo.m_CommandsList.size(), 0u );
237}
238
239
243BOOST_AUTO_TEST_CASE( FreeTransientUndoCommandsSparesLiveSymbol )
244{
245 int dtorCount = 0;
246
247 auto liveSymbol = std::make_unique<INSTRUMENTED_LIB_SYMBOL>( wxS( "live" ), &dtorCount );
248
250
251 // A command referencing the live symbol without the transient flag, which the helper must skip.
252 PICKED_ITEMS_LIST* liveCmd = new PICKED_ITEMS_LIST();
253 liveCmd->PushItem( ITEM_PICKER( nullptr, liveSymbol.get(), UNDO_REDO::LIBEDIT ) );
254 undo.PushCommand( liveCmd );
255
256 pushTransientCommand( undo, new INSTRUMENTED_LIB_SYMBOL( wxS( "copy" ), &dtorCount ) );
257
258 freeTransientUndoCommands( undo, liveSymbol.get() );
259
260 BOOST_CHECK_EQUAL( dtorCount, 1 );
261 BOOST_CHECK_EQUAL( undo.m_CommandsList.size(), 0u );
262
263 liveSymbol.reset();
264 BOOST_CHECK_EQUAL( dtorCount, 2 );
265}
266
267
void SetContentModified(bool aModified=true)
Definition base_screen.h:55
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:152
Headless tests for the Symbol Editor tabbed-document plumbing.
INSTRUMENTED_LIB_SYMBOL(const wxString &aName, int *aDtorCounter)
void CloneFrom(const JSON_SETTINGS_INTERNALS &aOther)
virtual void Load()
Updates the parameters of this object based on the current JSON document contents.
const std::string FormatAsString()
JSON_SETTINGS_INTERNALS * Internals()
virtual bool Store()
Stores the current parameters into the JSON document represented by this object Note: this doesn't do...
Definition kiid.h:44
wxString AsString() const
Definition kiid.cpp:242
Define a library symbol object.
Definition lib_symbol.h:79
wxString GetName() const override
Definition lib_symbol.h:141
LIB_SYMBOL(const wxString &aName, LIB_SYMBOL *aParent=nullptr, LEGACY_SYMBOL_LIB *aLibrary=nullptr)
A holder to handle information on schematic or board items.
void PushItem(const ITEM_PICKER &aItem)
Push aItem to the top of the list.
std::vector< OPEN_TAB > m_OpenTabs
One open symbol tab owning a working LIB_SYMBOL and screen lent to the frame while active.
wxString GetDisplayName() const override
Short label shown on the tab.
LIB_SYMBOL * GetSymbol() const
Observe the working symbol/screen.
const wxString & GetReference() const
static wxString MakeTabKey(const wxString &aLib, const wxString &aName)
De-duplication key for a library:symbol pair.
static wxString MakeInstanceTabKey(const KIID &aSchematicSymbolUUID)
De-duplication key for a placed schematic instance, in a namespace disjoint from library keys.
wxString GetTabKey() const override
Stable identity for persistence and de-duplication.
const KIID & GetSchematicSymbolUUID() const
bool IsModified() const override
True when the working screen carries unsaved edits.
bool IsTransient() const
True for an instance (schematic) tab, which is session-only and never persisted.
static void freeTransientUndoCommands(UNDO_REDO_CONTAINER &aList, const LIB_SYMBOL *aLiveSymbol)
Free every command in the list and the UR_TRANSIENT-flagged copies it owns, which the shared deleters...
A holder to handle a list of undo (or redo) commands.
void PushCommand(PICKED_ITEMS_LIST *aCommand)
#define UR_TRANSIENT
indicates the item is owned by the undo/redo stack
std::unique_ptr< SYMBOL_BUFFER > makeBuffer(const wxString &aName)
Build a buffer over a fresh symbol and screen for the given name.
void pushTransientCommand(UNDO_REDO_CONTAINER &aList, LIB_SYMBOL *aTransient)
Push a UR_TRANSIENT-flagged symbol copy as a single-item command.
static void freeTransientUndoCommands(UNDO_REDO_CONTAINER &aList, const LIB_SYMBOL *aLiveSymbol)
Expose the private static free helper under test.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(SymbolTabContextOwnsWorkingCopy)
The context owns a working copy cloned from the buffer, seeds its dirty flag, and round-trips the per...
BOOST_CHECK_EQUAL(result, "25.4")