KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_board_commit.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
20#include <boost/test/unit_test.hpp>
21#include <tool/tool_manager.h>
23#include <board.h>
24#include <board_commit.h>
26#include <netinfo.h>
27#include <footprint.h>
28#include <pad.h>
29#include <pcb_shape.h>
30#include <pcb_text.h>
31#include <pcb_group.h>
32#include <pcb_board_outline.h>
33#include <lset.h>
35#include <pcb_view.h>
37
38BOOST_AUTO_TEST_SUITE( BoardCommit )
39
40BOOST_AUTO_TEST_CASE( RecursesThroughGroups )
41{
42 BOARD board;
43 TOOL_MANAGER mgr;
44 mgr.SetEnvironment( &board, nullptr, nullptr, nullptr, nullptr );
46 mgr.RegisterTool( dummyTool );
47 BOARD_COMMIT commit( dummyTool );
48
49 PCB_SHAPE s1( nullptr, SHAPE_T::SEGMENT );
50 PCB_SHAPE s2( nullptr, SHAPE_T::SEGMENT );
51 PCB_GROUP group( nullptr );
52 group.AddItem( &s1 );
53 group.AddItem( &s2 );
54
55 commit.Stage( &group, CHT_MODIFY, nullptr, RECURSE_MODE::RECURSE );
56
57 BOOST_CHECK_EQUAL( commit.GetStatus( &s1 ), CHT_MODIFY );
58 BOOST_CHECK_EQUAL( commit.GetStatus( &s2 ), CHT_MODIFY );
59}
60
61// Deleting a footprint child must drop it from connectivity, like a board level item.
62BOOST_AUTO_TEST_CASE( RemovedFootprintChildLeavesConnectivity )
63{
64 BOARD board;
65 TOOL_MANAGER mgr;
66 mgr.SetEnvironment( &board, nullptr, nullptr, nullptr, nullptr );
68 mgr.RegisterTool( dummyTool );
69
70 board.Add( new NETINFO_ITEM( &board, wxT( "N1" ), 1 ) );
71
72 FOOTPRINT* fp = new FOOTPRINT( &board );
73 PAD* pad = new PAD( fp );
74 fp->Add( pad );
75 board.Add( fp );
76 pad->SetNetCode( 1 );
77
78 board.BuildConnectivity();
79
80 auto netPads = [&]()
81 {
82 return board.GetConnectivity()->GetNetItems( 1, { PCB_PAD_T } ).size();
83 };
84
85 BOOST_REQUIRE_EQUAL( netPads(), 1u );
86
87 BOARD_COMMIT commit( dummyTool );
88 commit.Remove( pad );
89 commit.Push( wxT( "Delete Pad" ) );
90
91 BOOST_CHECK_EQUAL( netPads(), 0u );
92}
93
94
95BOOST_AUTO_TEST_CASE( MakeImageCreatesTransientCopy )
96{
97 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
99
101 BOOST_CHECK( copy != &shape );
102 BOOST_CHECK( copy->HasFlag( UR_TRANSIENT ) );
103
104 delete copy;
105}
106
107BOOST_AUTO_TEST_CASE( ReturnsBoardFromManager )
108{
109 BOARD board;
110 TOOL_MANAGER mgr;
111 mgr.SetEnvironment( &board, nullptr, nullptr, nullptr, nullptr );
112 KI_TEST::DUMMY_TOOL* dummyTool = new KI_TEST::DUMMY_TOOL();
113 mgr.RegisterTool( dummyTool );
114
115 BOARD_COMMIT commit( dummyTool );
116
117 BOOST_CHECK_EQUAL( commit.GetBoard(), &board );
118}
119
120BOOST_AUTO_TEST_CASE( RemoveFootprintTextFromBoardEditor )
121{
122 BOARD board;
123 TOOL_MANAGER mgr;
124 mgr.SetEnvironment( &board, nullptr, nullptr, nullptr, nullptr );
125 KI_TEST::DUMMY_TOOL* dummyTool = new KI_TEST::DUMMY_TOOL();
126 mgr.RegisterTool( dummyTool );
127
128 FOOTPRINT* fp = new FOOTPRINT( &board );
129 PCB_TEXT* text = new PCB_TEXT( fp );
130 text->SetText( wxT( "${REFERENCE}" ) );
131 text->SetLayer( F_Fab );
132 fp->Add( text );
133 board.Add( fp );
134
135 BOOST_REQUIRE_EQUAL( fp->GraphicalItems().size(), 1 );
136
137 {
138 BOARD_COMMIT commit( &mgr, true, false );
139 commit.Remove( text );
140 commit.Push( wxT( "Delete" ), SKIP_UNDO );
141 }
142
143 BOOST_CHECK_EQUAL( fp->GraphicalItems().size(), 0 );
144}
145
146// A COMMIT object reused across Push() calls (such as the group tool's persistent commit)
147// must not carry m_addedItems from one commit into the next. If it does, modifying a
148// previously-added item in a later commit is silently dropped and no undo entry is created.
149// This is the root cause of nested-group undo corruption (work item 24146).
150BOOST_AUTO_TEST_CASE( ReusedCommitModifyAfterAdd )
151{
152 BOARD board;
153 TOOL_MANAGER mgr;
154 mgr.SetEnvironment( &board, nullptr, nullptr, nullptr, nullptr );
155 KI_TEST::DUMMY_TOOL* dummyTool = new KI_TEST::DUMMY_TOOL();
156 mgr.RegisterTool( dummyTool );
157
158 BOARD_COMMIT commit( &mgr, true, false );
159
160 PCB_SHAPE* shape = new PCB_SHAPE( &board, SHAPE_T::SEGMENT );
161
162 // First commit adds the shape. After Push the commit is reused.
163 commit.Add( shape );
164 commit.Push( wxT( "Add" ), SKIP_UNDO );
165
166 // Modifying the already-added shape in the next commit must record a change.
167 commit.Modify( shape );
168 BOOST_CHECK_EQUAL( commit.GetStatus( shape ), CHT_MODIFY );
169}
170
171// Removing a footprint frees its pads, fields and other owned children with it. A child that
172// sits in the selection on its own (the footprint itself unselected) must be pruned as well, or
173// PCB_SELECTION::updateDrawList() dereferences the freed child on the next repaint.
174BOOST_AUTO_TEST_CASE( RemoveFootprintPrunesSelectedChildren )
175{
176 BOARD board;
177 KIGFX::PCB_VIEW view;
178 TOOL_MANAGER mgr;
179 mgr.SetEnvironment( &board, &view, nullptr, nullptr, nullptr );
180
182 mgr.RegisterTool( selTool );
183
184 FOOTPRINT* fp = new FOOTPRINT( &board );
185 PAD* pad = new PAD( fp );
186 fp->Add( pad );
187 board.Add( fp );
188
189 selTool->AddItemToSel( pad, true );
190
191 BOOST_REQUIRE( selTool->GetSelection().Contains( pad ) );
192 BOOST_REQUIRE( !fp->IsSelected() );
193
194 BOARD_COMMIT commit( &mgr, true, false );
195 commit.Remove( fp );
196 commit.Push( wxT( "Delete footprint" ), SKIP_UNDO | SKIP_TEARDROPS );
197
198 BOOST_CHECK( !selTool->GetSelection().Contains( pad ) );
199
200 // With SKIP_UNDO the removed footprint is ours to free
201 delete fp;
202}
203
204// Moving a shape off Edge.Cuts must rebuild the board outline (issue 25551).
205BOOST_AUTO_TEST_CASE( LayerChangeOffEdgeCutsUpdatesBoardOutline )
206{
207 // view must outlive board so board items unregister from a live view at teardown.
208 KIGFX::PCB_VIEW view;
209 BOARD board;
210 TOOL_MANAGER mgr;
211 mgr.SetEnvironment( &board, &view, nullptr, nullptr, nullptr );
212
213 PCB_SHAPE* rect = new PCB_SHAPE( &board, SHAPE_T::RECTANGLE );
214 rect->SetLayer( Edge_Cuts );
215 rect->SetStart( VECTOR2I( 0, 0 ) );
216 rect->SetEnd( VECTOR2I( 10000000, 10000000 ) );
217 board.Add( rect );
218 board.UpdateBoardOutline();
219
220 BOOST_REQUIRE_GT( board.BoardOutline()->GetOutline().OutlineCount(), 0 );
221
222 BOARD_COMMIT commit( &mgr, true, false );
223 commit.Modify( rect );
224 rect->SetLayer( Cmts_User );
225 commit.Push( wxT( "Change layer" ), SKIP_UNDO );
226
228}
229
230// Undo after a drag must put the hops back with the stack.
231BOOST_AUTO_TEST_CASE( RevertAfterDraggingAViaStackRestoresItsHops )
232{
233 BOARD board;
234 TOOL_MANAGER mgr;
235
236 board.SetCopperLayerCount( 4 );
238 mgr.SetEnvironment( &board, nullptr, nullptr, nullptr, nullptr );
239
240 KI_TEST::DUMMY_TOOL* dummyTool = new KI_TEST::DUMMY_TOOL();
241 mgr.RegisterTool( dummyTool );
242
243 VECTOR2I origin( 10000000, 10000000 );
244
245 PCB_VIA_STACK* stack = new PCB_VIA_STACK( &board, F_Cu );
246 stack->SetStartLayer( F_Cu );
247 stack->SetEndLayer( In2_Cu );
248 stack->SetViaSize( 300000 );
249 stack->SetViaDrill( 150000 );
250 stack->SetPosition( origin );
251 board.Add( stack );
252 stack->Regenerate( &board, nullptr );
253
254 std::map<BOARD_ITEM*, VECTOR2I> before;
255
256 for( BOARD_ITEM* item : stack->GetBoardItems() )
257 before[item] = item->GetPosition();
258
259 BOOST_REQUIRE_EQUAL( before.size(), 2u );
260
261 BOARD_COMMIT commit( &mgr, true, false );
262
263 stack->EditStart( nullptr, &board, &commit );
264
265 // A drag is a stream of motion events.
266 for( const VECTOR2I& step : { VECTOR2I( 500000, 0 ), VECTOR2I( 500000, 250000 ) } )
267 {
268 stack->Move( step );
269 stack->Update( nullptr, &board, &commit );
270 }
271
272 stack->EditFinish( nullptr, &board, &commit );
273
274 commit.Revert();
275
276 BOOST_CHECK_EQUAL( stack->GetPosition(), origin );
277
278 for( const auto& [item, pos] : before )
279 {
280 BOOST_CHECK_MESSAGE( item->GetPosition() == pos,
281 "hop left behind at " + item->GetPosition().Format() + " instead of " + pos.Format() );
282 }
283}
284
285
286// Editing a stack stages its members, then rebuilds them. A member must not carry both a
287// modify and a remove line, or redo trips over the pair.
288BOOST_AUTO_TEST_CASE( RegeneratingAViaStackDoesNotDoubleStageItsHops )
289{
290 BOARD board;
291 TOOL_MANAGER mgr;
292
293 board.SetCopperLayerCount( 6 );
295 mgr.SetEnvironment( &board, nullptr, nullptr, nullptr, nullptr );
296
297 KI_TEST::DUMMY_TOOL* dummyTool = new KI_TEST::DUMMY_TOOL();
298 mgr.RegisterTool( dummyTool );
299
300 PCB_VIA_STACK* stack = new PCB_VIA_STACK( &board, F_Cu );
301 stack->SetStartLayer( F_Cu );
302 stack->SetEndLayer( In2_Cu );
303 stack->SetViaSize( 300000 );
304 stack->SetViaDrill( 150000 );
305 stack->SetPosition( VECTOR2I( 10000000, 10000000 ) );
306 board.Add( stack );
307 stack->Regenerate( &board, nullptr );
308
309 std::vector<BOARD_ITEM*> original( stack->GetBoardItems().begin(), stack->GetBoardItems().end() );
310 BOOST_REQUIRE_EQUAL( original.size(), 2u );
311
312 BOARD_COMMIT commit( &mgr, true, false );
313
314 stack->EditStart( nullptr, &board, &commit );
315
316 // Widening the span changes the hop set, so the members are replaced rather than reused.
317 stack->SetEndLayer( In3_Cu );
318 stack->Update( nullptr, &board, &commit );
319 stack->EditFinish( nullptr, &board, &commit );
320
321 for( BOARD_ITEM* item : original )
322 {
323 BOOST_CHECK_MESSAGE( commit.GetStatus( item ) == CHT_REMOVE,
324 "replaced hop is staged as " << commit.GetStatus( item ) << ", expected CHT_REMOVE only" );
325 }
326
327 commit.Revert();
328}
329
330
331// Update outside an edit must do nothing. Regenerating there would delete and rebuild the
332// hops behind the back of whatever holds them.
333BOOST_AUTO_TEST_CASE( UpdateOutsideAnEditIsInert )
334{
335 BOARD board;
336 TOOL_MANAGER mgr;
337
338 board.SetCopperLayerCount( 4 );
340 mgr.SetEnvironment( &board, nullptr, nullptr, nullptr, nullptr );
341
342 KI_TEST::DUMMY_TOOL* dummyTool = new KI_TEST::DUMMY_TOOL();
343 mgr.RegisterTool( dummyTool );
344
345 PCB_VIA_STACK* stack = new PCB_VIA_STACK( &board, F_Cu );
346 stack->SetStartLayer( F_Cu );
347 stack->SetEndLayer( In2_Cu );
348 stack->SetViaSize( 300000 );
349 stack->SetViaDrill( 150000 );
350 stack->SetPosition( VECTOR2I( 10000000, 10000000 ) );
351 board.Add( stack );
352 stack->Regenerate( &board, nullptr );
353
354 std::vector<BOARD_ITEM*> before( stack->GetBoardItems().begin(), stack->GetBoardItems().end() );
355 BOOST_REQUIRE_EQUAL( before.size(), 2u );
356
357 BOARD_COMMIT commit( &mgr, true, false );
358
359 // No EditStart, so IN_EDIT is not set.
360 BOOST_CHECK( !stack->Update( nullptr, &board, &commit ) );
361
362 std::vector<BOARD_ITEM*> after( stack->GetBoardItems().begin(), stack->GetBoardItems().end() );
363
364 BOOST_REQUIRE_EQUAL( after.size(), before.size() );
365 BOOST_CHECK_MESSAGE( std::set<BOARD_ITEM*>( before.begin(), before.end() )
366 == std::set<BOARD_ITEM*>( after.begin(), after.end() ),
367 "the hops were rebuilt outside an edit" );
368}
369
370
372
#define SKIP_TEARDROPS
void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
COMMIT & Stage(EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE) override
Add a change of the item aItem of type aChangeType to the change list.
BOARD * GetBoard() const
void Revert() override
Revert the commit by restoring the modified items state.
static EDA_ITEM * MakeImage(EDA_ITEM *aItem)
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1524
PCB_BOARD_OUTLINE * BoardOutline()
Definition board.h:486
void UpdateBoardOutline()
Definition board.cpp:4483
bool BuildConnectivity(PROGRESS_REPORTER *aReporter=nullptr)
Build or rebuild the board connectivity database for the board, especially the list of connected item...
Definition board.cpp:367
void SetCopperLayerCount(int aCount)
Definition board.cpp:1144
void SetEnabledLayers(const LSET &aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition board.cpp:1205
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:759
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
int GetStatus(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Returns status of an item.
Definition commit.cpp:235
const std::vector< BOARD_CONNECTED_ITEM * > GetNetItems(int aNetCode, const std::vector< KICAD_T > &aTypes) const
Return the list of items that belong to a certain net.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
bool IsSelected() const
Definition eda_item.h:134
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Adds an item to the container.
DRAWINGS & GraphicalItems()
Definition footprint.h:408
static const LSET & AllTechMask()
Return a mask holding all technical layers (no CU layer) on both side.
Definition lset.cpp:672
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:595
Handle the data for a net.
Definition netinfo.h:50
Definition pad.h:61
const SHAPE_POLY_SET & GetOutline() const
void SetPosition(const VECTOR2I &aPos) override
VECTOR2I GetPosition() const override
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:51
std::unordered_set< BOARD_ITEM * > GetBoardItems() const
The selection tool: currently supports:
PCB_SELECTION & GetSelection()
void SetEnd(const VECTOR2I &aEnd) override
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetStart(const VECTOR2I &aStart) override
A microvia stack: several single hop microvias, plus connecting traces for staggered stacks,...
bool Update(GENERATOR_TOOL *aTool, BOARD *aBoard, BOARD_COMMIT *aCommit) override
void EditStart(GENERATOR_TOOL *aTool, BOARD *aBoard, BOARD_COMMIT *aCommit) override
void EditFinish(GENERATOR_TOOL *aTool, BOARD *aBoard, BOARD_COMMIT *aCommit) override
void Regenerate(BOARD *aBoard, BOARD_COMMIT *aCommit)
Rebuild the member vias (and, for a staggered stack, the connecting traces) from the current stack se...
void SetStartLayer(PCB_LAYER_ID aLayer)
void SetViaSize(int aSize)
void SetViaDrill(int aDrill)
void Move(const VECTOR2I &aMoveVector) override
Move this object.
void SetEndLayer(PCB_LAYER_ID aLayer)
int AddItemToSel(const TOOL_EVENT &aEvent)
bool Contains(EDA_ITEM *aItem) const
Definition selection.cpp:84
int OutlineCount() const
Return the number of outlines in the set.
Master controller class:
void RegisterTool(TOOL_BASE *aTool)
Add a tool to the manager set and sets it up.
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).
@ CHT_MODIFY
Definition commit.h:40
@ CHT_REMOVE
Definition commit.h:39
@ RECURSE
Definition eda_item.h:51
#define UR_TRANSIENT
indicates the item is owned by the undo/redo stack
@ SEGMENT
Definition eda_shape.h:56
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
@ Edge_Cuts
Definition layer_ids.h:108
@ Cmts_User
Definition layer_ids.h:104
@ In2_Cu
Definition layer_ids.h:63
@ F_Fab
Definition layer_ids.h:115
@ In3_Cu
Definition layer_ids.h:64
@ F_Cu
Definition layer_ids.h:60
Class to handle a set of BOARD_ITEMs.
#define SKIP_UNDO
Definition sch_commit.h:38
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_CASE(RecursesThroughGroups)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_CHECK_EQUAL(result, "25.4")
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708