48 if( aJson.is_number() )
50 int v = aJson.get<
int>();
54 else if( aJson.is_object() )
56 if( aJson.contains(
"exact" ) )
58 int v = aJson[
"exact"];
64 if( aJson.contains(
"min" ) )
65 matcher.
m_min = aJson[
"min"];
67 if( aJson.contains(
"max" ) )
68 matcher.
m_max = aJson[
"max"];
73 throw std::runtime_error(
"Invalid count expectation: " + aJson.dump() );
82 matcher.
m_min = aValue;
83 matcher.
m_max = aValue;
87 void Test(
int aActual )
const
99 return "exactly " + std::to_string( *
m_min );
104 desc +=
"at least " + std::to_string( *
m_min );
111 desc +=
"at most " + std::to_string( *
m_max );
134 static bool matchPredicate(
const std::string& aStr,
const std::string& aPattern )
136 return wxString( aStr ).Matches( aPattern );
169 return std::string(
"Footprint: " ) + (
m_Count.has_value() ?
m_Count->Describe() :
"N/A" );
181 static bool nameMatches(
const std::string& aName,
const std::string& aPattern )
183 return wxString( aName ).Matches( aPattern );
188 std::vector<const NETINFO_ITEM*> matches;
195 matches.push_back( net );
204 if(
nameMatches( net->GetNetname().ToStdString(), pattern ) )
206 matches.push_back( net );
217 wxASSERT(
m_Count.has_value() );
245 m_Count->Test(
static_cast<int>( matches.size() ) );
253 const auto& netMatchesPattern = [&](
const NETINFO_ITEM* n )
255 return nameMatches( n->GetNetname().ToStdString(), pattern );
258 bool found = std::any_of( matches.begin(), matches.end(), netMatchesPattern );
270 std::string desc =
"Net";
285 desc +=
" ['" + joined +
"']";
289 desc +=
" count: " +
m_Count->Describe();
319 std::vector<std::string> actualNames;
322 for(
const auto& layer : cuLayers )
324 actualNames.push_back( aBrd.
GetLayerName( layer ).ToStdString() );
329 for(
size_t i = 0; i <
m_CuNames.size(); ++i )
341 return std::string(
"Layers: " ) + (
m_CuCount.has_value() ?
m_CuCount->Describe() :
"N/A" );
357 for(
const auto& drawing : aBrd.
Drawings() )
367 const int actualRadius = shape.
GetRadius();
370 bool layerMatches =
true;
373 wxString actualLayerName = aBrd.
GetLayerName( actualLayer );
380 if( positionMatches && radiusMatches && layerMatches )
393 std::ostringstream ss;
412 auto footprintExpectation = std::make_unique<FOOTPRINT_EXPECTATION>();
414 if( aExpectationEntry.contains(
"count" ) )
416 const auto& countEntry = aExpectationEntry[
"count"];
418 footprintExpectation->m_Count = countMatcher;
421 return footprintExpectation;
427 std::vector<std::string>
result;
429 if( aJson.is_string() )
431 result.push_back( aJson );
433 else if( aJson.is_array() )
435 for(
const auto& entry : aJson )
437 if( !entry.is_string() )
439 throw std::runtime_error(
"Expected a string or an array of strings" );
442 result.push_back( entry );
447 throw std::runtime_error(
"Expected a string or an array of strings" );
456 auto netExpectation = std::make_unique<NET_EXPECTATION>();
458 if( aExpectationEntry.contains(
"count" ) )
460 const auto& countEntry = aExpectationEntry[
"count"];
464 if( aExpectationEntry.contains(
"name" ) )
466 const auto& expectedNetName = aExpectationEntry[
"name"];
467 netExpectation->m_NamePatterns =
getStringArray( expectedNetName );
470 return netExpectation;
476 auto layerExpectation = std::make_unique<LAYER_EXPECTATION>();
478 if( aExpectationEntry.contains(
"cuNames" ) )
480 const auto& cuNamesEntry = aExpectationEntry[
"cuNames"];
481 std::vector<std::string> cuNames =
getStringArray( cuNamesEntry );
482 layerExpectation->m_CuNames = std::move( cuNames );
485 if( aExpectationEntry.contains(
"count" ) )
487 const auto& countEntry = aExpectationEntry[
"cuCount"];
490 else if( layerExpectation->m_CuNames.size() > 0 )
493 layerExpectation->m_CuCount =
INT_MATCHER::Exact(
static_cast<int>( layerExpectation->m_CuNames.size() ) );
496 return layerExpectation;
508 if( aJson.is_number_integer() )
512 else if( aJson.is_string() )
514 const std::string& dimStr = aJson.get<std::string>();
519 throw std::runtime_error(
"Expected dimension to be an integer or a string with units" );
523static int parsePcbDim(
const nlohmann::json& aJson,
const std::string& aFieldName )
525 if( !aJson.contains( aFieldName ) )
527 throw std::runtime_error(
"Expectation entry must have a '" + aFieldName +
"' field" );
536 if( !aJson.contains( aFieldName ) )
538 throw std::runtime_error(
"Expectation entry must have a '" + aFieldName +
"' field" );
541 const auto& field = aJson[aFieldName];
542 if( !field.is_array() || field.size() != 2 )
544 throw std::runtime_error(
"Expectation entry must have a '" + aFieldName
545 +
"' field with an array of 2 entries" );
557 auto circleExpectation = std::make_unique<CIRCLE_EXPECTATION>();
559 circleExpectation->m_Position =
parsePosition( aExpectationEntry,
"position" );
560 circleExpectation->m_Radius =
parsePcbDim( aExpectationEntry,
"radius" );
562 if( !aExpectationEntry.contains(
"layer" ) || !aExpectationEntry[
"layer"].is_string() )
564 throw std::runtime_error(
"Circle expectation must have a 'layer' field with a string value" );
567 circleExpectation->m_LayerName = wxString( aExpectationEntry[
"layer"].get<std::string>() );
569 return circleExpectation;
575 const auto& shapeEntry = aExpectationEntry[
"shape"];
577 if( !shapeEntry.is_string() )
579 throw std::runtime_error(
"Graphic expectation must have a string 'shape' field" );
582 const std::string shape = shapeEntry.get<std::string>();
584 if( shape ==
"circle" )
589 throw std::runtime_error(
"Unsupported graphic shape: " + shape );
594 const nlohmann::json& aBrdExpectations )
596 std::unique_ptr<BOARD_EXPECTATION_TEST>
test = std::make_unique<BOARD_EXPECTATION_TEST>( aBrdName );
598 if( !aBrdExpectations.is_array() )
600 throw std::runtime_error(
"Board expectations for board " + aBrdName +
" are not a valid JSON object" );
603 for(
const auto& expectationEntry : aBrdExpectations )
605 if( !expectationEntry.is_object() )
607 throw std::runtime_error(
"Expectation entry for board " + aBrdName +
" is not a valid JSON object" );
610 if( !expectationEntry.contains(
"type" ) || !expectationEntry[
"type"].is_string() )
612 throw std::runtime_error(
"Expectation entry for board " + aBrdName
613 +
" must have a string field named 'type'" );
616 const std::string expectationType = expectationEntry[
"type"];
618 std::unique_ptr<BOARD_EXPECTATION> expectation;
620 if( expectationType ==
"footprint" )
624 else if( expectationType ==
"net" )
628 else if( expectationType ==
"layers" )
632 else if( expectationType ==
"graphic" )
638 throw std::runtime_error(
"Unsupported expectation type '" + expectationType +
"' for board " + aBrdName );
642 test->m_expectations.push_back( std::move( expectation ) );
653 BOOST_TEST_CONTEXT( wxString::Format(
"Checking expectation of type %s", expectation->GetName() ) )
655 expectation->RunTest( aBrd );
constexpr EDA_IU_SCALE pcbIUScale
static std::unique_ptr< BOARD_EXPECTATION > createFootprintExpectation(const nlohmann::json &aExpectationEntry)
static std::unique_ptr< BOARD_EXPECTATION > createCircleExpectation(const nlohmann::json &aExpectationEntry)
static std::unique_ptr< BOARD_EXPECTATION > createNetExpectation(const nlohmann::json &aExpectationEntry)
static VECTOR2I parsePosition(const nlohmann::json &aJson, const std::string &aFieldName)
static std::unique_ptr< BOARD_EXPECTATION > createLayerExpectation(const nlohmann::json &aExpectationEntry)
static int parsePcbDim(const nlohmann::json &aJson)
Parse a dimension from JSON, which can be either an integer (in mm) or a string with units (e....
static std::vector< std::string > getStringArray(const nlohmann::json &aJson)
static std::unique_ptr< BOARD_EXPECTATION > createGraphicExpectation(const nlohmann::json &aExpectationEntry)
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Information pertinent to a Pcbnew printed circuit board.
const NETINFO_LIST & GetNetInfo() const
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
int GetCopperLayerCount() const
const FOOTPRINTS & Footprints() const
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
unsigned GetNetCount() const
const DRAWINGS & Drawings() const
std::optional< std::string > m_LayerName
void RunTest(const BOARD &aBrd) const override
std::optional< int > m_Radius
std::string GetName() const override
std::optional< VECTOR2I > m_Position
Simple binary expectation that checks if an integer value meets the expectation (exact,...
static INT_MATCHER Exact(int aValue)
std::string Describe() const
std::optional< int > m_min
std::optional< int > m_max
static INT_MATCHER FromJson(const nlohmann::json &aJson)
void Test(int aActual) const
std::vector< std::unique_ptr< BOARD_EXPECTATION > > m_expectations
static std::unique_ptr< BOARD_EXPECTATION_TEST > CreateFromJson(const std::string &aBrdName, const nlohmann::json &aBrdExpectations)
void RunTest(const BOARD &aBrd) const
Runs the test against the given board.
A single expectation about a board, which can be run as a test against a parsed BOARD.
std::string GetName() const override
void RunTest(const BOARD &aBrd) const override
std::vector< std::string > m_CuNames
std::optional< INT_MATCHER > m_CuCount
LSET is a set of PCB_LAYER_IDs.
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Handle the data for a net.
Container for NETINFO_ITEM elements, which are the nets.
std::optional< INT_MATCHER > m_Count
void RunTest(const BOARD &aBrd) const override
void doSimpleCountTest(const BOARD &aBrd) const
std::vector< std::string > m_NamePatterns
std::string GetName() const override
std::vector< const NETINFO_ITEM * > findMatchingNets(const BOARD &aBrd) const
static bool nameMatches(const std::string &aName, const std::string &aPattern)
VECTOR2I GetPosition() const override
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
static bool matchPredicate(const std::string &aStr, const std::string &aPattern)
void Test(const std::string &aStr) const
STRING_PATTERN_MATCHER(const std::string &aPattern)
PCB_LAYER_ID
A quick note on layer IDs:
KICOMMON_API double DoubleValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Convert aTextValue to a double.
BOOST_TEST(contains==c.ExpectedContains)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_CHECK_PREDICATE(ArePolylineEndPointsNearCircle,(chain)(c.m_geom.m_center_point)(radius)(accuracy+epsilon))
BOOST_TEST_CONTEXT("Test Clearance")
wxString result
Test unit parsing edge cases and error handling.
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
VECTOR2< int32_t > VECTOR2I