KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_board_item_index.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
20#include <boost/test/unit_test.hpp>
21
22#include <memory>
23
24#include <board.h>
25#include <footprint.h>
26#include <pad.h>
27#include <pcb_shape.h>
28#include <pcb_table.h>
29#include <pcb_tablecell.h>
30
31
32BOOST_AUTO_TEST_SUITE( BoardItemIndex )
33
34
35BOOST_AUTO_TEST_CASE( SwapItemDataReindexesFootprintChildren )
36{
37 BOARD* board = new BOARD();
38
39 auto footprint = std::make_unique<FOOTPRINT>( board );
40 auto pad = new PAD( footprint.get() );
41 pad->SetNumber( "1" );
42
43 PAD* originalPad = pad;
44 KIID originalPadId = originalPad->m_Uuid;
45
46 footprint->Add( pad );
47
48 FOOTPRINT* liveFootprint = footprint.get();
49 board->Add( footprint.release() );
50
51 auto image = std::unique_ptr<FOOTPRINT>( static_cast<FOOTPRINT*>( liveFootprint->Clone() ) );
52
53 liveFootprint->SwapItemData( image.get() );
54 image.reset();
55
56 BOARD_ITEM* resolved = board->ResolveItem( originalPadId, true );
57
58 BOOST_REQUIRE( resolved );
59 BOOST_CHECK_EQUAL( resolved->Type(), PCB_PAD_T );
60 BOOST_CHECK( resolved != originalPad );
61
62 delete board;
63}
64
65
66BOOST_AUTO_TEST_CASE( AttachedItemUuidRewriteDropsOldAlias )
67{
68 BOARD* board = new BOARD();
69
70 auto shape = std::make_unique<PCB_SHAPE>( board );
71 PCB_SHAPE* liveShape = shape.get();
72 const KIID oldId = liveShape->m_Uuid;
73 const KIID newId;
74
75 board->Add( shape.release() );
76
77 const_cast<KIID&>( liveShape->m_Uuid ) = newId;
78
79 BOOST_CHECK( board->ResolveItem( oldId, true ) == nullptr );
80 BOOST_CHECK_EQUAL( board->ResolveItem( newId, true ), liveShape );
81
82 delete board;
83}
84
85
86BOOST_AUTO_TEST_CASE( ResolvingNewUuidPurgesStaleOldAlias )
87{
88 BOARD* board = new BOARD();
89
90 auto shape = std::make_unique<PCB_SHAPE>( board );
91 PCB_SHAPE* liveShape = shape.get();
92 const KIID oldId = liveShape->m_Uuid;
93 const KIID newId;
94
95 board->Add( shape.release() );
96 const_cast<KIID&>( liveShape->m_Uuid ) = newId;
97
98 BOOST_CHECK_EQUAL( board->ResolveItem( newId, true ), liveShape );
99 BOOST_CHECK( board->GetItemByIdCache().contains( newId ) );
100 BOOST_CHECK( !board->GetItemByIdCache().contains( oldId ) );
101
102 delete board;
103}
104
105
106BOOST_AUTO_TEST_CASE( RebindItemUuidUpdatesCacheAtomically )
107{
108 BOARD* board = new BOARD();
109
110 auto shape = std::make_unique<PCB_SHAPE>( board );
111 PCB_SHAPE* liveShape = shape.get();
112 const KIID oldId = liveShape->m_Uuid;
113 const KIID newId;
114
115 board->Add( shape.release() );
116 board->RebindItemUuid( liveShape, newId );
117
118 BOOST_CHECK( liveShape->m_Uuid == newId );
119 BOOST_CHECK( !board->GetItemByIdCache().contains( oldId ) );
120 BOOST_CHECK( board->GetItemByIdCache().contains( newId ) );
121 BOOST_CHECK_EQUAL( board->ResolveItem( newId, true ), liveShape );
122
123 delete board;
124}
125
126
127BOOST_AUTO_TEST_CASE( AttachedChildSetUuidRebindsCache )
128{
129 BOARD* board = new BOARD();
130
131 auto footprint = std::make_unique<FOOTPRINT>( board );
132 auto pad = new PAD( footprint.get() );
133 PAD* livePad = pad;
134 const KIID oldId = livePad->m_Uuid;
135 const KIID newId;
136
137 footprint->Add( pad );
138 board->Add( footprint.release() );
139
140 livePad->SetUuid( newId );
141
142 BOOST_CHECK( livePad->m_Uuid == newId );
143 BOOST_CHECK( !board->GetItemByIdCache().contains( oldId ) );
144 BOOST_CHECK( board->GetItemByIdCache().contains( newId ) );
145 BOOST_CHECK_EQUAL( board->ResolveItem( newId, true ), livePad );
146
147 delete board;
148}
149
150
151BOOST_AUTO_TEST_CASE( CacheItemByIdCanonicalizesRewrittenUuid )
152{
153 BOARD* board = new BOARD();
154
155 auto shape = std::make_unique<PCB_SHAPE>( board );
156 PCB_SHAPE* liveShape = shape.get();
157 const KIID oldId = liveShape->m_Uuid;
158 const KIID newId;
159
160 board->Add( shape.release() );
161
162 const_cast<KIID&>( liveShape->m_Uuid ) = newId;
163 board->CacheItemById( liveShape );
164
165 BOOST_CHECK( !board->GetItemByIdCache().contains( oldId ) );
166 BOOST_CHECK( board->GetItemByIdCache().contains( newId ) );
167 BOOST_CHECK_EQUAL( board->ResolveItem( newId, true ), liveShape );
168
169 delete board;
170}
171
172
173BOOST_AUTO_TEST_CASE( RepairDuplicateItemUuidsKeepsEarlierTraversalWinner )
174{
175 BOARD* board = new BOARD();
176
177 auto footprint = std::make_unique<FOOTPRINT>( board );
178 FOOTPRINT* liveFootprint = footprint.get();
179 const KIID claimedId = liveFootprint->m_Uuid;
180
181 auto shape = std::make_unique<PCB_SHAPE>( board );
182 PCB_SHAPE* liveShape = shape.get();
183 const KIID originalShapeId = liveShape->m_Uuid;
184
185 board->Add( footprint.release() );
186 board->Add( shape.release() );
187
188 const_cast<KIID&>( liveShape->m_Uuid ) = claimedId;
189
191 BOOST_CHECK( liveFootprint->m_Uuid == claimedId );
192 BOOST_CHECK( liveShape->m_Uuid != claimedId );
193 BOOST_CHECK( liveShape->m_Uuid != originalShapeId );
194 BOOST_CHECK_EQUAL( board->ResolveItem( claimedId, true ), liveFootprint );
195 BOOST_CHECK( board->ResolveItem( originalShapeId, true ) == nullptr );
196 BOOST_CHECK_EQUAL( board->ResolveItem( liveShape->m_Uuid, true ), liveShape );
197
198 delete board;
199}
200
201
202BOOST_AUTO_TEST_CASE( RemovingResolvedChildEvictsCacheOnFootprintHolder )
203{
204 // Regression for the RC_TREE_MODEL::GetValue use-after-free: ResolveItem() caches a
205 // footprint child on demand, but the cache eviction in FOOTPRINT::Remove is gated on the
206 // parent footprint being indexed. A footprint-holder board never indexes anything (
207 // CacheItemById early-returns), yet ResolveItem still caches children, so removing and
208 // freeing a resolved child leaves a dangling id in the cache. A later resolve then hands
209 // back the freed pointer.
210 BOARD* board = new BOARD();
212
213 auto footprint = std::make_unique<FOOTPRINT>( board );
214 auto pad = new PAD( footprint.get() );
215 pad->SetNumber( "1" );
216
217 const KIID padId = pad->m_Uuid;
218 footprint->Add( pad );
219
220 FOOTPRINT* liveFootprint = footprint.get();
221 board->Add( footprint.release() );
222
223 // Resolve the pad the way a results-tree row does. On the buggy code this caches the pad
224 // even though the holder board never indexed its parent footprint.
225 BOOST_REQUIRE_EQUAL( board->ResolveItem( padId, true ), pad );
226
227 // Remove and destroy the pad through the footprint, as a footprint edit does.
228 liveFootprint->Remove( pad );
229 const bool staleEntrySurvived = board->GetItemByIdCache().contains( padId );
230
231 delete pad;
232 delete board;
233
234 BOOST_CHECK( !staleEntrySurvived );
235}
236
237
238BOOST_AUTO_TEST_CASE( TableCellResolvesToTheCellNotItsTable )
239{
240 // A cell carries its own KIID and is an editable item in its own right, so handing back the
241 // parent table forced every caller that can select a cell to special-case it. The cache is
242 // cleared to exercise the linear scan, which is the path that got this wrong.
243 BOARD board;
244 auto* table = new PCB_TABLE( &board, 0 );
245 auto* cell = new PCB_TABLECELL( table );
246
247 table->AddCell( cell );
248 board.Add( table );
249 board.ClearItemByIdCache();
250
251 const KIID tableId = table->m_Uuid;
252 const KIID cellId = cell->m_Uuid;
253
254 BOOST_CHECK_EQUAL( board.ResolveItem( cellId, true ), cell );
255 BOOST_CHECK_EQUAL( board.ResolveItem( tableId, true ), table );
256
257 // Caching the table under the cell's id also made the two lookups evict each other.
258 BOOST_CHECK_EQUAL( board.ResolveItem( cellId, true ), cell );
259 BOOST_CHECK( board.GetItemByIdCache().contains( tableId ) );
260 BOOST_CHECK( board.GetItemByIdCache().contains( cellId ) );
261}
262
263
264BOOST_AUTO_TEST_CASE( FootprintTableCellResolvesByItsOwnId )
265{
266 // The footprint branch walked GraphicalItems() without ever descending into a table, so a
267 // cell inside a footprint table could not be resolved at all.
268 BOARD board;
269 auto* footprint = new FOOTPRINT( &board );
270 auto* table = new PCB_TABLE( footprint, 0 );
271 auto* cell = new PCB_TABLECELL( table );
272
273 table->AddCell( cell );
274 footprint->Add( table, ADD_MODE::APPEND );
275 board.Add( footprint );
276 board.ClearItemByIdCache();
277
278 BOOST_CHECK_EQUAL( board.ResolveItem( cell->m_Uuid, true ), cell );
279}
280
281
@ FPHOLDER
Definition board.h:365
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:83
void SwapItemData(BOARD_ITEM *aImage)
Swap data between aItem and aImage.
void SetUuid(const KIID &aUuid)
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:373
void CacheItemById(BOARD_ITEM *aItem) const
Add an item to the item-by-id cache.
Definition board.cpp:2077
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1355
void SetBoardUse(BOARD_USE aUse)
Set what the board is going to be used for.
Definition board.h:385
void ClearItemByIdCache()
Definition board.cpp:2186
void RebindItemUuid(BOARD_ITEM *aItem, const KIID &aNewId)
Rebind the UUID of an attached item and keep the item-by-id cache coherent.
Definition board.cpp:2196
int RepairDuplicateItemUuids()
Rebind duplicate attached-item UUIDs so each live board item has a unique ID.
Definition board.cpp:2222
const std::unordered_map< KIID, BOARD_ITEM * > & GetItemByIdCache() const
Definition board.h:1564
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
Definition board.cpp:1928
const KIID m_Uuid
Definition eda_item.h:531
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
void Remove(BOARD_ITEM *aItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
EDA_ITEM * Clone() const override
Invoke a function on all children.
Definition kiid.h:46
Definition pad.h:61
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_CASE(SwapItemDataReindexesFootprintChildren)
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:80