KiCad PCB EDA Suite
test_property.cpp
Go to the documentation of this file.
1 /*
2 * This program source code file is part of KICAD, a free EDA CAD application.
3  *
4  * Copyright (C) 2020 CERN
5  * @author Maciej Suminski <maciej.suminski@cern.ch>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
22 #include <wx/gdicmn.h> // wxPoint
23 
24 #include <inspectable.h>
25 #include <property_mgr.h>
26 
27 using namespace std;
28 
29 class A : public INSPECTABLE
30 {
31 public:
32  virtual void setA( int a ) = 0;
33  virtual int getA() const = 0;
34  virtual const int& getA2() const { return m_a; }
35 
36  virtual void setPoint( const wxPoint& p ) { m_p = p; }
37  void setPoint2( wxPoint& p ) { m_p = p; }
38  void setPoint3( wxPoint p ) { m_p = p; }
39  void setPoint4( wxPoint p ) { m_p = p; }
40 
41  const wxPoint& getPoint() const { return m_p; }
42  wxPoint getPoint2() const { return m_p; }
43  wxPoint getPoint3() { return m_p; }
44  const wxPoint& getPoint4() const { return m_p; }
45 
46 protected:
47  int m_a = 0;
48  wxPoint m_p;
49 };
50 
51 class B : public A
52 {
53 public:
54  void setA( int a ) override { m_a = a; }
55  int getA() const override { return m_a; }
56 
57  void setC( int a ) { m_c = a; }
58  int getC() const { return m_c; }
59 
60 private:
61  int m_c = 0;
62 };
63 
64 class C
65 {
66 public:
67  bool getBool() const { return m_bool; }
68  void setBool( bool a ) { m_bool = a; }
69 
70  int getNew() const { return m_m; }
71  void setNew( int m ) { m_m = m; }
72 
73  int m_m = 0;
74  bool m_bool = false;
75 };
76 
77 enum enum_glob { TEST1 = 0, TEST2 = 1, TEST3 = 4 };
78 
79 class D : public A, public C
80 {
81 public:
82  enum enum_class { TESTA = 0, TESTB = 1, TESTC = 4 };
83 
84  // note 2x factor
85  virtual void setA( int a ) override { m_aa = 2 * a; }
86  virtual int getA() const override { return m_aa; }
87 
88  enum_glob getGlobEnum() const { return m_enum_glob; }
89  void setGlobEnum( enum_glob val ) { m_enum_glob = val; }
90 
91  enum_class getClassEnum() const { return m_enum_class; }
92  void setClassEnum( enum_class val ) { m_enum_class = val; }
93 
94  void setCond( int a ) { m_cond = a; }
95  int getCond() const { return m_cond; }
96 
99  int m_aa = 0;
100  int m_cond = 0;
101 };
102 
103 class E : public D
104 {
105 };
106 
107 static struct ENUM_GLOB_DESC
108 {
110  {
112  .Map( enum_glob::TEST1, "TEST1" )
113  .Map( enum_glob::TEST2, "TEST2" )
114  .Map( enum_glob::TEST3, "TEST3" );
115  }
117 
119 
120 static struct CLASS_A_DESC
121 {
123  {
125  propMgr.AddProperty( new PROPERTY<A, int>( "A", &A::setA, &A::getA ) );
126  propMgr.AddProperty( new PROPERTY<A, int>( "A2", &A::setA, &A::getA2 ) );
127  propMgr.AddProperty( new PROPERTY<A, wxPoint>( "point", &A::setPoint, &A::getPoint ) );
128  propMgr.AddProperty( new PROPERTY<A, wxPoint>( "point2", &A::setPoint, &A::getPoint2 ) );
129 
130  propMgr.AddProperty( new PROPERTY<A, wxPoint>( "point3", &A::setPoint3, &A::getPoint3 ) );
131  propMgr.AddProperty( new PROPERTY<A, wxPoint>( "point4", &A::setPoint4, &A::getPoint4 ) );
132  }
133 } _CLASS_A_DESC;
134 
135 static struct CLASS_B_DESC
136 {
138  {
140  propMgr.InheritsAfter( TYPE_HASH( B ), TYPE_HASH( A ) );
141  propMgr.AddProperty( new PROPERTY<B, int>( "C", &B::setC, &B::getC ) );
142  }
143 } _CLASS_B_DESC;
144 
145 static struct CLASS_C_DESC
146 {
148  {
150  propMgr.AddProperty( new PROPERTY<C, bool>( "bool", &C::setBool, &C::getBool ) );
151  propMgr.AddProperty( new PROPERTY<C, int>( "new", &C::setNew, &C::getNew ) );
152  }
153 } _CLASS_C_DESC;
154 
155 static struct CLASS_D_DESC
156 {
158  {
160  .Map( D::enum_class::TESTA, "TESTA" )
161  .Map( D::enum_class::TESTB, "TESTB" )
162  .Map( D::enum_class::TESTC, "TESTC" );
163 
165  propMgr.AddProperty( new PROPERTY_ENUM<D, enum_glob>( "enumGlob", &D::setGlobEnum, &D::getGlobEnum ) );
167  propMgr.AddProperty( new PROPERTY<D, wxPoint, A>( "point_alias", &D::setPoint, &D::getPoint ) );
168 
169  propMgr.ReplaceProperty( TYPE_HASH( C ), "bool",
170  new PROPERTY<D, bool, C>( "replaced_bool", &D::setBool, &D::getBool ) );
171 
172  // lines below are needed to indicate multiple inheritance
173  propMgr.AddTypeCast( new TYPE_CAST<D, A> );
174  propMgr.AddTypeCast( new TYPE_CAST<D, C> );
175  propMgr.InheritsAfter( TYPE_HASH( D ), TYPE_HASH( A ) );
176  propMgr.InheritsAfter( TYPE_HASH( D ), TYPE_HASH( C ) );
177 
178  auto cond = new PROPERTY<D, int>( "cond", &D::setCond, &D::getCond );
179  cond->SetAvailableFunc( [=](INSPECTABLE* aItem)->bool { return *aItem->Get<int>( "A" ) > 50; } );
180  propMgr.AddProperty( cond );
181  }
182 } _CLASS_D_DESC;
183 
184 static struct CLASS_E_DESC
185 {
187  {
188  wxArrayInt values;
189  values.Add( enum_glob::TEST1 );
190  values.Add( enum_glob::TEST3 );
191  wxArrayString labels;
192  labels.Add( "T1" );
193  labels.Add( "T3" );
194  wxPGChoices newChoices( labels, values );
195 
197  auto prop = new PROPERTY_ENUM<E, enum_glob, D>( "enumGlob",
199  prop->SetChoices( newChoices );
200  propMgr.ReplaceProperty( TYPE_HASH( D ), "enumGlob", prop );
201  }
202 } _CLASS_E_DESC;
203 
205 
206 
208 {
210  ptr( nullptr ),
211  propMgr( PROPERTY_MANAGER::Instance() )
212  {
213  }
214 
215  B b;
216  D d;
217  A* ptr;
219 };
220 
221 BOOST_FIXTURE_TEST_SUITE( Properties, PropertiesFixture )
222 
224 {
225  propMgr.Rebuild();
226 }
227 
228 // Basic Set() & Get()
230 {
231  ptr = &b;
232  ptr->Set( "A", 100 );
233  ptr->Set( "point", wxPoint( 100, 200 ) );
234  BOOST_CHECK_EQUAL( *ptr->Get<int>( "A" ), 100 );
235  BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point" ), wxPoint( 100, 200 ) );
236 
237  ptr = &d;
238  ptr->Set( "enumGlob", enum_glob::TEST2 );
239  ptr->Set( "enumClass", D::enum_class::TESTC );
240  BOOST_CHECK_EQUAL( *ptr->Get<enum_glob>( "enumGlob" ), enum_glob::TEST2 );
241  BOOST_CHECK_EQUAL( *ptr->Get<D::enum_class>( "enumClass" ), D::enum_class::TESTC );
242 }
243 
244 // Virtual methods
245 BOOST_AUTO_TEST_CASE( VirtualMethods )
246 {
247  // D::setA() saves a doubled value, while B::setA() saves unmodified value
248  ptr = &b;
249  ptr->Set( "A", 23 );
250  BOOST_CHECK_EQUAL( *ptr->Get<int>( "A" ), 23 ); // unmodified == 23
251 
252  ptr = &d;
253  ptr->Set( "A", 23 );
254  BOOST_CHECK_EQUAL( *ptr->Get<int>( "A" ), 46 ); // doubled == 46
255 }
256 
257 // Non-existing properties
258 BOOST_AUTO_TEST_CASE( NotexistingProperties )
259 {
260  ptr = &d;
261  BOOST_CHECK_EQUAL( ptr->Set<int>( "does not exist", 5 ), false );
262  //BOOST_CHECK_EQUAL( ptr->Get<int>( "neither" ).has_value(), false );
263 }
264 
265 // Request data using incorrect type
266 BOOST_AUTO_TEST_CASE( IncorrectType )
267 {
268  ptr = &d;
269  BOOST_CHECK_THROW( ptr->Get<wxPoint>( "A" ), std::invalid_argument );
270 }
271 
272 // Type-casting (for types with multiple inheritance)
273 BOOST_AUTO_TEST_CASE( TypeCasting )
274 {
275  ptr = &d;
276  A* D_to_A = static_cast<A*>( propMgr.TypeCast( ptr, TYPE_HASH( D ), TYPE_HASH( A ) ) );
277  BOOST_CHECK_EQUAL( D_to_A, dynamic_cast<A*>( ptr ) );
278 
279  C* D_to_C = static_cast<C*>( propMgr.TypeCast( ptr, TYPE_HASH( D ), TYPE_HASH( C ) ) );
280  BOOST_CHECK_EQUAL( D_to_C, dynamic_cast<C*>( ptr ) );
281 }
282 
284 {
285  PROPERTY_BASE* prop = propMgr.GetProperty( TYPE_HASH( D ), "enumGlob" );
286  BOOST_CHECK( prop->HasChoices() );
287 
288  wxArrayInt values;
289  values.Add( enum_glob::TEST1 );
290  values.Add( enum_glob::TEST2 );
291  values.Add( enum_glob::TEST3 );
292  wxArrayString labels;
293  labels.Add( "TEST1" );
294  labels.Add( "TEST2" );
295  labels.Add( "TEST3" );
296 
297  const wxPGChoices& v = prop->Choices();
298  BOOST_CHECK_EQUAL( v.GetCount(), values.GetCount() );
299  BOOST_CHECK_EQUAL( v.GetCount(), labels.GetCount() );
300 
301  for (int i = 0; i < values.GetCount(); ++i )
302  {
303  BOOST_CHECK_EQUAL( v.GetValue( i ), values[i] );
304  }
305 
306  for (int i = 0; i < labels.GetCount(); ++i )
307  {
308  BOOST_CHECK_EQUAL( v.GetLabel( i ), labels[i] );
309  }
310 
311  D item ;
312  wxString str;
313 
314  item.setGlobEnum( static_cast<enum_glob>( -1 ) );
315  wxAny any = item.Get( prop );
316  BOOST_CHECK_EQUAL( any.GetAs<wxString>( &str ), false );
317 
319  any = item.Get( prop );
320  BOOST_CHECK_EQUAL( any.GetAs<wxString>( &str ), true );
321 }
322 
324 {
325  PROPERTY_BASE* prop = propMgr.GetProperty( TYPE_HASH( D ), "enumClass" );
326  BOOST_CHECK( prop->HasChoices() );
327 
328  wxArrayInt values;
329  values.Add( D::enum_class::TESTA );
330  values.Add( D::enum_class::TESTB );
331  values.Add( D::enum_class::TESTC );
332  wxArrayString labels;
333  labels.Add( "TESTA" );
334  labels.Add( "TESTB" );
335  labels.Add( "TESTC" );
336 
337  const wxPGChoices& v = prop->Choices();
338  BOOST_CHECK_EQUAL( v.GetCount(), values.GetCount() );
339  BOOST_CHECK_EQUAL( v.GetCount(), labels.GetCount() );
340 
341  for (int i = 0; i < values.GetCount(); ++i )
342  {
343  BOOST_CHECK_EQUAL( v.GetValue( i ), values[i] );
344  }
345 
346  for (int i = 0; i < labels.GetCount(); ++i )
347  {
348  BOOST_CHECK_EQUAL( v.GetLabel( i ), labels[i] );
349  }
350 }
351 
352 // Tests conditional properties (which may depend on values or other properties)
353 BOOST_AUTO_TEST_CASE( Availability )
354 {
355  PROPERTY_BASE* propCond = propMgr.GetProperty( TYPE_HASH( D ), "cond" );
356  ptr = &d;
357 
358  // "cond" property is available only when "a" field is greater than 50
359  d.setA( 0 );
360  BOOST_CHECK( !propCond->Available( ptr ) );
361 
362  d.setA( 100 );
363  BOOST_CHECK( propCond->Available( ptr ) );
364 }
365 
366 // Using a different name for a parent property
368 {
369  ptr = &d;
370 
371  ptr->Set( "point", wxPoint( 100, 100 ) );
372  BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point" ), wxPoint( 100, 100 ) );
373  BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point_alias" ), wxPoint( 100, 100 ) );
374 
375  ptr->Set( "point_alias", wxPoint( 300, 300 ) );
376  BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point" ), wxPoint( 300, 300 ) );
377  BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point_alias" ), wxPoint( 300, 300 ) );
378 }
379 
380 // Property renaming
382 {
383  PROPERTY_BASE* prop;
384 
385  prop = propMgr.GetProperty( TYPE_HASH( D ), "bool" );
386  BOOST_CHECK_EQUAL( prop, nullptr );
387 
388  prop = propMgr.GetProperty( TYPE_HASH( D ), "replaced_bool" );
389  BOOST_CHECK( prop );
390 }
391 
392 // Different subset of enum values for a property
393 BOOST_AUTO_TEST_CASE( AlternativeEnum )
394 {
395  PROPERTY_BASE* prop = propMgr.GetProperty( TYPE_HASH( E ), "enumGlob" );
396  BOOST_CHECK( prop->HasChoices() );
397 
398  wxArrayInt values;
399  values.Add( enum_glob::TEST1 );
400  values.Add( enum_glob::TEST3 );
401  wxArrayString labels;
402  labels.Add( "T1" );
403  labels.Add( "T3" );
404 
405  const wxPGChoices& v = prop->Choices();
406  BOOST_CHECK_EQUAL( v.GetCount(), values.GetCount() );
407  BOOST_CHECK_EQUAL( v.GetCount(), labels.GetCount() );
408 
409  for (int i = 0; i < values.GetCount(); ++i )
410  {
411  BOOST_CHECK_EQUAL( v.GetValue( i ), values[i] );
412  }
413 
414  for (int i = 0; i < labels.GetCount(); ++i )
415  {
416  BOOST_CHECK_EQUAL( v.GetLabel( i ), labels[i] );
417  }
418 }
419 
420 BOOST_AUTO_TEST_SUITE_END()
void setCond(int a)
const wxPoint & getPoint() const
virtual int getA() const =0
void setC(int a)
virtual bool HasChoices() const
Return true if this PROPERTY has a limited set of possible values.
Definition: property.h:216
static PROPERTY_MANAGER & Instance()
Definition: property_mgr.h:65
#define TYPE_HASH(x)
Definition: property.h:59
static struct ENUM_GLOB_DESC _ENUM_GLOB_DESC
static struct CLASS_E_DESC _CLASS_E_DESC
bool getBool() const
enum_glob getGlobEnum() const
int getCond() const
static ENUM_MAP< T > & Instance()
Definition: property.h:510
Definition: bitmap.cpp:64
static struct CLASS_C_DESC _CLASS_C_DESC
enum_class getClassEnum() const
void setBool(bool a)
BOOST_CHECK(v2.Cross(v1)==1)
static struct CLASS_A_DESC _CLASS_A_DESC
int getNew() const
wxPoint m_p
enum_glob
void setPoint3(wxPoint p)
virtual int getA() const override
wxAny Get(PROPERTY_BASE *aProperty)
Definition: inspectable.h:86
enum_class
void InheritsAfter(TYPE_ID aDerived, TYPE_ID aBase)
Declare an inheritance relationship between types.
void setClassEnum(enum_class val)
const wxPoint & getPoint4() const
wxPoint getPoint3()
Class that other classes need to inherit from, in order to be inspectable.
Definition: inspectable.h:35
virtual void setA(int a) override
bool Init()
Perform application-specific initialization tasks.
Definition: gtk/app.cpp:40
ENUM_TO_WXANY(enum_glob)
virtual const wxPGChoices & Choices() const
Return a limited set of possible values (e.g.
Definition: property.h:198
BOOST_AUTO_TEST_CASE(Init)
enum_class m_enum_class
PROPERTY_MANAGER & propMgr
void setGlobEnum(enum_glob val)
static struct CLASS_B_DESC _CLASS_B_DESC
wxPoint getPoint2() const
int getC() const
void AddTypeCast(TYPE_CAST_BASE *aCast)
Register a type converter.
void AddProperty(PROPERTY_BASE *aProperty)
Register a property.
bool Available(INSPECTABLE *aObject) const
Return true if aObject offers this PROPERTY.
Definition: property.h:224
enum_glob m_enum_glob
void setA(int a) override
void ReplaceProperty(size_t aBase, const wxString &aName, PROPERTY_BASE *aNew)
Replace an existing property for a specific type.
static struct CLASS_D_DESC _CLASS_D_DESC
Provide class metadata.Helper macro to map type hashes to names.
Definition: property_mgr.h:62
virtual void setA(int a)=0
void setNew(int m)
int getA() const override
void setPoint4(wxPoint p)
virtual const int & getA2() const
virtual void setPoint(const wxPoint &p)
void setPoint2(wxPoint &p)