KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_pcb_differ.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 3
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, you may find one here:
18 * http://www.gnu.org/licenses/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
26
29
30#include <board.h>
31#include <board_item.h>
32#include <pad.h>
33#include <pcb_track.h>
34#include <footprint.h>
36
37#include <nlohmann/json.hpp>
38
39#include <map>
40
41
42using namespace KICAD_DIFF;
43
44
54{
56 {
57 KI_TEST::LoadBoard( m_settingsA, "complex_hierarchy", m_before );
58 KI_TEST::LoadBoard( m_settingsB, "complex_hierarchy", m_after );
59
62 }
63
66 std::unique_ptr<BOARD> m_before;
67 std::unique_ptr<BOARD> m_after;
68};
69
70
71BOOST_FIXTURE_TEST_SUITE( PcbDiffer, PCB_DIFFER_FIXTURE )
72
73
74BOOST_AUTO_TEST_CASE( TwoFreshLoadsAreIdentical )
75{
76 PCB_DIFFER differ( m_before.get(), m_after.get(), wxS( "complex_hierarchy.kicad_pcb" ) );
77 DOCUMENT_DIFF result = differ.Diff();
78
79 BOOST_CHECK_EQUAL( result.docType.ToStdString(), "kicad_pcb" );
80 BOOST_CHECK_MESSAGE( result.Empty(), "Two fresh loads of the same fixture should produce no diff; "
81 "got " << result.changes.size()
82 << " change records" );
83}
84
85
86BOOST_AUTO_TEST_CASE( RemovingAnItemSurfacesAsRemoved )
87{
88 BOARD_ITEM_SET items = m_after->GetItemSet();
89 BOOST_REQUIRE( !items.empty() );
90
91 // Pick the first track from the fixture and remove it from the after-board.
92 PCB_TRACK* victim = nullptr;
93
94 for( BOARD_ITEM* item : items )
95 {
96 if( item && item->Type() == PCB_TRACE_T )
97 {
98 victim = static_cast<PCB_TRACK*>( item );
99 break;
100 }
101 }
102
103 BOOST_REQUIRE( victim );
104 KIID victimUuid = victim->m_Uuid;
105 m_after->Remove( victim, REMOVE_MODE::NORMAL );
106
107 PCB_DIFFER differ( m_before.get(), m_after.get() );
108 DOCUMENT_DIFF result = differ.Diff();
109
110 BOOST_REQUIRE_EQUAL( result.changes.size(), 1u );
111 BOOST_CHECK( result.changes[0].kind == CHANGE_KIND::REMOVED );
112 BOOST_CHECK_EQUAL( result.changes[0].id.back().AsString().ToStdString(), victimUuid.AsString().ToStdString() );
113
114 delete victim;
115}
116
117
118BOOST_AUTO_TEST_CASE( WidthChangeProducesPropertyDelta )
119{
120 BOARD_ITEM_SET items = m_after->GetItemSet();
121 PCB_TRACK* subject = nullptr;
122
123 for( BOARD_ITEM* item : items )
124 {
125 if( item && item->Type() == PCB_TRACE_T )
126 {
127 subject = static_cast<PCB_TRACK*>( item );
128 break;
129 }
130 }
131
132 BOOST_REQUIRE( subject );
133
134 int originalWidth = subject->GetWidth();
135 int newWidth = originalWidth + 50000;
136 subject->SetWidth( newWidth );
137
138 PCB_DIFFER differ( m_before.get(), m_after.get() );
139 DOCUMENT_DIFF result = differ.Diff();
140
141 BOOST_REQUIRE_EQUAL( result.changes.size(), 1u );
142 BOOST_CHECK( result.changes[0].kind == CHANGE_KIND::MODIFIED );
143 BOOST_CHECK_EQUAL( result.changes[0].id.back().AsString().ToStdString(), subject->m_Uuid.AsString().ToStdString() );
144
145 bool foundWidthDelta = false;
146
147 for( const PROPERTY_DELTA& d : result.changes[0].properties )
148 {
149 if( d.name.Lower().Contains( wxS( "width" ) ) )
150 {
151 foundWidthDelta = true;
152 BOOST_CHECK_EQUAL( d.before.AsInt(), originalWidth );
153 BOOST_CHECK_EQUAL( d.after.AsInt(), newWidth );
154 }
155 }
156
157 BOOST_CHECK_MESSAGE( foundWidthDelta, "Expected Width property delta after SetWidth() on a track" );
158}
159
160
161BOOST_AUTO_TEST_CASE( RoutingChangesCarryNetNamesForPresentationGrouping )
162{
163 BOARD_ITEM_SET items = m_after->GetItemSet();
164 std::map<wxString, PCB_TRACK*> tracksByNet;
165 wxString selectedNet;
166 PCB_TRACK* first = nullptr;
167 PCB_TRACK* second = nullptr;
168
169 for( BOARD_ITEM* item : items )
170 {
171 if( !item || ( item->Type() != PCB_TRACE_T && item->Type() != PCB_VIA_T ) )
172 continue;
173
174 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
175
176 if( track->GetNetCode() <= 0 || track->GetNetname().IsEmpty() )
177 continue;
178
179 wxString netName = track->GetNetname();
180
181 if( tracksByNet.contains( netName ) )
182 {
183 selectedNet = netName;
184 first = tracksByNet[netName];
185 second = track;
186 break;
187 }
188
189 tracksByNet[netName] = track;
190 }
191
192 BOOST_REQUIRE( first );
193 BOOST_REQUIRE( second );
194
195 first->SetWidth( first->GetWidth() + 25000 );
196 second->SetWidth( second->GetWidth() + 50000 );
197
198 PCB_DIFFER differ( m_before.get(), m_after.get() );
199 DOCUMENT_DIFF result = differ.Diff();
200
201 std::size_t selectedNetChanges = 0;
202
203 for( const ITEM_CHANGE& change : result.changes )
204 {
205 if( change.refdes == selectedNet )
206 {
207 ++selectedNetChanges;
208 BOOST_CHECK( change.typeName == wxS( "PCB_TRACK" ) || change.typeName == wxS( "PCB_VIA" ) );
209 }
210 }
211
212 BOOST_CHECK_EQUAL( selectedNetChanges, 2u );
213}
214
215
216BOOST_AUTO_TEST_CASE( ChildEditOnFootprintNestsUnderFootprintRecord )
217{
218 BOARD_ITEM_SET items = m_after->GetItemSet();
219 FOOTPRINT* fp = nullptr;
220
221 for( BOARD_ITEM* item : items )
222 {
223 if( item && item->Type() == PCB_FOOTPRINT_T )
224 {
225 fp = static_cast<FOOTPRINT*>( item );
226
227 if( !fp->Pads().empty() )
228 break;
229
230 fp = nullptr;
231 }
232 }
233
234 BOOST_REQUIRE( fp );
235 BOOST_REQUIRE( !fp->Pads().empty() );
236
237 PAD* pad = fp->Pads().front();
238 pad->SetNumber( pad->GetNumber() + wxS( "X" ) );
239
240 PCB_DIFFER differ( m_before.get(), m_after.get() );
241 DOCUMENT_DIFF result = differ.Diff();
242
243 // We expect exactly one parent change (the footprint), with one child change (the pad).
244 BOOST_REQUIRE_EQUAL( result.changes.size(), 1u );
245 BOOST_CHECK( result.changes[0].kind == CHANGE_KIND::MODIFIED );
246 BOOST_CHECK_EQUAL( result.changes[0].id.back().AsString().ToStdString(), fp->m_Uuid.AsString().ToStdString() );
247 BOOST_REQUIRE_EQUAL( result.changes[0].children.size(), 1u );
248 BOOST_CHECK( result.changes[0].children[0].kind == CHANGE_KIND::MODIFIED );
249 BOOST_CHECK_EQUAL( result.changes[0].children[0].id.back().AsString().ToStdString(),
250 pad->m_Uuid.AsString().ToStdString() );
251}
252
253
254BOOST_AUTO_TEST_CASE( DiffOutputIsDeterministic )
255{
256 BOARD_ITEM_SET items = m_after->GetItemSet();
257 PCB_TRACK* subject = nullptr;
258
259 for( BOARD_ITEM* item : items )
260 {
261 if( item && item->Type() == PCB_TRACE_T )
262 {
263 subject = static_cast<PCB_TRACK*>( item );
264 break;
265 }
266 }
267
268 BOOST_REQUIRE( subject );
269 subject->SetWidth( subject->GetWidth() + 25000 );
270
271 PCB_DIFFER differ1( m_before.get(), m_after.get() );
272 PCB_DIFFER differ2( m_before.get(), m_after.get() );
273
274 DOCUMENT_DIFF r1 = differ1.Diff();
275 DOCUMENT_DIFF r2 = differ2.Diff();
276
277 BOOST_CHECK_EQUAL( r1.ToJson().dump(), r2.ToJson().dump() );
278}
279
280
281BOOST_AUTO_TEST_CASE( DiffJsonRoundTrip )
282{
283 BOARD_ITEM_SET items = m_after->GetItemSet();
284 PCB_TRACK* subject = nullptr;
285
286 for( BOARD_ITEM* item : items )
287 {
288 if( item && item->Type() == PCB_TRACE_T )
289 {
290 subject = static_cast<PCB_TRACK*>( item );
291 break;
292 }
293 }
294
295 BOOST_REQUIRE( subject );
296 subject->SetWidth( subject->GetWidth() + 30000 );
297
298 PCB_DIFFER differ( m_before.get(), m_after.get(), wxS( "complex_hierarchy.kicad_pcb" ) );
299 DOCUMENT_DIFF result = differ.Diff();
300
301 nlohmann::json j = result.ToJson();
303
304 BOOST_CHECK_EQUAL( back.path.ToStdString(), result.path.ToStdString() );
305 BOOST_CHECK_EQUAL( back.docType.ToStdString(), result.docType.ToStdString() );
306 BOOST_REQUIRE_EQUAL( back.changes.size(), result.changes.size() );
307 BOOST_CHECK( back.changes[0].kind == result.changes[0].kind );
308 BOOST_CHECK_EQUAL( back.changes[0].properties.size(), result.changes[0].properties.size() );
309}
310
311
312BOOST_AUTO_TEST_CASE( ExtractedGeometryCarriesBoardLayers )
313{
314 const KIGFX::COLOR4D color( 0.4, 0.4, 0.4, 0.6 );
315 DOCUMENT_GEOMETRY geometry = ExtractBoardGeometry( *m_after, color );
316
317 BOOST_CHECK( !geometry.Empty() );
318
319 LSET expectedTrackLayers;
320
321 for( const PCB_TRACK* track : m_after->Tracks() )
322 {
323 if( track )
324 expectedTrackLayers |= track->GetLayerSet();
325 }
326
327 BOOST_REQUIRE( expectedTrackLayers.any() );
328
329 const LSET geometryLayers = GeometryLayerSet( geometry );
330 BOOST_CHECK( ( geometryLayers & expectedTrackLayers ).any() );
331
332 bool segmentWithLayer = false;
333
334 for( const DOCUMENT_SEGMENT& segment : geometry.segments )
335 {
336 if( segment.layers.any() )
337 {
338 segmentWithLayer = true;
339 break;
340 }
341 }
342
343 BOOST_CHECK( segmentWithLayer );
344}
345
346
std::set< BOARD_ITEM *, CompareByUuid > BOARD_ITEM_SET
Set of BOARD_ITEMs ordered by UUID.
Definition board.h:304
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:80
const KIID m_Uuid
Definition eda_item.h:531
std::deque< PAD * > & Pads()
Definition footprint.h:373
Diff two already-parsed BOARDs and produce a DOCUMENT_DIFF.
Definition pcb_differ.h:52
DOCUMENT_DIFF Diff() override
Produce a DOCUMENT_DIFF of the inputs the concrete differ was constructed with.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
Definition kiid.h:44
wxString AsString() const
Definition kiid.cpp:242
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
Definition pad.h:61
virtual void SetWidth(int aWidth)
Definition pcb_track.h:86
virtual int GetWidth() const
Definition pcb_track.h:87
A type-safe container of any type.
Definition ki_any.h:92
LSET GeometryLayerSet(const DOCUMENT_GEOMETRY &aGeometry)
Return the union of every non-empty layer set carried by the geometry.
DOCUMENT_GEOMETRY ExtractBoardGeometry(const BOARD &aBoard, const KIGFX::COLOR4D &aColor)
Extract a coarse outline of a BOARD into a DOCUMENT_GEOMETRY for use as background context in DIFF_SC...
void LoadBoard(SETTINGS_MANAGER &aSettingsManager, const wxString &aRelPath, std::unique_ptr< BOARD > &aBoard)
The full set of changes between two parsed documents of one type.
nlohmann::json ToJson() const
static DOCUMENT_DIFF FromJson(const nlohmann::json &aJson)
std::vector< ITEM_CHANGE > changes
Aggregate of background geometry extracted from one source document.
Definition diff_scene.h:163
std::vector< DOCUMENT_SEGMENT > segments
Definition diff_scene.h:164
Stroked line segment from one of the source documents.
Definition diff_scene.h:117
One change record on a single item.
std::optional< wxString > refdes
Single (name, before, after) triple for one mutated property on an item.
Fixture: loads the same canonical board into two separate BOARD instances so tests can mutate one and...
std::unique_ptr< BOARD > m_before
SETTINGS_MANAGER m_settingsA
SETTINGS_MANAGER m_settingsB
std::unique_ptr< BOARD > m_after
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(TwoFreshLoadsAreIdentical)
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:90
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:79
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:89