kicad/qa/unittests/common/test_property.cpp

421 lines
12 KiB
C++

/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2020 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* 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 3
* 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, see <http://www.gnu.org/licenses/>.
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <wx/gdicmn.h> // wxPoint
#include <inspectable.h>
#include <property_mgr.h>
using namespace std;
class A : public INSPECTABLE
{
public:
virtual void setA( int a ) = 0;
virtual int getA() const = 0;
virtual const int& getA2() const { return m_a; }
virtual void setPoint( const wxPoint& p ) { m_p = p; }
void setPoint2( wxPoint& p ) { m_p = p; }
void setPoint3( wxPoint p ) { m_p = p; }
void setPoint4( wxPoint p ) { m_p = p; }
const wxPoint& getPoint() const { return m_p; }
wxPoint getPoint2() const { return m_p; }
wxPoint getPoint3() { return m_p; }
const wxPoint& getPoint4() const { return m_p; }
protected:
int m_a = 0;
wxPoint m_p;
};
class B : public A
{
public:
void setA( int a ) override { m_a = a; }
int getA() const override { return m_a; }
void setC( int a ) { m_c = a; }
int getC() const { return m_c; }
private:
int m_c = 0;
};
class C
{
public:
bool getBool() const { return m_bool; }
void setBool( bool a ) { m_bool = a; }
int getNew() const { return m_m; }
void setNew( int m ) { m_m = m; }
int m_m = 0;
bool m_bool = false;
};
enum enum_glob { TEST1 = 0, TEST2 = 1, TEST3 = 4 };
class D : public A, public C
{
public:
enum enum_class { TESTA = 0, TESTB = 1, TESTC = 4 };
// note 2x factor
virtual void setA( int a ) override { m_aa = 2 * a; }
virtual int getA() const override { return m_aa; }
enum_glob getGlobEnum() const { return m_enum_glob; }
void setGlobEnum( enum_glob val ) { m_enum_glob = val; }
enum_class getClassEnum() const { return m_enum_class; }
void setClassEnum( enum_class val ) { m_enum_class = val; }
void setCond( int a ) { m_cond = a; }
int getCond() const { return m_cond; }
enum_glob m_enum_glob;
enum_class m_enum_class;
int m_aa = 0;
int m_cond = 0;
};
class E : public D
{
};
static struct ENUM_GLOB_DESC
{
ENUM_GLOB_DESC()
{
ENUM_MAP<enum_glob>::Instance()
.Map( enum_glob::TEST1, "TEST1" )
.Map( enum_glob::TEST2, "TEST2" )
.Map( enum_glob::TEST3, "TEST3" );
}
} _ENUM_GLOB_DESC;
ENUM_TO_WXANY( enum_glob );
static struct CLASS_A_DESC
{
CLASS_A_DESC()
{
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
propMgr.AddProperty( new PROPERTY<A, int>( "A", &A::setA, &A::getA ) );
propMgr.AddProperty( new PROPERTY<A, int>( "A2", &A::setA, &A::getA2 ) );
propMgr.AddProperty( new PROPERTY<A, wxPoint>( "point", &A::setPoint, &A::getPoint ) );
propMgr.AddProperty( new PROPERTY<A, wxPoint>( "point2", &A::setPoint, &A::getPoint2 ) );
propMgr.AddProperty( new PROPERTY<A, wxPoint>( "point3", &A::setPoint3, &A::getPoint3 ) );
propMgr.AddProperty( new PROPERTY<A, wxPoint>( "point4", &A::setPoint4, &A::getPoint4 ) );
}
} _CLASS_A_DESC;
static struct CLASS_B_DESC
{
CLASS_B_DESC()
{
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
propMgr.InheritsAfter( TYPE_HASH( B ), TYPE_HASH( A ) );
propMgr.AddProperty( new PROPERTY<B, int>( "C", &B::setC, &B::getC ) );
}
} _CLASS_B_DESC;
static struct CLASS_C_DESC
{
CLASS_C_DESC()
{
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
propMgr.AddProperty( new PROPERTY<C, bool>( "bool", &C::setBool, &C::getBool ) );
propMgr.AddProperty( new PROPERTY<C, int>( "new", &C::setNew, &C::getNew ) );
}
} _CLASS_C_DESC;
static struct CLASS_D_DESC
{
CLASS_D_DESC()
{
ENUM_MAP<D::enum_class>::Instance()
.Map( D::enum_class::TESTA, "TESTA" )
.Map( D::enum_class::TESTB, "TESTB" )
.Map( D::enum_class::TESTC, "TESTC" );
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
propMgr.AddProperty( new PROPERTY_ENUM<D, enum_glob>( "enumGlob", &D::setGlobEnum, &D::getGlobEnum ) );
propMgr.AddProperty( new PROPERTY_ENUM<D, D::enum_class>( "enumClass", &D::setClassEnum, &D::getClassEnum ) );
propMgr.AddProperty( new PROPERTY<D, wxPoint, A>( "point_alias", &D::setPoint, &D::getPoint ) );
propMgr.ReplaceProperty( TYPE_HASH( C ), "bool",
new PROPERTY<D, bool, C>( "replaced_bool", &D::setBool, &D::getBool ) );
// lines below are needed to indicate multiple inheritance
propMgr.AddTypeCast( new TYPE_CAST<D, A> );
propMgr.AddTypeCast( new TYPE_CAST<D, C> );
propMgr.InheritsAfter( TYPE_HASH( D ), TYPE_HASH( A ) );
propMgr.InheritsAfter( TYPE_HASH( D ), TYPE_HASH( C ) );
auto cond = new PROPERTY<D, int>( "cond", &D::setCond, &D::getCond );
cond->SetAvailableFunc( [=](INSPECTABLE* aItem)->bool { return *aItem->Get<int>( "A" ) > 50; } );
propMgr.AddProperty( cond );
}
} _CLASS_D_DESC;
static struct CLASS_E_DESC
{
CLASS_E_DESC()
{
wxArrayInt values;
values.Add( enum_glob::TEST1 );
values.Add( enum_glob::TEST3 );
wxArrayString labels;
labels.Add( "T1" );
labels.Add( "T3" );
wxPGChoices newChoices( labels, values );
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
auto prop = new PROPERTY_ENUM<E, enum_glob, D>( "enumGlob",
&E::setGlobEnum, &E::getGlobEnum );
prop->SetChoices( newChoices );
propMgr.ReplaceProperty( TYPE_HASH( D ), "enumGlob", prop );
}
} _CLASS_E_DESC;
ENUM_TO_WXANY( D::enum_class );
struct PropertiesFixture
{
PropertiesFixture() :
ptr( nullptr ),
propMgr( PROPERTY_MANAGER::Instance() )
{
}
B b;
D d;
A* ptr;
PROPERTY_MANAGER& propMgr;
};
BOOST_FIXTURE_TEST_SUITE( Properties, PropertiesFixture )
BOOST_AUTO_TEST_CASE( Init )
{
propMgr.Rebuild();
}
// Basic Set() & Get()
BOOST_AUTO_TEST_CASE( SetGet )
{
ptr = &b;
ptr->Set( "A", 100 );
ptr->Set( "point", wxPoint( 100, 200 ) );
BOOST_CHECK_EQUAL( *ptr->Get<int>( "A" ), 100 );
BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point" ), wxPoint( 100, 200 ) );
ptr = &d;
ptr->Set( "enumGlob", enum_glob::TEST2 );
ptr->Set( "enumClass", D::enum_class::TESTC );
BOOST_CHECK_EQUAL( *ptr->Get<enum_glob>( "enumGlob" ), enum_glob::TEST2 );
BOOST_CHECK_EQUAL( *ptr->Get<D::enum_class>( "enumClass" ), D::enum_class::TESTC );
}
// Virtual methods
BOOST_AUTO_TEST_CASE( VirtualMethods )
{
// D::setA() saves a doubled value, while B::setA() saves unmodified value
ptr = &b;
ptr->Set( "A", 23 );
BOOST_CHECK_EQUAL( *ptr->Get<int>( "A" ), 23 ); // unmodified == 23
ptr = &d;
ptr->Set( "A", 23 );
BOOST_CHECK_EQUAL( *ptr->Get<int>( "A" ), 46 ); // doubled == 46
}
// Non-existing properties
BOOST_AUTO_TEST_CASE( NotexistingProperties )
{
ptr = &d;
BOOST_CHECK_EQUAL( ptr->Set<int>( "does not exist", 5 ), false );
//BOOST_CHECK_EQUAL( ptr->Get<int>( "neither" ).has_value(), false );
}
// Request data using incorrect type
BOOST_AUTO_TEST_CASE( IncorrectType )
{
ptr = &d;
BOOST_CHECK_THROW( ptr->Get<wxPoint>( "A" ), std::invalid_argument );
}
// Type-casting (for types with multiple inheritance)
BOOST_AUTO_TEST_CASE( TypeCasting )
{
ptr = &d;
A* D_to_A = static_cast<A*>( propMgr.TypeCast( ptr, TYPE_HASH( D ), TYPE_HASH( A ) ) );
BOOST_CHECK_EQUAL( D_to_A, dynamic_cast<A*>( ptr ) );
C* D_to_C = static_cast<C*>( propMgr.TypeCast( ptr, TYPE_HASH( D ), TYPE_HASH( C ) ) );
BOOST_CHECK_EQUAL( D_to_C, dynamic_cast<C*>( ptr ) );
}
BOOST_AUTO_TEST_CASE( EnumGlob )
{
PROPERTY_BASE* prop = propMgr.GetProperty( TYPE_HASH( D ), "enumGlob" );
BOOST_CHECK( prop->HasChoices() );
wxArrayInt values;
values.Add( enum_glob::TEST1 );
values.Add( enum_glob::TEST2 );
values.Add( enum_glob::TEST3 );
wxArrayString labels;
labels.Add( "TEST1" );
labels.Add( "TEST2" );
labels.Add( "TEST3" );
const wxPGChoices& v = prop->Choices();
BOOST_CHECK_EQUAL( v.GetCount(), values.GetCount() );
BOOST_CHECK_EQUAL( v.GetCount(), labels.GetCount() );
for (int i = 0; i < values.GetCount(); ++i )
{
BOOST_CHECK_EQUAL( v.GetValue( i ), values[i] );
}
for (int i = 0; i < labels.GetCount(); ++i )
{
BOOST_CHECK_EQUAL( v.GetLabel( i ), labels[i] );
}
D item ;
wxString str;
item.setGlobEnum( static_cast<enum_glob>( -1 ) );
wxAny any = item.Get( prop );
BOOST_CHECK_EQUAL( any.GetAs<wxString>( &str ), false );
item.setGlobEnum( enum_glob::TEST1 );
any = item.Get( prop );
BOOST_CHECK_EQUAL( any.GetAs<wxString>( &str ), true );
}
BOOST_AUTO_TEST_CASE( EnumClass )
{
PROPERTY_BASE* prop = propMgr.GetProperty( TYPE_HASH( D ), "enumClass" );
BOOST_CHECK( prop->HasChoices() );
wxArrayInt values;
values.Add( D::enum_class::TESTA );
values.Add( D::enum_class::TESTB );
values.Add( D::enum_class::TESTC );
wxArrayString labels;
labels.Add( "TESTA" );
labels.Add( "TESTB" );
labels.Add( "TESTC" );
const wxPGChoices& v = prop->Choices();
BOOST_CHECK_EQUAL( v.GetCount(), values.GetCount() );
BOOST_CHECK_EQUAL( v.GetCount(), labels.GetCount() );
for (int i = 0; i < values.GetCount(); ++i )
{
BOOST_CHECK_EQUAL( v.GetValue( i ), values[i] );
}
for (int i = 0; i < labels.GetCount(); ++i )
{
BOOST_CHECK_EQUAL( v.GetLabel( i ), labels[i] );
}
}
// Tests conditional properties (which may depend on values or other properties)
BOOST_AUTO_TEST_CASE( Availability )
{
PROPERTY_BASE* propCond = propMgr.GetProperty( TYPE_HASH( D ), "cond" );
ptr = &d;
// "cond" property is available only when "a" field is greater than 50
d.setA( 0 );
BOOST_CHECK( !propCond->Available( ptr ) );
d.setA( 100 );
BOOST_CHECK( propCond->Available( ptr ) );
}
// Using a different name for a parent property
BOOST_AUTO_TEST_CASE( Alias )
{
ptr = &d;
ptr->Set( "point", wxPoint( 100, 100 ) );
BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point" ), wxPoint( 100, 100 ) );
BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point_alias" ), wxPoint( 100, 100 ) );
ptr->Set( "point_alias", wxPoint( 300, 300 ) );
BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point" ), wxPoint( 300, 300 ) );
BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point_alias" ), wxPoint( 300, 300 ) );
}
// Property renaming
BOOST_AUTO_TEST_CASE( Rename )
{
PROPERTY_BASE* prop;
prop = propMgr.GetProperty( TYPE_HASH( D ), "bool" );
BOOST_CHECK_EQUAL( prop, nullptr );
prop = propMgr.GetProperty( TYPE_HASH( D ), "replaced_bool" );
BOOST_CHECK( prop );
}
// Different subset of enum values for a property
BOOST_AUTO_TEST_CASE( AlternativeEnum )
{
PROPERTY_BASE* prop = propMgr.GetProperty( TYPE_HASH( E ), "enumGlob" );
BOOST_CHECK( prop->HasChoices() );
wxArrayInt values;
values.Add( enum_glob::TEST1 );
values.Add( enum_glob::TEST3 );
wxArrayString labels;
labels.Add( "T1" );
labels.Add( "T3" );
const wxPGChoices& v = prop->Choices();
BOOST_CHECK_EQUAL( v.GetCount(), values.GetCount() );
BOOST_CHECK_EQUAL( v.GetCount(), labels.GetCount() );
for (int i = 0; i < values.GetCount(); ++i )
{
BOOST_CHECK_EQUAL( v.GetValue( i ), values[i] );
}
for (int i = 0; i < labels.GetCount(); ++i )
{
BOOST_CHECK_EQUAL( v.GetLabel( i ), labels[i] );
}
}
BOOST_AUTO_TEST_SUITE_END()