KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_property_holder.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 The KiCad Developers, see AUTHORS.TXT for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
27
29
30// Code under test
31#include <property_holder.h>
32
33#include <string>
34#include <vector>
35#include <memory>
36
37namespace
38{
39
43class TestWidget : public PROPERTY_MIXIN
44{
45public:
46 TestWidget( const std::string& name ) : m_name( name )
47 {
48 SetProperty( "widget_name", name );
49 SetProperty( "default_enabled", true );
50 }
51
52 const std::string& GetName() const { return m_name; }
53
54private:
55 std::string m_name;
56};
57
61struct CustomData
62{
63 int value;
64 std::string text;
65
66 CustomData( int v, const std::string& t ) : value( v ), text( t ) {}
67
68 bool operator==( const CustomData& other ) const
69 {
70 return value == other.value && text == other.text;
71 }
72};
73
77struct PROPERTY_HOLDER_TEST_FIXTURE
78{
79 PROPERTY_HOLDER_TEST_FIXTURE()
80 {
81 // Set up a holder with various types
82 m_populatedHolder.SetProperty( "bool_prop", true );
83 m_populatedHolder.SetProperty( "int_prop", 42 );
84 m_populatedHolder.SetProperty( "double_prop", 3.14159 );
85 m_populatedHolder.SetProperty( "string_prop", std::string( "hello world" ) );
86 m_populatedHolder.SetProperty( "cstring_prop", "c-style string" );
87
88 // Add a custom object
89 m_populatedHolder.SetProperty( "custom_prop", CustomData( 100, "custom" ) );
90
91 // Add a vector
92 std::vector<int> numbers = { 1, 2, 3, 4, 5 };
93 m_populatedHolder.SetProperty( "vector_prop", numbers );
94 }
95
96 PROPERTY_HOLDER m_emptyHolder;
97 PROPERTY_HOLDER m_populatedHolder;
98};
99
100} // namespace
101
105BOOST_FIXTURE_TEST_SUITE( TEST_PROPERTY_HOLDER, PROPERTY_HOLDER_TEST_FIXTURE )
106
107
111{
112 PROPERTY_HOLDER holder;
113
114 // Test various basic types
115 holder.SetProperty( "test_bool", true );
116 holder.SetProperty( "test_int", 123 );
117 holder.SetProperty( "test_double", 2.71828 );
118 holder.SetProperty( "test_string", std::string( "test value" ) );
119
120 // Verify retrieval with correct types
121 auto boolResult = holder.GetProperty<bool>( "test_bool" );
122 BOOST_CHECK( boolResult.has_value() );
123 BOOST_CHECK_EQUAL( *boolResult, true );
124
125 auto intResult = holder.GetProperty<int>( "test_int" );
126 BOOST_CHECK( intResult.has_value() );
127 BOOST_CHECK_EQUAL( *intResult, 123 );
128
129 auto doubleResult = holder.GetProperty<double>( "test_double" );
130 BOOST_CHECK( doubleResult.has_value() );
131 BOOST_CHECK_CLOSE( *doubleResult, 2.71828, 0.0001 );
132
133 auto stringResult = holder.GetProperty<std::string>( "test_string" );
134 BOOST_CHECK( stringResult.has_value() );
135 BOOST_CHECK_EQUAL( *stringResult, "test value" );
136}
137
142{
143 PROPERTY_HOLDER holder;
144 holder.SetProperty( "int_value", 42 );
145
146 // Correct type retrieval
147 auto intResult = holder.GetProperty<int>( "int_value" );
148 BOOST_CHECK( intResult.has_value() );
149 BOOST_CHECK_EQUAL( *intResult, 42 );
150
151 // Wrong type retrieval should return nullopt
152 auto stringResult = holder.GetProperty<std::string>( "int_value" );
153 BOOST_CHECK( !stringResult.has_value() );
154
155 auto doubleResult = holder.GetProperty<double>( "int_value" );
156 BOOST_CHECK( !doubleResult.has_value() );
157
158 auto boolResult = holder.GetProperty<bool>( "int_value" );
159 BOOST_CHECK( !boolResult.has_value() );
160}
161
165BOOST_AUTO_TEST_CASE( DefaultValues )
166{
167 PROPERTY_HOLDER holder;
168 holder.SetProperty( "existing_prop", 100 );
169
170 // Existing property should return stored value
171 int existingValue = holder.GetPropertyOr( "existing_prop", 999 );
172 BOOST_CHECK_EQUAL( existingValue, 100 );
173
174 // Non-existing property should return default
175 int nonExistingValue = holder.GetPropertyOr( "missing_prop", 777 );
176 BOOST_CHECK_EQUAL( nonExistingValue, 777 );
177
178 // Wrong type should return default
179 std::string wrongTypeValue = holder.GetPropertyOr<std::string>( "existing_prop", "default" );
180 BOOST_CHECK_EQUAL( wrongTypeValue, "default" );
181
182 // Test with different types
183 bool defaultBool = holder.GetPropertyOr( "missing_bool", false );
184 BOOST_CHECK_EQUAL( defaultBool, false );
185
186 std::string defaultString = holder.GetPropertyOr<std::string>( "missing_string", "fallback" );
187 BOOST_CHECK_EQUAL( defaultString, "fallback" );
188}
189
193BOOST_AUTO_TEST_CASE( ExistenceChecking )
194{
195 // Empty holder
196 BOOST_CHECK_EQUAL( false, m_emptyHolder.HasProperty( "any_key" ) );
197
198 // Populated holder
199 BOOST_CHECK( m_populatedHolder.HasProperty( "bool_prop" ) );
200 BOOST_CHECK( m_populatedHolder.HasProperty( "int_prop" ) );
201 BOOST_CHECK( m_populatedHolder.HasProperty( "string_prop" ) );
202 BOOST_CHECK_EQUAL( false, m_populatedHolder.HasProperty( "missing_prop" ) );
203}
204
208BOOST_AUTO_TEST_CASE( TypeSpecificExistence )
209{
210 // Check correct type detection
211 BOOST_CHECK( m_populatedHolder.HasPropertyOfType<bool>( "bool_prop" ) );
212 BOOST_CHECK( m_populatedHolder.HasPropertyOfType<int>( "int_prop" ) );
213 BOOST_CHECK( m_populatedHolder.HasPropertyOfType<double>( "double_prop" ) );
214 BOOST_CHECK( m_populatedHolder.HasPropertyOfType<std::string>( "string_prop" ) );
215
216 // Check wrong type detection
217 BOOST_CHECK_EQUAL( false, m_populatedHolder.HasPropertyOfType<int>( "bool_prop" ) );
218 BOOST_CHECK_EQUAL( false, m_populatedHolder.HasPropertyOfType<bool>( "int_prop" ) );
219 BOOST_CHECK_EQUAL( false, m_populatedHolder.HasPropertyOfType<std::string>( "int_prop" ) );
220
221 // Non-existing property
222 BOOST_CHECK_EQUAL( false, m_populatedHolder.HasPropertyOfType<bool>( "missing_prop" ) );
223}
224
228BOOST_AUTO_TEST_CASE( PropertyRemoval )
229{
230 PROPERTY_HOLDER holder;
231 holder.SetProperty( "temp_prop", 42 );
232
233 // Property should exist
234 BOOST_CHECK( holder.HasProperty( "temp_prop" ) );
235 BOOST_CHECK_EQUAL( 1, holder.Size() );
236
237 // Remove existing property
238 bool removed = holder.RemoveProperty( "temp_prop" );
239 BOOST_CHECK( removed );
240 BOOST_CHECK_EQUAL( false, holder.HasProperty( "temp_prop" ) );
241 BOOST_CHECK_EQUAL( 0, holder.Size() );
242
243 // Try to remove non-existing property
244 bool notRemoved = holder.RemoveProperty( "non_existing" );
245 BOOST_CHECK_EQUAL( false, notRemoved );
246}
247
251BOOST_AUTO_TEST_CASE( ClearProperties )
252{
253 PROPERTY_HOLDER holder;
254 holder.SetProperty( "prop1", 1 );
255 holder.SetProperty( "prop2", "two" );
256 holder.SetProperty( "prop3", true );
257
258 BOOST_CHECK_EQUAL( 3, holder.Size() );
259 BOOST_CHECK_EQUAL( false, holder.Empty() );
260
261 holder.Clear();
262
263 BOOST_CHECK_EQUAL( 0, holder.Size() );
264 BOOST_CHECK( holder.Empty() );
265 BOOST_CHECK_EQUAL( false, holder.HasProperty( "prop1" ) );
266 BOOST_CHECK_EQUAL( false, holder.HasProperty( "prop2" ) );
267 BOOST_CHECK_EQUAL( false, holder.HasProperty( "prop3" ) );
268}
269
273BOOST_AUTO_TEST_CASE( SizeAndEmpty )
274{
275 // Empty holder
276 BOOST_CHECK_EQUAL( 0, m_emptyHolder.Size() );
277 BOOST_CHECK( m_emptyHolder.Empty() );
278
279 // Populated holder (from fixture: 6 properties)
280 BOOST_CHECK_EQUAL( 7, m_populatedHolder.Size() );
281 BOOST_CHECK_EQUAL( false, m_populatedHolder.Empty() );
282
283 // Add one more
284 m_populatedHolder.SetProperty( "new_prop", 999 );
285 BOOST_CHECK_EQUAL( 8, m_populatedHolder.Size() );
286
287 // Remove one
288 m_populatedHolder.RemoveProperty( "new_prop" );
289 BOOST_CHECK_EQUAL( 7, m_populatedHolder.Size() );
290}
291
295BOOST_AUTO_TEST_CASE( KeyRetrieval )
296{
297 PROPERTY_HOLDER holder;
298 holder.SetProperty( "key1", 1 );
299 holder.SetProperty( "key2", "two" );
300 holder.SetProperty( "key3", true );
301
302 auto keys = holder.GetKeys();
303 BOOST_CHECK_EQUAL( 3, keys.size() );
304
305 // Keys should contain all our added keys (order not guaranteed)
306 std::sort( keys.begin(), keys.end() );
307 std::vector<std::string> expected = { "key1", "key2", "key3" };
308 std::sort( expected.begin(), expected.end() );
309
310 BOOST_CHECK_EQUAL_COLLECTIONS( keys.begin(), keys.end(), expected.begin(), expected.end() );
311
312 // Empty holder should return empty vector
313 auto emptyKeys = m_emptyHolder.GetKeys();
314 BOOST_CHECK_EQUAL( 0, emptyKeys.size() );
315}
316
320BOOST_AUTO_TEST_CASE( TypeInformation )
321{
322 PROPERTY_HOLDER holder;
323 holder.SetProperty( "int_val", 42 );
324 holder.SetProperty( "string_val", std::string( "hello" ) );
325
326 // Existing properties should return type info
327 auto intTypeInfo = holder.GetPropertyType( "int_val" );
328 BOOST_CHECK( intTypeInfo.has_value() );
329 BOOST_CHECK( intTypeInfo->get() == typeid( int ) );
330
331 auto stringTypeInfo = holder.GetPropertyType( "string_val" );
332 BOOST_CHECK( stringTypeInfo.has_value() );
333 BOOST_CHECK( stringTypeInfo->get() == typeid( std::string ) );
334
335 // Non-existing property should return nullopt
336 auto missingTypeInfo = holder.GetPropertyType( "missing" );
337 BOOST_CHECK( !missingTypeInfo.has_value() );
338}
339
343BOOST_AUTO_TEST_CASE( ComplexObjects )
344{
345 PROPERTY_HOLDER holder;
346
347 // Store custom object
348 CustomData original( 123, "test data" );
349 holder.SetProperty( "custom", original );
350
351 // Retrieve and verify
352 auto retrieved = holder.GetProperty<CustomData>( "custom" );
353 BOOST_CHECK( retrieved.has_value() );
354 BOOST_CHECK( *retrieved == original );
355
356 // Store vector
357 std::vector<std::string> stringVec = { "one", "two", "three" };
358 holder.SetProperty( "string_vector", stringVec );
359
360 auto vecRetrieved = holder.GetProperty<std::vector<std::string>>( "string_vector" );
361 BOOST_CHECK( vecRetrieved.has_value() );
362 BOOST_CHECK_EQUAL_COLLECTIONS( vecRetrieved->begin(), vecRetrieved->end(),
363 stringVec.begin(), stringVec.end() );
364
365 // Store smart pointer
366 auto smartPtr = std::make_shared<int>( 456 );
367 holder.SetProperty( "smart_ptr", smartPtr );
368
369 auto ptrRetrieved = holder.GetProperty<std::shared_ptr<int>>( "smart_ptr" );
370 BOOST_CHECK( ptrRetrieved.has_value() );
371 BOOST_CHECK_EQUAL( **ptrRetrieved, 456 );
372 BOOST_CHECK_EQUAL( ptrRetrieved->use_count(), 3 ); // Original + stored + retrieved copy
373}
374
378BOOST_AUTO_TEST_CASE( PropertyOverwriting )
379{
380 PROPERTY_HOLDER holder;
381
382 // Set initial value
383 holder.SetProperty( "changeable", 100 );
384 BOOST_CHECK_EQUAL( holder.GetPropertyOr( "changeable", 0 ), 100 );
385 BOOST_CHECK_EQUAL( 1, holder.Size() );
386
387 // Overwrite with same type
388 holder.SetProperty( "changeable", 200 );
389 BOOST_CHECK_EQUAL( holder.GetPropertyOr( "changeable", 0 ), 200 );
390 BOOST_CHECK_EQUAL( 1, holder.Size() ); // Size shouldn't change
391
392 // Overwrite with different type
393 holder.SetProperty( "changeable", std::string( "now a string" ) );
394 BOOST_CHECK_EQUAL( holder.GetPropertyOr<std::string>( "changeable", "default" ), "now a string" );
395 BOOST_CHECK_EQUAL( 1, holder.Size() ); // Size still shouldn't change
396
397 // Original type should no longer work
398 auto intResult = holder.GetProperty<int>( "changeable" );
399 BOOST_CHECK( !intResult.has_value() );
400}
401
403
404
407BOOST_AUTO_TEST_SUITE( TEST_PROPERTY_MIXIN )
408
413{
414 TestWidget widget( "test_widget" );
415
416 // Properties set in constructor should be available
417 BOOST_CHECK_EQUAL( widget.GetPropertyOr<std::string>( "widget_name", "" ), "test_widget" );
418 BOOST_CHECK_EQUAL( widget.GetPropertyOr( "default_enabled", false ), true );
419
420 // Should be able to add new properties
421 widget.SetProperty( "runtime_prop", 42 );
422 BOOST_CHECK_EQUAL( widget.GetPropertyOr( "runtime_prop", 0 ), 42 );
423
424 // Should be able to check existence
425 BOOST_CHECK( widget.HasProperty( "widget_name" ) );
426 BOOST_CHECK( widget.HasProperty( "default_enabled" ) );
427 BOOST_CHECK( widget.HasProperty( "runtime_prop" ) );
428 BOOST_CHECK_EQUAL( false, widget.HasProperty( "missing_prop" ) );
429}
430
434BOOST_AUTO_TEST_CASE( MultipleInstances )
435{
436 TestWidget widget1( "widget1" );
437 TestWidget widget2( "widget2" );
438
439 // Each should have independent properties
440 widget1.SetProperty( "unique_to_1", 100 );
441 widget2.SetProperty( "unique_to_2", 200 );
442
443 BOOST_CHECK_EQUAL( widget1.GetPropertyOr( "unique_to_1", 0 ), 100 );
444 BOOST_CHECK_EQUAL( widget1.GetPropertyOr( "unique_to_2", 0 ), 0 ); // Should get default
445
446 BOOST_CHECK_EQUAL( widget2.GetPropertyOr( "unique_to_1", 0 ), 0 ); // Should get default
447 BOOST_CHECK_EQUAL( widget2.GetPropertyOr( "unique_to_2", 0 ), 200 );
448
449 // Names should be different
450 BOOST_CHECK_EQUAL( widget1.GetPropertyOr<std::string>( "widget_name", "" ), "widget1" );
451 BOOST_CHECK_EQUAL( widget2.GetPropertyOr<std::string>( "widget_name", "" ), "widget2" );
452}
453
457BOOST_AUTO_TEST_CASE( PropertyHolderAccess )
458{
459 TestWidget widget( "access_test" );
460
461 // Should be able to access the holder directly
462 PROPERTY_HOLDER& holder = widget.GetPropertyHolder();
463 const PROPERTY_HOLDER& constHolder = widget.GetPropertyHolder();
464
465 // Operations on holder should affect widget properties
466 holder.SetProperty( "direct_access", 999 );
467 BOOST_CHECK_EQUAL( widget.GetPropertyOr( "direct_access", 0 ), 999 );
468
469 // Should be able to use holder methods
470 BOOST_CHECK( constHolder.HasProperty( "widget_name" ) );
471 auto keys = constHolder.GetKeys();
472 BOOST_CHECK( keys.size() >= 2 ); // At least widget_name and default_enabled
473}
474
476
477
480BOOST_AUTO_TEST_SUITE( PropertyHolderEdgeCases )
481
486{
487 PROPERTY_HOLDER holder;
488
489 // Empty key should work (though not recommended)
490 holder.SetProperty( "", 42 );
491 BOOST_CHECK( holder.HasProperty( "" ) );
492 BOOST_CHECK_EQUAL( holder.GetPropertyOr( "", 0 ), 42 );
493
494 // Very long key should work
495 std::string longKey( 1000, 'x' );
496 holder.SetProperty( longKey, "long key value" );
497 BOOST_CHECK( holder.HasProperty( longKey ) );
498}
499
503BOOST_AUTO_TEST_CASE( MoveSemantics )
504{
505 PROPERTY_HOLDER holder;
506
507 // Test move construction of value
508 std::vector<int> bigVector( 1000, 42 );
509 holder.SetProperty( "big_vector", std::move( bigVector ) );
510
511 // Original vector should be moved from (implementation detail, but worth testing)
512 auto retrieved = holder.GetProperty<std::vector<int>>( "big_vector" );
513 BOOST_CHECK( retrieved.has_value() );
514 BOOST_CHECK_EQUAL( retrieved->size(), 1000 );
515 BOOST_CHECK_EQUAL( (*retrieved)[0], 42 );
516}
517
521BOOST_AUTO_TEST_CASE( NullScenarios )
522{
523 PROPERTY_HOLDER holder;
524
525 // Getting from empty holder
526 auto result = holder.GetProperty<int>( "anything" );
527 BOOST_CHECK( !result.has_value() );
528
529 // Getting with empty key
530 auto emptyResult = holder.GetProperty<int>( "" );
531 BOOST_CHECK( !emptyResult.has_value() );
532
533 // Type info for non-existing property
534 auto typeInfo = holder.GetPropertyType( "missing" );
535 BOOST_CHECK( !typeInfo.has_value() );
536
537 // Removing non-existing property
538 BOOST_CHECK_EQUAL( false, holder.RemoveProperty( "non_existing" ) );
539}
540
542
543
546BOOST_AUTO_TEST_SUITE( PropertyHolderMagicValue )
547
551BOOST_AUTO_TEST_CASE( MagicValueValidation )
552{
553 PROPERTY_HOLDER holder;
554
555 // New holder should be valid
556 BOOST_CHECK( holder.IsValid() );
557 BOOST_CHECK_EQUAL( PROPERTY_HOLDER::MAGIC_VALUE, 0x50524F5048444C52ULL );
558
559 // Should be able to use all functions when valid
560 BOOST_CHECK( holder.SetProperty( "test", 42 ) );
561 BOOST_CHECK( holder.HasProperty( "test" ) );
562 BOOST_CHECK_EQUAL( holder.GetPropertyOr( "test", 0 ), 42 );
563}
564
569{
570 // Create a PROPERTY_HOLDER
571 PROPERTY_HOLDER* original = new PROPERTY_HOLDER();
572 original->SetProperty( "test_prop", 123 );
573
574 // Cast to void* (simulating client data storage)
575 void* clientData = static_cast<void*>( original );
576
577 // Safe cast should succeed
578 PROPERTY_HOLDER* casted = PROPERTY_HOLDER::SafeCast( clientData );
579 BOOST_CHECK_NE( nullptr, casted );
580 BOOST_CHECK( casted->IsValid() );
581 BOOST_CHECK_EQUAL( casted->GetPropertyOr( "test_prop", 0 ), 123 );
582
583 // Should be the same object
584 BOOST_CHECK_EQUAL( original, casted );
585
586 // Const version should also work
587 const void* constClientData = static_cast<const void*>( original );
588 const PROPERTY_HOLDER* constCasted = PROPERTY_HOLDER::SafeCast( constClientData );
589 BOOST_CHECK_NE( nullptr, constCasted );
590 BOOST_CHECK( constCasted->IsValid() );
591
592 delete original;
593}
594
598BOOST_AUTO_TEST_CASE( SafeCastingInvalid )
599{
600 // Null pointer should return null
601 BOOST_CHECK_EQUAL( nullptr, PROPERTY_HOLDER::SafeCast( static_cast<void*>( nullptr ) ) );
602 BOOST_CHECK_EQUAL( nullptr, PROPERTY_HOLDER::SafeCast( static_cast<const void*>( nullptr ) ) );
603
604 // Random memory should return null. Use uint64_t to match the size of the magic value
605 // that SafeCast reads, avoiding buffer overflow when reading the magic field.
606 uint64_t randomValue = 12345;
607 void* randomPtr = &randomValue;
608 BOOST_CHECK_EQUAL( nullptr, PROPERTY_HOLDER::SafeCast( randomPtr ) );
609
610 // Memory with wrong magic value should return null
611 struct FakeHolder {
612 uint64_t wrong_magic = 0x1234567890ABCDEFULL;
613 int some_data = 42;
614 };
615
616 FakeHolder fake;
617 void* fakePtr = &fake;
618 BOOST_CHECK_EQUAL( nullptr, PROPERTY_HOLDER::SafeCast( fakePtr ) );
619}
620
628// Detect AddressSanitizer / ThreadSanitizer - must use nested #if because __has_feature()
629// can't appear in a preprocessor expression on compilers that don't define it. Either
630// sanitizer flags the deliberate read-through-freed-pointer that this test relies on.
631#if defined( __has_feature )
632 #if __has_feature( address_sanitizer ) || __has_feature( thread_sanitizer )
633 #define KICAD_ASAN_ENABLED 1
634 #endif
635#endif
636
637#if defined( __SANITIZE_ADDRESS__ ) || defined( __SANITIZE_THREAD__ )
638 #define KICAD_ASAN_ENABLED 1
639#endif
640
641#ifndef KICAD_ASAN_ENABLED
642BOOST_AUTO_TEST_CASE( UseAfterFreeDetection )
643{
644 PROPERTY_HOLDER* holder = new PROPERTY_HOLDER();
645 holder->SetProperty( "test", 42 );
646
647 // Should be valid before deletion
648 BOOST_CHECK( holder->IsValid() );
649
650 void* ptr = static_cast<void*>( holder );
651 BOOST_CHECK_NE( nullptr, PROPERTY_HOLDER::SafeCast( ptr ) );
652
653 // Delete the holder
654 delete holder;
655
656 // Now safe cast should fail (magic value was cleared in destructor)
658}
659#endif
660
661#undef KICAD_ASAN_ENABLED
662
666BOOST_AUTO_TEST_CASE( ClientDataHelpers )
667{
668 PROPERTY_HOLDER* holder = new PROPERTY_HOLDER();
669 BOOST_CHECK_NE( nullptr, holder );
670 BOOST_CHECK( holder->IsValid() );
671
672 // Should be able to use it
673 BOOST_CHECK( holder->SetProperty( "client_test", 999 ) );
674 BOOST_CHECK_EQUAL( holder->GetPropertyOr( "client_test", 0 ), 999 );
675
676 // Safe delete should succeed
677 BOOST_CHECK( PROPERTY_HOLDER::SafeDelete( reinterpret_cast<void*>( holder ) ) );
678
679 // Safe delete of invalid pointer should fail. Use uint64_t to match the size of the magic
680 // value that SafeCast reads, avoiding buffer overflow when reading the magic field.
681 uint64_t randomData = 42;
682 BOOST_CHECK_EQUAL( false, PROPERTY_HOLDER::SafeDelete( &randomData ) );
683 BOOST_CHECK_EQUAL( false, PROPERTY_HOLDER::SafeDelete( static_cast<void*>( nullptr ) ) );
684}
685
689BOOST_AUTO_TEST_CASE( CopyMoveConstructors )
690{
691 PROPERTY_HOLDER original;
692 original.SetProperty( "test", 42 );
693 BOOST_CHECK( original.IsValid() );
694
695 // Copy constructor
696 PROPERTY_HOLDER copied( original );
697 BOOST_CHECK( copied.IsValid() );
698 BOOST_CHECK_EQUAL( copied.GetPropertyOr( "test", 0 ), 42 );
699
700 // Move constructor
701 PROPERTY_HOLDER moved( std::move( original ) );
702 BOOST_CHECK( moved.IsValid() );
703 BOOST_CHECK_EQUAL( moved.GetPropertyOr( "test", 0 ), 42 );
704
705 // Original should still be valid (magic value preserved)
706 BOOST_CHECK( original.IsValid() );
707}
708
712BOOST_AUTO_TEST_CASE( AssignmentOperators )
713{
714 PROPERTY_HOLDER source;
715 source.SetProperty( "source_prop", 123 );
716
717 PROPERTY_HOLDER target;
718 target.SetProperty( "target_prop", 456 );
719
720 BOOST_CHECK( source.IsValid() );
721 BOOST_CHECK( target.IsValid() );
722
723 // Copy assignment
724 target = source;
725 BOOST_CHECK( target.IsValid() );
726 BOOST_CHECK_EQUAL( target.GetPropertyOr( "source_prop", 0 ), 123 );
727 BOOST_CHECK_EQUAL( false, target.HasProperty( "target_prop" ) ); // Should be overwritten
728
729 // Move assignment
730 PROPERTY_HOLDER another;
731 another.SetProperty( "another_prop", 789 );
732
733 target = std::move( another );
734 BOOST_CHECK( target.IsValid() );
735 BOOST_CHECK_EQUAL( target.GetPropertyOr( "another_prop", 0 ), 789 );
736 BOOST_CHECK( another.IsValid() ); // Magic value preserved
737}
738
742BOOST_AUTO_TEST_CASE( InvalidObjectBehavior )
743{
744 // Create a fake PROPERTY_HOLDER with wrong magic value
745 struct FakePropertyHolder {
746 uint64_t wrong_magic = 0xDEADBEEF;
747 std::unordered_map<std::string, std::any> fake_properties;
748 };
749
750 FakePropertyHolder fake;
751 PROPERTY_HOLDER* fakeHolder = reinterpret_cast<PROPERTY_HOLDER*>( &fake );
752
753 // All methods should fail gracefully
754 BOOST_CHECK_EQUAL( false, fakeHolder->IsValid() );
755 BOOST_CHECK_EQUAL( false, fakeHolder->SetProperty( "test", 42 ) );
756 BOOST_CHECK_EQUAL( false, fakeHolder->HasProperty( "anything" ) );
757 BOOST_CHECK_EQUAL( false, fakeHolder->RemoveProperty( "anything" ) );
758 BOOST_CHECK_EQUAL( false, fakeHolder->Clear() );
759 BOOST_CHECK_EQUAL( 0, fakeHolder->Size() );
760 BOOST_CHECK( fakeHolder->Empty() ); // Returns true for invalid objects
761 BOOST_CHECK_EQUAL( 0, fakeHolder->GetKeys().size() );
762 BOOST_CHECK_EQUAL( false, fakeHolder->HasPropertyOfType<int>( "anything" ) );
763
764 // GetProperty should return nullopt
765 auto result = fakeHolder->GetProperty<int>( "anything" );
766 BOOST_CHECK( !result.has_value() );
767
768 // GetPropertyOr should return default
769 BOOST_CHECK_EQUAL( fakeHolder->GetPropertyOr( "anything", 999 ), 999 );
770
771 // GetPropertyType should return nullopt
772 auto typeInfo = fakeHolder->GetPropertyType( "anything" );
773 BOOST_CHECK( !typeInfo.has_value() );
774}
775
const char * name
bool operator==(const wxAuiPaneInfo &aLhs, const wxAuiPaneInfo &aRhs)
bool SetProperty(const std::string &aKey, T &&aValue)
Set a property with the given key and value.
static bool SafeDelete(void *aPtr) noexcept
Safely delete a PROPERTY_HOLDER from client data.
std::optional< std::reference_wrapper< const std::type_info > > GetPropertyType(const std::string &aKey) const
Get the type information for a property.
bool Empty() const
Check if there are no properties stored.
std::vector< std::string > GetKeys() const
Get all property keys.
static PROPERTY_HOLDER * SafeCast(void *aPtr) noexcept
Safely cast a void pointer to PROPERTY_HOLDER*.
bool RemoveProperty(const std::string &aKey)
Remove a property.
static constexpr uint64_t MAGIC_VALUE
Magic value for memory validation (ASCII: "PROP" + "HLDR")
std::optional< T > GetProperty(const std::string &aKey) const
Get a property value with type checking.
T GetPropertyOr(const std::string &aKey, T &&aDefaultValue) const
Get a property value with a default fallback.
bool HasPropertyOfType(const std::string &aKey) const
Check if a property exists and has the expected type.
bool HasProperty(const std::string &aKey) const
Check if a property exists.
bool IsValid() const noexcept
Check if this instance has a valid magic value.
bool Clear()
Clear all properties.
size_t Size() const
Get the number of stored properties.
Mixin class to add property support to any class.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
bool copied
bool moved
VECTOR3I expected(15, 30, 45)
BOOST_AUTO_TEST_CASE(BasicSetGet)
Declare the test suite.
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")