/* * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 2020 CERN * @author Maciej Suminski * * 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 . */ #include #include // wxPoint #include #include 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::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", &A::setA, &A::getA ) ); propMgr.AddProperty( new PROPERTY( "A2", &A::setA, &A::getA2 ) ); propMgr.AddProperty( new PROPERTY( "point", &A::setPoint, &A::getPoint ) ); propMgr.AddProperty( new PROPERTY( "point2", &A::setPoint, &A::getPoint2 ) ); propMgr.AddProperty( new PROPERTY( "point3", &A::setPoint3, &A::getPoint3 ) ); propMgr.AddProperty( new PROPERTY( "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( "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( "bool", &C::setBool, &C::getBool ) ); propMgr.AddProperty( new PROPERTY( "new", &C::setNew, &C::getNew ) ); } } _CLASS_C_DESC; static struct CLASS_D_DESC { CLASS_D_DESC() { ENUM_MAP::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( "enumGlob", &D::setGlobEnum, &D::getGlobEnum ) ); propMgr.AddProperty( new PROPERTY_ENUM( "enumClass", &D::setClassEnum, &D::getClassEnum ) ); propMgr.AddProperty( new PROPERTY( "point_alias", &D::setPoint, &D::getPoint ) ); propMgr.ReplaceProperty( TYPE_HASH( C ), "bool", new PROPERTY( "replaced_bool", &D::setBool, &D::getBool ) ); // lines below are needed to indicate multiple inheritance propMgr.AddTypeCast( new TYPE_CAST ); propMgr.AddTypeCast( new TYPE_CAST ); propMgr.InheritsAfter( TYPE_HASH( D ), TYPE_HASH( A ) ); propMgr.InheritsAfter( TYPE_HASH( D ), TYPE_HASH( C ) ); auto cond = new PROPERTY( "cond", &D::setCond, &D::getCond ); cond->SetAvailableFunc( [=](INSPECTABLE* aItem)->bool { return *aItem->Get( "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( "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( "A" ), 100 ); BOOST_CHECK_EQUAL( *ptr->Get( "point" ), wxPoint( 100, 200 ) ); ptr = &d; ptr->Set( "enumGlob", enum_glob::TEST2 ); ptr->Set( "enumClass", D::enum_class::TESTC ); BOOST_CHECK_EQUAL( *ptr->Get( "enumGlob" ), enum_glob::TEST2 ); BOOST_CHECK_EQUAL( *ptr->Get( "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( "A" ), 23 ); // unmodified == 23 ptr = &d; ptr->Set( "A", 23 ); BOOST_CHECK_EQUAL( *ptr->Get( "A" ), 46 ); // doubled == 46 } // Non-existing properties BOOST_AUTO_TEST_CASE( NotexistingProperties ) { ptr = &d; BOOST_CHECK_EQUAL( ptr->Set( "does not exist", 5 ), false ); //BOOST_CHECK_EQUAL( ptr->Get( "neither" ).has_value(), false ); } // Request data using incorrect type BOOST_AUTO_TEST_CASE( IncorrectType ) { ptr = &d; BOOST_CHECK_THROW( ptr->Get( "A" ), std::invalid_argument ); } // Type-casting (for types with multiple inheritance) BOOST_AUTO_TEST_CASE( TypeCasting ) { ptr = &d; A* D_to_A = static_cast( propMgr.TypeCast( ptr, TYPE_HASH( D ), TYPE_HASH( A ) ) ); BOOST_CHECK_EQUAL( D_to_A, dynamic_cast( ptr ) ); C* D_to_C = static_cast( propMgr.TypeCast( ptr, TYPE_HASH( D ), TYPE_HASH( C ) ) ); BOOST_CHECK_EQUAL( D_to_C, dynamic_cast( 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( -1 ) ); wxAny any = item.Get( prop ); BOOST_CHECK_EQUAL( any.GetAs( &str ), false ); item.setGlobEnum( enum_glob::TEST1 ); any = item.Get( prop ); BOOST_CHECK_EQUAL( any.GetAs( &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( "point" ), wxPoint( 100, 100 ) ); BOOST_CHECK_EQUAL( *ptr->Get( "point_alias" ), wxPoint( 100, 100 ) ); ptr->Set( "point_alias", wxPoint( 300, 300 ) ); BOOST_CHECK_EQUAL( *ptr->Get( "point" ), wxPoint( 300, 300 ) ); BOOST_CHECK_EQUAL( *ptr->Get( "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()