54 {
"ClaySight_MK1",
"ClaySight_MK1.asc" },
55 {
"TMS1mmX19",
"TMS1mmX19.asc" },
56 {
"MC4_PLUS_CSHAPE",
"MC4_PLUS_CSHAPE.asc" },
57 {
"MC2_PLUS_REV1",
"MC2_PLUS_REV1.asc" },
58 {
"Ems4_Rev2",
"Ems4_Rev2.asc" },
59 {
"LCORE_4",
"LCORE_4.asc" },
60 {
"LCORE_2",
"LCORE_2.asc" },
61 {
"Dexter_MotorCtrl",
"Dexter_MotorCtrl.asc" },
62 {
"MAIS_FC",
"MAIS_FC.asc" },
63 {
"ClaySight_MK2",
"ClaySight_MK2.asc" },
83 aBoard.
dir <<
" should be a readable PADS file" );
85 std::unique_ptr<BOARD> board;
89 board.reset( plugin.
LoadBoard( filename,
nullptr,
nullptr,
nullptr ) );
91 catch(
const std::exception& e )
93 BOOST_WARN_MESSAGE(
false,
94 aBoard.
dir <<
" threw exception during load: " << e.what() );
98 BOOST_REQUIRE_MESSAGE( board !=
nullptr, aBoard.
dir <<
" failed to load" );
99 BOOST_CHECK_MESSAGE( board->Footprints().size() > 0,
100 aBoard.
dir <<
" should have footprints" );
120 BOOST_WARN_MESSAGE( board->Tracks().size() > 0,
121 aBoard.
dir <<
" has no tracks (parser may not support this format version)" );
123 if( board->Tracks().size() > 0 && board->Footprints().size() > 0 )
128 for(
FOOTPRINT* fp : board->Footprints() )
129 fpBbox.
Merge( fp->GetBoundingBox() );
135 trackBbox.
Merge( trk->GetBoundingBox() );
137 BOOST_CHECK_MESSAGE( fpBbox.
Intersects( trackBbox ),
138 aBoard.
dir <<
" footprint and track bounding boxes should overlap" );
142 std::set<std::pair<int, int>> viaPositions;
143 bool hasDuplicate =
false;
152 auto key = std::make_pair(
via->GetPosition().x,
via->GetPosition().y );
154 if( viaPositions.count( key ) )
160 viaPositions.insert( key );
163 BOOST_CHECK_MESSAGE( !hasDuplicate,
164 aBoard.
dir <<
" should have no duplicate through-hole vias" );
172 aBoard.
dir <<
" track on non-copper layer " << trk->GetLayer() );
177 for(
FOOTPRINT* fp : board->Footprints() )
179 for(
PAD*
pad : fp->Pads() )
183 aBoard.
dir <<
" " << fp->GetReference() <<
" pad has zero size" );
188 for(
ZONE* zone : board->Zones() )
191 BOOST_REQUIRE_MESSAGE( outline !=
nullptr,
192 aBoard.
dir <<
" zone has null outline" );
197 aBoard.
dir <<
" zone outline " << ii <<
" has "
232 for(
FOOTPRINT* fp : board->Footprints() )
233 totalPads += fp->Pads().size();
258 int edgeCutsCount = 0;
276 if(
dynamic_cast<PCB_TEXT*
>( item ) )
283 std::set<wxString> trackNets;
301 BOOST_CHECK_MESSAGE( layer ==
F_Cu || layer ==
B_Cu,
302 "trace on unexpected layer " << layer );
363 std::unique_ptr<BOARD> board( plugin.
LoadBoard( filename,
nullptr,
nullptr,
nullptr ) );
369 for(
PCB_TRACK* track : board->Tracks() )
378 BOOST_CHECK( track_count > 0 );
389 std::unique_ptr<BOARD> board( plugin.
LoadBoard( filename,
nullptr,
nullptr,
nullptr ) );
393 int silkscreen_count = 0;
394 int comments_count = 0;
395 int copper_count = 0;
409 else if( layer ==
F_Cu )
445 for(
FOOTPRINT* fp : board->Footprints() )
446 totalPads += fp->Pads().size();
466 std::set<wxString> trackNets;
484 "trace on non-copper layer " << trk->GetLayer() );
498 if( shape->GetLayer() ==
F_SilkS )
514 BOOST_CHECK( defaultNc->GetViaDiameter() > 0 );
515 BOOST_CHECK( defaultNc->GetViaDrill() > 0 );
516 BOOST_CHECK( defaultNc->GetViaDiameter() > defaultNc->GetViaDrill() );
523 int edgeCutsCount = 0;
538 wxString textContent;
545 textContent =
text->GetText();
554 BOOST_CHECK( textContent.Contains( wxT(
"\n" ) ) );
555 BOOST_CHECK( !textContent.Contains( wxT(
"_" ) ) );
556 BOOST_CHECK( textContent.Contains( wxT(
"CLAYSIGHT MCU V.2" ) ) );
557 BOOST_CHECK( textContent.Contains( wxT(
"The Ohio State University" ) ) );
578 bool foundCopperThickness =
false;
579 bool foundDielectric =
false;
585 if( item->GetThickness() > 0 )
586 foundCopperThickness =
true;
590 if( item->GetEpsilonR() > 3.0 )
591 foundDielectric =
true;
595 BOOST_CHECK_MESSAGE( foundCopperThickness,
"stackup should have non-zero copper thickness" );
596 BOOST_CHECK_MESSAGE( foundDielectric,
"stackup should have dielectric constant > 3.0" );
614 +
"plugins/pads/synthetic_degenerate_pour.asc";
616 std::unique_ptr<BOARD> board( plugin.
LoadBoard( filename,
nullptr,
nullptr,
nullptr ) );
626 if( board->Zones().size() == 1 )
628 ZONE* zone = board->Zones()[0];
648 +
"plugins/pads/synthetic_filled_copper.asc";
650 std::unique_ptr<BOARD> board( plugin.
LoadBoard( filename,
nullptr,
nullptr,
nullptr ) );
653 BOOST_REQUIRE_EQUAL( board->Zones().size(), 1 );
655 ZONE* zone = board->Zones()[0];
676 std::unique_ptr<BOARD> board( plugin.
LoadBoard( filename,
nullptr,
nullptr,
nullptr ) );
684 int dwgsUserCount = 0;
695 BOOST_CHECK_MESSAGE( dwgsUserCount > 0,
696 "graphics on PADS layer 18 (drill drawing) should map to Dwgs_User" );
703 for(
FOOTPRINT* fp : board->Footprints() )
705 if( fp->GetReference() == wxT(
"U1" ) )
712 BOOST_REQUIRE_MESSAGE( u1 !=
nullptr,
"U1 footprint should exist" );
714 bool foundOvalPad =
false;
727 BOOST_CHECK_MESSAGE( foundOvalPad,
"U1 should have oval pads (OF shape, not RT thermal)" );
732 int pourZoneCount = 0;
733 int filledZoneCount = 0;
735 for(
ZONE* zone : board->Zones() )
737 if( !zone->GetIsRuleArea() )
741 if( zone->IsFilled() )
746 BOOST_CHECK_MESSAGE( pourZoneCount <= 13,
747 "should not have duplicate zones from HATOUT; got " << pourZoneCount );
749 BOOST_CHECK_MESSAGE( filledZoneCount > 0,
"HATOUT records should produce filled zones" );
765 BOOST_CHECK_MESSAGE( start.
y ==
end.y,
766 "horizontal dimension endpoints should have equal Y coordinates; "
767 "start.y=" << start.
y <<
" end.y=" <<
end.y );
771 BOOST_CHECK_MESSAGE( dimCount > 0,
"should have at least one dimension" );
793 std::unique_ptr<BOARD> board( plugin.
LoadBoard( filename,
nullptr,
nullptr,
nullptr ) );
798 std::map<std::pair<int, int>,
int> viaPositionCount;
800 int throughCount = 0;
806 const int maxExpectedViaWidth = 1200000;
808 int oversizedViaCount = 0;
810 for(
PCB_TRACK* track : board->Tracks() )
818 auto key = std::make_pair( pos.
x, pos.
y );
819 viaPositionCount[key]++;
826 if(
via->GetWidth(
F_Cu ) > maxExpectedViaWidth )
831 BOOST_CHECK_MESSAGE( blindCount == 0,
832 "no vias should be blind; STANDARDVIA spans all copper layers; got "
833 << blindCount <<
" blind vias" );
835 BOOST_CHECK_MESSAGE( throughCount > 0,
"should have through-hole vias" );
838 BOOST_CHECK_MESSAGE( oversizedViaCount == 0,
839 "via size should use copper pad, not soldermask opening; got "
840 << oversizedViaCount <<
" oversized vias" );
843 int duplicateCount = 0;
845 for(
const auto& [pos, count] : viaPositionCount )
851 BOOST_CHECK_MESSAGE( duplicateCount == 0,
852 "should not have duplicate vias at the same position; got "
853 << duplicateCount <<
" positions with duplicates" );
858 int backTentedCount = 0;
861 for(
PCB_TRACK* track : board->Tracks() )
874 BOOST_CHECK_MESSAGE( backTentedCount == totalVias,
875 "vias without soldermask opening should be tented; "
876 << backTentedCount <<
" of " << totalVias <<
" back-tented" );
894 std::unique_ptr<BOARD> board( plugin.
LoadBoard( filename,
nullptr,
nullptr,
nullptr ) );
900 for(
FOOTPRINT* fp : board->Footprints() )
902 if( fp->GetReference() ==
"U1" )
909 BOOST_REQUIRE_MESSAGE( u1,
"U1 not found on board" );
914 const int expectedMajor = 6000000;
915 const int expectedMinor = 1500000;
916 const int tolerance = 10000;
922 wxString padNum =
pad->GetNumber();
924 if( padNum ==
"1" || padNum ==
"2" || padNum ==
"3"
925 || padNum ==
"4" || padNum ==
"5" )
928 "pad " << padNum <<
" should have oblong drill" );
931 int major = std::max( drillSize.
x, drillSize.
y );
932 int minor = std::min( drillSize.
x, drillSize.
y );
934 BOOST_CHECK_MESSAGE(
std::abs( major - expectedMajor ) < tolerance,
935 "pad " << padNum <<
" drill major axis " << major
936 <<
" should be ~" << expectedMajor );
938 BOOST_CHECK_MESSAGE(
std::abs( minor - expectedMinor ) < tolerance,
939 "pad " << padNum <<
" drill minor axis " << minor
940 <<
" should be ~" << expectedMinor );
947 BOOST_CHECK_MESSAGE( oblongCount == 5,
948 "expected 5 pads with oblong drill, got " << oblongCount );
966 std::unique_ptr<BOARD> board( plugin.
LoadBoard( filename,
nullptr,
nullptr,
nullptr ) );
972 for(
FOOTPRINT* fp : board->Footprints() )
974 if( fp->GetReference() ==
"M4" )
981 BOOST_REQUIRE_MESSAGE( m4,
"M4 not found on board" );
985 const int expectedPadSize = 6350000;
986 const int expectedDrill = 3175000;
987 const int tolerance = 10000;
989 BOOST_REQUIRE_MESSAGE( m4->
Pads().size() == 1,
990 "MTHOLEAAAB has 1 terminal; got " << m4->
Pads().size() );
995 "M4 pad 1 drill should be circular" );
998 int padDim = std::max( padSize.
x, padSize.
y );
1000 BOOST_CHECK_MESSAGE(
std::abs( padDim - expectedPadSize ) < tolerance,
1001 "M4 pad size " << padDim <<
" should be ~" << expectedPadSize
1005 int drillDim = std::max( drillSize.
x, drillSize.
y );
1007 BOOST_CHECK_MESSAGE(
std::abs( drillDim - expectedDrill ) < tolerance,
1008 "M4 drill size " << drillDim <<
" should be ~" << expectedDrill
1027 std::unique_ptr<BOARD> board( plugin.
LoadBoard( filename,
nullptr,
nullptr,
nullptr ) );
1035 auto hasTrueCrossing = [](
const SHAPE_POLY_SET& aPoly,
int aIdx ) ->
bool
1037 std::vector<SEG> segs;
1040 segs.emplace_back( *it );
1042 for(
size_t i = 0; i < segs.size(); i++ )
1044 for(
size_t j = i + 1; j < segs.size(); j++ )
1048 if( segs[i].
A == segs[j].
A || segs[i].
A == segs[j].
B
1049 || segs[i].
B == segs[j].
A || segs[i].
B == segs[j].
B )
1054 if( segs[i].
Collide( segs[j], 0 ) )
1062 int zonesChecked = 0;
1064 for(
ZONE* zone : board->Zones() )
1066 if( !zone->IsFilled() )
1071 if( !zone->HasFilledPolysForLayer( layer ) )
1074 std::shared_ptr<SHAPE_POLY_SET> fill = zone->GetFilledPolysList( layer );
1076 if( !fill || fill->OutlineCount() == 0 )
1081 for(
int pi = 0; pi < fill->OutlineCount(); pi++ )
1083 if( fill->Outline( pi ).PointCount() < 3 )
1086 BOOST_CHECK_MESSAGE(
1087 !hasTrueCrossing( *fill, pi ),
1088 "zone \"" << zone->GetNetname() <<
"\" on "
1089 << board->GetLayerName( layer )
1090 <<
" outline " << pi
1091 <<
" has self-intersecting fill polygon" );
1096 BOOST_CHECK_MESSAGE( zonesChecked > 0,
"no filled zones found to check" );
General utilities for PCB file IO for QA programs.
@ BS_ITEM_TYPE_DIELECTRIC
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
int m_CopperEdgeClearance
int GetBoardThickness() const
The full thickness of the board including copper and masks.
BOARD_STACKUP & GetStackupDescriptor()
std::vector< VIA_DIMENSION > m_ViasDimensionsList
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Manage one layer needed to make a physical board.
Manage layers needed to make a physical board.
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
constexpr void SetMaximum()
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Handle the data for a net.
const wxString & GetNetname() const
std::shared_ptr< NETCLASS > GetDefaultNetclass()
Gets the default netclass for the project.
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
For better understanding of the points that make a dimension:
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.
int PointCount() const
Return the number of points (vertices) in this line chain.
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
CONST_SEGMENT_ITERATOR CIterateSegmentsWithHoles() const
Return an iterator object, for the aOutline-th outline in the set (with holes).
int OutlineCount() const
Return the number of outlines in the set.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Handle a list of polygons defining a copper zone.
SHAPE_POLY_SET * Outline()
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
PCB_LAYER_ID
A quick note on layer IDs:
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
static bool Collide(const SHAPE_CIRCLE &aA, const SHAPE_CIRCLE &aB, int aClearance, int *aActual, VECTOR2I *aLocation, VECTOR2I *aMTV)
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
static void RunStructuralChecks(const PADS_BOARD_INFO &aBoard)
Run structural integrity checks on a successfully loaded board.
static wxString GetBoardPath(const PADS_BOARD_INFO &aBoard)
static const PADS_BOARD_INFO PADS_BOARDS[]
static std::unique_ptr< BOARD > LoadAndVerify(const PADS_BOARD_INFO &aBoard)
Verify that the PADS file is recognized and loads without crashing.
BOOST_AUTO_TEST_CASE(ImportClaySight_MK1)
BOOST_CHECK_EQUAL(result, "25.4")
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
VECTOR2< int32_t > VECTOR2I