KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_identity_reconciler.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, you may find one here:
18 * http://www.gnu.org/licenses/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <boost/test/unit_test.hpp>
25
27
28#include <chrono>
29
30
31using namespace KICAD_DIFF;
32
33
34namespace
35{
36
37ITEM_DESCRIPTOR make( const wxString& aType, const KIID& aUuid,
38 int aX = 0, int aY = 0,
39 std::vector<std::pair<wxString, std::string>> aProps = {} )
40{
42 d.id.push_back( aUuid );
43 d.type = aType;
44 d.position = VECTOR2I( aX, aY );
45 d.bbox = BOX2I( VECTOR2I( aX, aY ), VECTOR2I( 10, 10 ) );
46 d.keyProps = std::move( aProps );
47 return d;
48}
49
50} // namespace
51
52
53BOOST_AUTO_TEST_SUITE( IdentityReconciler )
54
55
56BOOST_AUTO_TEST_CASE( DirectUuidMatch )
57{
59 KIID id1, id2;
60
61 std::vector<ITEM_DESCRIPTOR> a = { make( wxS( "PCB_TRACK" ), id1 ),
62 make( wxS( "PCB_VIA" ), id2 ) };
63
64 std::vector<ITEM_DESCRIPTOR> b = { make( wxS( "PCB_TRACK" ), id1 ),
65 make( wxS( "PCB_VIA" ), id2 ) };
66
69
70 BOOST_CHECK_EQUAL( result.aToB.size(), 2u );
71 BOOST_CHECK_EQUAL( result.aOnly.size(), 0u );
72 BOOST_CHECK_EQUAL( result.bOnly.size(), 0u );
73 BOOST_CHECK_EQUAL( result.similarityMatches, 0u );
74}
75
76
77BOOST_AUTO_TEST_CASE( UnmatchedItems )
78{
80 KIID id1, id2, id3;
81
82 std::vector<ITEM_DESCRIPTOR> a = { make( wxS( "PCB_TRACK" ), id1 ),
83 make( wxS( "PCB_VIA" ), id2 ) };
84
85 std::vector<ITEM_DESCRIPTOR> b = { make( wxS( "PCB_TRACK" ), id1 ),
86 make( wxS( "ZONE" ), id3 ) };
87
90 cfg.enableSimilarity = false; // pure UUID match for this test
91 r.SetConfig( cfg );
92
94
95 BOOST_CHECK_EQUAL( result.aToB.size(), 1u );
96 BOOST_CHECK_EQUAL( result.aOnly.size(), 1u );
97 BOOST_CHECK_EQUAL( result.bOnly.size(), 1u );
98}
99
100
101BOOST_AUTO_TEST_CASE( DuplicateUuidDetection )
102{
104 KIID id;
105
106 std::vector<ITEM_DESCRIPTOR> a = { make( wxS( "PCB_TRACK" ), id ),
107 make( wxS( "PCB_TRACK" ), id ) };
108 std::vector<ITEM_DESCRIPTOR> b;
109
111 RECONCILIATION result = r.Reconcile( a, b );
112
113 BOOST_CHECK_EQUAL( result.duplicatesA.size(), 1u );
114}
115
116
117BOOST_AUTO_TEST_CASE( SimilarityFallback )
118{
120 KIID idOld, idNew;
121
122 // Same item, but UUID has churned (e.g., from import). Both have identical
123 // position, bbox, and a matching key property.
124 std::vector<std::pair<wxString, std::string>> props =
125 { { wxS( "lib_id" ), "Device:R" }, { wxS( "reference" ), "R1" } };
126
127 std::vector<ITEM_DESCRIPTOR> a = { make( wxS( "FOOTPRINT" ), idOld, 100, 200, props ) };
128 std::vector<ITEM_DESCRIPTOR> b = { make( wxS( "FOOTPRINT" ), idNew, 100, 200, props ) };
129
131 RECONCILIATION result = r.Reconcile( a, b );
132
133 BOOST_CHECK_EQUAL( result.aToB.size(), 1u );
134 BOOST_CHECK_EQUAL( result.similarityMatches, 1u );
135 BOOST_CHECK_EQUAL( result.aOnly.size(), 0u );
136 BOOST_CHECK_EQUAL( result.bOnly.size(), 0u );
137}
138
139
140BOOST_AUTO_TEST_CASE( SimilarityRespectsTypeMismatch )
141{
143 KIID id1, id2;
144
145 std::vector<std::pair<wxString, std::string>> p1 = { { wxS( "k" ), "v" } };
146 std::vector<std::pair<wxString, std::string>> p2 = p1;
147
148 std::vector<ITEM_DESCRIPTOR> a = { make( wxS( "PCB_TRACK" ), id1, 0, 0, p1 ) };
149 std::vector<ITEM_DESCRIPTOR> b = { make( wxS( "PCB_VIA" ), id2, 0, 0, p2 ) };
150
152 RECONCILIATION result = r.Reconcile( a, b );
153
154 // Different type -> never match
155 BOOST_CHECK_EQUAL( result.aToB.size(), 0u );
156 BOOST_CHECK_EQUAL( result.aOnly.size(), 1u );
157 BOOST_CHECK_EQUAL( result.bOnly.size(), 1u );
158}
159
160
161BOOST_AUTO_TEST_CASE( ScoreSimilarityBreakdown )
162{
164 KIID id1, id2;
165
166 auto p = std::vector<std::pair<wxString, std::string>>{ { wxS( "k" ), "v" } };
167 ITEM_DESCRIPTOR a = make( wxS( "PCB_TRACK" ), id1, 100, 200, p );
168 ITEM_DESCRIPTOR b = make( wxS( "PCB_TRACK" ), id2, 100, 200, p );
169
171 double score = r.ScoreSimilarity( a, b );
172 // Position (0.4) + bbox (0.2) + full key prop overlap (0.4) = 1.0
173 BOOST_CHECK_CLOSE( score, 1.0, 0.001 );
174
175 // Move position; bbox originates from position above so this perturbs both.
176 b.position = VECTOR2I( 999, 999 );
177 b.bbox = BOX2I( VECTOR2I( 999, 999 ), VECTOR2I( 10, 10 ) );
178 score = r.ScoreSimilarity( a, b );
179 // Only keyProps match now: 0.4
180 BOOST_CHECK_CLOSE( score, 0.4, 0.001 );
181}
182
183
184BOOST_AUTO_TEST_CASE( GreedyAssignment )
185{
187 KIID idA1, idA2, idB1, idB2;
188
189 auto p1 = std::vector<std::pair<wxString, std::string>>{ { wxS( "k" ), "X" } };
190 auto p2 = std::vector<std::pair<wxString, std::string>>{ { wxS( "k" ), "Y" } };
191
192 std::vector<ITEM_DESCRIPTOR> a = { make( wxS( "FOOTPRINT" ), idA1, 0, 0, p1 ),
193 make( wxS( "FOOTPRINT" ), idA2, 100, 100, p2 ) };
194
195 // B has them in different order with new UUIDs but matching props.
196 std::vector<ITEM_DESCRIPTOR> b = { make( wxS( "FOOTPRINT" ), idB1, 100, 100, p2 ),
197 make( wxS( "FOOTPRINT" ), idB2, 0, 0, p1 ) };
198
200 RECONCILIATION result = r.Reconcile( a, b );
201
202 BOOST_CHECK_EQUAL( result.aToB.size(), 2u );
203 BOOST_CHECK_EQUAL( result.aOnly.size(), 0u );
204 BOOST_CHECK_EQUAL( result.bOnly.size(), 0u );
205 BOOST_CHECK_EQUAL( result.similarityMatches, 2u );
206}
207
208
209BOOST_AUTO_TEST_CASE( DeterministicOutput )
210{
212 KIID id1, id2, id3, id4;
213
214 std::vector<ITEM_DESCRIPTOR> a = { make( wxS( "PCB_TRACK" ), id1 ),
215 make( wxS( "PCB_TRACK" ), id2 ),
216 make( wxS( "PCB_TRACK" ), id3 ) };
217 std::vector<ITEM_DESCRIPTOR> b = { make( wxS( "PCB_TRACK" ), id1 ),
218 make( wxS( "PCB_TRACK" ), id4 ) };
219
221 RECONCILIATION one = r.Reconcile( a, b );
222 RECONCILIATION two = r.Reconcile( a, b );
223
224 BOOST_CHECK( one.aToB == two.aToB );
225 BOOST_CHECK( one.aOnly == two.aOnly );
226 BOOST_CHECK( one.bOnly == two.bOnly );
227}
228
229
230BOOST_AUTO_TEST_CASE( EmptyKeyPropsScoreZero )
231{
232 KIID::SeedGenerator( 100 );
233 KIID id1, id2;
234
235 // Same position, same bbox, no keyProps — should NOT score 1.0.
236 // Otherwise generic items at the same coordinates false-match perfectly.
237 ITEM_DESCRIPTOR a = make( wxS( "PCB_TRACK" ), id1, 100, 200 );
238 ITEM_DESCRIPTOR b = make( wxS( "PCB_TRACK" ), id2, 100, 200 );
239 a.keyProps.clear();
240 b.keyProps.clear();
241
243 double score = r.ScoreSimilarity( a, b );
244
245 // Position (0.4) + bbox (0.2) = 0.6; keyProps contribute 0.
246 BOOST_CHECK_CLOSE( score, 0.6, 0.001 );
247 BOOST_CHECK_LT( score, r.GetConfig().similarityThreshold );
248}
249
250
251BOOST_AUTO_TEST_CASE( DuplicatesExcludedFromMatching )
252{
253 KIID::SeedGenerator( 101 );
254 KIID id;
255
256 // Two items in A with the same UUID, one item in B with the same UUID.
257 // Without the duplicate-exclude logic, the first-seen A entry would match
258 // B normally — masking the duplicate from the report.
259 std::vector<ITEM_DESCRIPTOR> a = { make( wxS( "PCB_TRACK" ), id ),
260 make( wxS( "PCB_TRACK" ), id ) };
261 std::vector<ITEM_DESCRIPTOR> b = { make( wxS( "PCB_TRACK" ), id ) };
262
264 RECONCILIATION result = r.Reconcile( a, b );
265
266 BOOST_CHECK_EQUAL( result.duplicatesA.size(), 1u );
267 BOOST_CHECK_EQUAL( result.aToB.size(), 0u ); // duplicates not matched
268 BOOST_CHECK_EQUAL( result.bOnly.size(), 1u );
269}
270
271
272BOOST_AUTO_TEST_CASE( DuplicatesOnBSide )
273{
274 // Mirror of DuplicatesExcludedFromMatching — duplicate in B, not A. The
275 // duplicatesB vector must populate symmetrically.
276 KIID::SeedGenerator( 102 );
277 KIID id;
278
279 std::vector<ITEM_DESCRIPTOR> a = { make( wxS( "PCB_TRACK" ), id ) };
280 std::vector<ITEM_DESCRIPTOR> b = { make( wxS( "PCB_TRACK" ), id ),
281 make( wxS( "PCB_TRACK" ), id ) };
282
284 RECONCILIATION result = r.Reconcile( a, b );
285
286 BOOST_CHECK_EQUAL( result.duplicatesB.size(), 1u );
287 BOOST_CHECK_EQUAL( result.aToB.size(), 0u );
288 BOOST_CHECK_EQUAL( result.aOnly.size(), 1u );
289}
290
291
292BOOST_AUTO_TEST_CASE( DetectDuplicatesDisabledSkipsReporting )
293{
294 // detectDuplicates=false means colliding KIIDs aren't surfaced; the engine
295 // still has to choose some mapping but the user opted out of the warning.
296 // B has one matching item so the test also exercises the "still choose a
297 // mapping" path rather than just "duplicate vector is empty".
298 KIID::SeedGenerator( 103 );
299 KIID id;
300
301 std::vector<ITEM_DESCRIPTOR> a = { make( wxS( "PCB_TRACK" ), id ),
302 make( wxS( "PCB_TRACK" ), id ) };
303 std::vector<ITEM_DESCRIPTOR> b = { make( wxS( "PCB_TRACK" ), id ) };
304
306 cfg.detectDuplicates = false;
307 IDENTITY_RECONCILER r( cfg );
308
309 RECONCILIATION result = r.Reconcile( a, b );
310 BOOST_CHECK( result.duplicatesA.empty() );
311 // With the toggle off, the first-seen A entry maps to B.
312 BOOST_CHECK_EQUAL( result.aToB.size(), 1u );
313 BOOST_CHECK( result.bOnly.empty() );
314}
315
316
317BOOST_AUTO_TEST_CASE( EnableSimilarityFalseSkipsFallback )
318{
319 // With UUIDs churned and similarity off, items should land in aOnly/bOnly
320 // even when they're an obvious positional + key-prop match.
321 KIID::SeedGenerator( 104 );
322 KIID idA, idB;
323
324 auto props = std::vector<std::pair<wxString, std::string>>{ { wxS( "k" ), "v" } };
325 std::vector<ITEM_DESCRIPTOR> a = { make( wxS( "FOOTPRINT" ), idA, 0, 0, props ) };
326 std::vector<ITEM_DESCRIPTOR> b = { make( wxS( "FOOTPRINT" ), idB, 0, 0, props ) };
327
329 cfg.enableSimilarity = false;
330 IDENTITY_RECONCILER r( cfg );
331
332 RECONCILIATION result = r.Reconcile( a, b );
333 BOOST_CHECK_EQUAL( result.similarityMatches, 0u );
334 BOOST_CHECK_EQUAL( result.aOnly.size(), 1u );
335 BOOST_CHECK_EQUAL( result.bOnly.size(), 1u );
336}
337
338
339BOOST_AUTO_TEST_CASE( SimilarityThresholdGatesMatch )
340{
341 // Score 0.6 (position + bbox, no keyProps) sits below the default 0.85
342 // threshold so it's NOT matched. Raise the threshold higher → still no
343 // match. Lower below 0.6 → match. `make()` defaults keyProps to empty,
344 // so no clear() needed.
345 KIID::SeedGenerator( 105 );
346 KIID idA, idB;
347
348 std::vector<ITEM_DESCRIPTOR> aVec = { make( wxS( "FOOTPRINT" ), idA, 100, 200 ) };
349 std::vector<ITEM_DESCRIPTOR> bVec = { make( wxS( "FOOTPRINT" ), idB, 100, 200 ) };
350
351 {
352 IDENTITY_RECONCILER r; // default threshold 0.85
353 BOOST_CHECK_EQUAL( r.Reconcile( aVec, bVec ).similarityMatches, 0u );
354 }
355
356 {
358 cfg.similarityThreshold = 0.5;
359 IDENTITY_RECONCILER r( cfg );
360 BOOST_CHECK_EQUAL( r.Reconcile( aVec, bVec ).similarityMatches, 1u );
361 }
362}
363
364
365BOOST_AUTO_TEST_CASE( EmptyInputsProduceEmptyReconciliation )
366{
368 RECONCILIATION result = r.Reconcile( {}, {} );
369
370 BOOST_CHECK( result.aToB.empty() );
371 BOOST_CHECK( result.bToA.empty() );
372 BOOST_CHECK( result.aOnly.empty() );
373 BOOST_CHECK( result.bOnly.empty() );
374 BOOST_CHECK( result.duplicatesA.empty() );
375 BOOST_CHECK( result.duplicatesB.empty() );
376 BOOST_CHECK_EQUAL( result.similarityMatches, 0u );
377}
378
379
380BOOST_AUTO_TEST_CASE( OneSideEmptyOtherFullyUnmatched )
381{
382 KIID::SeedGenerator( 106 );
383 KIID id1, id2;
384
385 std::vector<ITEM_DESCRIPTOR> a = { make( wxS( "PCB_TRACK" ), id1 ),
386 make( wxS( "PCB_TRACK" ), id2 ) };
387
389 RECONCILIATION result = r.Reconcile( a, {} );
390
391 BOOST_CHECK_EQUAL( result.aOnly.size(), 2u );
392 BOOST_CHECK( result.bOnly.empty() );
393 BOOST_CHECK_EQUAL( result.aToB.size(), 0u );
394}
395
396
397BOOST_AUTO_TEST_CASE( ReverseLookupBToAMirrorsAToB_DirectMatch )
398{
399 // Pass-1 (direct UUID match) symmetry: aToB[x] == y implies bToA[y] == x.
400 KIID::SeedGenerator( 107 );
401 KIID id1, id2;
402
403 std::vector<ITEM_DESCRIPTOR> a = { make( wxS( "PCB_TRACK" ), id1 ),
404 make( wxS( "PCB_VIA" ), id2 ) };
405 std::vector<ITEM_DESCRIPTOR> b = a;
406
408 RECONCILIATION result = r.Reconcile( a, b );
409
410 BOOST_REQUIRE_EQUAL( result.aToB.size(), 2u );
411 for( const auto& [aId, bId] : result.aToB )
412 {
413 BOOST_REQUIRE( result.bToA.count( bId ) == 1 );
414 BOOST_CHECK( result.bToA.at( bId ) == aId );
415 }
416}
417
418
419BOOST_AUTO_TEST_CASE( ReverseLookupBToAMirrorsAToB_SimilarityPath )
420{
421 // Mirror coverage for the Pass-2 similarity path. When `b = a` was used
422 // above, every match came from Pass-1 (identical UUIDs) so the bToA
423 // assignment inside the similarity loop was never exercised. Force the
424 // similarity path by churning B's UUIDs while keeping position + props
425 // identical, then assert the same a<->b symmetry.
426 KIID::SeedGenerator( 108 );
427 KIID idA1, idA2, idB1, idB2;
428
429 auto props = std::vector<std::pair<wxString, std::string>>{
430 { wxS( "lib_id" ), "Device:R" },
431 { wxS( "reference" ), "R1" } };
432
433 std::vector<ITEM_DESCRIPTOR> a = { make( wxS( "FOOTPRINT" ), idA1, 0, 0, props ),
434 make( wxS( "FOOTPRINT" ), idA2, 100, 100, props ) };
435 std::vector<ITEM_DESCRIPTOR> b = { make( wxS( "FOOTPRINT" ), idB1, 0, 0, props ),
436 make( wxS( "FOOTPRINT" ), idB2, 100, 100, props ) };
437
439 RECONCILIATION result = r.Reconcile( a, b );
440
441 BOOST_REQUIRE_EQUAL( result.aToB.size(), 2u );
442 BOOST_REQUIRE_EQUAL( result.similarityMatches, 2u );
443 for( const auto& [aId, bId] : result.aToB )
444 {
445 BOOST_REQUIRE( result.bToA.count( bId ) == 1 );
446 BOOST_CHECK( result.bToA.at( bId ) == aId );
447 }
448}
449
450
451// 100 equal-scoring candidates: greedy-best with deterministic tie-break must
452// produce the same assignment every run. Pin determinism by running the
453// reconciler twice on the same inputs and asserting identical aToB.
454BOOST_AUTO_TEST_CASE( TieBreakDeterminismWithManyEqualScoreCandidates )
455{
456 constexpr int N = 100;
457
458 KIID::SeedGenerator( 200 );
459 std::vector<KIID> aIds( N );
460 std::vector<KIID> bIds( N );
461
462 auto props = std::vector<std::pair<wxString, std::string>>{
463 { wxS( "lib_id" ), "Device:R" },
464 { wxS( "reference" ), "R1" } };
465
466 std::vector<ITEM_DESCRIPTOR> a;
467 std::vector<ITEM_DESCRIPTOR> b;
468 a.reserve( N );
469 b.reserve( N );
470
471 // All items share the same position and props so similarity scores
472 // collide. Different UUIDs on both sides force the similarity fallback
473 // path.
474 for( int i = 0; i < N; ++i )
475 {
476 a.push_back( make( wxS( "FOOTPRINT" ), aIds[i], 0, 0, props ) );
477 b.push_back( make( wxS( "FOOTPRINT" ), bIds[i], 0, 0, props ) );
478 }
479
481 RECONCILIATION first = r.Reconcile( a, b );
482 RECONCILIATION second = r.Reconcile( a, b );
483
484 BOOST_REQUIRE_EQUAL( first.aToB.size(), second.aToB.size() );
485
486 for( const auto& [aId, bId] : first.aToB )
487 {
488 BOOST_REQUIRE( second.aToB.count( aId ) );
489 BOOST_CHECK( second.aToB.at( aId ) == bId );
490 }
491}
492
493
494// 1000-item input: the reconciler must complete in a reasonable time budget.
495// This is a smoke test for asymptotic explosion -- pin a generous 5-second
496// upper bound (typical run is under 1s on modern hardware) so a future
497// O(n^3) regression in similarity matching shows up as a CI timeout.
498BOOST_AUTO_TEST_CASE( LargeInputPerformanceSmoke )
499{
500 constexpr int N = 1000;
501
502 KIID::SeedGenerator( 300 );
503 std::vector<ITEM_DESCRIPTOR> a;
504 std::vector<ITEM_DESCRIPTOR> b;
505 a.reserve( N );
506 b.reserve( N );
507
508 for( int i = 0; i < N; ++i )
509 {
510 KIID idA, idB;
511 // Mix of direct-UUID matches and similarity-only matches so both
512 // paths exercise. Even i: same UUID on both sides; odd i: only
513 // similarity path.
514 if( ( i % 2 ) == 0 )
515 idB = idA;
516
517 auto props = std::vector<std::pair<wxString, std::string>>{
518 { wxS( "lib_id" ), wxString::Format( "Device:R%d", i ).ToStdString() },
519 { wxS( "reference" ), wxString::Format( "R%d", i ).ToStdString() } };
520
521 a.push_back( make( wxS( "FOOTPRINT" ), idA, i, i, props ) );
522 b.push_back( make( wxS( "FOOTPRINT" ), idB, i, i, props ) );
523 }
524
526 const auto start = std::chrono::steady_clock::now();
527 RECONCILIATION result = r.Reconcile( a, b );
528 const auto elapsed = std::chrono::steady_clock::now() - start;
529
530 const double seconds = std::chrono::duration<double>( elapsed ).count();
531
532 BOOST_TEST_MESSAGE( "Reconcile(1k items) elapsed: " << seconds << "s" );
533 BOOST_CHECK( seconds < 5.0 );
534
535 // Every A must have found a match.
536 BOOST_CHECK_EQUAL( result.aToB.size(), static_cast<size_t>( N ) );
537}
538
539
540// Non-zero positionTolerance: items at slightly different positions should
541// score as identical-position when within tolerance. Use a position delta
542// that would fail a strict-equality (tolerance=0) check but pass at
543// tolerance=1000nm.
544BOOST_AUTO_TEST_CASE( PositionToleranceAllowsNearMatches )
545{
546 KIID::SeedGenerator( 400 );
547 KIID idA1, idB1;
548
549 auto props = std::vector<std::pair<wxString, std::string>>{
550 { wxS( "lib_id" ), "Device:R" },
551 { wxS( "reference" ), "R1" } };
552
553 // A at (0,0), B at (500, 500) -- 500nm apart, within 1000nm tolerance.
554 std::vector<ITEM_DESCRIPTOR> a = { make( wxS( "FOOTPRINT" ), idA1, 0, 0, props ) };
555 std::vector<ITEM_DESCRIPTOR> b = { make( wxS( "FOOTPRINT" ), idB1, 500, 500, props ) };
556
558 config.positionTolerance = 1000;
560 RECONCILIATION result = r.Reconcile( a, b );
561
562 BOOST_CHECK_EQUAL( result.aToB.size(), 1u );
563 BOOST_CHECK_EQUAL( result.similarityMatches, 1u );
564}
565
566
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
Reconciles item identity across two snapshots of the same document.
RECONCILIATION Reconcile(const std::vector< ITEM_DESCRIPTOR > &aA, const std::vector< ITEM_DESCRIPTOR > &aB) const
void SetConfig(const CONFIG &aConfig)
double ScoreSimilarity(const ITEM_DESCRIPTOR &aA, const ITEM_DESCRIPTOR &aB) const
Compute the similarity score between two items of the same type.
Definition kiid.h:44
static void SeedGenerator(unsigned int aSeed)
Re-initialize the UUID generator with a given seed (for testing or QA purposes)
Definition kiid.cpp:331
Descriptor used by the identity reconciler to compare items across two documents.
std::vector< std::pair< wxString, std::string > > keyProps
Maps every item in document A to either a peer in document B or to "only-in-A", and vice versa.
std::set< KIID_PATH > aOnly
std::map< KIID_PATH, KIID_PATH > aToB
std::set< KIID_PATH > bOnly
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(DirectUuidMatch)
BOOST_TEST_MESSAGE("\n=== Real-World Polygon PIP Benchmark ===\n"<< formatTable(table))
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683