KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_pads_binary_import.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 (C) 2026 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
23
26#include <layer_ids.h>
27#include <padstack.h>
28#include <board.h>
29#include <pcb_text.h>
30#include <pcb_shape.h>
31#include <pcb_field.h>
32#include <pad.h>
33#include <pcb_track.h>
34#include <footprint.h>
35#include <zone.h>
37#include <set>
38
39
41{
42 std::string dir;
43 std::string binaryFile;
44 std::string ascFile;
46};
47
48
50 { "TMS1mmX19", "TMS1mmX19.pcb", "TMS1mmX19.asc", false },
51 { "MC4_PLUS_CSHAPE", "MC4_PLUS_CSHAPE.pcb", "MC4_PLUS_CSHAPE.asc", false },
52 { "MC2_PLUS_REV1", "MC2_PLUS_REV1.pcb", "MC2_PLUS_REV1.asc", true },
53 { "Ems4_Rev2", "Ems4_Rev2.pcb", "Ems4_Rev2.asc", false },
54 { "LCORE_4", "LCORE_4.pcb", "LCORE_4.asc", false },
55 { "LCORE_2", "LCORE_2.pcb", "LCORE_2.asc", false },
56 { "Dexter_MotorCtrl", "Dexter_MotorCtrl.pcb", "Dexter_MotorCtrl.asc", true },
57 { "MAIS_FC", "MAIS_FC.pcb", "MAIS_FC.asc", true },
58};
59
60
61static wxString GetBinaryPath( const PADS_BINARY_BOARD_INFO& aBoard )
62{
63 return KI_TEST::GetPcbnewTestDataDir() + "plugins/pads/" + aBoard.dir + "/"
64 + aBoard.binaryFile;
65}
66
67
68static wxString GetAscPath( const PADS_BINARY_BOARD_INFO& aBoard )
69{
70 return KI_TEST::GetPcbnewTestDataDir() + "plugins/pads/" + aBoard.dir + "/" + aBoard.ascFile;
71}
72
73
78static std::unique_ptr<BOARD> LoadBinary( const PADS_BINARY_BOARD_INFO& aBoard )
79{
80 PCB_IO_PADS_BINARY plugin;
81 wxString filename = GetBinaryPath( aBoard );
82
83 BOOST_CHECK_MESSAGE( plugin.CanReadBoard( filename ),
84 aBoard.dir << " binary should be readable by PCB_IO_PADS_BINARY" );
85
86 std::unique_ptr<BOARD> board;
87
88 try
89 {
90 board.reset( plugin.LoadBoard( filename, nullptr, nullptr, nullptr ) );
91 }
92 catch( const std::exception& e )
93 {
94 BOOST_WARN_MESSAGE( false,
95 aBoard.dir << " binary threw exception during load: " << e.what() );
96 return nullptr;
97 }
98
99 BOOST_CHECK_MESSAGE( board != nullptr, aBoard.dir << " binary failed to load" );
100 return board;
101}
102
103
104static std::unique_ptr<BOARD> LoadAsc( const PADS_BINARY_BOARD_INFO& aBoard )
105{
106 PCB_IO_PADS plugin;
107 wxString filename = GetAscPath( aBoard );
108
109 std::unique_ptr<BOARD> board;
110
111 try
112 {
113 board.reset( plugin.LoadBoard( filename, nullptr, nullptr, nullptr ) );
114 }
115 catch( const std::exception& e )
116 {
117 BOOST_FAIL( aBoard.dir << " ASC threw exception during load: " << e.what() );
118 return nullptr;
119 }
120
121 BOOST_REQUIRE_MESSAGE( board != nullptr, aBoard.dir << " ASC failed to load" );
122 return board;
123}
124
125
126static int CountEdgeCutsShapes( const BOARD* aBoard )
127{
128 int count = 0;
129
130 for( BOARD_ITEM* item : aBoard->Drawings() )
131 {
132 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item ) )
133 {
134 if( shape->GetLayer() == Edge_Cuts )
135 count++;
136 }
137 }
138
139 return count;
140}
141
142
148static void CheckCountWithTolerance( const std::string& aLabel, size_t aBinaryCount,
149 size_t aAscCount, bool aDifferentRevision )
150{
151 if( aDifferentRevision )
152 {
153 BOOST_WARN_MESSAGE( aBinaryCount == aAscCount,
154 aLabel << " binary=" << aBinaryCount
155 << " asc=" << aAscCount << " (different revision)" );
156 return;
157 }
158
159 if( aBinaryCount == aAscCount )
160 {
161 BOOST_CHECK_MESSAGE( true, aLabel << " counts match: " << aBinaryCount );
162 return;
163 }
164
165 size_t maxCount = std::max( aBinaryCount, aAscCount );
166 size_t diff = ( aBinaryCount > aAscCount ) ? aBinaryCount - aAscCount
167 : aAscCount - aBinaryCount;
168
169 bool withinTolerance = ( diff <= 2 ) || ( diff * 100 / maxCount <= 5 );
170
171 BOOST_CHECK_MESSAGE( withinTolerance,
172 aLabel << " counts differ beyond tolerance: binary=" << aBinaryCount
173 << " asc=" << aAscCount );
174
175 BOOST_WARN_MESSAGE( aBinaryCount == aAscCount,
176 aLabel << " exact count mismatch: binary=" << aBinaryCount
177 << " asc=" << aAscCount );
178}
179
180
186 const BOARD* aBinaryBoard )
187{
188 BOOST_WARN_MESSAGE( aBinaryBoard->Tracks().size() > 0,
189 aBoard.dir << " binary has no tracks (v0x2021 not yet supported)" );
190
191 std::set<std::pair<int, int>> viaPositions;
192 bool hasDuplicate = false;
193
194 for( PCB_TRACK* trk : aBinaryBoard->Tracks() )
195 {
196 PCB_VIA* via = dynamic_cast<PCB_VIA*>( trk );
197
198 if( !via || via->GetViaType() != VIATYPE::THROUGH )
199 continue;
200
201 auto key = std::make_pair( via->GetPosition().x, via->GetPosition().y );
202
203 if( viaPositions.count( key ) )
204 {
205 hasDuplicate = true;
206 break;
207 }
208
209 viaPositions.insert( key );
210 }
211
212 BOOST_CHECK_MESSAGE( !hasDuplicate,
213 aBoard.dir << " binary should have no duplicate through-hole vias" );
214
215 for( PCB_TRACK* trk : aBinaryBoard->Tracks() )
216 {
217 if( trk->Type() == PCB_TRACE_T || trk->Type() == PCB_ARC_T )
218 {
219 BOOST_CHECK_MESSAGE( IsCopperLayer( trk->GetLayer() ),
220 aBoard.dir << " binary track on non-copper layer "
221 << trk->GetLayer() );
222 }
223 }
224
225 for( FOOTPRINT* fp : aBinaryBoard->Footprints() )
226 {
227 for( PAD* pad : fp->Pads() )
228 {
229 BOOST_WARN_MESSAGE( pad->GetSize( PADSTACK::ALL_LAYERS ).x > 0
230 && pad->GetSize( PADSTACK::ALL_LAYERS ).y > 0,
231 aBoard.dir << " " << fp->GetReference() << " pad has zero size" );
232 }
233 }
234}
235
236
237BOOST_AUTO_TEST_SUITE( PadsBinaryImport )
238
239
240BOOST_AUTO_TEST_CASE( BinaryFileDetection )
241{
242 PCB_IO_PADS_BINARY binaryPlugin;
243 PCB_IO_PADS ascPlugin;
244
245 for( const auto& board : PADS_BINARY_BOARDS )
246 {
247 wxString binaryPath = GetBinaryPath( board );
248
249 BOOST_CHECK_MESSAGE( binaryPlugin.CanReadBoard( binaryPath ),
250 board.dir << " binary should be recognized by PCB_IO_PADS_BINARY" );
251
252 BOOST_CHECK_MESSAGE( !ascPlugin.CanReadBoard( binaryPath ),
253 board.dir << " binary should NOT be recognized by PCB_IO_PADS" );
254 }
255}
256
257
258BOOST_AUTO_TEST_CASE( AsciiFileRejection )
259{
260 PCB_IO_PADS_BINARY binaryPlugin;
261
262 for( const auto& board : PADS_BINARY_BOARDS )
263 {
264 wxString ascPath = GetAscPath( board );
265
266 BOOST_CHECK_MESSAGE( !binaryPlugin.CanReadBoard( ascPath ),
267 board.dir << " ASCII should NOT be recognized by PCB_IO_PADS_BINARY" );
268 }
269}
270
271
272#define BINARY_LOAD_TEST( name, idx ) \
273 BOOST_AUTO_TEST_CASE( BasicLoad_##name ) \
274 { \
275 auto board = LoadBinary( PADS_BINARY_BOARDS[idx] ); \
276 \
277 if( board ) \
278 BOOST_CHECK( board->Footprints().size() > 0 ); \
279 }
280
281BINARY_LOAD_TEST( TMS1mmX19, 0 )
282BINARY_LOAD_TEST( MC4_PLUS_CSHAPE, 1 )
283BINARY_LOAD_TEST( MC2_PLUS_REV1, 2 )
284BINARY_LOAD_TEST( Ems4_Rev2, 3 )
285BINARY_LOAD_TEST( LCORE_4, 4 )
286BINARY_LOAD_TEST( LCORE_2, 5 )
287BINARY_LOAD_TEST( Dexter_MotorCtrl, 6 )
288BINARY_LOAD_TEST( MAIS_FC, 7 )
289
290
291#define FOOTPRINT_COUNT_TEST( name, idx ) \
292 BOOST_AUTO_TEST_CASE( FootprintCount_##name ) \
293 { \
294 auto bin = LoadBinary( PADS_BINARY_BOARDS[idx] ); \
295 \
296 if( !bin ) \
297 return; \
298 \
299 auto asc = LoadAsc( PADS_BINARY_BOARDS[idx] ); \
300 \
301 CheckCountWithTolerance( #name " footprints", bin->Footprints().size(), \
302 asc->Footprints().size(), \
303 PADS_BINARY_BOARDS[idx].differentRevision ); \
304 }
305
306FOOTPRINT_COUNT_TEST( TMS1mmX19, 0 )
307FOOTPRINT_COUNT_TEST( MC4_PLUS_CSHAPE, 1 )
308FOOTPRINT_COUNT_TEST( MC2_PLUS_REV1, 2 )
309FOOTPRINT_COUNT_TEST( Ems4_Rev2, 3 )
310FOOTPRINT_COUNT_TEST( LCORE_4, 4 )
311FOOTPRINT_COUNT_TEST( LCORE_2, 5 )
312FOOTPRINT_COUNT_TEST( Dexter_MotorCtrl, 6 )
313FOOTPRINT_COUNT_TEST( MAIS_FC, 7 )
314
315
316#define NET_COUNT_TEST( name, idx ) \
317 BOOST_AUTO_TEST_CASE( NetCount_##name ) \
318 { \
319 auto bin = LoadBinary( PADS_BINARY_BOARDS[idx] ); \
320 \
321 if( !bin ) \
322 return; \
323 \
324 auto asc = LoadAsc( PADS_BINARY_BOARDS[idx] ); \
325 \
326 CheckCountWithTolerance( #name " nets", bin->GetNetCount(), asc->GetNetCount(), \
327 PADS_BINARY_BOARDS[idx].differentRevision ); \
328 }
329
330NET_COUNT_TEST( TMS1mmX19, 0 )
331NET_COUNT_TEST( MC4_PLUS_CSHAPE, 1 )
332NET_COUNT_TEST( MC2_PLUS_REV1, 2 )
333NET_COUNT_TEST( Ems4_Rev2, 3 )
334NET_COUNT_TEST( LCORE_4, 4 )
335NET_COUNT_TEST( LCORE_2, 5 )
336NET_COUNT_TEST( Dexter_MotorCtrl, 6 )
337NET_COUNT_TEST( MAIS_FC, 7 )
338
339
340#define TRACK_COUNT_TEST( name, idx ) \
341 BOOST_AUTO_TEST_CASE( TrackCount_##name ) \
342 { \
343 auto bin = LoadBinary( PADS_BINARY_BOARDS[idx] ); \
344 \
345 if( !bin ) \
346 return; \
347 \
348 auto asc = LoadAsc( PADS_BINARY_BOARDS[idx] ); \
349 \
350 BOOST_WARN_MESSAGE( bin->Tracks().size() > 0, \
351 #name " binary track parsing not supported for v0x2021" ); \
352 \
353 BOOST_WARN_EQUAL( bin->Tracks().size(), asc->Tracks().size() ); \
354 }
355
356TRACK_COUNT_TEST( TMS1mmX19, 0 )
357TRACK_COUNT_TEST( MC4_PLUS_CSHAPE, 1 )
358TRACK_COUNT_TEST( Ems4_Rev2, 3 )
359TRACK_COUNT_TEST( LCORE_4, 4 )
360TRACK_COUNT_TEST( LCORE_2, 5 )
361TRACK_COUNT_TEST( MAIS_FC, 7 )
362
363
364BOOST_AUTO_TEST_CASE( BoardOutline_LCORE_4 )
365{
366 auto board = LoadBinary( PADS_BINARY_BOARDS[4] );
367
368 if( !board )
369 return;
370
371 BOOST_CHECK_MESSAGE( CountEdgeCutsShapes( board.get() ) > 0,
372 "LCORE_4 binary should have board outline shapes" );
373}
374
375
376BOOST_AUTO_TEST_CASE( BoardOutline_LCORE_2 )
377{
378 auto board = LoadBinary( PADS_BINARY_BOARDS[5] );
379
380 if( !board )
381 return;
382
383 BOOST_CHECK_MESSAGE( CountEdgeCutsShapes( board.get() ) > 0,
384 "LCORE_2 binary should have board outline shapes" );
385}
386
387
388BOOST_AUTO_TEST_CASE( BoardOutline_OtherVersions )
389{
390 int indices[] = { 0, 1, 2, 3, 6, 7 };
391
392 for( int i : indices )
393 {
394 auto board = LoadBinary( PADS_BINARY_BOARDS[i] );
395
396 if( !board )
397 continue;
398
399 BOOST_WARN_MESSAGE( CountEdgeCutsShapes( board.get() ) > 0,
400 PADS_BINARY_BOARDS[i].dir
401 << " binary outline parsing not yet complete" );
402 }
403}
404
405
406#define STRUCTURAL_INTEGRITY_TEST( name, idx ) \
407 BOOST_AUTO_TEST_CASE( StructuralIntegrity_##name ) \
408 { \
409 auto board = LoadBinary( PADS_BINARY_BOARDS[idx] ); \
410 \
411 if( !board ) \
412 return; \
413 \
414 RunStructuralChecks( PADS_BINARY_BOARDS[idx], board.get() ); \
415 }
416
417STRUCTURAL_INTEGRITY_TEST( TMS1mmX19, 0 )
418STRUCTURAL_INTEGRITY_TEST( MC4_PLUS_CSHAPE, 1 )
419STRUCTURAL_INTEGRITY_TEST( MC2_PLUS_REV1, 2 )
420STRUCTURAL_INTEGRITY_TEST( Ems4_Rev2, 3 )
421STRUCTURAL_INTEGRITY_TEST( LCORE_4, 4 )
422STRUCTURAL_INTEGRITY_TEST( LCORE_2, 5 )
423STRUCTURAL_INTEGRITY_TEST( Dexter_MotorCtrl, 6 )
424STRUCTURAL_INTEGRITY_TEST( MAIS_FC, 7 )
425
426
General utilities for PCB file IO for QA programs.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:81
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:372
const FOOTPRINTS & Footprints() const
Definition board.h:420
const TRACKS & Tracks() const
Definition board.h:418
const DRAWINGS & Drawings() const
Definition board.h:422
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
Definition pad.h:61
PCB I/O plugin for importing PADS binary .pcb files.
BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties, PROJECT *aProject) override
Load information from some input file format that this PCB_IO implementation knows about into either ...
bool CanReadBoard(const wxString &aFileName) const override
Checks if this PCB_IO can read the specified board file.
BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties, PROJECT *aProject) override
Load information from some input file format that this PCB_IO implementation knows about into either ...
bool CanReadBoard(const wxString &aFileName) const override
Checks if this PCB_IO can read the specified board file.
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:675
@ Edge_Cuts
Definition layer_ids.h:108
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
static int CountEdgeCutsShapes(const BOARD *aBoard)
static std::unique_ptr< BOARD > LoadAsc(const PADS_BINARY_BOARD_INFO &aBoard)
#define TRACK_COUNT_TEST(name, idx)
static std::unique_ptr< BOARD > LoadBinary(const PADS_BINARY_BOARD_INFO &aBoard)
Load a binary .pcb file.
BOOST_AUTO_TEST_CASE(BinaryFileDetection)
#define BINARY_LOAD_TEST(name, idx)
#define FOOTPRINT_COUNT_TEST(name, idx)
#define STRUCTURAL_INTEGRITY_TEST(name, idx)
static wxString GetAscPath(const PADS_BINARY_BOARD_INFO &aBoard)
#define NET_COUNT_TEST(name, idx)
static void CheckCountWithTolerance(const std::string &aLabel, size_t aBinaryCount, size_t aAscCount, bool aDifferentRevision)
Compare counts with tolerance for binary/ASC differences.
static const PADS_BINARY_BOARD_INFO PADS_BINARY_BOARDS[]
static wxString GetBinaryPath(const PADS_BINARY_BOARD_INFO &aBoard)
static void RunStructuralChecks(const PADS_BINARY_BOARD_INFO &aBoard, const BOARD *aBinaryBoard)
Structural integrity checks.
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:91
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:89