7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-04-18 22:21:40 +00:00

QA: Use data-driven tests in a few places

This means that you can run just a single case from the command line,
for example:

   `qa_pcbnew -t DRCCopperSliver/_0`

The case is also automatically printed as context.

This means that when isolating a specific defect in one case,
you can run only that one case, which is useful under debuggers
and also when using slow tools like valgrind.

Not all possibe tests are doing this, but they're easy to do
when needed (i.e. when you want to run just one case)
This commit is contained in:
John Beard 2025-01-24 17:33:11 +08:00
parent 09bbaa5813
commit d9a243d98c
20 changed files with 1133 additions and 1273 deletions

View File

@ -175,6 +175,23 @@ private:
};
struct BOARD_LOAD_TEST_CASE
{
wxString m_BoardFileRelativePath;
// These tests may well test specific versions of the board file format,
// so don't let it change accidentally in the files (e.g. by resaving in newer KiCad)!
std::optional<int> m_ExpectedBoardVersion;
// Be default, printing the board file is sufficent to identify the test case
friend std::ostream& operator<<( std::ostream& os, const BOARD_LOAD_TEST_CASE& aTestCase )
{
os << aTestCase.m_BoardFileRelativePath;
return os;
}
};
void LoadBoard( SETTINGS_MANAGER& aSettingsManager, const wxString& aRelPath,
std::unique_ptr<BOARD>& aBoard );

View File

@ -258,6 +258,24 @@ bool CollectionHasNoDuplicates( const T& aCollection )
}
/**
* A named data-driven test case.
*
* Inherit from this class to provide a printable name for a data-driven test case.
* (you can also not use this class and provide you own name printer).
*/
struct NAMED_CASE
{
std::string m_CaseName;
friend std::ostream& operator<<( std::ostream& os, const NAMED_CASE& aCase )
{
os << aCase.m_CaseName;
return os;
}
};
/**
* A test macro to check a wxASSERT is thrown.
*

View File

@ -19,6 +19,8 @@
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/data/test_case.hpp>
#include <geometry/circle.h>
#include <geometry/seg.h> // for SEG
#include <geometry/shape.h> // for MIN_PRECISION_IU
@ -76,9 +78,8 @@ BOOST_AUTO_TEST_CASE( ParameterCtorMod )
/**
* Struct to hold test cases for a given circle, a point and an expected return boolean
*/
struct CIR_PT_BOOL_CASE
struct CIR_PT_BOOL_CASE : public KI_TEST::NAMED_CASE
{
std::string m_case_name;
CIRCLE m_circle;
VECTOR2I m_point;
bool m_exp_result;
@ -165,26 +166,18 @@ static const std::vector<CIR_PT_BOOL_CASE> contains_cases = {
// clang-format on
BOOST_AUTO_TEST_CASE( Contains )
BOOST_DATA_TEST_CASE( Contains, boost::unit_test::data::make( contains_cases ), c )
{
for( const auto& c : contains_cases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
bool ret = c.m_circle.Contains( c.m_point );
BOOST_CHECK_EQUAL( ret, c.m_exp_result );
}
}
bool ret = c.m_circle.Contains( c.m_point );
BOOST_CHECK_EQUAL( ret, c.m_exp_result );
}
/**
* Struct to hold test cases for a given circle, a point and an expected return point
*/
struct CIR_PT_PT_CASE
struct CIR_PT_PT_CASE : public KI_TEST::NAMED_CASE
{
std::string m_case_name;
CIRCLE m_circle;
VECTOR2I m_point;
VECTOR2I m_exp_result;
@ -223,25 +216,18 @@ static const std::vector<CIR_PT_PT_CASE> nearest_point_cases = {
// clang-format on
BOOST_AUTO_TEST_CASE( NearestPoint )
BOOST_DATA_TEST_CASE( NearestPoint, boost::unit_test::data::make( nearest_point_cases ), c )
{
for( const auto& c : nearest_point_cases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
VECTOR2I ret = c.m_circle.NearestPoint( c.m_point );
BOOST_CHECK_EQUAL( ret, c.m_exp_result );
}
}
VECTOR2I ret = c.m_circle.NearestPoint( c.m_point );
BOOST_CHECK_EQUAL( ret, c.m_exp_result );
}
/**
* Struct to hold test cases for two circles, and an vector of points
*/
struct CIR_CIR_VECPT_CASE
struct CIR_CIR_VECPT_CASE : public KI_TEST::NAMED_CASE
{
std::string m_case_name;
CIRCLE m_circle1;
CIRCLE m_circle2;
std::vector<VECTOR2I> m_exp_result;
@ -338,24 +324,21 @@ static const std::vector<CIR_CIR_VECPT_CASE> intersect_circle_cases = {
// clang-format on
BOOST_AUTO_TEST_CASE( IntersectCircle )
BOOST_DATA_TEST_CASE( IntersectCircle, boost::unit_test::data::make( intersect_circle_cases ), c )
{
for( const auto& c : intersect_circle_cases )
BOOST_TEST_CONTEXT( c.m_CaseName + " Case 1" )
{
BOOST_TEST_CONTEXT( c.m_case_name + " Case 1" )
{
std::vector<VECTOR2I> ret1 = c.m_circle1.Intersect( c.m_circle2 );
BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret1.size() );
KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret1, CompareVector2I );
}
std::vector<VECTOR2I> ret1 = c.m_circle1.Intersect( c.m_circle2 );
BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret1.size() );
KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret1, CompareVector2I );
}
BOOST_TEST_CONTEXT( c.m_case_name + " Case 2" )
{
// Test the other direction
std::vector<VECTOR2I> ret2 = c.m_circle2.Intersect( c.m_circle1 );
BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret2.size() );
KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret2, CompareVector2I );
}
BOOST_TEST_CONTEXT( c.m_CaseName + " Case 2" )
{
// Test the other direction
std::vector<VECTOR2I> ret2 = c.m_circle2.Intersect( c.m_circle1 );
BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret2.size() );
KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret2, CompareVector2I );
}
}
@ -363,9 +346,8 @@ BOOST_AUTO_TEST_CASE( IntersectCircle )
/**
* Struct to hold test cases for a circle, a line and an expected vector of points
*/
struct SEG_SEG_VECPT_CASE
struct SEG_SEG_VECPT_CASE : public KI_TEST::NAMED_CASE
{
std::string m_case_name;
CIRCLE m_circle;
SEG m_seg;
std::vector<VECTOR2I> m_exp_result;
@ -422,17 +404,11 @@ static const std::vector<SEG_SEG_VECPT_CASE> intersect_seg_cases = {
// clang-format on
BOOST_AUTO_TEST_CASE( Intersect )
BOOST_DATA_TEST_CASE( Intersect, boost::unit_test::data::make( intersect_seg_cases ), c )
{
for( const auto& c : intersect_seg_cases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
std::vector<VECTOR2I> ret = c.m_circle.Intersect( c.m_seg );
BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret.size() );
KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret, CompareVector2I );
}
}
std::vector<VECTOR2I> ret = c.m_circle.Intersect( c.m_seg );
BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret.size() );
KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret, CompareVector2I );
}
@ -488,17 +464,11 @@ static const std::vector<SEG_SEG_VECPT_CASE> intersect_line_cases = {
// clang-format on
BOOST_AUTO_TEST_CASE( IntersectLine )
BOOST_DATA_TEST_CASE( IntersectLine, boost::unit_test::data::make( intersect_line_cases ), c )
{
for( const auto& c : intersect_line_cases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
std::vector<VECTOR2I> ret = c.m_circle.IntersectLine( c.m_seg );
BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret.size() );
KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret, CompareVector2I );
}
}
std::vector<VECTOR2I> ret = c.m_circle.IntersectLine( c.m_seg );
BOOST_CHECK_EQUAL( c.m_exp_result.size(), ret.size() );
KI_TEST::CheckUnorderedMatches( c.m_exp_result, ret, CompareVector2I );
}
@ -506,9 +476,8 @@ BOOST_AUTO_TEST_CASE( IntersectLine )
/**
* Struct to hold test cases for two lines, a point and an expected returned circle
*/
struct CIR_SEG_VECPT_CASE
struct CIR_SEG_VECPT_CASE : public KI_TEST::NAMED_CASE
{
std::string m_case_name;
SEG m_segA;
SEG m_segB;
VECTOR2I m_pt;
@ -575,25 +544,19 @@ static const std::vector<CIR_SEG_VECPT_CASE> construct_tan_tan_pt_cases = {
// clang-format on
BOOST_AUTO_TEST_CASE( ConstructFromTanTanPt )
BOOST_DATA_TEST_CASE( ConstructFromTanTanPt,
boost::unit_test::data::make( construct_tan_tan_pt_cases ), c )
{
for( const auto& c : construct_tan_tan_pt_cases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
CIRCLE circle;
circle.ConstructFromTanTanPt( c.m_segA, c.m_segB, c.m_pt );
BOOST_CHECK_MESSAGE( CompareVector2I( c.m_exp_result.Center, circle.Center ),
"\nCenter point mismatch: "
<< "\n Got: " << circle.Center
<< "\n Expected: " << c.m_exp_result.Center );
CIRCLE circle;
circle.ConstructFromTanTanPt( c.m_segA, c.m_segB, c.m_pt );
BOOST_CHECK_MESSAGE( CompareVector2I( c.m_exp_result.Center, circle.Center ),
"\nCenter point mismatch: " << "\n Got: " << circle.Center
<< "\n Expected: "
<< c.m_exp_result.Center );
BOOST_CHECK_MESSAGE( CompareLength( c.m_exp_result.Radius, circle.Radius ),
"\nRadius mismatch: "
<< "\n Got: " << circle.Radius
<< "\n Expected: " << c.m_exp_result.Radius );
}
}
BOOST_CHECK_MESSAGE( CompareLength( c.m_exp_result.Radius, circle.Radius ),
"\nRadius mismatch: " << "\n Got: " << circle.Radius
<< "\n Expected: " << c.m_exp_result.Radius );
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -22,6 +22,7 @@
*/
#include <boost/test/unit_test.hpp>
#include <boost/test/data/test_case.hpp>
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <qa_utils/geometry/line_chain_construction.h>
@ -33,14 +34,8 @@
#include "geom_test_utils.h"
struct FilletFixture
{
};
/**
* Declares the FilletFixture struct as the boost test fixture.
*/
BOOST_FIXTURE_TEST_SUITE( Fillet, FilletFixture )
BOOST_AUTO_TEST_SUITE( Fillet )
/*
* @brief check that a single segment of a fillet complies with the geometric
@ -174,9 +169,15 @@ struct SquareFilletTestCase
int squareSize;
int radius;
int error;
friend std::ostream& operator<<( std::ostream& os, const SquareFilletTestCase& testCase )
{
return os << "Square size: " << testCase.squareSize << ", Radius: " << testCase.radius
<< ", Error: " << testCase.error;
}
};
const std::vector<SquareFilletTestCase> squareFilletCases {
const std::vector<SquareFilletTestCase> squareFilletCases{
{ 1000, 120, 10 },
{ 1000, 10, 1 },
@ -191,20 +192,15 @@ const std::vector<SquareFilletTestCase> squareFilletCases {
* Tests the SHAPE_POLY_SET::FilletPolygon method against certain geometric
* constraints.
*/
BOOST_AUTO_TEST_CASE( SquareFillet )
BOOST_DATA_TEST_CASE( SquareFillet, boost::unit_test::data::make( squareFilletCases ), testCase )
{
for ( const auto& testCase : squareFilletCases )
{
TestSquareFillet( testCase.squareSize, testCase.radius, testCase.error );
}
TestSquareFillet( testCase.squareSize, testCase.radius, testCase.error );
}
BOOST_AUTO_TEST_CASE( SquareConcaveFillet )
BOOST_DATA_TEST_CASE( SquareConcaveFillet, boost::unit_test::data::make( squareFilletCases ),
testCase )
{
for ( const auto& testCase : squareFilletCases )
{
TestConcaveSquareFillet( testCase.squareSize, testCase.radius, testCase.error );
}
TestConcaveSquareFillet( testCase.squareSize, testCase.radius, testCase.error );
}

View File

@ -22,6 +22,7 @@
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/data/test_case.hpp>
#include <geometry/half_line.h>
#include <geometry/shape_utils.h>
@ -29,162 +30,142 @@
#include "geom_test_utils.h"
struct HalfLineFixture
{
};
BOOST_AUTO_TEST_SUITE( HalfLine )
struct HalfLineBoxClipCase
struct HalfLineBoxClipCase : public KI_TEST::NAMED_CASE
{
std::string Description;
HALF_LINE Hl;
BOX2I Box;
std::optional<SEG> ExpectedClippedSeg;
};
struct HalfLineHalfLineIntersectionCase
struct HalfLineHalfLineIntersectionCase : public KI_TEST::NAMED_CASE
{
std::string Description;
HALF_LINE HlA;
HALF_LINE HlB;
std::optional<VECTOR2I> ExpectedIntersection;
};
struct HalfLineContainsPointCase
struct HalfLineContainsPointCase : public KI_TEST::NAMED_CASE
{
std::string Description;
HALF_LINE Hl;
VECTOR2I Point;
bool ExpectedContains;
};
BOOST_FIXTURE_TEST_SUITE( HalfLine, HalfLineFixture )
BOOST_AUTO_TEST_CASE( Contains )
{
const std::vector<HalfLineContainsPointCase> cases{
{
"Point on the ray",
HALF_LINE( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) ),
VECTOR2I( 50, 50 ),
true,
},
{
"Point on the ray start",
HALF_LINE( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) ),
VECTOR2I( 0, 0 ),
true,
},
{
"Point on the ray end",
HALF_LINE( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) ),
VECTOR2I( 100, 100 ),
true,
},
{
"Point on the ray, past the end",
HALF_LINE( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) ),
VECTOR2I( 150, 150 ),
true,
},
{
"Point on the infinite line, but on the wrong side",
HALF_LINE( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) ),
VECTOR2I( -50, -50 ),
false,
},
{
"Point to one side",
HALF_LINE( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) ),
VECTOR2I( 50, 0 ),
false,
}
};
for( const auto& c : cases )
const std::vector<HalfLineContainsPointCase> Contains_cases{
{
BOOST_TEST_INFO( c.Description );
const bool contains = c.Hl.Contains( c.Point );
BOOST_TEST( contains == c.ExpectedContains );
"Point on the ray",
HALF_LINE( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) ),
VECTOR2I( 50, 50 ),
true,
},
{
"Point on the ray start",
HALF_LINE( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) ),
VECTOR2I( 0, 0 ),
true,
},
{
"Point on the ray end",
HALF_LINE( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) ),
VECTOR2I( 100, 100 ),
true,
},
{
"Point on the ray, past the end",
HALF_LINE( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) ),
VECTOR2I( 150, 150 ),
true,
},
{
"Point on the infinite line, but on the wrong side",
HALF_LINE( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) ),
VECTOR2I( -50, -50 ),
false,
},
{
"Point to one side",
HALF_LINE( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) ),
VECTOR2I( 50, 0 ),
false,
}
};
BOOST_DATA_TEST_CASE( Contains, boost::unit_test::data::make( Contains_cases ), c )
{
const bool contains = c.Hl.Contains( c.Point );
BOOST_TEST( contains == c.ExpectedContains );
}
BOOST_AUTO_TEST_CASE( Intersect )
{
const std::vector<HalfLineHalfLineIntersectionCase> cases{
{
"Simple cross",
HALF_LINE( SEG( VECTOR2I( -100, -100 ), VECTOR2I( 0, 0 ) ) ),
HALF_LINE( SEG( VECTOR2I( 100, -100 ), VECTOR2I( 0, 0 ) ) ),
VECTOR2I( 0, 0 ),
},
{
"Parallel, no intersection",
HALF_LINE( SEG( VECTOR2I( -100, 0 ), VECTOR2I( -100, 100 ) ) ),
HALF_LINE( SEG( VECTOR2I( 100, 0 ), VECTOR2I( 100, 100 ) ) ),
std::nullopt,
}
};
for( const auto& c : cases )
const std::vector<HalfLineHalfLineIntersectionCase> Intersect_cases{
{
BOOST_TEST_INFO( c.Description );
"Simple cross",
HALF_LINE( SEG( VECTOR2I( -100, -100 ), VECTOR2I( 0, 0 ) ) ),
HALF_LINE( SEG( VECTOR2I( 100, -100 ), VECTOR2I( 0, 0 ) ) ),
VECTOR2I( 0, 0 ),
},
{
"Parallel, no intersection",
HALF_LINE( SEG( VECTOR2I( -100, 0 ), VECTOR2I( -100, 100 ) ) ),
HALF_LINE( SEG( VECTOR2I( 100, 0 ), VECTOR2I( 100, 100 ) ) ),
std::nullopt,
}
};
std::optional<VECTOR2I> intersection = c.HlA.Intersect( c.HlB );
BOOST_REQUIRE( intersection.has_value() == c.ExpectedIntersection.has_value() );
BOOST_DATA_TEST_CASE( Intersect, boost::unit_test::data::make( Intersect_cases ), c )
{
std::optional<VECTOR2I> intersection = c.HlA.Intersect( c.HlB );
if( !intersection )
continue;
BOOST_REQUIRE( intersection.has_value() == c.ExpectedIntersection.has_value() );
if( intersection )
{
BOOST_TEST( *intersection == *c.ExpectedIntersection );
}
}
BOOST_AUTO_TEST_CASE( ClipToBox )
{
const std::vector<HalfLineBoxClipCase> cases{
{
"Center to right edge",
HALF_LINE( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 100, 0 ) ) ),
BOX2I{ VECTOR2{ -1000, -1000 }, VECTOR2{ 2000, 2000 } },
SEG( VECTOR2I( 0, 0 ), VECTOR2I( 1000, 0 ) ),
},
{
"Centre to corner",
HALF_LINE( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) ),
BOX2I{ VECTOR2{ -1000, -1000 }, VECTOR2{ 2000, 2000 } },
SEG( VECTOR2I( 0, 0 ), VECTOR2I( 1000, 1000 ) ),
},
{
"Ray not in the box",
HALF_LINE( SEG( VECTOR2I( 1500, 0 ), VECTOR2I( 1600, 0 ) ) ),
BOX2I{ VECTOR2{ -1000, -1000 }, VECTOR2{ 2000, 2000 } },
std::nullopt,
},
{
"Ray starts outside but crosses box",
HALF_LINE( SEG( VECTOR2I( -1500, 0 ), VECTOR2I( 0, 0 ) ) ),
BOX2I{ VECTOR2{ -1000, -1000 }, VECTOR2{ 2000, 2000 } },
SEG( VECTOR2I( -1000, 0 ), VECTOR2I( 1000, 0 ) ),
},
};
for( const auto& c : cases )
const std::vector<HalfLineBoxClipCase> ClipToBox_cases{
{
BOOST_TEST_INFO( c.Description );
"Center to right edge",
HALF_LINE( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 100, 0 ) ) ),
BOX2I{ VECTOR2{ -1000, -1000 }, VECTOR2{ 2000, 2000 } },
SEG( VECTOR2I( 0, 0 ), VECTOR2I( 1000, 0 ) ),
},
{
"Centre to corner",
HALF_LINE( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 100, 100 ) ) ),
BOX2I{ VECTOR2{ -1000, -1000 }, VECTOR2{ 2000, 2000 } },
SEG( VECTOR2I( 0, 0 ), VECTOR2I( 1000, 1000 ) ),
},
{
"Ray not in the box",
HALF_LINE( SEG( VECTOR2I( 1500, 0 ), VECTOR2I( 1600, 0 ) ) ),
BOX2I{ VECTOR2{ -1000, -1000 }, VECTOR2{ 2000, 2000 } },
std::nullopt,
},
{
"Ray starts outside but crosses box",
HALF_LINE( SEG( VECTOR2I( -1500, 0 ), VECTOR2I( 0, 0 ) ) ),
BOX2I{ VECTOR2{ -1000, -1000 }, VECTOR2{ 2000, 2000 } },
SEG( VECTOR2I( -1000, 0 ), VECTOR2I( 1000, 0 ) ),
},
};
std::optional<SEG> clipped = KIGEOM::ClipHalfLineToBox( c.Hl, c.Box );
BOOST_REQUIRE( clipped.has_value() == c.ExpectedClippedSeg.has_value() );
BOOST_DATA_TEST_CASE( ClipToBox, boost::unit_test::data::make( ClipToBox_cases ), c )
{
std::optional<SEG> clipped = KIGEOM::ClipHalfLineToBox( c.Hl, c.Box );
if( !clipped )
continue;
BOOST_REQUIRE( clipped.has_value() == c.ExpectedClippedSeg.has_value() );
if( clipped )
{
BOOST_CHECK_PREDICATE( GEOM_TEST::SegmentsHaveSameEndPoints,
( *clipped )( *c.ExpectedClippedSeg ) );
}

View File

@ -24,9 +24,13 @@
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/data/test_case.hpp>
#include <geometry/seg.h>
namespace
{
/**
* Predicate to check expected collision between two segments
* @param aSegA the first #SEG
@ -215,6 +219,8 @@ bool SegPerpendicularCorrect( const SEG& aSegA, const SEG& aSegB, bool aExp )
return ok;
}
} // namespace
BOOST_AUTO_TEST_SUITE( Segment )
/**
@ -241,9 +247,8 @@ BOOST_AUTO_TEST_CASE( EndpointCtorMod )
BOOST_CHECK_EQUAL( segment.B, VECTOR2I( 200, 300 ) );
}
struct SEG_SEG_DISTANCE_CASE
struct SEG_SEG_DISTANCE_CASE : public KI_TEST::NAMED_CASE
{
std::string m_case_name;
SEG m_seg_a;
SEG m_seg_b;
int m_exp_dist;
@ -292,21 +297,14 @@ static const std::vector<SEG_SEG_DISTANCE_CASE> seg_seg_dist_cases = {
// clang-format on
BOOST_AUTO_TEST_CASE( SegSegDistance )
BOOST_DATA_TEST_CASE( SegSegDistance, boost::unit_test::data::make( seg_seg_dist_cases ), c )
{
for( const auto& c : seg_seg_dist_cases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
BOOST_CHECK_PREDICATE( SegDistanceCorrect, ( c.m_seg_a )( c.m_seg_b )( c.m_exp_dist ) );
}
}
BOOST_CHECK_PREDICATE( SegDistanceCorrect, ( c.m_seg_a )( c.m_seg_b )( c.m_exp_dist ) );
}
struct SEG_VECTOR_DISTANCE_CASE
struct SEG_VECTOR_DISTANCE_CASE : public KI_TEST::NAMED_CASE
{
std::string m_case_name;
SEG m_seg;
VECTOR2I m_vec;
int m_exp_dist;
@ -367,15 +365,9 @@ static const std::vector<SEG_VECTOR_DISTANCE_CASE> seg_vec_dist_cases = {
// clang-format on
BOOST_AUTO_TEST_CASE( SegVecDistance )
BOOST_DATA_TEST_CASE( SegVecDistance, boost::unit_test::data::make( seg_vec_dist_cases ), c )
{
for( const auto& c : seg_vec_dist_cases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
BOOST_CHECK_PREDICATE( SegVecDistanceCorrect, ( c.m_seg )( c.m_vec )( c.m_exp_dist ) );
}
}
BOOST_CHECK_PREDICATE( SegVecDistanceCorrect, ( c.m_seg )( c.m_vec )( c.m_exp_dist ) );
}
@ -383,9 +375,8 @@ BOOST_AUTO_TEST_CASE( SegVecDistance )
* Test cases for collisions (with clearance, for no clearance,
* it's just a SEG_SEG_DISTANCE_CASE of 0)
*/
struct SEG_SEG_COLLIDE_CASE
struct SEG_SEG_COLLIDE_CASE : public KI_TEST::NAMED_CASE
{
std::string m_case_name;
SEG m_seg_a;
SEG m_seg_b;
int m_clearance;
@ -434,25 +425,18 @@ static const std::vector<SEG_SEG_COLLIDE_CASE> seg_seg_coll_cases = {
// clang-format on
BOOST_AUTO_TEST_CASE( SegSegCollision )
BOOST_DATA_TEST_CASE( SegSegCollision, boost::unit_test::data::make( seg_seg_coll_cases ), c )
{
for( const auto& c : seg_seg_coll_cases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
BOOST_CHECK_PREDICATE( SegCollideCorrect,
( c.m_seg_a )( c.m_seg_b )( c.m_clearance )( c.m_exp_coll ) );
}
}
BOOST_CHECK_PREDICATE( SegCollideCorrect,
( c.m_seg_a )( c.m_seg_b )( c.m_clearance )( c.m_exp_coll ) );
}
/**
* Struct to hold general cases for collinearity, parallelism and perpendicularity
*/
struct SEG_SEG_BOOLEAN_CASE
struct SEG_SEG_BOOLEAN_CASE : public KI_TEST::NAMED_CASE
{
std::string m_case_name;
SEG m_seg_a;
SEG m_seg_b;
bool m_exp_result;
@ -497,16 +481,9 @@ static const std::vector<SEG_SEG_BOOLEAN_CASE> seg_vec_collinear_cases = {
// clang-format on
BOOST_AUTO_TEST_CASE( SegSegCollinear )
BOOST_DATA_TEST_CASE( SegSegCollinear, boost::unit_test::data::make( seg_vec_collinear_cases ), c )
{
for( const auto& c : seg_vec_collinear_cases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
BOOST_CHECK_PREDICATE( SegCollinearCorrect,
( c.m_seg_a )( c.m_seg_b )( c.m_exp_result ) );
}
}
BOOST_CHECK_PREDICATE( SegCollinearCorrect, ( c.m_seg_a )( c.m_seg_b )( c.m_exp_result ) );
}
@ -549,16 +526,9 @@ static const std::vector<SEG_SEG_BOOLEAN_CASE> seg_vec_parallel_cases = {
// clang-format on
BOOST_AUTO_TEST_CASE( SegSegParallel )
BOOST_DATA_TEST_CASE( SegSegParallel, boost::unit_test::data::make( seg_vec_parallel_cases ), c )
{
for( const auto& c : seg_vec_parallel_cases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
BOOST_CHECK_PREDICATE( SegParallelCorrect,
( c.m_seg_a )( c.m_seg_b )( c.m_exp_result ) );
}
}
BOOST_CHECK_PREDICATE( SegParallelCorrect, ( c.m_seg_a )( c.m_seg_b )( c.m_exp_result ) );
}
@ -625,25 +595,18 @@ static const std::vector<SEG_SEG_BOOLEAN_CASE> seg_vec_perpendicular_cases = {
// clang-format on
BOOST_AUTO_TEST_CASE( SegSegPerpendicular )
BOOST_DATA_TEST_CASE( SegSegPerpendicular,
boost::unit_test::data::make( seg_vec_perpendicular_cases ), c )
{
for( const auto& c : seg_vec_perpendicular_cases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
BOOST_CHECK_PREDICATE( SegPerpendicularCorrect,
( c.m_seg_a )( c.m_seg_b )( c.m_exp_result ) );
}
}
BOOST_CHECK_PREDICATE( SegPerpendicularCorrect, ( c.m_seg_a )( c.m_seg_b )( c.m_exp_result ) );
}
/**
* Struct to hold cases for operations with a #SEG, and a #VECTOR2I
*/
struct SEG_VEC_CASE
struct SEG_VEC_CASE : public KI_TEST::NAMED_CASE
{
std::string m_case_name;
SEG m_seg;
VECTOR2I m_vec;
};
@ -688,32 +651,22 @@ static const std::vector<SEG_VEC_CASE> segment_and_point_cases = {
// clang-format on
BOOST_AUTO_TEST_CASE( SegCreateParallel )
BOOST_DATA_TEST_CASE( SegCreateParallel, boost::unit_test::data::make( segment_and_point_cases ),
c )
{
for( const auto& c : segment_and_point_cases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
SEG perpendicular = c.m_seg.ParallelSeg( c.m_vec );
const SEG perpendicular = c.m_seg.ParallelSeg( c.m_vec );
BOOST_CHECK_PREDICATE( SegParallelCorrect, ( perpendicular )( c.m_seg )( true ) );
BOOST_CHECK_PREDICATE( SegVecDistanceCorrect, ( perpendicular )( c.m_vec )( 0 ) );
}
}
BOOST_CHECK_PREDICATE( SegParallelCorrect, (perpendicular) ( c.m_seg )( true ) );
BOOST_CHECK_PREDICATE( SegVecDistanceCorrect, (perpendicular) ( c.m_vec )( 0 ) );
}
BOOST_AUTO_TEST_CASE( SegCreatePerpendicular )
BOOST_DATA_TEST_CASE( SegCreatePerpendicular,
boost::unit_test::data::make( segment_and_point_cases ), c )
{
for( const auto& c : segment_and_point_cases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
SEG perpendicular = c.m_seg.PerpendicularSeg( c.m_vec );
const SEG perpendicular = c.m_seg.PerpendicularSeg( c.m_vec );
BOOST_CHECK_PREDICATE( SegPerpendicularCorrect, ( perpendicular )( c.m_seg )( true ) );
BOOST_CHECK_PREDICATE( SegVecDistanceCorrect, ( perpendicular )( c.m_vec )( 0 ) );
}
}
BOOST_CHECK_PREDICATE( SegPerpendicularCorrect, (perpendicular) ( c.m_seg )( true ) );
BOOST_CHECK_PREDICATE( SegVecDistanceCorrect, (perpendicular) ( c.m_vec )( 0 ) );
}
BOOST_AUTO_TEST_CASE( LineDistance )

View File

@ -21,6 +21,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/data/test_case.hpp>
#include <convert_basic_shapes_to_polygon.h>
#include <geometry/shape_arc.h>
@ -28,7 +31,6 @@
#include <qa_utils/geometry/geometry.h>
#include <qa_utils/numeric.h>
#include <qa_utils/wx_utils/unit_test_utils.h>
#include "geom_test_utils.h"
@ -165,11 +167,8 @@ struct ARC_CENTRE_PT_ANGLE
};
struct ARC_CPA_CASE
struct ARC_CPA_CASE : public KI_TEST::NAMED_CASE
{
/// The text context name
std::string m_ctx_name;
/// Geom of the arc
ARC_CENTRE_PT_ANGLE m_geom;
@ -326,20 +325,14 @@ static const std::vector<ARC_CPA_CASE> arc_cases = {
};
BOOST_AUTO_TEST_CASE( BasicCPAGeom )
BOOST_DATA_TEST_CASE( BasicCPAGeom, boost::unit_test::data::make( arc_cases ), c )
{
for( const auto& c : arc_cases )
{
BOOST_TEST_CONTEXT( c.m_ctx_name )
{
const auto this_arc = SHAPE_ARC{ c.m_geom.m_center_point, c.m_geom.m_start_point,
EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ),
c.m_width };
const auto this_arc = SHAPE_ARC{ c.m_geom.m_center_point, c.m_geom.m_start_point,
EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ),
c.m_width };
CheckArc( this_arc, c.m_properties );
}
}
CheckArc( this_arc, c.m_properties );
}
@ -355,11 +348,8 @@ struct ARC_TAN_TAN_RADIUS
};
struct ARC_TTR_CASE
struct ARC_TTR_CASE : public KI_TEST::NAMED_CASE
{
/// The text context name
std::string m_ctx_name;
/// Geom of the arc
ARC_TAN_TAN_RADIUS m_geom;
@ -434,56 +424,50 @@ static const std::vector<ARC_TTR_CASE> arc_ttr_cases = {
};
BOOST_AUTO_TEST_CASE( BasicTTRGeom )
BOOST_DATA_TEST_CASE( BasicTTRGeom, boost::unit_test::data::make( arc_ttr_cases ), c )
{
for( const auto& c : arc_ttr_cases )
for( int testCase = 0; testCase < 8; ++testCase )
{
BOOST_TEST_CONTEXT( c.m_ctx_name )
SEG seg1 = c.m_geom.m_segment_1;
SEG seg2 = c.m_geom.m_segment_2;
ARC_PROPERTIES props = c.m_properties;
if( testCase > 3 )
{
for( int testCase = 0; testCase < 8; ++testCase )
{
SEG seg1 = c.m_geom.m_segment_1;
SEG seg2 = c.m_geom.m_segment_2;
ARC_PROPERTIES props = c.m_properties;
//Swap input segments.
seg1 = c.m_geom.m_segment_2;
seg2 = c.m_geom.m_segment_1;
if( testCase > 3 )
{
//Swap input segments.
seg1 = c.m_geom.m_segment_2;
seg2 = c.m_geom.m_segment_1;
//The result should swap start and end points and invert the angles:
props.m_end_point = c.m_properties.m_start_point;
props.m_start_point = c.m_properties.m_end_point;
props.m_start_angle = c.m_properties.m_end_angle;
props.m_end_angle = c.m_properties.m_start_angle;
props.m_center_angle = -c.m_properties.m_center_angle;
}
//Test all combinations of start and end points for the segments
if( ( testCase % 4 ) == 1 || ( testCase % 4 ) == 3 )
{
//Swap start and end points for seg1
VECTOR2I temp = seg1.A;
seg1.A = seg1.B;
seg1.B = temp;
}
if( ( testCase % 4 ) == 2 || ( testCase % 4 ) == 3 )
{
//Swap start and end points for seg2
VECTOR2I temp = seg2.A;
seg2.A = seg2.B;
seg2.B = temp;
}
const auto this_arc = SHAPE_ARC{ seg1, seg2,
c.m_geom.m_radius, c.m_width };
// Error of 4 IU permitted for the center and radius calculation
CheckArc( this_arc, props, SHAPE_ARC::MIN_PRECISION_IU );
}
//The result should swap start and end points and invert the angles:
props.m_end_point = c.m_properties.m_start_point;
props.m_start_point = c.m_properties.m_end_point;
props.m_start_angle = c.m_properties.m_end_angle;
props.m_end_angle = c.m_properties.m_start_angle;
props.m_center_angle = -c.m_properties.m_center_angle;
}
//Test all combinations of start and end points for the segments
if( ( testCase % 4 ) == 1 || ( testCase % 4 ) == 3 )
{
//Swap start and end points for seg1
VECTOR2I temp = seg1.A;
seg1.A = seg1.B;
seg1.B = temp;
}
if( ( testCase % 4 ) == 2 || ( testCase % 4 ) == 3 )
{
//Swap start and end points for seg2
VECTOR2I temp = seg2.A;
seg2.A = seg2.B;
seg2.B = temp;
}
const auto this_arc = SHAPE_ARC{ seg1, seg2,
c.m_geom.m_radius, c.m_width };
// Error of 4 IU permitted for the center and radius calculation
CheckArc( this_arc, props, SHAPE_ARC::MIN_PRECISION_IU );
}
}
@ -499,11 +483,8 @@ struct ARC_START_END_CENTER
};
struct ARC_SEC_CASE
struct ARC_SEC_CASE : public KI_TEST::NAMED_CASE
{
/// The text context name
std::string m_ctx_name;
/// Geom of the arc
ARC_START_END_CENTER m_geom;
@ -526,29 +507,22 @@ static const std::vector<ARC_SEC_CASE> arc_sec_cases = {
};
BOOST_AUTO_TEST_CASE( BasicSECGeom )
BOOST_DATA_TEST_CASE( BasicSECGeom, boost::unit_test::data::make( arc_sec_cases ), c )
{
for( const auto& c : arc_sec_cases )
{
BOOST_TEST_CONTEXT( c.m_ctx_name )
{
VECTOR2I start = c.m_geom.m_start;
VECTOR2I end = c.m_geom.m_end;
VECTOR2I center = c.m_geom.m_center;
bool cw = c.m_clockwise;
VECTOR2I start = c.m_geom.m_start;
VECTOR2I end = c.m_geom.m_end;
VECTOR2I center = c.m_geom.m_center;
bool cw = c.m_clockwise;
SHAPE_ARC this_arc;
this_arc.ConstructFromStartEndCenter( start, end, center, cw );
SHAPE_ARC this_arc;
this_arc.ConstructFromStartEndCenter( start, end, center, cw );
BOOST_CHECK_EQUAL( this_arc.GetArcMid(), c.m_expected_mid );
}
}
BOOST_CHECK_EQUAL( this_arc.GetArcMid(), c.m_expected_mid );
}
struct ARC_PT_COLLIDE_CASE
struct ARC_PT_COLLIDE_CASE : public KI_TEST::NAMED_CASE
{
std::string m_ctx_name;
ARC_CENTRE_PT_ANGLE m_geom;
int m_arc_clearance;
VECTOR2I m_point;
@ -612,47 +586,36 @@ static const std::vector<ARC_PT_COLLIDE_CASE> arc_pt_collide_cases = {
};
BOOST_AUTO_TEST_CASE( CollidePt )
BOOST_DATA_TEST_CASE( CollidePt, boost::unit_test::data::make( arc_pt_collide_cases ), c )
{
for( const auto& c : arc_pt_collide_cases )
SHAPE_ARC arc( c.m_geom.m_center_point, c.m_geom.m_start_point,
EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ) );
// Test a zero width arc (distance should equal the clearance)
BOOST_TEST_CONTEXT( "Test Clearance" )
{
BOOST_TEST_CONTEXT( c.m_ctx_name )
{
SHAPE_ARC arc( c.m_geom.m_center_point, c.m_geom.m_start_point,
EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ) );
int dist = -1;
BOOST_CHECK_EQUAL( arc.Collide( c.m_point, c.m_arc_clearance, &dist ),
c.m_exp_result );
BOOST_CHECK_EQUAL( dist, c.m_exp_distance );
}
// Test a zero width arc (distance should equal the clearance)
BOOST_TEST_CONTEXT( "Test Clearance" )
{
int dist = -1;
BOOST_CHECK_EQUAL( arc.Collide( c.m_point, c.m_arc_clearance, &dist ),
c.m_exp_result );
BOOST_CHECK_EQUAL( dist, c.m_exp_distance );
}
// Test by changing the width of the arc (distance should equal zero)
BOOST_TEST_CONTEXT( "Test Width" )
{
int dist = -1;
arc.SetWidth( c.m_arc_clearance * 2 );
BOOST_CHECK_EQUAL( arc.Collide( c.m_point, 0, &dist ), c.m_exp_result );
// Test by changing the width of the arc (distance should equal zero)
BOOST_TEST_CONTEXT( "Test Width" )
{
int dist = -1;
arc.SetWidth( c.m_arc_clearance * 2 );
BOOST_CHECK_EQUAL( arc.Collide( c.m_point, 0, &dist ), c.m_exp_result );
if( c.m_exp_result )
BOOST_CHECK_EQUAL( dist, 0 );
else
BOOST_CHECK_EQUAL( dist, -1 );
}
}
if( c.m_exp_result )
BOOST_CHECK_EQUAL( dist, 0 );
else
BOOST_CHECK_EQUAL( dist, -1 );
}
}
struct ARC_SEG_COLLIDE_CASE
struct ARC_SEG_COLLIDE_CASE : public KI_TEST::NAMED_CASE
{
std::string m_ctx_name;
ARC_CENTRE_PT_ANGLE m_geom;
int m_arc_clearance;
SEG m_seg;
@ -677,42 +640,34 @@ static const std::vector<ARC_SEG_COLLIDE_CASE> arc_seg_collide_cases = {
};
BOOST_AUTO_TEST_CASE( CollideSeg )
BOOST_DATA_TEST_CASE( CollideSeg, boost::unit_test::data::make( arc_seg_collide_cases ), c )
{
for( const auto& c : arc_seg_collide_cases )
SHAPE_ARC arc( c.m_geom.m_center_point, c.m_geom.m_start_point,
EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ) );
// Test a zero width arc (distance should equal the clearance)
BOOST_TEST_CONTEXT( "Test Clearance" )
{
BOOST_TEST_CONTEXT( c.m_ctx_name )
{
SHAPE_ARC arc( c.m_geom.m_center_point, c.m_geom.m_start_point,
EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ) );
int dist = -1;
BOOST_CHECK_EQUAL( arc.Collide( c.m_seg, c.m_arc_clearance, &dist ),
c.m_exp_result );
BOOST_CHECK_EQUAL( dist, c.m_exp_distance );
}
// Test a zero width arc (distance should equal the clearance)
BOOST_TEST_CONTEXT( "Test Clearance" )
{
int dist = -1;
BOOST_CHECK_EQUAL( arc.Collide( c.m_seg, c.m_arc_clearance, &dist ),
c.m_exp_result );
BOOST_CHECK_EQUAL( dist, c.m_exp_distance );
}
// Test by changing the width of the arc (distance should equal zero)
BOOST_TEST_CONTEXT( "Test Width" )
{
int dist = -1;
arc.SetWidth( c.m_arc_clearance * 2 );
BOOST_CHECK_EQUAL( arc.Collide( c.m_seg, 0, &dist ), c.m_exp_result );
// Test by changing the width of the arc (distance should equal zero)
BOOST_TEST_CONTEXT( "Test Width" )
{
int dist = -1;
arc.SetWidth( c.m_arc_clearance * 2 );
BOOST_CHECK_EQUAL( arc.Collide( c.m_seg, 0, &dist ), c.m_exp_result );
if( c.m_exp_result )
BOOST_CHECK_EQUAL( dist, 0 );
else
BOOST_CHECK_EQUAL( dist, -1 );
}
}
if( c.m_exp_result )
BOOST_CHECK_EQUAL( dist, 0 );
else
BOOST_CHECK_EQUAL( dist, -1 );
}
}
struct ARC_DATA_MM
{
// Coordinates and dimensions in millimeters
@ -734,9 +689,8 @@ struct ARC_DATA_MM
};
struct ARC_ARC_COLLIDE_CASE
struct ARC_ARC_COLLIDE_CASE : public KI_TEST::NAMED_CASE
{
std::string m_ctx_name;
ARC_DATA_MM m_arc1;
ARC_DATA_MM m_arc2;
double m_clearance;
@ -823,54 +777,48 @@ static const std::vector<ARC_ARC_COLLIDE_CASE> arc_arc_collide_cases = {
};
BOOST_AUTO_TEST_CASE( CollideArc )
BOOST_DATA_TEST_CASE( CollideArc, boost::unit_test::data::make( arc_arc_collide_cases ), c )
{
for( const auto& c : arc_arc_collide_cases )
{
BOOST_TEST_CONTEXT( c.m_ctx_name )
{
SHAPE_ARC arc1( c.m_arc1.GenerateArc() );
SHAPE_ARC arc2( c.m_arc2.GenerateArc() );
SHAPE_ARC arc1( c.m_arc1.GenerateArc() );
SHAPE_ARC arc2( c.m_arc2.GenerateArc() );
SHAPE_LINE_CHAIN arc1_slc( c.m_arc1.GenerateArc() );
arc1_slc.SetWidth( 0 );
SHAPE_LINE_CHAIN arc1_slc( c.m_arc1.GenerateArc() );
arc1_slc.SetWidth( 0 );
SHAPE_LINE_CHAIN arc2_slc( c.m_arc2.GenerateArc() );
arc2_slc.SetWidth( 0 );
SHAPE_LINE_CHAIN arc2_slc( c.m_arc2.GenerateArc() );
arc2_slc.SetWidth( 0 );
int actual = 0;
VECTOR2I location;
int actual = 0;
VECTOR2I location;
SHAPE* arc1_sh = &arc1;
SHAPE* arc2_sh = &arc2;
SHAPE* arc1_slc_sh = &arc1_slc;
SHAPE* arc2_slc_sh = &arc2_slc;
SHAPE* arc1_sh = &arc1;
SHAPE* arc2_sh = &arc2;
SHAPE* arc1_slc_sh = &arc1_slc;
SHAPE* arc2_slc_sh = &arc2_slc;
bool result_arc_to_arc = arc1_sh->Collide( arc2_sh, pcbIUScale.mmToIU( c.m_clearance ),
&actual, &location );
bool result_arc_to_arc = arc1_sh->Collide( arc2_sh, pcbIUScale.mmToIU( c.m_clearance ),
&actual, &location );
// For arc to chain collisions, we need to re-calculate the clearances because the
// SHAPE_LINE_CHAIN is zero width
int clearance = pcbIUScale.mmToIU( c.m_clearance ) + ( arc2.GetWidth() / 2 );
// For arc to chain collisions, we need to re-calculate the clearances because the
// SHAPE_LINE_CHAIN is zero width
int clearance = pcbIUScale.mmToIU( c.m_clearance ) + ( arc2.GetWidth() / 2 );
bool result_arc_to_chain =
arc1_sh->Collide( arc2_slc_sh, clearance, &actual, &location );
bool result_arc_to_chain =
arc1_sh->Collide( arc2_slc_sh, clearance, &actual, &location );
clearance = pcbIUScale.mmToIU( c.m_clearance ) + ( arc1.GetWidth() / 2 );
bool result_chain_to_arc =
arc1_slc_sh->Collide( arc2_sh, clearance, &actual, &location );
clearance = pcbIUScale.mmToIU( c.m_clearance ) + ( arc1.GetWidth() / 2 );
bool result_chain_to_arc =
arc1_slc_sh->Collide( arc2_sh, clearance, &actual, &location );
clearance = ( arc1.GetWidth() / 2 ) + ( arc2.GetWidth() / 2 );
bool result_chain_to_chain =
arc1_slc_sh->Collide( arc2_slc_sh, clearance, &actual, &location );
clearance = ( arc1.GetWidth() / 2 ) + ( arc2.GetWidth() / 2 );
bool result_chain_to_chain =
arc1_slc_sh->Collide( arc2_slc_sh, clearance, &actual, &location );
BOOST_CHECK_EQUAL( result_arc_to_arc, c.m_exp_result );
BOOST_CHECK_EQUAL( result_arc_to_chain, c.m_exp_result );
BOOST_CHECK_EQUAL( result_chain_to_arc, c.m_exp_result );
BOOST_CHECK_EQUAL( result_chain_to_chain, c.m_exp_result );
}
}
BOOST_CHECK_EQUAL( result_arc_to_arc, c.m_exp_result );
BOOST_CHECK_EQUAL( result_arc_to_chain, c.m_exp_result );
BOOST_CHECK_EQUAL( result_chain_to_arc, c.m_exp_result );
BOOST_CHECK_EQUAL( result_chain_to_chain, c.m_exp_result );
}
@ -942,9 +890,8 @@ BOOST_AUTO_TEST_CASE( CollideArcToPolygonApproximation )
}
struct ARC_TO_POLYLINE_CASE
struct ARC_TO_POLYLINE_CASE : public KI_TEST::NAMED_CASE
{
std::string m_ctx_name;
ARC_CENTRE_PT_ANGLE m_geom;
};
@ -996,46 +943,47 @@ bool ArePolylineMidPointsNearCircle( const SHAPE_LINE_CHAIN& aPolyline, const VE
}
BOOST_AUTO_TEST_CASE( ArcToPolyline )
{
const std::vector<ARC_TO_POLYLINE_CASE> cases = {
const std::vector<ARC_TO_POLYLINE_CASE> ArcToPolyline_cases{
{
"Zero rad",
{
"Zero rad",
{
{ 0, 0 },
{ 0, 0 },
180,
},
{ 0, 0 },
{ 0, 0 },
180,
},
},
{
"Semicircle",
{
"Semicircle",
{
{ 0, 0 },
{ -1000000, 0 },
180,
},
{ 0, 0 },
{ -1000000, 0 },
180,
},
},
{
// check that very small circles don't fall apart and that reverse angles
// work too
"Extremely small semicircle",
{
// check that very small circles don't fall apart and that reverse angles
// work too
"Extremely small semicircle",
{
{ 0, 0 },
{ -1000, 0 },
-180,
},
{ 0, 0 },
{ -1000, 0 },
-180,
},
},
{
// Make sure it doesn't only work for "easy" angles
"Non-round geometry",
{
// Make sure it doesn't only work for "easy" angles
"Non-round geometry",
{
{ 0, 0 },
{ 1234567, 0 },
42.22,
},
{ 0, 0 },
{ 1234567, 0 },
42.22,
},
};
},
};
BOOST_DATA_TEST_CASE( ArcToPolyline, boost::unit_test::data::make( ArcToPolyline_cases ), c )
{
const int width = 0;
// Note: do not expect accuracies around 1 to work. We use integers internally so we're
@ -1044,33 +992,27 @@ BOOST_AUTO_TEST_CASE( ArcToPolyline )
const int accuracy = 100;
const int epsilon = 1;
for( const auto& c : cases )
{
BOOST_TEST_CONTEXT( c.m_ctx_name )
{
const SHAPE_ARC this_arc{ c.m_geom.m_center_point, c.m_geom.m_start_point,
EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ), width };
const SHAPE_ARC this_arc{ c.m_geom.m_center_point, c.m_geom.m_start_point,
EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ), width };
const SHAPE_LINE_CHAIN chain = this_arc.ConvertToPolyline( accuracy );
const SHAPE_LINE_CHAIN chain = this_arc.ConvertToPolyline( accuracy );
BOOST_TEST_MESSAGE( "Polyline has " << chain.PointCount() << " points" );
BOOST_TEST_MESSAGE( "Polyline has " << chain.PointCount() << " points" );
// Start point (exactly) where expected
BOOST_CHECK_EQUAL( chain.CPoint( 0 ), c.m_geom.m_start_point );
// Start point (exactly) where expected
BOOST_CHECK_EQUAL( chain.CPoint( 0 ), c.m_geom.m_start_point );
// End point (exactly) where expected
BOOST_CHECK_EQUAL( chain.CPoint( -1 ), this_arc.GetP1() );
// End point (exactly) where expected
BOOST_CHECK_EQUAL( chain.CPoint( -1 ), this_arc.GetP1() );
int radius = ( c.m_geom.m_center_point - c.m_geom.m_start_point ).EuclideanNorm();
int radius = ( c.m_geom.m_center_point - c.m_geom.m_start_point ).EuclideanNorm();
// Other points within accuracy + epsilon (for rounding) of where they should be
BOOST_CHECK_PREDICATE( ArePolylineEndPointsNearCircle,
( chain )( c.m_geom.m_center_point )( radius )( accuracy + epsilon ) );
// Other points within accuracy + epsilon (for rounding) of where they should be
BOOST_CHECK_PREDICATE( ArePolylineEndPointsNearCircle,
( chain )( c.m_geom.m_center_point )( radius )( accuracy + epsilon ) );
BOOST_CHECK_PREDICATE( ArePolylineMidPointsNearCircle,
( chain )( c.m_geom.m_center_point )( radius )( accuracy + epsilon ) );
}
}
BOOST_CHECK_PREDICATE( ArePolylineMidPointsNearCircle,
( chain )( c.m_geom.m_center_point )( radius )( accuracy + epsilon ) );
}

View File

@ -21,8 +21,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/data/test_case.hpp>
#include <pcbnew_utils/board_test_utils.h>
#include <board.h>
#include <board_design_settings.h>
#include <pad.h>
@ -44,64 +47,65 @@ struct DRC_REGRESSION_TEST_FIXTURE
};
BOOST_FIXTURE_TEST_CASE( DRCCopperSliver, DRC_REGRESSION_TEST_FIXTURE )
// clang-format off
static const std::vector<std::pair<wxString, int>> DRCCopperSliver_cases =
{
{ "sliver", 1 },
{ "issue14449", 0 },
{ "issue14549", 0 },
{ "issue14549_2", 0 },
{ "issue14559", 0 }
};
// clang-format on
BOOST_DATA_TEST_CASE_F( DRC_REGRESSION_TEST_FIXTURE, DRCCopperSliver,
boost::unit_test::data::make( DRCCopperSliver_cases ), test )
{
// Check for minimum copper connection errors
std::vector<std::pair<wxString, int>> tests =
{
{ "sliver", 1 },
{ "issue14449", 0 },
{ "issue14549", 0 },
{ "issue14549_2", 0 },
{ "issue14559", 0 }
};
KI_TEST::LoadBoard( m_settingsManager, test.first, m_board );
KI_TEST::FillZones( m_board.get() );
for( const std::pair<wxString, int>& test : tests )
{
KI_TEST::LoadBoard( m_settingsManager, test.first, m_board );
KI_TEST::FillZones( m_board.get() );
std::vector<DRC_ITEM> violations;
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
std::vector<DRC_ITEM> violations;
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
// Disable DRC tests not useful or not handled in this testcase
for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
bds.m_DRCSeverities[ii] = SEVERITY::RPT_SEVERITY_IGNORE;
// Disable DRC tests not useful or not handled in this testcase
for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
bds.m_DRCSeverities[ ii ] = SEVERITY::RPT_SEVERITY_IGNORE;
// Ensure that our desired error is fired
bds.m_DRCSeverities[DRCE_COPPER_SLIVER] = SEVERITY::RPT_SEVERITY_ERROR;
// Ensure that our desired error is fired
bds.m_DRCSeverities[ DRCE_COPPER_SLIVER ] = SEVERITY::RPT_SEVERITY_ERROR;
bds.m_DRCEngine->SetViolationHandler(
[&]( const std::shared_ptr<DRC_ITEM>& aItem, VECTOR2I aPos, int aLayer,
DRC_CUSTOM_MARKER_HANDLER* aCustomHandler )
{
if( bds.GetSeverity( aItem->GetErrorCode() ) == SEVERITY::RPT_SEVERITY_ERROR )
violations.push_back( *aItem );
} );
bds.m_DRCEngine->RunTests( EDA_UNITS::MILLIMETRES, true, false );
if( violations.size() == test.second )
{
BOOST_CHECK_EQUAL( 1, 1 ); // quiet "did not check any assertions" warning
BOOST_TEST_MESSAGE( wxString::Format( "DRC copper sliver: %s, passed", test.first ) );
}
else
{
UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::INCHES );
std::map<KIID, EDA_ITEM*> itemMap;
m_board->FillItemMap( itemMap );
for( const DRC_ITEM& item : violations )
bds.m_DRCEngine->SetViolationHandler(
[&]( const std::shared_ptr<DRC_ITEM>& aItem, VECTOR2I aPos, int aLayer,
DRC_CUSTOM_MARKER_HANDLER* aCustomHandler )
{
BOOST_TEST_MESSAGE( item.ShowReport( &unitsProvider, RPT_SEVERITY_ERROR,
itemMap ) );
}
if( bds.GetSeverity( aItem->GetErrorCode() ) == SEVERITY::RPT_SEVERITY_ERROR )
violations.push_back( *aItem );
} );
BOOST_ERROR( wxString::Format( "DRC copper sliver: %s, failed (violations found %d expected %d)",
test.first, (int)violations.size(), test.second ) );
bds.m_DRCEngine->RunTests( EDA_UNITS::MILLIMETRES, true, false );
if( violations.size() == test.second )
{
BOOST_CHECK_EQUAL( 1, 1 ); // quiet "did not check any assertions" warning
BOOST_TEST_MESSAGE( wxString::Format( "DRC copper sliver: %s, passed", test.first ) );
}
else
{
UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::INCHES );
std::map<KIID, EDA_ITEM*> itemMap;
m_board->FillItemMap( itemMap );
for( const DRC_ITEM& item : violations )
{
BOOST_TEST_MESSAGE( item.ShowReport( &unitsProvider, RPT_SEVERITY_ERROR, itemMap ) );
}
BOOST_ERROR(
wxString::Format( "DRC copper sliver: %s, failed (violations found %d expected %d)",
test.first, (int) violations.size(), test.second ) );
}
}

View File

@ -21,14 +21,17 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/data/test_case.hpp>
#include <board.h>
#include <kiid.h>
#include <footprint.h>
#include <pcbnew_utils/board_file_utils.h>
#include <pcbnew_utils/board_test_utils.h>
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <settings/settings_manager.h>
namespace
{
@ -42,60 +45,58 @@ struct FOOTPRINT_LOAD_TEST_CASE
};
struct FOOTPRINT_LOAD_BOARD_TEST_CASE
struct FOOTPRINT_LOAD_BOARD_TEST_CASE: public KI_TEST::BOARD_LOAD_TEST_CASE
{
// The board to load
wxString m_boardFileRelativePath;
// List of images to check
std::vector<FOOTPRINT_LOAD_TEST_CASE> m_fpCases;
};
} // namespace
const std::vector<FOOTPRINT_LOAD_BOARD_TEST_CASE> FootprintLoadSave_testCases{
{
"footprints_load_save",
std::nullopt,
{
// From top to bottom in the board file
{
"898cf321-03c7-40bb-8d78-4bc5e52986c2",
false,
{ 100, 80 },
},
{
"0775cd70-2e84-4592-a160-456c37a8f4f6",
true,
{ 100, 100 },
},
},
},
};
} // namespace
/**
* Simple tests cases that load a board file and check expected properties of the footprints.
* This is not the same as FpLibLoadSave as it loads _board files_, and not footprint
* files from a library.
*/
BOOST_AUTO_TEST_CASE( FootprintLoadSave )
BOOST_DATA_TEST_CASE( FootprintLoadSave,
boost::unit_test::data::make( FootprintLoadSave_testCases ), testCase )
{
const std::vector<FOOTPRINT_LOAD_BOARD_TEST_CASE> testCases{
const auto doBoardTest = [&]( const BOARD& aBoard )
{
for( const FOOTPRINT_LOAD_TEST_CASE& fooprintTestCase : testCase.m_fpCases )
{
"footprints_load_save",
{
// From top to bottom in the board file
{
"898cf321-03c7-40bb-8d78-4bc5e52986c2",
false,
{ 100, 80 },
},
{
"0775cd70-2e84-4592-a160-456c37a8f4f6",
true,
{ 100, 100 },
},
},
},
BOOST_TEST_MESSAGE( "Checking for footprint with UUID: "
<< fooprintTestCase.m_footprintUuid.AsString() );
const auto& fp = static_cast<FOOTPRINT&>( KI_TEST::RequireBoardItemWithTypeAndId(
aBoard, PCB_FOOTPRINT_T, fooprintTestCase.m_footprintUuid ) );
BOOST_CHECK_EQUAL( fp.IsLocked(), fooprintTestCase.m_expectedLocked );
BOOST_CHECK_EQUAL( fp.GetPosition(), fooprintTestCase.m_expectedPos * 1000000 );
}
};
for( const FOOTPRINT_LOAD_BOARD_TEST_CASE& testCase : testCases )
{
const auto doBoardTest = [&]( const BOARD& aBoard )
{
for( const FOOTPRINT_LOAD_TEST_CASE& fooprintTestCase : testCase.m_fpCases )
{
BOOST_TEST_MESSAGE( "Checking for footprint with UUID: "
<< fooprintTestCase.m_footprintUuid.AsString() );
const auto& fp = static_cast<FOOTPRINT&>( KI_TEST::RequireBoardItemWithTypeAndId(
aBoard, PCB_FOOTPRINT_T, fooprintTestCase.m_footprintUuid ) );
BOOST_CHECK_EQUAL( fp.IsLocked(), fooprintTestCase.m_expectedLocked );
BOOST_CHECK_EQUAL( fp.GetPosition(), fooprintTestCase.m_expectedPos * 1000000 );
}
};
KI_TEST::LoadAndTestBoardFile( testCase.m_boardFileRelativePath, true, doBoardTest );
}
KI_TEST::LoadAndTestBoardFile( testCase.m_BoardFileRelativePath, true, doBoardTest,
testCase.m_ExpectedBoardVersion );
}

View File

@ -21,12 +21,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/data/test_case.hpp>
#include <board.h>
#include <kiid.h>
#include <footprint.h>
#include <pcbnew_utils/board_file_utils.h>
#include <pcbnew_utils/board_test_utils.h>
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <settings/settings_manager.h>
namespace
@ -42,6 +44,24 @@ struct FPLIB_LOAD_FP_TEST_CASE
// If set, the expected number of pads in the footprint
std::optional<unsigned> m_expectedPadCount;
// For printing the test name
friend std::ostream& operator<<( std::ostream& os, const FPLIB_LOAD_FP_TEST_CASE& aTestCase )
{
os << aTestCase.m_fpName;
return os;
}
};
const std::vector<FPLIB_LOAD_FP_TEST_CASE> FpLibLoadSave_testCases{
{
"plugins/kicad_sexpr/fp.pretty",
"R_0201_0603Metric",
20240108U,
// 2 SMD pads, 2 paste pads
4U,
},
};
} // namespace
@ -53,29 +73,17 @@ struct FPLIB_LOAD_FP_TEST_CASE
* _board files_ that contain footprints. This tests loading and saving of _footprint
* files_, and includes at least some of the library IO code.
*/
BOOST_AUTO_TEST_CASE( FpLibLoadSave )
BOOST_DATA_TEST_CASE( FpLibLoadSave, boost::unit_test::data::make( FpLibLoadSave_testCases ),
testCase )
{
const std::vector<FPLIB_LOAD_FP_TEST_CASE> testCases{
const auto doFootprintTest = [&]( const FOOTPRINT& aBoard )
{
if( testCase.m_expectedPadCount )
{
"plugins/kicad_sexpr/fp.pretty",
"R_0201_0603Metric",
20240108U,
// 2 SMD pads, 2 paste pads
4U,
},
BOOST_CHECK_EQUAL( aBoard.Pads().size(), *testCase.m_expectedPadCount );
}
};
for( const FPLIB_LOAD_FP_TEST_CASE& testCase : testCases )
{
const auto doFootprintTest = [&]( const FOOTPRINT& aBoard )
{
if( testCase.m_expectedPadCount )
{
BOOST_CHECK_EQUAL( aBoard.Pads().size(), *testCase.m_expectedPadCount );
}
};
KI_TEST::LoadAndTestFootprintFile( testCase.m_libraryPath, testCase.m_fpName, true,
doFootprintTest, testCase.m_expectedFootprintVersion );
}
KI_TEST::LoadAndTestFootprintFile( testCase.m_libraryPath, testCase.m_fpName, true,
doFootprintTest, testCase.m_expectedFootprintVersion );
}

View File

@ -21,23 +21,18 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/data/test_case.hpp>
#include <board.h>
#include <kiid.h>
#include <pcb_generator.h>
#include <pcbnew_utils/board_file_utils.h>
#include <pcbnew_utils/board_test_utils.h>
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <settings/settings_manager.h>
namespace
{
struct GENERATOR_LOAD_TEST_FIXTURE
{
GENERATOR_LOAD_TEST_FIXTURE() {}
};
struct GENERATOR_LOAD_TEST_CASE
{
// Which one to look at in the file?
@ -49,74 +44,65 @@ struct GENERATOR_LOAD_TEST_CASE
};
struct GENERATOR_LOAD_BOARD_TEST_CASE
struct GENERATOR_LOAD_BOARD_TEST_CASE: public KI_TEST::BOARD_LOAD_TEST_CASE
{
// The board to load
wxString m_boardFileRelativePath;
// These tests may well test specific versions of the board file format,
// so don't let it change accidentally!
int m_expectedBoardVersion;
// List of images to check
std::vector<GENERATOR_LOAD_TEST_CASE> m_generatorCases;
};
const std::vector<GENERATOR_LOAD_BOARD_TEST_CASE> GeneratorLoading_testCases{
{
"tuning_generators_load_save",
20231231,
{
// From top to bottom in the board file
{
"4f22a815-3048-42b3-86fa-eb71720d35ae",
false,
"Tuning Pattern",
47,
},
},
},
{
// Before 20231231, 'id' was used in generator s-exprs
// and we need to continue to load it for compatibility
"tuning_generators_load_save_v20231212",
20231212,
{
// From top to bottom in the board file
{
"4f22a815-3048-42b3-86fa-eb71720d35ae",
false,
"Tuning Pattern",
47,
},
},
},
};
} // namespace
BOOST_FIXTURE_TEST_CASE( GeneratorLoading, GENERATOR_LOAD_TEST_FIXTURE )
BOOST_DATA_TEST_CASE( GeneratorLoading, boost::unit_test::data::make( GeneratorLoading_testCases ),
testCase )
{
const std::vector<GENERATOR_LOAD_BOARD_TEST_CASE> testCases{
const auto doBoardTest = [&]( const BOARD& aBoard )
{
for( const GENERATOR_LOAD_TEST_CASE& testCase : testCase.m_generatorCases )
{
"tuning_generators_load_save",
20231231,
{
// From top to bottom in the board file
{
"4f22a815-3048-42b3-86fa-eb71720d35ae",
false,
"Tuning Pattern",
47,
},
},
},
{
// Before 20231231, 'id' was used in generator s-exprs
// and we need to continue to load it for compatibility
"tuning_generators_load_save_v20231212",
20231212,
{
// From top to bottom in the board file
{
"4f22a815-3048-42b3-86fa-eb71720d35ae",
false,
"Tuning Pattern",
47,
},
},
},
BOOST_TEST_MESSAGE(
"Checking for generator with UUID: " << testCase.m_searchUuid.AsString() );
const auto& generator =
static_cast<PCB_GENERATOR&>( KI_TEST::RequireBoardItemWithTypeAndId(
aBoard, PCB_GENERATOR_T, testCase.m_searchUuid ) );
BOOST_CHECK_EQUAL( generator.IsLocked(), testCase.m_expectedLocked );
BOOST_CHECK_EQUAL( generator.GetName(), testCase.m_expectedName );
BOOST_CHECK_EQUAL( generator.GetItems().size(), testCase.m_expectedMemberCount );
}
};
for( const GENERATOR_LOAD_BOARD_TEST_CASE& testCase : testCases )
{
const auto doBoardTest = [&]( const BOARD& aBoard )
{
for( const GENERATOR_LOAD_TEST_CASE& testCase : testCase.m_generatorCases )
{
BOOST_TEST_MESSAGE(
"Checking for generator with UUID: " << testCase.m_searchUuid.AsString() );
const auto& generator =
static_cast<PCB_GENERATOR&>( KI_TEST::RequireBoardItemWithTypeAndId(
aBoard, PCB_GENERATOR_T, testCase.m_searchUuid ) );
BOOST_CHECK_EQUAL( generator.IsLocked(), testCase.m_expectedLocked );
BOOST_CHECK_EQUAL( generator.GetName(), testCase.m_expectedName );
BOOST_CHECK_EQUAL( generator.GetItems().size(), testCase.m_expectedMemberCount );
}
};
KI_TEST::LoadAndTestBoardFile( testCase.m_boardFileRelativePath, true, doBoardTest,
testCase.m_expectedBoardVersion );
}
KI_TEST::LoadAndTestBoardFile( testCase.m_BoardFileRelativePath, true, doBoardTest,
testCase.m_ExpectedBoardVersion );
}

View File

@ -21,12 +21,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/data/test_case.hpp>
#include <board.h>
#include <kiid.h>
#include <pcb_shape.h>
#include <pcbnew_utils/board_file_utils.h>
#include <pcbnew_utils/board_test_utils.h>
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <settings/settings_manager.h>
namespace
@ -41,72 +43,63 @@ struct GRAPHICS_LOAD_TEST_CASE
};
struct GRAPHICS_LOAD_BOARD_TEST_CASE
struct GRAPHICS_LOAD_BOARD_TEST_CASE: public KI_TEST::BOARD_LOAD_TEST_CASE
{
// The board to load
wxString m_boardFileRelativePath;
// These tests may well test specific versions of the board file format,
// so don't let it change accidentally!
int m_expectedBoardVersion;
// List of images to check
std::vector<GRAPHICS_LOAD_TEST_CASE> m_generatorCases;
};
const std::vector<GRAPHICS_LOAD_BOARD_TEST_CASE> GraphicsLoad_testCases{
{
// Before 20241129, 'fill' would be solid or none
// in PCB shapes
"graphics_load_save_v20240108",
20240108,
{
// Filled poly
{
"65596b4f-7b03-48e9-8be7-5824316ea7fd",
true,
},
// Unfilled poly
{
"cf265305-49c9-43d8-bb2a-ad34997b22d6",
false,
},
// Filled rect
{
"d0669ae2-442f-427f-af0f-bc3008af779a",
true,
},
// Unfilled rect
{
"fd1649a3-9a92-4dd3-96b4-88469cb257ba",
false,
},
},
},
};
} // namespace
BOOST_AUTO_TEST_CASE( GraphicsLoad )
BOOST_DATA_TEST_CASE( GraphicsLoad, boost::unit_test::data::make( GraphicsLoad_testCases ),
testCase )
{
const std::vector<GRAPHICS_LOAD_BOARD_TEST_CASE> testCases{
{
// Before 20241129, 'fill' would be solid or none
// in PCB shapes
"graphics_load_save_v20240108",
20240108,
{
// Filled poly
{
"65596b4f-7b03-48e9-8be7-5824316ea7fd",
true,
},
// Unfilled poly
{
"cf265305-49c9-43d8-bb2a-ad34997b22d6",
false,
},
// Filled rect
{
"d0669ae2-442f-427f-af0f-bc3008af779a",
true,
},
// Unfilled rect
{
"fd1649a3-9a92-4dd3-96b4-88469cb257ba",
false,
},
},
},
};
for( const GRAPHICS_LOAD_BOARD_TEST_CASE& testCase : testCases )
const auto doBoardTest = [&]( const BOARD& aBoard )
{
const auto doBoardTest = [&]( const BOARD& aBoard )
{
for( const GRAPHICS_LOAD_TEST_CASE& testCase : testCase.m_generatorCases )
{
BOOST_TEST_MESSAGE(
"Checking for graphic with UUID: " << testCase.m_searchUuid.AsString() );
BOOST_TEST_MESSAGE(
"Checking for graphic with UUID: " << testCase.m_searchUuid.AsString() );
const auto& graphic =
static_cast<PCB_SHAPE&>( KI_TEST::RequireBoardItemWithTypeAndId(
aBoard, PCB_SHAPE_T, testCase.m_searchUuid ) );
const auto& graphic =
static_cast<PCB_SHAPE&>( KI_TEST::RequireBoardItemWithTypeAndId(
aBoard, PCB_SHAPE_T, testCase.m_searchUuid ) );
BOOST_CHECK_EQUAL( graphic.IsFilled(), testCase.m_expectedFilled );
BOOST_CHECK_EQUAL( graphic.IsFilled(), testCase.m_expectedFilled );
}
};
};
KI_TEST::LoadAndTestBoardFile( testCase.m_boardFileRelativePath, true, doBoardTest,
testCase.m_expectedBoardVersion );
}
KI_TEST::LoadAndTestBoardFile( testCase.m_BoardFileRelativePath, true, doBoardTest,
testCase.m_ExpectedBoardVersion );
}

View File

@ -21,23 +21,18 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/data/test_case.hpp>
#include <board.h>
#include <kiid.h>
#include <pcb_group.h>
#include <pcbnew_utils/board_file_utils.h>
#include <pcbnew_utils/board_test_utils.h>
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <settings/settings_manager.h>
namespace
{
struct GROUP_LOAD_TEST_FIXTURE
{
GROUP_LOAD_TEST_FIXTURE() {}
};
struct GROUP_LOAD_TEST_CASE
{
// Which one to look at in the file?
@ -49,19 +44,44 @@ struct GROUP_LOAD_TEST_CASE
};
struct GROUP_LOAD_BOARD_TEST_CASE
struct GROUP_LOAD_BOARD_TEST_CASE: public KI_TEST::BOARD_LOAD_TEST_CASE
{
// The board to load
wxString m_boardFileRelativePath;
// These tests may well test specific versions of the board file format,
// so don't let it change accidentally!
int m_expectedBoardVersion;
// List of items to check on this board
std::vector<GROUP_LOAD_TEST_CASE> m_groupCases;
};
const std::vector<GROUP_LOAD_BOARD_TEST_CASE> GroupsLoadSave_testCases{
{
"groups_load_save",
20231231,
{
// From top to bottom in the board file
{
"a78cc65c-451e-451e-9147-4460cc669685",
true,
"GroupName",
2,
},
},
},
{
// Before 20231231, 'id' was used in group s-exprs
// and we need to continue to load it for compatibility
"groups_load_save_v20231212",
20231212,
{
// From top to bottom in the board file
{
"a78cc65c-451e-451e-9147-4460cc669685",
true,
"GroupName",
2,
},
},
},
};
} // namespace
/**
@ -74,61 +94,26 @@ struct GROUP_LOAD_BOARD_TEST_CASE
*
* But it's less good at programmatic generation of interesting groups in the first place.
*/
BOOST_FIXTURE_TEST_CASE( GroupsLoadSave, GROUP_LOAD_TEST_FIXTURE )
BOOST_DATA_TEST_CASE( GroupsLoadSave, boost::unit_test::data::make( GroupsLoadSave_testCases ),
testCase )
{
const std::vector<GROUP_LOAD_BOARD_TEST_CASE> groupTestCases{
const auto doBoardTest = [&]( const BOARD& aBoard )
{
for( const GROUP_LOAD_TEST_CASE& groupTestCase : testCase.m_groupCases )
{
"groups_load_save",
20231231,
{
// From top to bottom in the board file
{
"a78cc65c-451e-451e-9147-4460cc669685",
true,
"GroupName",
2,
},
},
},
{
// Before 20231231, 'id' was used in group s-exprs
// and we need to continue to load it for compatibility
"groups_load_save_v20231212",
20231212,
{
// From top to bottom in the board file
{
"a78cc65c-451e-451e-9147-4460cc669685",
true,
"GroupName",
2,
},
},
},
BOOST_TEST_MESSAGE( "Checking for group with UUID: "
<< groupTestCase.m_searchUuid.AsString()
<< " and name: " << groupTestCase.m_expectedName );
const auto& group = static_cast<PCB_GROUP&>( KI_TEST::RequireBoardItemWithTypeAndId(
aBoard, PCB_GROUP_T, groupTestCase.m_searchUuid ) );
BOOST_CHECK_EQUAL( group.IsLocked(), groupTestCase.m_expectedLocked );
BOOST_CHECK_EQUAL( group.GetName(), groupTestCase.m_expectedName );
BOOST_CHECK_EQUAL( group.GetItems().size(), groupTestCase.m_expectedMemberCount );
}
};
for( const GROUP_LOAD_BOARD_TEST_CASE& testCase : groupTestCases )
{
BOOST_TEST_MESSAGE( "Test case on file: " << testCase.m_boardFileRelativePath );
const auto doBoardTest = [&]( const BOARD& aBoard )
{
for( const GROUP_LOAD_TEST_CASE& groupTestCase : testCase.m_groupCases )
{
BOOST_TEST_MESSAGE( "Checking for group with UUID: "
<< groupTestCase.m_searchUuid.AsString()
<< " and name: " << groupTestCase.m_expectedName );
const auto& group = static_cast<PCB_GROUP&>( KI_TEST::RequireBoardItemWithTypeAndId(
aBoard, PCB_GROUP_T, groupTestCase.m_searchUuid ) );
BOOST_CHECK_EQUAL( group.IsLocked(), groupTestCase.m_expectedLocked );
BOOST_CHECK_EQUAL( group.GetName(), groupTestCase.m_expectedName );
BOOST_CHECK_EQUAL( group.GetItems().size(), groupTestCase.m_expectedMemberCount );
}
};
KI_TEST::LoadAndTestBoardFile( testCase.m_boardFileRelativePath, true, doBoardTest,
testCase.m_expectedBoardVersion );
}
KI_TEST::LoadAndTestBoardFile( testCase.m_BoardFileRelativePath, true, doBoardTest,
testCase.m_ExpectedBoardVersion );
}

View File

@ -18,16 +18,16 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/data/test_case.hpp>
#include <pcbnew_utils/board_test_utils.h>
#include <pcbnew_utils/board_file_utils.h>
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <pcbnew/pcb_io/pcb_io.h>
#include <pcbnew/pcb_io/pcb_io_mgr.h>
BOOST_AUTO_TEST_SUITE( IOMGR )
@ -36,6 +36,12 @@ struct PCB_IO_PLUGIN_CASE
std::string m_case_name;
std::string m_file_rel_path;
PCB_IO_MGR::PCB_FILE_T m_expected_type;
friend std::ostream& operator<<( std::ostream& os, const PCB_IO_PLUGIN_CASE& aCase )
{
os << aCase.m_case_name;
return os;
}
};
@ -246,64 +252,46 @@ static const std::vector<PCB_IO_PLUGIN_CASE> LibraryPluginCases = {
// clang-format on
BOOST_AUTO_TEST_CASE( FindBoardPluginType )
BOOST_DATA_TEST_CASE( FindBoardPluginType, boost::unit_test::data::make( BoardPluginCases ), c )
{
for( auto& c : BoardPluginCases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
wxString dataPath =
wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() + c.m_file_rel_path );
wxString dataPath =
wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() + c.m_file_rel_path );
BOOST_CHECK_EQUAL( PCB_IO_MGR::FindPluginTypeFromBoardPath( dataPath ),
c.m_expected_type );
BOOST_CHECK_EQUAL( PCB_IO_MGR::FindPluginTypeFromBoardPath( dataPath ),
c.m_expected_type );
// Todo add tests to check if it still works with upper/lower case ext.
// ( FindPluginTypeFromBoardPath should be case insensitive)
}
}
// Todo add tests to check if it still works with upper/lower case ext.
// ( FindPluginTypeFromBoardPath should be case insensitive)
}
BOOST_AUTO_TEST_CASE( GuessLibraryPluginType )
BOOST_DATA_TEST_CASE( GuessLibraryPluginType, boost::unit_test::data::make( LibraryPluginCases ), c )
{
for( auto& c : LibraryPluginCases )
{
BOOST_TEST_CONTEXT( c.m_case_name )
{
wxString dataPath =
wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() + c.m_file_rel_path );
wxString dataPath =
wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() + c.m_file_rel_path );
BOOST_CHECK_EQUAL( PCB_IO_MGR::GuessPluginTypeFromLibPath( dataPath ), c.m_expected_type );
BOOST_CHECK_EQUAL( PCB_IO_MGR::GuessPluginTypeFromLibPath( dataPath ), c.m_expected_type );
// Todo add tests to check if it still works with upper/lower case ext.
// ( GuessPluginTypeFromLibPath should be case insensitive)
}
}
// Todo add tests to check if it still works with upper/lower case ext.
// ( GuessPluginTypeFromLibPath should be case insensitive)
}
BOOST_AUTO_TEST_CASE( CheckCanReadBoard )
BOOST_DATA_TEST_CASE( CheckCanReadBoard, boost::unit_test::data::make( BoardPluginCases ), c )
{
for( auto& c : BoardPluginCases )
wxString dataPath =
wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() + c.m_file_rel_path );
auto& pluginEntries = PCB_IO_MGR::PLUGIN_REGISTRY::Instance()->AllPlugins();
for( auto& entry : pluginEntries )
{
BOOST_TEST_CONTEXT( c.m_case_name )
BOOST_TEST_CONTEXT( entry.m_name )
{
wxString dataPath =
wxString::FromUTF8( KI_TEST::GetPcbnewTestDataDir() + c.m_file_rel_path );
auto plugin = IO_RELEASER<PCB_IO>( PCB_IO_MGR::PluginFind( entry.m_type ) );
bool expectValidHeader = c.m_expected_type == entry.m_type;
auto& pluginEntries = PCB_IO_MGR::PLUGIN_REGISTRY::Instance()->AllPlugins();
for( auto& entry : pluginEntries )
{
BOOST_TEST_CONTEXT( entry.m_name )
{
auto plugin = IO_RELEASER<PCB_IO>( PCB_IO_MGR::PluginFind( entry.m_type ) );
bool expectValidHeader = c.m_expected_type == entry.m_type;
BOOST_CHECK_EQUAL( plugin->CanReadBoard( dataPath ), expectValidHeader );
}
}
BOOST_CHECK_EQUAL( plugin->CanReadBoard( dataPath ), expectValidHeader );
}
}
}

View File

@ -21,9 +21,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <wx/wx.h>
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/data/test_case.hpp>
#include <wx/wx.h>
#include <layer_ids.h>
#include <pcbnew/pcbexpr_evaluator.h>
@ -38,6 +39,12 @@ struct EXPR_TO_TEST
wxString expression;
bool expectError;
LIBEVAL::VALUE expectedResult;
friend std::ostream& operator<<( std::ostream& os, const EXPR_TO_TEST& expr )
{
os << expr.expression;
return os;
}
};
using VAL = LIBEVAL::VALUE;
@ -137,12 +144,9 @@ static bool testEvalExpr( const wxString& expr, const LIBEVAL::VALUE& expectedRe
}
BOOST_AUTO_TEST_CASE( SimpleExpressions )
BOOST_DATA_TEST_CASE( SimpleExpressions, boost::unit_test::data::make( simpleExpressions ), expr )
{
for( const auto& expr : simpleExpressions )
{
testEvalExpr( expr.expression, expr.expectedResult, expr.expectError );
}
testEvalExpr( expr.expression, expr.expectedResult, expr.expectError );
}

View File

@ -21,12 +21,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/data/test_case.hpp>
#include <board.h>
#include <kiid.h>
#include <pcb_reference_image.h>
#include <pcbnew_utils/board_file_utils.h>
#include <pcbnew_utils/board_test_utils.h>
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <settings/settings_manager.h>
namespace
@ -51,72 +53,71 @@ struct REFERENCE_IMAGE_LOAD_TEST_CASE
};
struct REFERENCE_IMAGE_LOAD_BOARD_TEST_CASE
struct REFERENCE_IMAGE_LOAD_BOARD_TEST_CASE: public KI_TEST::BOARD_LOAD_TEST_CASE
{
// The board to load
wxString m_boardFileRelativePath;
// List of images to check
std::vector<REFERENCE_IMAGE_LOAD_TEST_CASE> m_imageCases;
};
const std::vector<REFERENCE_IMAGE_LOAD_BOARD_TEST_CASE> ReferenceImageLoading_testCases{
{
"reference_images_load_save",
std::nullopt,
{
// From top to bottom in the board file
{
"7dde345e-020a-4fdd-af77-588b452be5e0",
false,
{ 100, 46 },
1.0,
{ 64, 64 },
},
{
"e4fd52dd-1d89-4c43-b621-aebfc9788d5c",
true,
{ 100, 65 },
1.0,
{ 64, 64 },
},
{
"d402397e-bce0-4cae-a398-b5aeef397e87",
false,
{ 100, 90 },
2.0,
{ 64, 64 }, // It's the same size, but scaled
},
},
},
};
} // namespace
BOOST_FIXTURE_TEST_CASE( ReferenceImageLoading, REFERENCE_IMAGE_LOAD_TEST_FIXTURE )
BOOST_DATA_TEST_CASE_F( REFERENCE_IMAGE_LOAD_TEST_FIXTURE, ReferenceImageLoading,
boost::unit_test::data::make( ReferenceImageLoading_testCases ), testCase )
{
const std::vector<REFERENCE_IMAGE_LOAD_BOARD_TEST_CASE> testCases{
const auto doBoardTest = [&]( const BOARD& aBoard )
{
for( const REFERENCE_IMAGE_LOAD_TEST_CASE& imageTestCase : testCase.m_imageCases )
{
"reference_images_load_save",
{
// From top to bottom in the board file
{
"7dde345e-020a-4fdd-af77-588b452be5e0",
false,
{ 100, 46 },
1.0,
{ 64, 64 },
},
{
"e4fd52dd-1d89-4c43-b621-aebfc9788d5c",
true,
{ 100, 65 },
1.0,
{ 64, 64 },
},
{
"d402397e-bce0-4cae-a398-b5aeef397e87",
false,
{ 100, 90 },
2.0,
{ 64, 64 }, // It's the same size, but scaled
},
},
},
BOOST_TEST_MESSAGE(
"Checking for image with UUID: " << imageTestCase.m_imageUuid.AsString() );
const auto& image =
static_cast<PCB_REFERENCE_IMAGE&>( KI_TEST::RequireBoardItemWithTypeAndId(
aBoard, PCB_REFERENCE_IMAGE_T, imageTestCase.m_imageUuid ) );
const REFERENCE_IMAGE& refImage = image.GetReferenceImage();
BOOST_CHECK_EQUAL( image.IsLocked(), imageTestCase.m_expectedLocked );
BOOST_CHECK_EQUAL( image.GetPosition(), imageTestCase.m_expectedPos * 1000000 );
BOOST_CHECK_CLOSE( refImage.GetImageScale(), imageTestCase.m_expectedScale, 1e-6 );
const BITMAP_BASE& bitmap = refImage.GetImage();
BOOST_CHECK_EQUAL( bitmap.GetSizePixels(), imageTestCase.m_expectedPixelSize );
}
};
for( const REFERENCE_IMAGE_LOAD_BOARD_TEST_CASE& testCase : testCases )
{
const auto doBoardTest = [&]( const BOARD& aBoard )
{
for( const REFERENCE_IMAGE_LOAD_TEST_CASE& imageTestCase : testCase.m_imageCases )
{
BOOST_TEST_MESSAGE(
"Checking for image with UUID: " << imageTestCase.m_imageUuid.AsString() );
const auto& image =
static_cast<PCB_REFERENCE_IMAGE&>( KI_TEST::RequireBoardItemWithTypeAndId(
aBoard, PCB_REFERENCE_IMAGE_T, imageTestCase.m_imageUuid ) );
const REFERENCE_IMAGE& refImage = image.GetReferenceImage();
BOOST_CHECK_EQUAL( image.IsLocked(), imageTestCase.m_expectedLocked );
BOOST_CHECK_EQUAL( image.GetPosition(), imageTestCase.m_expectedPos * 1000000 );
BOOST_CHECK_CLOSE( refImage.GetImageScale(), imageTestCase.m_expectedScale, 1e-6 );
const BITMAP_BASE& bitmap = refImage.GetImage();
BOOST_CHECK_EQUAL( bitmap.GetSizePixels(), imageTestCase.m_expectedPixelSize );
}
};
KI_TEST::LoadAndTestBoardFile( testCase.m_boardFileRelativePath, true, doBoardTest );
}
KI_TEST::LoadAndTestBoardFile( testCase.m_BoardFileRelativePath, true, doBoardTest,
testCase.m_ExpectedBoardVersion );
}

View File

@ -24,12 +24,16 @@
#include <filesystem>
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/data/test_case.hpp>
#include <pcbnew_utils/board_test_utils.h>
#include <pcbnew_utils/board_file_utils.h>
#include <board.h>
#include <settings/settings_manager.h>
namespace
{
struct SAVE_LOAD_TEST_FIXTURE
{
SAVE_LOAD_TEST_FIXTURE() :
@ -40,32 +44,31 @@ struct SAVE_LOAD_TEST_FIXTURE
std::unique_ptr<BOARD> m_board;
};
const std::vector<wxString> RegressionSaveLoadTests_tests = {
"issue18",
"issue832",
"issue2568",
"issue5313",
"issue5854",
"issue6260",
"issue6945",
"issue7267",
"padstacks",
"padstacks_normal",
"padstacks_complex"
/* "issue8003", */ // issue8003 is flaky on some platforms
};
BOOST_FIXTURE_TEST_CASE( RegressionSaveLoadTests, SAVE_LOAD_TEST_FIXTURE )
}; // namespace
BOOST_DATA_TEST_CASE_F( SAVE_LOAD_TEST_FIXTURE, RegressionSaveLoadTests,
boost::unit_test::data::make( RegressionSaveLoadTests_tests ), relPath )
{
std::vector<wxString> tests = { "issue18",
"issue832",
"issue2568",
"issue5313",
"issue5854",
"issue6260",
"issue6945",
"issue7267",
"padstacks",
"padstacks_normal",
"padstacks_complex"
/* "issue8003" */ }; // issue8003 is flaky on some platforms
const std::filesystem::path savePath = std::filesystem::temp_directory_path() / "group_saveload_tst.kicad_pcb";
auto savePath = std::filesystem::temp_directory_path() / "group_saveload_tst.kicad_pcb";
KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
KI_TEST::DumpBoardToFile( *m_board.get(), savePath.string() );
for( const wxString& relPath : tests )
{
BOOST_TEST_CONTEXT( relPath )
KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
KI_TEST::DumpBoardToFile( *m_board.get(), savePath.string() );
std::unique_ptr<BOARD> board2 = KI_TEST::ReadBoardFromFileOrStream( savePath.string() );
}
std::unique_ptr<BOARD> board2 = KI_TEST::ReadBoardFromFileOrStream( savePath.string() );
}

View File

@ -22,6 +22,8 @@
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/data/test_case.hpp>
#include <pcbnew_utils/board_test_utils.h>
#include <board.h>
#include <board_commit.h>
@ -33,6 +35,8 @@
#include <settings/settings_manager.h>
#include <tool/tool_manager.h>
namespace
{
struct TRACK_CLEANER_TEST_FIXTURE
{
TRACK_CLEANER_TEST_FIXTURE() :
@ -54,175 +58,183 @@ struct TEST_DESCRIPTION
bool m_TracksInPads;
bool m_DanglingVias;
int m_Expected;
friend std::ostream& operator<<( std::ostream& os, const TEST_DESCRIPTION& aDesc )
{
os << aDesc.m_File;
return os;
}
};
BOOST_FIXTURE_TEST_CASE( FailedToCleanRegressionTests, TRACK_CLEANER_TEST_FIXTURE )
/*
* This one ensures that certain cleanup items are indeed found and marked for cleanup.
*/
std::vector<TEST_DESCRIPTION> FailedToCleanRegressionTests_tests =
{
/*
* This one ensures that certain cleanup items are indeed found and marked for cleanup.
*/
std::vector<TEST_DESCRIPTION> tests =
// short redundant redundant dangling tracks dangling
// circuits vias tracks tracks in pads vias expected
{ "issue2904", false, false, false, true, false, false, 9 },
{ "issue5093", false, false, false, false, true, false, 117 },
{ "issue7004", false, true, false, false, false, true, 25 },
{ "issue8883", true, true, true, true, false, true, 81 },
{ "issue10916", false, false, true, false, false, false, 0 },
{ "issue19574", false, false, true, false, false, false, 2 },
};
} // namespace
BOOST_DATA_TEST_CASE_F( TRACK_CLEANER_TEST_FIXTURE, FailedToCleanRegressionTests,
boost::unit_test::data::make( FailedToCleanRegressionTests_tests ), entry )
{
KI_TEST::LoadBoard( m_settingsManager, entry.m_File, m_board );
KI_TEST::FillZones( m_board.get() );
m_board->GetConnectivity()->RecalculateRatsnest();
m_board->UpdateRatsnestExclusions();
TOOL_MANAGER toolMgr;
toolMgr.SetEnvironment( m_board.get(), nullptr, nullptr, nullptr, nullptr );
KI_TEST::DUMMY_TOOL* dummyTool = new KI_TEST::DUMMY_TOOL();
toolMgr.RegisterTool( dummyTool );
BOARD_COMMIT commit( dummyTool );
TRACKS_CLEANER cleaner( m_board.get(), commit );
std::vector< std::shared_ptr<CLEANUP_ITEM> > dryRunItems;
std::vector< std::shared_ptr<CLEANUP_ITEM> > realRunItems;
cleaner.CleanupBoard( true, &dryRunItems, entry.m_Shorts,
entry.m_RedundantVias,
entry.m_RedundantTracks,
entry.m_DanglingTracks,
entry.m_TracksInPads,
entry.m_DanglingVias );
cleaner.CleanupBoard( true, &realRunItems, entry.m_Shorts,
entry.m_RedundantVias,
entry.m_RedundantTracks,
entry.m_DanglingTracks,
entry.m_TracksInPads,
entry.m_DanglingVias );
if( dryRunItems.size() == entry.m_Expected && realRunItems.size() == entry.m_Expected )
{
// short redundant redundant dangling tracks dangling
// circuits vias tracks tracks in pads vias expected
{ "issue2904", false, false, false, true, false, false, 9 },
{ "issue5093", false, false, false, false, true, false, 117 },
{ "issue7004", false, true, false, false, false, true, 25 },
{ "issue8883", true, true, true, true, false, true, 81 },
{ "issue10916", false, false, true, false, false, false, 0 },
{ "issue19574", false, false, true, false, false, false, 2 }
};
for( const TEST_DESCRIPTION& entry : tests )
BOOST_CHECK_EQUAL( 1, 1 ); // quiet "did not check any assertions" warning
BOOST_TEST_MESSAGE( wxString::Format( "Track cleaner regression: %s, passed",
entry.m_File ) );
}
else
{
KI_TEST::LoadBoard( m_settingsManager, entry.m_File, m_board );
KI_TEST::FillZones( m_board.get() );
m_board->GetConnectivity()->RecalculateRatsnest();
m_board->UpdateRatsnestExclusions();
BOOST_CHECK_EQUAL( dryRunItems.size(), entry.m_Expected );
BOOST_CHECK_EQUAL( realRunItems.size(), entry.m_Expected );
TOOL_MANAGER toolMgr;
toolMgr.SetEnvironment( m_board.get(), nullptr, nullptr, nullptr, nullptr );
UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::INCHES );
KI_TEST::DUMMY_TOOL* dummyTool = new KI_TEST::DUMMY_TOOL();
toolMgr.RegisterTool( dummyTool );
std::map<KIID, EDA_ITEM*> itemMap;
m_board->FillItemMap( itemMap );
BOARD_COMMIT commit( dummyTool );
TRACKS_CLEANER cleaner( m_board.get(), commit );
std::vector< std::shared_ptr<CLEANUP_ITEM> > dryRunItems;
std::vector< std::shared_ptr<CLEANUP_ITEM> > realRunItems;
cleaner.CleanupBoard( true, &dryRunItems, entry.m_Shorts,
entry.m_RedundantVias,
entry.m_RedundantTracks,
entry.m_DanglingTracks,
entry.m_TracksInPads,
entry.m_DanglingVias );
cleaner.CleanupBoard( true, &realRunItems, entry.m_Shorts,
entry.m_RedundantVias,
entry.m_RedundantTracks,
entry.m_DanglingTracks,
entry.m_TracksInPads,
entry.m_DanglingVias );
if( dryRunItems.size() == entry.m_Expected && realRunItems.size() == entry.m_Expected )
for( const std::shared_ptr<CLEANUP_ITEM>& item : realRunItems )
{
BOOST_CHECK_EQUAL( 1, 1 ); // quiet "did not check any assertions" warning
BOOST_TEST_MESSAGE( wxString::Format( "Track cleaner regression: %s, passed",
entry.m_File ) );
BOOST_TEST_MESSAGE( item->ShowReport( &unitsProvider, RPT_SEVERITY_ERROR,
itemMap ) );
}
else
{
BOOST_CHECK_EQUAL( dryRunItems.size(), entry.m_Expected );
BOOST_CHECK_EQUAL( realRunItems.size(), entry.m_Expected );
UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::INCHES );
std::map<KIID, EDA_ITEM*> itemMap;
m_board->FillItemMap( itemMap );
for( const std::shared_ptr<CLEANUP_ITEM>& item : realRunItems )
{
BOOST_TEST_MESSAGE( item->ShowReport( &unitsProvider, RPT_SEVERITY_ERROR,
itemMap ) );
}
BOOST_ERROR( wxString::Format( "Track cleaner regression: %s, failed",
entry.m_File ) );
}
BOOST_ERROR( wxString::Format( "Track cleaner regression: %s, failed",
entry.m_File ) );
}
}
BOOST_FIXTURE_TEST_CASE( TrackCleanerRegressionTests, TRACK_CLEANER_TEST_FIXTURE )
namespace
{
/*
* This one just makes sure that the dry-run counts agree with the "real" counts, and that
* the cleaning doesn't produce any connectivity changes.
*/
std::vector<wxString> tests = { //"issue832",
"issue4257",
//"issue8909"
};
for( const wxString& relPath : tests )
{
KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
KI_TEST::FillZones( m_board.get() );
m_board->GetConnectivity()->RecalculateRatsnest();
m_board->UpdateRatsnestExclusions();
std::vector<wxString> TrackCleanerRegressionTests_tests = {
//"issue832",
"issue4257",
//"issue8909"
};
TOOL_MANAGER toolMgr;
toolMgr.SetEnvironment( m_board.get(), nullptr, nullptr, nullptr, nullptr );
} // namespace
KI_TEST::DUMMY_TOOL* dummyTool = new KI_TEST::DUMMY_TOOL();
toolMgr.RegisterTool( dummyTool );
/*
* This one just makes sure that the dry-run counts agree with the "real" counts, and that
* the cleaning doesn't produce any connectivity changes.
*/
BOOST_DATA_TEST_CASE_F( TRACK_CLEANER_TEST_FIXTURE, TrackCleanerRegressionTests,
boost::unit_test::data::make( TrackCleanerRegressionTests_tests ), relPath )
{
KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
KI_TEST::FillZones( m_board.get() );
m_board->GetConnectivity()->RecalculateRatsnest();
m_board->UpdateRatsnestExclusions();
BOARD_COMMIT commit( dummyTool );
TRACKS_CLEANER cleaner( m_board.get(), commit );
std::vector< std::shared_ptr<CLEANUP_ITEM> > dryRunItems;
std::vector< std::shared_ptr<CLEANUP_ITEM> > realRunItems;
TOOL_MANAGER toolMgr;
toolMgr.SetEnvironment( m_board.get(), nullptr, nullptr, nullptr, nullptr );
cleaner.CleanupBoard( true, &dryRunItems, true, // short circuits
true, // redundant vias
true, // redundant tracks
true, // dangling tracks
true, // tracks in pads
true ); // dangling vias
KI_TEST::DUMMY_TOOL* dummyTool = new KI_TEST::DUMMY_TOOL();
toolMgr.RegisterTool( dummyTool );
cleaner.CleanupBoard( true, &realRunItems, true, // short circuits
true, // redundant vias
true, // redundant tracks
true, // dangling tracks
true, // tracks in pads
true ); // dangling vias
BOARD_COMMIT commit( dummyTool );
TRACKS_CLEANER cleaner( m_board.get(), commit );
std::vector< std::shared_ptr<CLEANUP_ITEM> > dryRunItems;
std::vector< std::shared_ptr<CLEANUP_ITEM> > realRunItems;
BOOST_CHECK_EQUAL( dryRunItems.size(), realRunItems.size() );
cleaner.CleanupBoard( true, &dryRunItems, true, // short circuits
true, // redundant vias
true, // redundant tracks
true, // dangling tracks
true, // tracks in pads
true ); // dangling vias
std::vector<DRC_ITEM> violations;
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
cleaner.CleanupBoard( true, &realRunItems, true, // short circuits
true, // redundant vias
true, // redundant tracks
true, // dangling tracks
true, // tracks in pads
true ); // dangling vias
// Disable some DRC tests not useful in this testcase
bds.m_DRCSeverities[ DRCE_LIB_FOOTPRINT_ISSUES ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_LIB_FOOTPRINT_MISMATCH ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_COPPER_SLIVER ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_STARVED_THERMAL ] = SEVERITY::RPT_SEVERITY_IGNORE;
// Also disable this test: for some reason it generate an exception inside this QA
// test ans it is useless
bds.m_DRCSeverities[ DRCE_SOLDERMASK_BRIDGE ] = SEVERITY::RPT_SEVERITY_IGNORE;
BOOST_CHECK_EQUAL( dryRunItems.size(), realRunItems.size() );
bds.m_DRCEngine->SetViolationHandler(
[&]( const std::shared_ptr<DRC_ITEM>& aItem, VECTOR2I aPos, int aLayer,
DRC_CUSTOM_MARKER_HANDLER* aCustomHandler )
{
if( aItem->GetErrorCode() == DRCE_UNCONNECTED_ITEMS )
violations.push_back( *aItem );
} );
std::vector<DRC_ITEM> violations;
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
bds.m_DRCEngine->RunTests( EDA_UNITS::MILLIMETRES, true, false );
// Disable some DRC tests not useful in this testcase
bds.m_DRCSeverities[ DRCE_LIB_FOOTPRINT_ISSUES ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_LIB_FOOTPRINT_MISMATCH ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_COPPER_SLIVER ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_STARVED_THERMAL ] = SEVERITY::RPT_SEVERITY_IGNORE;
// Also disable this test: for some reason it generate an exception inside this QA
// test ans it is useless
bds.m_DRCSeverities[ DRCE_SOLDERMASK_BRIDGE ] = SEVERITY::RPT_SEVERITY_IGNORE;
if( violations.empty() )
{
BOOST_TEST_MESSAGE( wxString::Format( "Track cleaner regression: %s, passed",
relPath ) );
}
else
{
UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::INCHES );
std::map<KIID, EDA_ITEM*> itemMap;
m_board->FillItemMap( itemMap );
for( const DRC_ITEM& item : violations )
bds.m_DRCEngine->SetViolationHandler(
[&]( const std::shared_ptr<DRC_ITEM>& aItem, VECTOR2I aPos, int aLayer,
DRC_CUSTOM_MARKER_HANDLER* aCustomHandler )
{
BOOST_TEST_MESSAGE( item.ShowReport( &unitsProvider, RPT_SEVERITY_ERROR,
itemMap ) );
}
if( aItem->GetErrorCode() == DRCE_UNCONNECTED_ITEMS )
violations.push_back( *aItem );
} );
BOOST_ERROR( wxString::Format( "Track cleaner regression: %s, failed",
relPath ) );
bds.m_DRCEngine->RunTests( EDA_UNITS::MILLIMETRES, true, false );
if( violations.empty() )
{
BOOST_TEST_MESSAGE( wxString::Format( "Track cleaner regression: %s, passed",
relPath ) );
}
else
{
UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::INCHES );
std::map<KIID, EDA_ITEM*> itemMap;
m_board->FillItemMap( itemMap );
for( const DRC_ITEM& item : violations )
{
BOOST_TEST_MESSAGE( item.ShowReport( &unitsProvider, RPT_SEVERITY_ERROR,
itemMap ) );
}
BOOST_ERROR( wxString::Format( "Track cleaner regression: %s, failed",
relPath ) );
}
}

View File

@ -22,6 +22,8 @@
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/data/test_case.hpp>
#include <pcbnew_utils/board_test_utils.h>
#include <board.h>
#include <board_design_settings.h>
@ -33,6 +35,8 @@
#include <settings/settings_manager.h>
namespace {
struct TRIANGULATE_TEST_FIXTURE
{
TRIANGULATE_TEST_FIXTURE() :
@ -43,70 +47,68 @@ struct TRIANGULATE_TEST_FIXTURE
std::unique_ptr<BOARD> m_board;
};
const std::vector<wxString> RegressionTriangulationTests_tests = {
"issue2568",
"issue5313",
"issue5320",
"issue5567",
"issue5830",
"issue6039",
"issue6260",
"issue7086",
"issue14294",
"issue17559",
"bad_triangulation_case"
};
BOOST_FIXTURE_TEST_CASE( RegressionTriangulationTests, TRIANGULATE_TEST_FIXTURE )
}; // namespace
BOOST_DATA_TEST_CASE_F( TRIANGULATE_TEST_FIXTURE, RegressionTriangulationTests,
boost::unit_test::data::make( RegressionTriangulationTests_tests ), relPath )
{
std::vector<wxString> tests = {
"issue2568",
"issue5313",
"issue5320",
"issue5567",
"issue5830",
"issue6039",
"issue6260",
"issue7086",
"issue14294",
"issue17559",
"bad_triangulation_case"
};
KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
for( const wxString& relPath : tests )
for( ZONE* zone : m_board->Zones() )
{
KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
if( zone->GetIsRuleArea() )
continue;
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
for( ZONE* zone : m_board->Zones() )
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq())
{
if( zone->GetIsRuleArea() )
auto poly = zone->GetFilledPolysList( layer );
if( poly->OutlineCount() == 0 )
continue;
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq())
BOOST_CHECK_MESSAGE(
poly->IsTriangulationUpToDate(),
"Triangulation invalid for " + relPath + " layer " + LayerName( layer )
+ " zone " + zone->GetFriendlyName() + " net '" + zone->GetNetname()
+ "' with " + std::to_string( poly->OutlineCount() )
+ " polygons at position " + std::to_string( poly->BBox().Centre().x )
+ ", " + std::to_string( poly->BBox().Centre().y ) );
double area = poly->Area();
double tri_area = 0.0;
for( int ii = 0; ii < poly->TriangulatedPolyCount(); ii++ )
{
auto poly = zone->GetFilledPolysList( layer );
const auto tri_poly = poly->TriangulatedPolygon( ii );
if( poly->OutlineCount() == 0 )
continue;
BOOST_CHECK_MESSAGE(
poly->IsTriangulationUpToDate(),
"Triangulation invalid for " + relPath + " layer " + LayerName( layer )
+ " zone " + zone->GetFriendlyName() + " net '" + zone->GetNetname()
+ "' with " + std::to_string( poly->OutlineCount() )
+ " polygons at position " + std::to_string( poly->BBox().Centre().x )
+ ", " + std::to_string( poly->BBox().Centre().y ) );
double area = poly->Area();
double tri_area = 0.0;
for( int ii = 0; ii < poly->TriangulatedPolyCount(); ii++ )
{
const auto tri_poly = poly->TriangulatedPolygon( ii );
for( const auto& tri : tri_poly->Triangles() )
tri_area += tri.Area();
}
double diff = std::abs( area - tri_area );
// The difference should be less than 1e-3 square mm
BOOST_CHECK_MESSAGE( diff < 1e9,
"Triangulation area mismatch in " + relPath + " layer "
+ LayerName( layer )
+ " difference: " + std::to_string( diff ) );
for( const auto& tri : tri_poly->Triangles() )
tri_area += tri.Area();
}
double diff = std::abs( area - tri_area );
// The difference should be less than 1e-3 square mm
BOOST_CHECK_MESSAGE( diff < 1e9,
"Triangulation area mismatch in " + relPath + " layer "
+ LayerName( layer )
+ " difference: " + std::to_string( diff ) );
}
}
}

View File

@ -22,6 +22,8 @@
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/data/test_case.hpp>
#include <pcbnew_utils/board_test_utils.h>
#include <board.h>
#include <board_design_settings.h>
@ -166,142 +168,143 @@ BOOST_FIXTURE_TEST_CASE( NotchedZones, ZONE_FILL_TEST_FIXTURE )
}
BOOST_FIXTURE_TEST_CASE( RegressionZoneFillTests, ZONE_FILL_TEST_FIXTURE )
static const std::vector<wxString> RegressionZoneFillTests_tests = {
"issue18",
"issue2568",
"issue3812",
"issue5102",
"issue5313",
"issue5320",
"issue5567",
"issue5830",
"issue6039",
"issue6260",
"issue6284",
"issue7086",
"issue14294", // Bad Clipper2 fill
"fill_bad" // Missing zone clearance expansion
};
BOOST_DATA_TEST_CASE_F( ZONE_FILL_TEST_FIXTURE, RegressionZoneFillTests,
boost::unit_test::data::make( RegressionZoneFillTests_tests ), relPath )
{
std::vector<wxString> tests = { "issue18",
"issue2568",
"issue3812",
"issue5102",
"issue5313",
"issue5320",
"issue5567",
"issue5830",
"issue6039",
"issue6260",
"issue6284",
"issue7086",
"issue14294", // Bad Clipper2 fill
"fill_bad" // Missing zone clearance expansion
};
KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
for( const wxString& relPath : tests )
{
KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
KI_TEST::FillZones( m_board.get() );
KI_TEST::FillZones( m_board.get() );
std::vector<DRC_ITEM> violations;
std::vector<DRC_ITEM> violations;
bds.m_DRCEngine->SetViolationHandler(
[&]( const std::shared_ptr<DRC_ITEM>& aItem, VECTOR2I aPos, int aLayer,
DRC_CUSTOM_MARKER_HANDLER* aCustomHandler )
{
if( aItem->GetErrorCode() == DRCE_CLEARANCE )
violations.push_back( *aItem );
} );
bds.m_DRCEngine->RunTests( EDA_UNITS::MILLIMETRES, true, false );
if( violations.empty() )
{
BOOST_CHECK_EQUAL( 1, 1 ); // quiet "did not check any assertions" warning
BOOST_TEST_MESSAGE( (const char*)(wxString::Format( "Zone fill regression: %s passed", relPath ).utf8_str()) );
}
else
{
UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::INCHES );
std::map<KIID, EDA_ITEM*> itemMap;
m_board->FillItemMap( itemMap );
for( const DRC_ITEM& item : violations )
bds.m_DRCEngine->SetViolationHandler(
[&]( const std::shared_ptr<DRC_ITEM>& aItem, VECTOR2I aPos, int aLayer,
DRC_CUSTOM_MARKER_HANDLER* aCustomHandler )
{
BOOST_TEST_MESSAGE( item.ShowReport( &unitsProvider, RPT_SEVERITY_ERROR,
itemMap ) );
}
if( aItem->GetErrorCode() == DRCE_CLEARANCE )
violations.push_back( *aItem );
} );
BOOST_ERROR( (const char*)(wxString::Format( "Zone fill regression: %s failed", relPath ).utf8_str()) );
bds.m_DRCEngine->RunTests( EDA_UNITS::MILLIMETRES, true, false );
if( violations.empty() )
{
BOOST_CHECK_EQUAL( 1, 1 ); // quiet "did not check any assertions" warning
BOOST_TEST_MESSAGE( (const char*)(wxString::Format( "Zone fill regression: %s passed", relPath ).utf8_str()) );
}
else
{
UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::INCHES );
std::map<KIID, EDA_ITEM*> itemMap;
m_board->FillItemMap( itemMap );
for( const DRC_ITEM& item : violations )
{
BOOST_TEST_MESSAGE( item.ShowReport( &unitsProvider, RPT_SEVERITY_ERROR,
itemMap ) );
}
BOOST_ERROR( (const char*)(wxString::Format( "Zone fill regression: %s failed", relPath ).utf8_str()) );
}
}
BOOST_FIXTURE_TEST_CASE( RegressionSliverZoneFillTests, ZONE_FILL_TEST_FIXTURE )
static const std::vector<wxString> RegressionSliverZoneFillTests_tests = {
"issue16182" // Slivers
};
BOOST_DATA_TEST_CASE_F( ZONE_FILL_TEST_FIXTURE, RegressionSliverZoneFillTests,
boost::unit_test::data::make( RegressionSliverZoneFillTests_tests ), relPath )
{
std::vector<wxString> tests = { "issue16182" // Slivers
};
KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
for( const wxString& relPath : tests )
{
KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
KI_TEST::FillZones( m_board.get() );
KI_TEST::FillZones( m_board.get() );
std::vector<DRC_ITEM> violations;
std::vector<DRC_ITEM> violations;
bds.m_DRCEngine->SetViolationHandler(
[&]( const std::shared_ptr<DRC_ITEM>& aItem, VECTOR2I aPos, int aLayer,
DRC_CUSTOM_MARKER_HANDLER* aCustomHandler )
{
if( aItem->GetErrorCode() == DRCE_COPPER_SLIVER )
violations.push_back( *aItem );
} );
bds.m_DRCEngine->RunTests( EDA_UNITS::MILLIMETRES, true, false );
if( violations.empty() )
{
BOOST_CHECK_EQUAL( 1, 1 ); // quiet "did not check any assertions" warning
BOOST_TEST_MESSAGE( (const char*)(wxString::Format( "Zone fill copper sliver regression: %s passed", relPath ).utf8_str()) );
}
else
{
UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::INCHES );
std::map<KIID, EDA_ITEM*> itemMap;
m_board->FillItemMap( itemMap );
for( const DRC_ITEM& item : violations )
bds.m_DRCEngine->SetViolationHandler(
[&]( const std::shared_ptr<DRC_ITEM>& aItem, VECTOR2I aPos, int aLayer,
DRC_CUSTOM_MARKER_HANDLER* aCustomHandler )
{
BOOST_TEST_MESSAGE( item.ShowReport( &unitsProvider, RPT_SEVERITY_ERROR,
itemMap ) );
}
if( aItem->GetErrorCode() == DRCE_COPPER_SLIVER )
violations.push_back( *aItem );
} );
BOOST_ERROR( (const char*)(wxString::Format( "Zone fill copper sliver regression: %s failed", relPath ).utf8_str()) );
}
}
}
bds.m_DRCEngine->RunTests( EDA_UNITS::MILLIMETRES, true, false );
BOOST_FIXTURE_TEST_CASE( RegressionTeardropFill, ZONE_FILL_TEST_FIXTURE )
{
std::vector<std::pair<wxString,int>> tests = { { "teardrop_issue_JPC2", 5 } // Arcs with teardrops connecting to pads
};
for( auto& [ relPath, count ] : tests )
if( violations.empty() )
{
KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
BOOST_CHECK_EQUAL( 1, 1 ); // quiet "did not check any assertions" warning
BOOST_TEST_MESSAGE( (const char*)(wxString::Format( "Zone fill copper sliver regression: %s passed", relPath ).utf8_str()) );
}
else
{
UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::INCHES );
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
std::map<KIID, EDA_ITEM*> itemMap;
m_board->FillItemMap( itemMap );
KI_TEST::FillZones( m_board.get() );
int zoneCount = 0;
for( ZONE* zone : m_board->Zones() )
for( const DRC_ITEM& item : violations )
{
if( zone->IsTeardropArea() )
zoneCount++;
BOOST_TEST_MESSAGE( item.ShowReport( &unitsProvider, RPT_SEVERITY_ERROR,
itemMap ) );
}
BOOST_CHECK_MESSAGE( zoneCount == count, "Expected " << count << " teardrop zones in "
<< relPath << ", found "
<< zoneCount );
BOOST_ERROR( (const char*)(wxString::Format( "Zone fill copper sliver regression: %s failed", relPath ).utf8_str()) );
}
}
static const std::vector<std::pair<wxString,int>> RegressionTeardropFill_tests = {
{ "teardrop_issue_JPC2", 5 }, // Arcs with teardrops connecting to pads
};
BOOST_DATA_TEST_CASE_F( ZONE_FILL_TEST_FIXTURE, RegressionTeardropFill,
boost::unit_test::data::make( RegressionTeardropFill_tests ), test )
{
const wxString& relPath = test.first;
const int count = test.second;
KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
KI_TEST::FillZones( m_board.get() );
int zoneCount = 0;
for( ZONE* zone : m_board->Zones() )
{
if( zone->IsTeardropArea() )
zoneCount++;
}
BOOST_CHECK_MESSAGE( zoneCount == count, "Expected " << count << " teardrop zones in "
<< relPath << ", found "
<< zoneCount );
}