KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_lib_merge_applier.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 <map>
29#include <memory>
30#include <utility>
31#include <vector>
32
33#include <wx/string.h>
34
35
36using namespace KICAD_DIFF;
37
38
39namespace
40{
41
42// Minimal copy-constructible stand-in. The applier only does
43// std::make_unique<ITEM>(*src) on the chosen side, so anything
44// copyable works — we just stamp a side tag so the test can verify
45// which side flowed through.
46struct TestItem
47{
48 wxString name;
49 wxString side; // "anc" / "ours" / "theirs" — set by the fixture, never copied from
50};
51
52
53using ItemStore = std::vector<TestItem>;
54using ItemMap = std::map<wxString, const TestItem*>;
55
56
57ItemMap MakeMap( ItemStore& aStore )
58{
59 ItemMap m;
60
61 for( const TestItem& it : aStore )
62 m[it.name] = &it;
63
64 return m;
65}
66
67
68ITEM_RESOLUTION Action( const wxString& aName, ITEM_RES aKind )
69{
71 r.id = LibraryItemKiidPath( aName );
72 r.kind = aKind;
73 return r;
74}
75
76
77const TestItem* Find( const std::vector<std::unique_ptr<TestItem>>& aOut, const wxString& aName )
78{
79 for( const auto& up : aOut )
80 {
81 if( up && up->name == aName )
82 return up.get();
83 }
84
85 return nullptr;
86}
87
88} // namespace
89
90
91BOOST_AUTO_TEST_SUITE( LibMergeApplier )
92
93
94BOOST_AUTO_TEST_CASE( TakeOurs_PicksOursSide )
95{
96 ItemStore anc{ { wxS( "R" ), wxS( "anc" ) } };
97 ItemStore ours{ { wxS( "R" ), wxS( "ours" ) } };
98 ItemStore theirs{ { wxS( "R" ), wxS( "theirs" ) } };
99
100 MERGE_PLAN plan;
101 plan.actions.push_back( Action( wxS( "R" ), ITEM_RES::TAKE_OURS ) );
102
103 ItemMap ancMap = MakeMap( anc );
104
105 ItemMap oursMap = MakeMap( ours );
106
107 ItemMap theirsMap = MakeMap( theirs );
108
109 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
110 auto out = applier.Apply();
111
112 BOOST_REQUIRE_EQUAL( out.size(), 1 );
113 BOOST_CHECK( out[0]->name == wxS( "R" ) );
114 BOOST_CHECK( out[0]->side == wxS( "ours" ) );
116}
117
118
119BOOST_AUTO_TEST_CASE( TakeTheirs_PicksTheirsSide )
120{
121 ItemStore anc{ { wxS( "R" ), wxS( "anc" ) } };
122 ItemStore ours{ { wxS( "R" ), wxS( "ours" ) } };
123 ItemStore theirs{ { wxS( "R" ), wxS( "theirs" ) } };
124
125 MERGE_PLAN plan;
126 plan.actions.push_back( Action( wxS( "R" ), ITEM_RES::TAKE_THEIRS ) );
127
128 ItemMap ancMap = MakeMap( anc );
129
130 ItemMap oursMap = MakeMap( ours );
131
132 ItemMap theirsMap = MakeMap( theirs );
133
134 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
135 auto out = applier.Apply();
136
137 BOOST_REQUIRE_EQUAL( out.size(), 1 );
138 BOOST_CHECK( out[0]->side == wxS( "theirs" ) );
140}
141
142
143BOOST_AUTO_TEST_CASE( TakeAncestor_PicksAncestorSide )
144{
145 ItemStore anc{ { wxS( "R" ), wxS( "anc" ) } };
146 ItemStore ours{ { wxS( "R" ), wxS( "ours" ) } };
147 ItemStore theirs{ { wxS( "R" ), wxS( "theirs" ) } };
148
149 MERGE_PLAN plan;
150 plan.actions.push_back( Action( wxS( "R" ), ITEM_RES::TAKE_ANCESTOR ) );
151
152 ItemMap ancMap = MakeMap( anc );
153
154 ItemMap oursMap = MakeMap( ours );
155
156 ItemMap theirsMap = MakeMap( theirs );
157
158 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
159 auto out = applier.Apply();
160
161 BOOST_REQUIRE_EQUAL( out.size(), 1 );
162 BOOST_CHECK( out[0]->side == wxS( "anc" ) );
164}
165
166
167BOOST_AUTO_TEST_CASE( TakeOurs_MissingInOurs_ErasesFromOutput )
168{
169 // TAKE_OURS where ours doesn't have the item — applier must erase
170 // the live entry rather than leaving the ancestor copy.
171 ItemStore anc{ { wxS( "R" ), wxS( "anc" ) } };
172 ItemStore ours; // empty
173 ItemStore theirs{ { wxS( "R" ), wxS( "theirs" ) } };
174
175 MERGE_PLAN plan;
176 plan.actions.push_back( Action( wxS( "R" ), ITEM_RES::TAKE_OURS ) );
177
178 ItemMap ancMap = MakeMap( anc );
179
180 ItemMap oursMap = MakeMap( ours );
181
182 ItemMap theirsMap = MakeMap( theirs );
183
184 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
185 auto out = applier.Apply();
186
187 BOOST_CHECK( out.empty() );
189}
190
191
192BOOST_AUTO_TEST_CASE( Delete_RemovesItem )
193{
194 ItemStore anc{ { wxS( "R" ), wxS( "anc" ) } };
195 ItemStore ours{ { wxS( "R" ), wxS( "ours" ) } };
196 ItemStore theirs{ { wxS( "R" ), wxS( "theirs" ) } };
197
198 MERGE_PLAN plan;
199 plan.actions.push_back( Action( wxS( "R" ), ITEM_RES::DELETE_ITEM ) );
200
201 ItemMap ancMap = MakeMap( anc );
202
203 ItemMap oursMap = MakeMap( ours );
204
205 ItemMap theirsMap = MakeMap( theirs );
206
207 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
208 auto out = applier.Apply();
209
210 BOOST_CHECK( out.empty() );
212}
213
214
215BOOST_AUTO_TEST_CASE( Keep_PrefersAncestor )
216{
217 // KEEP with ancestor present should pick ancestor.
218 ItemStore anc{ { wxS( "R" ), wxS( "anc" ) } };
219 ItemStore ours{ { wxS( "R" ), wxS( "ours" ) } };
220 ItemStore theirs{ { wxS( "R" ), wxS( "theirs" ) } };
221
222 MERGE_PLAN plan;
223 plan.actions.push_back( Action( wxS( "R" ), ITEM_RES::KEEP ) );
224
225 ItemMap ancMap = MakeMap( anc );
226
227 ItemMap oursMap = MakeMap( ours );
228
229 ItemMap theirsMap = MakeMap( theirs );
230
231 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
232 auto out = applier.Apply();
233
234 BOOST_REQUIRE_EQUAL( out.size(), 1 );
235 BOOST_CHECK( out[0]->side == wxS( "anc" ) );
236 BOOST_CHECK_EQUAL( applier.GetReport().itemsKept, 1 );
237}
238
239
240BOOST_AUTO_TEST_CASE( Keep_FallsBackToOurs_WhenAncestorMissing )
241{
242 // The both-added case: no ancestor, item exists on both sides.
243 // KEEP walks anc -> ours -> theirs so ours wins.
244 ItemStore anc;
245 ItemStore ours{ { wxS( "R" ), wxS( "ours" ) } };
246 ItemStore theirs{ { wxS( "R" ), wxS( "theirs" ) } };
247
248 MERGE_PLAN plan;
249 plan.actions.push_back( Action( wxS( "R" ), ITEM_RES::KEEP ) );
250
251 ItemMap ancMap = MakeMap( anc );
252
253 ItemMap oursMap = MakeMap( ours );
254
255 ItemMap theirsMap = MakeMap( theirs );
256
257 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
258 auto out = applier.Apply();
259
260 BOOST_REQUIRE_EQUAL( out.size(), 1 );
261 BOOST_CHECK( out[0]->side == wxS( "ours" ) );
262 BOOST_CHECK_EQUAL( applier.GetReport().itemsKept, 1 );
263}
264
265
266BOOST_AUTO_TEST_CASE( Keep_FallsBackToTheirs_WhenAncestorAndOursMissing )
267{
268 // Only theirs has the item. KEEP should still produce it (otherwise an
269 // independent same-name add on theirs vanishes — exactly the documented
270 // bug the fallback walk guards against).
271 ItemStore anc;
272 ItemStore ours;
273 ItemStore theirs{ { wxS( "R" ), wxS( "theirs" ) } };
274
275 MERGE_PLAN plan;
276 plan.actions.push_back( Action( wxS( "R" ), ITEM_RES::KEEP ) );
277
278 ItemMap ancMap = MakeMap( anc );
279
280 ItemMap oursMap = MakeMap( ours );
281
282 ItemMap theirsMap = MakeMap( theirs );
283
284 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
285 auto out = applier.Apply();
286
287 BOOST_REQUIRE_EQUAL( out.size(), 1 );
288 BOOST_CHECK( out[0]->side == wxS( "theirs" ) );
289 BOOST_CHECK_EQUAL( applier.GetReport().itemsKept, 1 );
290}
291
292
293BOOST_AUTO_TEST_CASE( Keep_AllSidesMissing_ProducesNothing )
294{
295 // Defensive: a KEEP resolution for an item that's in none of the maps.
296 // Should not crash; should produce no output and still bump itemsKept.
297 ItemStore anc, ours, theirs;
298
299 MERGE_PLAN plan;
300 plan.actions.push_back( Action( wxS( "Ghost" ), ITEM_RES::KEEP ) );
301
302 ItemMap ancMap = MakeMap( anc );
303
304 ItemMap oursMap = MakeMap( ours );
305
306 ItemMap theirsMap = MakeMap( theirs );
307
308 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
309 auto out = applier.Apply();
310
311 BOOST_CHECK( out.empty() );
312 // The action is keyed by KIID_PATH; with no item present in any map,
313 // the action is unreachable (allNames is empty). itemsKept stays 0.
314 BOOST_CHECK_EQUAL( applier.GetReport().itemsKept, 0 );
315}
316
317
318BOOST_AUTO_TEST_CASE( MergeProps_FallsBackToOurs_AndTracksId )
319{
320 ItemStore anc{ { wxS( "R" ), wxS( "anc" ) } };
321 ItemStore ours{ { wxS( "R" ), wxS( "ours" ) } };
322 ItemStore theirs{ { wxS( "R" ), wxS( "theirs" ) } };
323
324 MERGE_PLAN plan;
325 plan.actions.push_back( Action( wxS( "R" ), ITEM_RES::MERGE_PROPS ) );
326
327 ItemMap ancMap = MakeMap( anc );
328
329 ItemMap oursMap = MakeMap( ours );
330
331 ItemMap theirsMap = MakeMap( theirs );
332
333 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
334 auto out = applier.Apply();
335
336 BOOST_REQUIRE_EQUAL( out.size(), 1 );
337 BOOST_CHECK( out[0]->side == wxS( "ours" ) );
339 BOOST_REQUIRE_EQUAL( applier.GetReport().mergePropsFallbackIds.size(), 1 );
340 BOOST_CHECK( applier.GetReport().mergePropsFallbackIds[0]
341 == LibraryItemKiidPath( wxS( "R" ) ) );
342}
343
344
345BOOST_AUTO_TEST_CASE( NoActionForItem_PassesAncestorThrough )
346{
347 // An item present in ancestor but with no plan action stays in `live`
348 // because the applier seeds `live = m_ancestor` and only mutates entries
349 // that have a matching action.
350 ItemStore anc{ { wxS( "R" ), wxS( "anc" ) } };
351 ItemStore ours{ { wxS( "R" ), wxS( "ours" ) } };
352 ItemStore theirs;
353
354 MERGE_PLAN plan; // empty — no actions
355
356 ItemMap ancMap = MakeMap( anc );
357
358 ItemMap oursMap = MakeMap( ours );
359
360 ItemMap theirsMap = MakeMap( theirs );
361
362 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
363 auto out = applier.Apply();
364
365 BOOST_REQUIRE_EQUAL( out.size(), 1 );
366 BOOST_CHECK( out[0]->side == wxS( "anc" ) );
367}
368
369
370BOOST_AUTO_TEST_CASE( ActionForUnknownName_IsHarmless )
371{
372 // A plan action whose id doesn't match any item in any map should be
373 // silently ignored (allNames doesn't contain it, so the inner loop
374 // never visits that action).
375 ItemStore anc{ { wxS( "R" ), wxS( "anc" ) } };
376 ItemStore ours, theirs;
377
378 MERGE_PLAN plan;
379 plan.actions.push_back( Action( wxS( "Phantom" ), ITEM_RES::DELETE_ITEM ) );
380
381 ItemMap ancMap = MakeMap( anc );
382
383 ItemMap oursMap = MakeMap( ours );
384
385 ItemMap theirsMap = MakeMap( theirs );
386
387 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
388 auto out = applier.Apply();
389
390 BOOST_REQUIRE_EQUAL( out.size(), 1 ); // R passed through
391 BOOST_CHECK( out[0]->name == wxS( "R" ) );
393}
394
395
396BOOST_AUTO_TEST_CASE( OutputSortedByName )
397{
398 // live is std::map<wxString, ...>, so output should be in name order.
399 ItemStore anc{ { wxS( "C" ), wxS( "anc" ) }, { wxS( "A" ), wxS( "anc" ) },
400 { wxS( "B" ), wxS( "anc" ) } };
401 ItemStore ours, theirs;
402
403 MERGE_PLAN plan; // no actions — pure pass-through
404
405 ItemMap ancMap = MakeMap( anc );
406
407 ItemMap oursMap = MakeMap( ours );
408
409 ItemMap theirsMap = MakeMap( theirs );
410
411 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
412 auto out = applier.Apply();
413
414 BOOST_REQUIRE_EQUAL( out.size(), 3 );
415 BOOST_CHECK( out[0]->name == wxS( "A" ) );
416 BOOST_CHECK( out[1]->name == wxS( "B" ) );
417 BOOST_CHECK( out[2]->name == wxS( "C" ) );
418}
419
420
421BOOST_AUTO_TEST_CASE( OutputIsFreshCopy_NotPointerAlias )
422{
423 // Applier returns owned unique_ptrs via std::make_unique<ITEM>(*src).
424 // Mutating the source map's item after Apply() must not affect output.
425 ItemStore anc{ { wxS( "R" ), wxS( "anc" ) } };
426 ItemStore ours, theirs;
427
428 MERGE_PLAN plan; // no actions
429
430 ItemMap ancMap = MakeMap( anc );
431
432 ItemMap oursMap = MakeMap( ours );
433
434 ItemMap theirsMap = MakeMap( theirs );
435
436 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
437 auto out = applier.Apply();
438
439 BOOST_REQUIRE_EQUAL( out.size(), 1 );
440 anc[0].side = wxS( "MUTATED" );
441 BOOST_CHECK( out[0]->side == wxS( "anc" ) );
442}
443
444
445BOOST_AUTO_TEST_CASE( MixedActions_AllCountersAdvance )
446{
447 // One action of each kind in a single plan. Verifies counters increment
448 // independently and the same applier instance can handle a mixed plan.
449 ItemStore anc{
450 { wxS( "Keep" ), wxS( "anc" ) },
451 { wxS( "DelMe" ), wxS( "anc" ) },
452 { wxS( "Anc" ), wxS( "anc" ) },
453 { wxS( "MergeProps" ), wxS( "anc" ) },
454 };
455 ItemStore ours{
456 { wxS( "Keep" ), wxS( "ours" ) },
457 { wxS( "DelMe" ), wxS( "ours" ) },
458 { wxS( "Anc" ), wxS( "ours" ) },
459 { wxS( "OursOnly" ), wxS( "ours" ) },
460 { wxS( "MergeProps" ), wxS( "ours" ) },
461 };
462 ItemStore theirs{
463 { wxS( "Keep" ), wxS( "theirs" ) },
464 { wxS( "TheirsOnly" ), wxS( "theirs" ) },
465 { wxS( "MergeProps" ), wxS( "theirs" ) },
466 };
467
468 MERGE_PLAN plan;
469 plan.actions.push_back( Action( wxS( "Keep" ), ITEM_RES::KEEP ) );
470 plan.actions.push_back( Action( wxS( "DelMe" ), ITEM_RES::DELETE_ITEM ) );
471 plan.actions.push_back( Action( wxS( "Anc" ), ITEM_RES::TAKE_ANCESTOR ) );
472 plan.actions.push_back( Action( wxS( "OursOnly" ), ITEM_RES::TAKE_OURS ) );
473 plan.actions.push_back( Action( wxS( "TheirsOnly" ), ITEM_RES::TAKE_THEIRS ) );
474 plan.actions.push_back( Action( wxS( "MergeProps" ), ITEM_RES::MERGE_PROPS ) );
475
476 ItemMap ancMap = MakeMap( anc );
477
478 ItemMap oursMap = MakeMap( ours );
479
480 ItemMap theirsMap = MakeMap( theirs );
481
482 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
483 auto out = applier.Apply();
484
485 const auto& r = applier.GetReport();
486 BOOST_CHECK_EQUAL( r.itemsKept, 1 );
487 BOOST_CHECK_EQUAL( r.itemsDeleted, 1 );
488 BOOST_CHECK_EQUAL( r.itemsTakenAncestor, 1 );
489 BOOST_CHECK_EQUAL( r.itemsTakenOurs, 1 );
490 BOOST_CHECK_EQUAL( r.itemsTakenTheirs, 1 );
491 BOOST_CHECK_EQUAL( r.mergePropsFallback, 1 );
492
493 // Five live items: Keep, Anc, OursOnly, TheirsOnly, MergeProps; DelMe was
494 // deleted. Pinning the exact size catches accidental over-emission.
495 BOOST_REQUIRE_EQUAL( out.size(), 5 );
496
497 const TestItem* keep = Find( out, wxS( "Keep" ) );
498 const TestItem* anc_pick = Find( out, wxS( "Anc" ) );
499 const TestItem* ours_only = Find( out, wxS( "OursOnly" ) );
500 const TestItem* theirs_one = Find( out, wxS( "TheirsOnly" ) );
501 const TestItem* merge = Find( out, wxS( "MergeProps" ) );
502
503 BOOST_REQUIRE( keep );
504 BOOST_REQUIRE( anc_pick );
505 BOOST_REQUIRE( ours_only );
506 BOOST_REQUIRE( theirs_one );
507 BOOST_REQUIRE( merge );
508 BOOST_CHECK( keep->side == wxS( "anc" ) );
509 BOOST_CHECK( anc_pick->side == wxS( "anc" ) );
510 BOOST_CHECK( ours_only->side == wxS( "ours" ) );
511 BOOST_CHECK( theirs_one->side == wxS( "theirs" ) );
512 BOOST_CHECK( merge->side == wxS( "ours" ) );
513 BOOST_CHECK( Find( out, wxS( "DelMe" ) ) == nullptr );
514}
515
516
517BOOST_AUTO_TEST_CASE( MergeProps_OursMissing_StillTracksIdAndErasesLive )
518{
519 // MERGE_PROPS where ours doesn't have the item: take(m_ours, ...) must
520 // erase the live entry (because pick(m_ours) returns null), and the
521 // mergePropsFallbackIds vector must still record the ID so the job
522 // handler can surface the unresolved conflict to the user.
523 ItemStore anc{ { wxS( "R" ), wxS( "anc" ) } };
524 ItemStore ours;
525 ItemStore theirs{ { wxS( "R" ), wxS( "theirs" ) } };
526
527 MERGE_PLAN plan;
528 plan.actions.push_back( Action( wxS( "R" ), ITEM_RES::MERGE_PROPS ) );
529
530 ItemMap ancMap = MakeMap( anc );
531 ItemMap oursMap = MakeMap( ours );
532 ItemMap theirsMap = MakeMap( theirs );
533 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
534 auto out = applier.Apply();
535
536 BOOST_CHECK( out.empty() );
538 BOOST_REQUIRE_EQUAL( applier.GetReport().mergePropsFallbackIds.size(), 1 );
539 BOOST_CHECK( applier.GetReport().mergePropsFallbackIds[0]
540 == LibraryItemKiidPath( wxS( "R" ) ) );
541}
542
543
544BOOST_AUTO_TEST_CASE( NonMergeProps_DoesNotPopulateFallbackIds )
545{
546 // Pin that fallbackIds is only touched by the MERGE_PROPS branch. A plan
547 // of pure non-MERGE_PROPS actions must leave the vector empty.
548 ItemStore anc{ { wxS( "A" ), wxS( "anc" ) }, { wxS( "B" ), wxS( "anc" ) },
549 { wxS( "C" ), wxS( "anc" ) } };
550 ItemStore ours{ { wxS( "A" ), wxS( "ours" ) } };
551 ItemStore theirs{ { wxS( "B" ), wxS( "theirs" ) } };
552
553 MERGE_PLAN plan;
554 plan.actions.push_back( Action( wxS( "A" ), ITEM_RES::TAKE_OURS ) );
555 plan.actions.push_back( Action( wxS( "B" ), ITEM_RES::TAKE_THEIRS ) );
556 plan.actions.push_back( Action( wxS( "C" ), ITEM_RES::DELETE_ITEM ) );
557
558 ItemMap ancMap = MakeMap( anc );
559 ItemMap oursMap = MakeMap( ours );
560 ItemMap theirsMap = MakeMap( theirs );
561 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
562 applier.Apply();
563
565 BOOST_CHECK( applier.GetReport().mergePropsFallbackIds.empty() );
566}
567
568
569BOOST_AUTO_TEST_CASE( ReportResetOnReapply )
570{
571 // GetReport() must reset on every Apply() — otherwise counters would
572 // double when callers re-run the applier (e.g., regression-test loops).
573 // Include a MERGE_PROPS action so the vector field (mergePropsFallbackIds)
574 // is also exercised: a non-trivial reset path must clear the vector too.
575 ItemStore anc{ { wxS( "M" ), wxS( "anc" ) } };
576 ItemStore ours{ { wxS( "R" ), wxS( "ours" ) }, { wxS( "M" ), wxS( "ours" ) } };
577 ItemStore theirs{ { wxS( "M" ), wxS( "theirs" ) } };
578
579 MERGE_PLAN plan;
580 plan.actions.push_back( Action( wxS( "R" ), ITEM_RES::TAKE_OURS ) );
581 plan.actions.push_back( Action( wxS( "M" ), ITEM_RES::MERGE_PROPS ) );
582
583 ItemMap ancMap = MakeMap( anc );
584 ItemMap oursMap = MakeMap( ours );
585 ItemMap theirsMap = MakeMap( theirs );
586 LIB_MERGE_APPLIER<TestItem> applier( ancMap, oursMap, theirsMap, std::move( plan ) );
587 applier.Apply();
588 applier.Apply();
589
592 BOOST_CHECK_EQUAL( applier.GetReport().mergePropsFallbackIds.size(), 1u );
593}
594
595
const char * name
Materialize a MERGE_PLAN into a merged name-keyed library.
const REPORT & GetReport() const
std::vector< std::unique_ptr< ITEM > > Apply()
ITEM_RES
Resolution kind for a whole item.
KIID_PATH LibraryItemKiidPath(const wxString &aName)
Build a deterministic synthetic KIID_PATH from a library item name (symbol name or footprint name).
std::vector< KIID_PATH > mergePropsFallbackIds
Item IDs (KIID_PATH from LibraryItemKiidPath) for resolutions that the applier silently downgraded (c...
Result of planning a 3-way merge.
std::vector< ITEM_RESOLUTION > actions
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(TakeOurs_PicksOursSide)
BOOST_CHECK_EQUAL(result, "25.4")