KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 <[email protected]>
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
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
22#include <wx/gdicmn.h> // wxPoint
23
24#include <inspectable_impl.h>
26
27using namespace std;
28
29class A : public INSPECTABLE
30{
31public:
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
46protected:
47 int m_a = 0;
48 wxPoint m_p;
49};
50
51class B : public A
52{
53public:
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
60private:
61 int m_c = 0;
62};
63
64class C
65{
66public:
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
77enum enum_glob { TEST1 = 0, TEST2 = 1, TEST3 = 4 };
78
79class D : public A, public C
80{
81public:
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
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
103class E : public D
104{
105};
106
107static 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
120static 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 // TODO non-const getters are not supported
131 //propMgr.AddProperty( new PROPERTY<A, wxPoint>( "point3", &A::setPoint3, &A::getPoint3 ) );
132 propMgr.AddProperty( new PROPERTY<A, wxPoint>( "point4", &A::setPoint4, &A::getPoint4 ) );
133 }
135
136static struct CLASS_B_DESC
137{
139 {
141 propMgr.InheritsAfter( TYPE_HASH( B ), TYPE_HASH( A ) );
142 propMgr.AddProperty( new PROPERTY<B, int>( "C", &B::setC, &B::getC ) );
143 }
145
146static struct CLASS_C_DESC
147{
149 {
151 propMgr.AddProperty( new PROPERTY<C, bool>( "bool", &C::setBool, &C::getBool ) );
152 propMgr.AddProperty( new PROPERTY<C, int>( "new", &C::setNew, &C::getNew ) );
153 }
155
156static struct CLASS_D_DESC
157{
159 {
161 .Map( D::enum_class::TESTA, "TESTA" )
162 .Map( D::enum_class::TESTB, "TESTB" )
163 .Map( D::enum_class::TESTC, "TESTC" );
164
166 propMgr.AddProperty( new PROPERTY_ENUM<D, enum_glob>( "enumGlob", &D::setGlobEnum, &D::getGlobEnum ) );
167 propMgr.AddProperty( new PROPERTY_ENUM<D, D::enum_class>( "enumClass", &D::setClassEnum, &D::getClassEnum ) );
168 propMgr.AddProperty( new PROPERTY<D, wxPoint, A>( "point_alias", &D::setPoint, &D::getPoint ) );
169
170 propMgr.ReplaceProperty( TYPE_HASH( C ), "bool",
171 new PROPERTY<D, bool, C>( "replaced_bool", &D::setBool, &D::getBool ) );
172
173 // lines below are needed to indicate multiple inheritance
174 propMgr.AddTypeCast( new TYPE_CAST<D, A> );
175 propMgr.AddTypeCast( new TYPE_CAST<D, C> );
176 propMgr.InheritsAfter( TYPE_HASH( D ), TYPE_HASH( A ) );
177 propMgr.InheritsAfter( TYPE_HASH( D ), TYPE_HASH( C ) );
178
179 propMgr.AddProperty( new PROPERTY<D, int>( "cond",
180 &D::setCond, &D::getCond ) )
181 .SetAvailableFunc( [=]( INSPECTABLE* aItem )->bool
182 {
183 return *aItem->Get<enum_glob>( "enumGlob" ) == enum_glob::TEST1;
184 } );
185 }
187
188static struct CLASS_E_DESC
189{
191 {
192 wxArrayInt values;
193 values.Add( enum_glob::TEST1 );
194 values.Add( enum_glob::TEST3 );
195 wxArrayString labels;
196 labels.Add( "T1" );
197 labels.Add( "T3" );
198 wxPGChoices newChoices( labels, values );
199
201 auto prop = new PROPERTY_ENUM<E, enum_glob, D>( "enumGlob",
203 prop->SetChoices( newChoices );
204 propMgr.ReplaceProperty( TYPE_HASH( D ), "enumGlob", prop );
205 }
207
208ENUM_TO_WXANY( D::enum_class );
209
210
212{
214 ptr( nullptr ),
215 propMgr( PROPERTY_MANAGER::Instance() )
216 {
217 }
218
223};
224
225BOOST_FIXTURE_TEST_SUITE( Properties, PropertiesFixture )
226
227// Basic Set() & Get()
229{
230 ptr = &b;
231 ptr->Set( "A", 100 );
232 ptr->Set( "point", wxPoint( 100, 200 ) );
233 BOOST_CHECK_EQUAL( *ptr->Get<int>( "A" ), 100 );
234 BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point" ), wxPoint( 100, 200 ) );
235
236 ptr = &d;
237 ptr->Set( "enumGlob", enum_glob::TEST2 );
238 ptr->Set( "enumClass", D::enum_class::TESTC );
239 BOOST_CHECK_EQUAL( *ptr->Get<enum_glob>( "enumGlob" ), enum_glob::TEST2 );
240 BOOST_CHECK_EQUAL( *ptr->Get<D::enum_class>( "enumClass" ), D::enum_class::TESTC );
241}
242
243// Virtual methods
244BOOST_AUTO_TEST_CASE( VirtualMethods )
245{
246 // D::setA() saves a doubled value, while B::setA() saves unmodified value
247 ptr = &b;
248 ptr->Set( "A", 23 );
249 BOOST_CHECK_EQUAL( *ptr->Get<int>( "A" ), 23 ); // unmodified == 23
250
251 ptr = &d;
252 ptr->Set( "A", 23 );
253 BOOST_CHECK_EQUAL( *ptr->Get<int>( "A" ), 46 ); // doubled == 46
254}
255
256// Non-existing properties
257BOOST_AUTO_TEST_CASE( NotexistingProperties )
258{
259 ptr = &d;
260 BOOST_CHECK_EQUAL( ptr->Set<int>( "does not exist", 5 ), false );
261 BOOST_CHECK_EQUAL( static_cast<bool>( ptr->Get<int>( "neither" ) ), false );
262}
263
264// Request data using incorrect type
265BOOST_AUTO_TEST_CASE( IncorrectType )
266{
267 ptr = &d;
268 BOOST_CHECK_THROW( ptr->Get<wxPoint>( "A" ), std::invalid_argument );
269}
270
271// Type-casting (for types with multiple inheritance)
273{
274 ptr = &d;
275 A* D_to_A = static_cast<A*>( propMgr.TypeCast( ptr, TYPE_HASH( D ), TYPE_HASH( A ) ) );
276 BOOST_CHECK_EQUAL( D_to_A, dynamic_cast<A*>( ptr ) );
277
278 C* D_to_C = static_cast<C*>( propMgr.TypeCast( ptr, TYPE_HASH( D ), TYPE_HASH( C ) ) );
279 BOOST_CHECK_EQUAL( D_to_C, dynamic_cast<C*>( ptr ) );
280}
281
283{
284 PROPERTY_BASE* prop = propMgr.GetProperty( TYPE_HASH( D ), "enumGlob" );
285 BOOST_CHECK( prop->HasChoices() );
286
287 wxArrayInt values;
288 values.Add( enum_glob::TEST1 );
289 values.Add( enum_glob::TEST2 );
290 values.Add( enum_glob::TEST3 );
291 wxArrayString labels;
292 labels.Add( "TEST1" );
293 labels.Add( "TEST2" );
294 labels.Add( "TEST3" );
295
296 const wxPGChoices& v = prop->Choices();
297 BOOST_CHECK_EQUAL( v.GetCount(), values.GetCount() );
298 BOOST_CHECK_EQUAL( v.GetCount(), labels.GetCount() );
299
300 for (unsigned int i = 0; i < values.GetCount(); ++i )
301 {
302 BOOST_CHECK_EQUAL( v.GetValue( i ), values[i] );
303 }
304
305 for (unsigned int i = 0; i < labels.GetCount(); ++i )
306 {
307 BOOST_CHECK_EQUAL( v.GetLabel( i ), labels[i] );
308 }
309
310 D item ;
311 wxString str;
312
313 item.setGlobEnum( static_cast<enum_glob>( -1 ) );
314 wxAny any = item.Get( prop );
315 BOOST_CHECK_EQUAL( any.GetAs<wxString>( &str ), false );
316
318 any = item.Get( prop );
319 BOOST_CHECK_EQUAL( any.GetAs<wxString>( &str ), true );
320}
321
323{
324 PROPERTY_BASE* prop = propMgr.GetProperty( TYPE_HASH( D ), "enumClass" );
325 BOOST_CHECK( prop->HasChoices() );
326
327 wxArrayInt values;
328 values.Add( D::enum_class::TESTA );
329 values.Add( D::enum_class::TESTB );
330 values.Add( D::enum_class::TESTC );
331 wxArrayString labels;
332 labels.Add( "TESTA" );
333 labels.Add( "TESTB" );
334 labels.Add( "TESTC" );
335
336 const wxPGChoices& v = prop->Choices();
337 BOOST_CHECK_EQUAL( v.GetCount(), values.GetCount() );
338 BOOST_CHECK_EQUAL( v.GetCount(), labels.GetCount() );
339
340 for (unsigned int i = 0; i < values.GetCount(); ++i )
341 {
342 BOOST_CHECK_EQUAL( v.GetValue( i ), values[i] );
343 }
344
345 for (unsigned int i = 0; i < labels.GetCount(); ++i )
346 {
347 BOOST_CHECK_EQUAL( v.GetLabel( i ), labels[i] );
348 }
349}
350
351// Tests conditional properties (which may depend on values or other properties)
352BOOST_AUTO_TEST_CASE( Availability )
353{
354 PROPERTY_BASE* propCond = propMgr.GetProperty( TYPE_HASH( D ), "cond" );
355 ptr = &d;
356
357 // "cond" property is available only when "a" field is greater than 50 //TODO fix desc
358 d.setGlobEnum( enum_glob::TEST3 );
359 BOOST_CHECK( !propCond->Available( ptr ) );
360
361 d.setGlobEnum( enum_glob::TEST1 );
362 BOOST_CHECK( propCond->Available( ptr ) );
363}
364
365// Using a different name for a parent property
367{
368 ptr = &d;
369
370 ptr->Set( "point", wxPoint( 100, 100 ) );
371 BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point" ), wxPoint( 100, 100 ) );
372 BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point_alias" ), wxPoint( 100, 100 ) );
373
374 ptr->Set( "point_alias", wxPoint( 300, 300 ) );
375 BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point" ), wxPoint( 300, 300 ) );
376 BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point_alias" ), wxPoint( 300, 300 ) );
377}
378
379// Property renaming
381{
382 PROPERTY_BASE* prop;
383
384 prop = propMgr.GetProperty( TYPE_HASH( D ), "bool" );
385 BOOST_CHECK_EQUAL( prop, nullptr );
386
387 prop = propMgr.GetProperty( TYPE_HASH( D ), "replaced_bool" );
388 BOOST_CHECK( prop );
389}
390
391// Different subset of enum values for a property
392BOOST_AUTO_TEST_CASE( AlternativeEnum )
393{
394 PROPERTY_BASE* prop = propMgr.GetProperty( TYPE_HASH( E ), "enumGlob" );
395 BOOST_CHECK( prop->HasChoices() );
396
397 wxArrayInt values;
398 values.Add( enum_glob::TEST1 );
399 values.Add( enum_glob::TEST3 );
400 wxArrayString labels;
401 labels.Add( "T1" );
402 labels.Add( "T3" );
403
404 const wxPGChoices& v = prop->Choices();
405 BOOST_CHECK_EQUAL( v.GetCount(), values.GetCount() );
406 BOOST_CHECK_EQUAL( v.GetCount(), labels.GetCount() );
407
408 for (unsigned int i = 0; i < values.GetCount(); ++i )
409 {
410 BOOST_CHECK_EQUAL( v.GetValue( i ), values[i] );
411 }
412
413 for (unsigned int i = 0; i < labels.GetCount(); ++i )
414 {
415 BOOST_CHECK_EQUAL( v.GetLabel( i ), labels[i] );
416 }
417}
418
419// Duplicate registration should not crash or leak
420BOOST_AUTO_TEST_CASE( DuplicateProperty )
421{
422 PROPERTY_BASE* original = propMgr.GetProperty( TYPE_HASH( B ), "C" );
423 BOOST_REQUIRE( original );
424
425 int origValue = 42;
426 b.setC( origValue );
427
428 // Attempt to register a duplicate property with the same name and owner
429 PROPERTY_BASE& returned = propMgr.AddProperty( new PROPERTY<B, int>( "C", &B::setC, &B::getC ) );
430
431 // AddProperty should return the existing property, not the new one
432 BOOST_CHECK_EQUAL( &returned, original );
433
434 // The original property should still function correctly
435 BOOST_CHECK_EQUAL( *b.Get<int>( "C" ), origValue );
436
437 // Rebuild should succeed without crashing on dangling pointers
438 propMgr.Rebuild();
439
440 PROPERTY_BASE* afterRebuild = propMgr.GetProperty( TYPE_HASH( B ), "C" );
441 BOOST_CHECK_EQUAL( afterRebuild, original );
442}
443
int m_c
int getA() const override
void setC(int a)
void setA(int a) override
int getC() const
bool m_bool
int m_m
bool getBool() const
void setBool(bool a)
void setNew(int m)
int getNew() const
virtual int getA() const override
int getCond() const
enum_class m_enum_class
enum_glob m_enum_glob
enum_glob getGlobEnum() const
void setCond(int a)
enum_class getClassEnum() const
virtual void setA(int a) override
void setGlobEnum(enum_glob val)
int m_cond
int m_aa
void setClassEnum(enum_class val)
static ENUM_MAP< T > & Instance()
Definition property.h:770
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:39
wxAny Get(PROPERTY_BASE *aProperty) const
PROPERTY_BASE & SetAvailableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Set a callback function to determine whether an object provides this property.
Definition property.h:263
virtual bool HasChoices() const
Return true if this PROPERTY has a limited set of possible values.
Definition property.h:247
bool Available(INSPECTABLE *aObject) const
Return true if aObject offers this PROPERTY.
Definition property.h:255
virtual const wxPGChoices & Choices() const
Return a limited set of possible values (e.g.
Definition property.h:227
Provide class metadata.Helper macro to map type hashes to names.
void InheritsAfter(TYPE_ID aDerived, TYPE_ID aBase)
Declare an inheritance relationship between types.
static PROPERTY_MANAGER & Instance()
PROPERTY_BASE & AddProperty(PROPERTY_BASE *aProperty, const wxString &aGroup=wxEmptyString)
Register a property.
PROPERTY_BASE & ReplaceProperty(size_t aBase, const wxString &aName, PROPERTY_BASE *aNew, const wxString &aGroup=wxEmptyString)
Replace an existing property for a specific type.
void AddTypeCast(TYPE_CAST_BASE *aCast)
Register a type converter.
A type-safe container of any type.
Definition ki_any.h:92
STL namespace.
#define TYPE_HASH(x)
Definition property.h:74
#define ENUM_TO_WXANY(type)
Macro to define read-only fields (no setter method available)
Definition property.h:877
#define D(x)
Definition ptree.cpp:37
int m_a
void setPoint2(wxPoint &p)
wxPoint m_p
virtual const int & getA2() const
virtual void setPoint(const wxPoint &p)
wxPoint getPoint3()
const wxPoint & getPoint4() const
virtual int getA() const =0
void setPoint3(wxPoint p)
virtual void setA(int a)=0
A(const A &)=default
void setPoint4(wxPoint p)
wxPoint getPoint2() const
const wxPoint & getPoint() const
PROPERTY_MANAGER & propMgr
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
static struct CLASS_A_DESC _CLASS_A_DESC
static struct CLASS_D_DESC _CLASS_D_DESC
static struct ENUM_GLOB_DESC s_ENUM_GLOB_DESC
enum_glob
@ TEST3
@ TEST1
@ TEST2
static struct CLASS_C_DESC _CLASS_C_DESC
static struct CLASS_E_DESC _CLASS_E_DESC
BOOST_AUTO_TEST_CASE(SetGet)
static struct CLASS_B_DESC _CLASS_B_DESC
BOOST_CHECK_EQUAL(result, "25.4")