kicad/qa/unittests/pcbnew/test_board_item.cpp

378 lines
14 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <eda_item_test_utils.h>
#include <core/typeinfo.h>
#include <drc/drc_item.h>
// Code under test
#include <board.h>
#include <board_item.h>
#include <footprint.h>
#include <pad.h>
#include <pcb_shape.h>
#include <pcb_text.h>
#include <pcb_textbox.h>
#include <pcb_bitmap.h>
#include <fp_text.h>
#include <fp_textbox.h>
#include <fp_shape.h>
#include <zone.h>
#include <pcb_track.h>
#include <pcb_marker.h>
#include <pcb_dimension.h>
#include <pcb_target.h>
#include <netinfo.h>
#include <pcb_group.h>
class TEST_BOARD_ITEM_FIXTURE
{
public:
BOARD m_board;
FOOTPRINT m_footprint;
std::shared_ptr<DRC_ITEM> m_drcItem;
PCB_TEXT m_text;
TEST_BOARD_ITEM_FIXTURE() :
m_board(),
m_footprint( &m_board ),
m_drcItem( DRC_ITEM::Create( DRCE_MALFORMED_COURTYARD ) ),
m_text( &m_board )
{
}
~TEST_BOARD_ITEM_FIXTURE()
{
m_text.SetParentGroup( nullptr );
}
BOARD_ITEM* Instantiate( KICAD_T aType )
{
if( !IsPcbnewType( aType ) )
return nullptr;
if( !IsInstantiableType( aType ) )
return nullptr;
switch( aType )
{
case PCB_FOOTPRINT_T: return new FOOTPRINT( &m_board );
case PCB_PAD_T: return new PAD( &m_footprint );
case PCB_SHAPE_T: return new PCB_SHAPE( &m_board );
case PCB_TEXT_T: return new PCB_TEXT( &m_board );
case PCB_TEXTBOX_T: return new PCB_TEXTBOX( &m_board );
case PCB_BITMAP_T: return new PCB_BITMAP( &m_board );
case PCB_FP_TEXT_T: return new FP_TEXT( &m_footprint );
case PCB_FP_TEXTBOX_T: return new FP_TEXTBOX( &m_footprint );
case PCB_FP_SHAPE_T: return new FP_SHAPE( &m_footprint );
case PCB_FP_DIM_ALIGNED_T: return new PCB_DIM_ALIGNED( &m_footprint, PCB_FP_DIM_ALIGNED_T );
case PCB_FP_DIM_LEADER_T: return new PCB_DIM_LEADER( &m_footprint, true );
case PCB_FP_DIM_CENTER_T: return new PCB_DIM_CENTER( &m_footprint, true );
case PCB_FP_DIM_RADIAL_T: return new PCB_DIM_RADIAL( &m_footprint, true );
case PCB_FP_DIM_ORTHOGONAL_T: return new PCB_DIM_ORTHOGONAL( &m_footprint, true );
case PCB_FP_ZONE_T:
{
FP_ZONE* fpZone = new FP_ZONE( &m_footprint );
fpZone->AppendCorner( VECTOR2I( pcbIUScale.mmToIU( -100 ), pcbIUScale.mmToIU( -50 ) ), -1 );
fpZone->AppendCorner( VECTOR2I( pcbIUScale.mmToIU( -100 ), pcbIUScale.mmToIU( 50 ) ), -1 );
fpZone->AppendCorner( VECTOR2I( pcbIUScale.mmToIU( 100 ), pcbIUScale.mmToIU( 50 ) ), -1 );
fpZone->AppendCorner( VECTOR2I( pcbIUScale.mmToIU( 100 ), pcbIUScale.mmToIU( -50 ) ), -1 );
return fpZone;
}
case PCB_TRACE_T: return new PCB_TRACK( &m_board );
case PCB_VIA_T: return new PCB_VIA( &m_board );
case PCB_ARC_T: return new PCB_ARC( &m_board );
case PCB_MARKER_T: return new PCB_MARKER( m_drcItem, VECTOR2I( 0, 0 ) );
case PCB_DIM_ALIGNED_T: return new PCB_DIM_ALIGNED( &m_board, PCB_DIM_ALIGNED_T );
case PCB_DIM_LEADER_T: return new PCB_DIM_LEADER( &m_board );
case PCB_DIM_CENTER_T: return new PCB_DIM_CENTER( &m_board );
case PCB_DIM_RADIAL_T: return new PCB_DIM_RADIAL( &m_board );
case PCB_DIM_ORTHOGONAL_T: return new PCB_DIM_ORTHOGONAL( &m_board );
case PCB_TARGET_T: return new PCB_TARGET( &m_board );
case PCB_ZONE_T:
{
ZONE* zone = new ZONE( &m_board );
zone->AppendCorner( VECTOR2I( pcbIUScale.mmToIU( -100 ), pcbIUScale.mmToIU( -50 ) ), -1 );
zone->AppendCorner( VECTOR2I( pcbIUScale.mmToIU( -100 ), pcbIUScale.mmToIU( 50 ) ), -1 );
zone->AppendCorner( VECTOR2I( pcbIUScale.mmToIU( 100 ), pcbIUScale.mmToIU( 50 ) ), -1 );
zone->AppendCorner( VECTOR2I( pcbIUScale.mmToIU( 100 ), pcbIUScale.mmToIU( -50 ) ), -1 );
return zone;
}
case PCB_GROUP_T:
{
PCB_GROUP* group = new PCB_GROUP( &m_board );
// Group position only makes sense if there's at least one item in the group.
group->AddItem( &m_text );
return group;
}
case PCB_T:
case PCB_ITEM_LIST_T:
case PCB_NETINFO_T:
return nullptr;
default:
BOOST_FAIL( wxString::Format(
"Unhandled type: %d "
"(if you created a new type you need to handle it in this switch statement)",
aType ) );
return nullptr;
}
}
static void CompareItems( BOARD_ITEM* aItem, BOARD_ITEM* aOriginalItem )
{
BOOST_CHECK_EQUAL( aItem->GetPosition(), aOriginalItem->GetPosition() );
BOOST_CHECK_EQUAL( aItem->GetBoundingBox().GetTop(),
aOriginalItem->GetBoundingBox().GetTop() );
BOOST_CHECK_EQUAL( aItem->GetBoundingBox().GetLeft(),
aOriginalItem->GetBoundingBox().GetLeft() );
BOOST_CHECK_EQUAL( aItem->GetBoundingBox().GetBottom(),
aOriginalItem->GetBoundingBox().GetBottom() );
BOOST_CHECK_EQUAL( aItem->GetBoundingBox().GetRight(),
aOriginalItem->GetBoundingBox().GetRight() );
}
};
BOOST_FIXTURE_TEST_SUITE( PcbItem, TEST_BOARD_ITEM_FIXTURE )
BOOST_AUTO_TEST_CASE( Move )
{
for( int i = 0; i < MAX_STRUCT_TYPE_ID; i++ )
{
KICAD_T type = static_cast<KICAD_T>( i );
auto item = std::unique_ptr<BOARD_ITEM>( Instantiate( type ) );
if( item == nullptr )
continue;
BOOST_TEST_CONTEXT( "Class: " << item->GetClass() )
{
IterateOverPositionsAndReferences<BOARD_ITEM>(
item.get(),
[]( BOARD_ITEM* aOriginalItem, VECTOR2I aRef )
{
FP_SHAPE* originalFpShape = dynamic_cast<FP_SHAPE*>( aOriginalItem );
if( originalFpShape != nullptr )
originalFpShape->SetDrawCoord();
// FIXME: Update() has to be called after SetPosition() to update dimension
// shapes.
PCB_DIMENSION_BASE* originalDimension =
dynamic_cast<PCB_DIMENSION_BASE*>( aOriginalItem );
if( originalDimension != nullptr )
originalDimension->Update();
auto item = std::unique_ptr<BOARD_ITEM>( aOriginalItem->Duplicate() );
VECTOR2I originalPos = item->GetPosition();
// Move to a point, then go back.
// This has to be an identity transformation.
item->Move( aRef );
FP_SHAPE* fpShape = dynamic_cast<FP_SHAPE*>( item.get() );
if( fpShape != nullptr )
fpShape->SetDrawCoord();
BOOST_CHECK_EQUAL( item->GetPosition(), originalPos + aRef );
item->Move( -aRef );
if( fpShape != nullptr )
fpShape->SetDrawCoord();
CompareItems( item.get(), aOriginalItem );
} );
}
}
}
BOOST_AUTO_TEST_CASE( Rotate )
{
for( int i = 0; i < MAX_STRUCT_TYPE_ID; i++ )
{
KICAD_T type = static_cast<KICAD_T>( i );
auto item = std::unique_ptr<BOARD_ITEM>( Instantiate( type ) );
if( item == nullptr )
continue;
BOOST_TEST_CONTEXT( "Class: " << item->GetClass() )
{
// Four same 90 degree rotations are an identity.
IterateOverPositionsAndReferences<BOARD_ITEM>(
item.get(),
[]( BOARD_ITEM* aOriginalItem, VECTOR2I aRef )
{
FP_SHAPE* originalFpShape = dynamic_cast<FP_SHAPE*>( aOriginalItem );
if( originalFpShape != nullptr )
originalFpShape->SetDrawCoord();
// FIXME: Update() has to be called after SetPosition() to update dimension
// shapes.
PCB_DIMENSION_BASE* originalDimension =
dynamic_cast<PCB_DIMENSION_BASE*>( aOriginalItem );
if( originalDimension != nullptr )
originalDimension->Update();
auto item = std::unique_ptr<BOARD_ITEM>( aOriginalItem->Duplicate() );
// Four equivalent 90 degree rotations are an identity.
item->Rotate( aRef, EDA_ANGLE( 90.0, DEGREES_T ) );
item->Rotate( aRef, EDA_ANGLE( 90.0, DEGREES_T ) );
item->Rotate( aRef, EDA_ANGLE( 90.0, DEGREES_T ) );
item->Rotate( aRef, EDA_ANGLE( 90.0, DEGREES_T ) );
FP_SHAPE* fpShape = dynamic_cast<FP_SHAPE*>( aOriginalItem );
if( fpShape != nullptr )
fpShape->SetDrawCoord();
CompareItems( item.get(), aOriginalItem );
} );
}
}
}
BOOST_AUTO_TEST_CASE( FlipLeftRight )
{
for( int i = 0; i < MAX_STRUCT_TYPE_ID; i++ )
{
KICAD_T type = static_cast<KICAD_T>( i );
auto item = std::unique_ptr<BOARD_ITEM>( Instantiate( type ) );
if( item == nullptr )
continue;
BOOST_TEST_CONTEXT( "Class: " << item->GetClass() )
{
IterateOverPositionsAndReferences<BOARD_ITEM>(
item.get(),
[]( BOARD_ITEM* aOriginalItem, VECTOR2I aRef )
{
FP_SHAPE* originalFpShape = dynamic_cast<FP_SHAPE*>( aOriginalItem );
if( originalFpShape != nullptr )
originalFpShape->SetDrawCoord();
// FIXME: Update() has to be called after SetPosition() to update dimension
// shapes.
PCB_DIMENSION_BASE* originalDimension =
dynamic_cast<PCB_DIMENSION_BASE*>( aOriginalItem );
if( originalDimension != nullptr )
originalDimension->Update();
auto item = std::unique_ptr<BOARD_ITEM>( aOriginalItem->Duplicate() );
// Two equivalent flips are an identity.
item->Flip( aRef, true );
item->Flip( aRef, true );
FP_SHAPE* fpShape = dynamic_cast<FP_SHAPE*>( aOriginalItem );
if( fpShape != nullptr )
fpShape->SetDrawCoord();
CompareItems( item.get(), aOriginalItem );
} );
}
}
}
BOOST_AUTO_TEST_CASE( FlipUpDown )
{
for( int i = 0; i < MAX_STRUCT_TYPE_ID; i++ )
{
KICAD_T type = static_cast<KICAD_T>( i );
auto item = std::unique_ptr<BOARD_ITEM>( Instantiate( type ) );
if( item == nullptr )
continue;
BOOST_TEST_CONTEXT( "Class: " << item->GetClass() )
{
IterateOverPositionsAndReferences<BOARD_ITEM>(
item.get(),
[]( BOARD_ITEM* aOriginalItem, VECTOR2I aRef )
{
FP_SHAPE* originalFpShape = dynamic_cast<FP_SHAPE*>( aOriginalItem );
if( originalFpShape != nullptr )
originalFpShape->SetDrawCoord();
// FIXME: Update() has to be called after SetPosition() to update dimension
// shapes.
PCB_DIMENSION_BASE* originalDimension =
dynamic_cast<PCB_DIMENSION_BASE*>( aOriginalItem );
if( originalDimension != nullptr )
originalDimension->Update();
auto item = std::unique_ptr<BOARD_ITEM>( aOriginalItem->Duplicate() );
// Two equivalent flips are an identity.
item->Flip( aRef, false );
item->Flip( aRef, false );
FP_SHAPE* fpShape = dynamic_cast<FP_SHAPE*>( aOriginalItem );
if( fpShape != nullptr )
fpShape->SetDrawCoord();
CompareItems( item.get(), aOriginalItem );
} );
}
}
}
BOOST_AUTO_TEST_SUITE_END()