File size: 41,346 Bytes
985c397
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
// SPDX-License-Identifier: LGPL-2.1-or-later

#include <gtest/gtest.h>
#include "src/App/InitApplication.h"
#include "Mod/Part/App/WireJoiner.h"
#include <Mod/Part/App/TopoShapeOpCode.h>

#include "PartTestHelpers.h"

#include <BRepBuilderAPI_MakeShape.hxx>

// NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)

using namespace Part;
using namespace PartTestHelpers;

class WireJoinerTest: public ::testing::Test
{
protected:
    static void SetUpTestSuite()
    {
        tests::initApplication();
    }

    void SetUp() override
    {
        _docName = App::GetApplication().getUniqueDocumentName("test");
        App::GetApplication().newDocument(_docName.c_str(), "testUser");
        _hasher = Base::Reference<App::StringHasher>(new App::StringHasher);
        ASSERT_EQ(_hasher.getRefCount(), 1);
    }

    void TearDown() override
    {
        App::GetApplication().closeDocument(_docName.c_str());
    }


private:
    std::string _docName;
    Data::ElementIDRefs _sid;
    App::StringHasherRef _hasher;
};

TEST_F(WireJoinerTest, addShape)
{
    // Arrange

    // Create various edges that will be used to define the arguments of the various
    // WireJoiner::addShape() calls

    auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()};
    auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge()};
    auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 1.0, 0.0)).Edge()};
    auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()};

    // A vector of TopoDS_Shape used as argument for wjvTDS.addShape()
    std::vector<TopoDS_Shape> edges {edge1, edge2, edge3, edge4};

    // Create various TopoShapes used as arguments for wjvTS.addShape()

    auto edge1TS {TopoShape(edge1, 1)};
    auto edge2TS {TopoShape(edge2, 2)};

    // A wire TopoShape used as argument for wjTS.addShape()
    auto wireTS {TopoShape(BRepBuilderAPI_MakeWire(edge1, edge2, edge3).Wire(), 3)};
    // An empty TopoShape that will contain the shapes added by wjvTS.addShape()
    auto wirevTS {TopoShape(4)};
    // An empty TopoShape that will contain the shapes added by wjvTDS.addShape()
    auto wirevTDS {TopoShape(5)};

    // Create 3 WireJoiner objects, one for every definition of WireJoiner::addShape()

    // A WireJoiner object where it will be added a TopoShape without calling WireJoiner::Build()
    // afterwards
    auto wjTSNotDone {WireJoiner()};
    // A WireJoiner object where it will be added a TopoShape
    auto wjTS {WireJoiner()};
    // A WireJoiner object where it will be added a vector of TopoShapes
    auto wjvTS {WireJoiner()};
    // A WireJoiner object where it will be added a vector of TopoDS_Shapes
    auto wjvTDS {WireJoiner()};

    // Act

    // Calling only WireJoiner::addShape(). Expected result is that wjTSNotDone.Done() is false
    wjTSNotDone.addShape(wireTS);

    // Calling WireJoiner::addShape() and then WireJoiner::Build().
    // If we don't call WireJoiner::Build() we can't see the effect of WireJoiner::addShape() as it
    // adds the shapes in the private member WireJoinerP::sourceEdgeArray

    wjTS.addShape(wireTS);
    wjTS.Build();
    // The wire in wjTS is open, therefore to see the effect of wjTS.addShape() we must call
    // wjTS.getOpenWires() and put the result in wireTS
    wjTS.getOpenWires(wireTS, nullptr, false);

    wjvTS.addShape({edge1TS, edge2TS});
    wjvTS.Build();
    // The wire in wjvTS is open, therefore to see the effect of wjvTS.addShape() we must call
    // wjvTS.getOpenWires() and put the result in wirevTS
    wjvTS.getOpenWires(wirevTS, nullptr, false);

    wjvTDS.addShape(edges);
    wjvTDS.Build();
    // The wire in wjvTDS is closed, therefore to see the effect of wjvTDS.addShape() we can simply
    // call wjvTDS.Shape() to replace the shape in wirevTDS
    wirevTDS.setShape(wjvTDS.Shape());

    // Assert

    // Check the output of WireJoiner::IsDone().
    // It should be true for all the objects that executed also WireJoiner::Build()
    // (All except wjTSNotDone)
    EXPECT_FALSE(wjTSNotDone.IsDone());
    EXPECT_TRUE(wjTS.IsDone());
    EXPECT_TRUE(wjvTS.IsDone());
    EXPECT_TRUE(wjvTDS.IsDone());

    // wireTS is build with 3 edges. The same quantity should be in the shape built
    EXPECT_EQ(wireTS.getSubTopoShapes(TopAbs_EDGE).size(), 3);
    // wirevTS is build with 2 edges. The same quantity should be in the shape built
    EXPECT_EQ(wirevTS.getSubTopoShapes(TopAbs_EDGE).size(), 2);
    // wirevTDS is build with 4 edges. The same quantity should be in the shape built
    EXPECT_EQ(wirevTDS.getSubTopoShapes(TopAbs_EDGE).size(), 4);
}

TEST_F(WireJoinerTest, setOutline)
{
    // Arrange

    // Create various edges that will be used for the WireJoiner objects tests

    auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()};
    auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge()};
    auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()};

    auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, -0.1, 0.0), gp_Pnt(-0.1, 1.1, 0.0)).Edge()};
    auto edge5 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 1.1, 0.0), gp_Pnt(1.1, 1.1, 0.0)).Edge()};
    auto edge6 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()};

    // A vector of edges used as argument for wjNoOutline.addShape()
    std::vector<TopoDS_Shape> edgesNoOutline {edge1, edge2, edge3, edge4, edge5, edge6};
    // A vector of edges used as argument for wjOutline.addShape()
    std::vector<TopoDS_Shape> edgesOutline {edge1, edge2, edge3, edge4, edge5, edge6};

    // To see the effect of setOutline() it is necessary to set the user parameter "Iteration"
    // to a value, in this case, less than zero before the WireObjects are initialized.
    // To see the correct value for this parameter refer to method WireJoinerP::canShowShape()

    auto hParam {App::GetApplication().GetParameterGroupByPath(
        "User parameter:BaseApp/Preferences/WireJoiner"
    )};
    auto catchIteration {hParam->GetInt("Iteration", 0)};
    hParam->SetInt("Iteration", -1);

    // A document where there will WireJoiner.Build() will be called having setOutline() set to
    // false
    auto docNoOutline {App::GetApplication().getActiveDocument()};

    auto _docNameOutline {App::GetApplication().getUniqueDocumentName("docOutline")};
    App::GetApplication().newDocument(_docNameOutline.c_str(), "docOutlineUser");
    // A document where there will WireJoiner.Build() will be called having setOutline() set to true
    auto docOutline {App::GetApplication().getActiveDocument()};

    // A WireJoiner object where the value of setOutline() will be changed but no shapes will be
    // built
    auto wjNoBuild {WireJoiner()};
    // A WireJoiner object where setOutline() will be set to false
    auto wjNoOutline {WireJoiner()};
    // A WireJoiner object where setOutline() will be set to true
    auto wjOutline {WireJoiner()};

    // Reset the parameter to its previous value.
    hParam->SetInt("Iteration", catchIteration);

    // Act

    // Changing only the value of setOutline(). This should set wjNoBuild.IsDone() to false
    wjNoBuild.setOutline(true);

    // We can see the effect of setOutline by searching, among all the DocumentObjects created by
    // the logic, if there are any with a label containing "removed"

    // Testing first the Document where WireJoiner::Build() will be executed with setOutline set to
    // false

    App::GetApplication().setActiveDocument(docNoOutline);

    wjNoOutline.addShape(edgesNoOutline);
    wjNoOutline.setOutline(false);
    wjNoOutline.Build();

    auto objsInDoc {docNoOutline->getObjects()};

    auto foundRemovedNoOutline {false};
    for (const auto& obj : objsInDoc) {
        foundRemovedNoOutline
            = (std::string(obj->Label.getValue()).find("removed") != std::string::npos);
        if (foundRemovedNoOutline) {
            break;
        }
    }

    // Then testing the Document where WireJoiner::Build() will be executed with setOutline set to
    // true

    App::GetApplication().setActiveDocument(docOutline);

    wjOutline.addShape(edgesOutline);
    // same as wjOutline.setOutline(true);
    wjOutline.setOutline();
    wjOutline.Build();

    objsInDoc = docOutline->getObjects();

    auto foundRemovedOutline {false};
    for (const auto& obj : objsInDoc) {
        foundRemovedOutline = (std::string(obj->Label.getValue()).find("removed") != std::string::npos);
        if (foundRemovedOutline) {
            break;
        }
    }

    // Assert

    // Without calling wjNoBuild.Build() the value of wjNoBuild.IsDone() should be false even if we
    // only changed the value of setOutline()
    EXPECT_FALSE(wjNoBuild.IsDone());

    // In a document where WireJoiner::Build() is executed with setOutline() set to false there
    // shouldn't be DocumentObjects with "removed" in their label
    EXPECT_FALSE(foundRemovedNoOutline);

    // In a document where WireJoiner::Build() is executed with setOutline() set to true there
    // should be at least a DocumentObject with "removed" in its label
    EXPECT_TRUE(foundRemovedOutline);
}

TEST_F(WireJoinerTest, setTightBound)
{
    // Arrange

    // Create various edges that will be used for the WireJoiner objects tests

    auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()};
    auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge()};
    auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()};
    auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 1.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()};

    // A vector of edges used as argument for wjNoTightBound.addShape()
    std::vector<TopoDS_Shape> edgesNoTightBound {edge1, edge2, edge3, edge4};
    // A vector of edges used as argument for wjTightBound.addShape()
    std::vector<TopoDS_Shape> edgesTightBound {edge1, edge2, edge3, edge4};

    // A WireJoiner object where the value of setTightBound() will be changed but no shapes will be
    // built
    auto wjNoBuild {WireJoiner()};
    // A WireJoiner object where setTightBound() will be set to false
    auto wjNoTightBound {WireJoiner()};
    // A WireJoiner object where setTightBound() will be set to true
    auto wjTightBound {WireJoiner()};

    // An empty TopoShape that will contain the shapes returned by wjNoTightBound.getOpenWires()
    auto wireNoTightBound {TopoShape(1)};
    // An empty TopoShape that will contain the shapes returned by wjTightBound.getOpenWires()
    auto wireTightBound {TopoShape(2)};

    // Act

    // Changing only the value of setTightBound(). This should set wjNoBuild.IsDone() to false
    wjNoBuild.setTightBound(false);

    // To see the effect of setTightBound() we call WireJoiner::Build() and then
    // WireJoiner::getOpenWires()

    wjNoTightBound.addShape(edgesNoTightBound);
    wjNoTightBound.setTightBound(false);
    // Calling wjNoTightBound.Build() will put all the edges inside the private object
    // WireJoiner::WireJoinerP::openWireCompound.
    wjNoTightBound.Build();
    wjNoTightBound.getOpenWires(wireNoTightBound, nullptr, false);

    wjTightBound.addShape(edgesTightBound);
    // same as wjTightBound.setTightBound(true);
    wjTightBound.setTightBound();
    // Calling wjTightBound.Build() will put inside the private object
    // WireJoiner::WireJoinerP::openWireCompound only the edges that don't contribute to the
    // creation of any closed wire.
    wjTightBound.Build();
    wjTightBound.getOpenWires(wireTightBound, nullptr, false);

    // Assert

    // Without calling wjNoBuild.Build() the value of wjNoBuild.IsDone() should be false even if we
    // only changed the value of setTightBound()
    EXPECT_FALSE(wjNoBuild.IsDone());

    // In this case the number of edges is equal to 6 because:
    // edge1 and edge2 aren't modified => 2 edges
    // edge3 and edge4 are both split in 2 edges => 4 edges
    // The split is made at the intersection point (0.5, 0.5, 0.0)
    EXPECT_EQ(wireNoTightBound.getSubTopoShapes(TopAbs_EDGE).size(), 6);

    // In this case the number of those edges is equal to 1 and that edge is the one with vertexes
    // at the coordinates (0.5, 0.5, 0.0) - (0.0, 1.0, 0.0)
    EXPECT_EQ(wireTightBound.getSubTopoShapes(TopAbs_EDGE).size(), 1);
}

TEST_F(WireJoinerTest, setSplitEdges)
{
    // Arrange

    // Create various edges that will be used for the WireJoiner objects tests

    auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()};
    auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 1.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()};

    // A vector of edges used as argument for wjNoSplitEdges.addShape()
    std::vector<TopoDS_Shape> edgesNoSplitEdges {edge1, edge2};
    // A vector of edges used as argument for wjSplitEdges.addShape()
    std::vector<TopoDS_Shape> edgesSplitEdges {edge1, edge2};

    // A WireJoiner object where the value of setSplitEdges() will be changed but no shapes will be
    // built
    auto wjNoBuild {WireJoiner()};

    // A WireJoiner object where setSplitEdges() will be set to false
    auto wjNoSplitEdges {WireJoiner()};
    // To see it's effect it's necessary also to call setTightBound(false) otherwise
    // WireJoiner::WireJoinerP::splitEdges() will be called in any case
    wjNoSplitEdges.setTightBound(false);

    // A WireJoiner object where setSplitEdges() will be set to true
    auto wjSplitEdges {WireJoiner()};
    // To see it's effect it's necessary also to call setTightBound(false) otherwise
    // WireJoiner::WireJoinerP::splitEdges() will be called in any case
    wjSplitEdges.setTightBound(false);

    // An empty TopoShape that will contain the shapes returned by wjNoSplitEdges.getOpenWires()
    auto wireNoSplitEdges {TopoShape(1)};
    // An empty TopoShape that will contain the shapes returned by wjSplitEdges.getOpenWires()
    auto wireSplitEdges {TopoShape(2)};

    // Act

    // Changing only the value of setSplitEdges(). This should set wjNoBuild.IsDone() to false
    wjNoBuild.setSplitEdges(false);

    // To see the effect of setSplitEdges() we call WireJoiner::Build() and then
    // WireJoiner::getOpenWires()

    wjNoSplitEdges.addShape(edgesNoSplitEdges);
    wjNoSplitEdges.setSplitEdges(false);
    // Calling wjNoSplitEdges.Build() will put all the edges that don't contribute to the creation
    // of a closed wire inside the private object WireJoiner::WireJoinerP::openWireCompound.
    wjNoSplitEdges.Build();
    wjNoSplitEdges.getOpenWires(wireNoSplitEdges, nullptr, false);

    wjSplitEdges.addShape(edgesSplitEdges);
    // same as wjSplitEdges.setSplitEdges(true);
    wjSplitEdges.setSplitEdges();
    // Calling wjSplitEdges.Build() will put inside the private object
    // WireJoiner::WireJoinerP::openWireCompound all the edges processed by
    // WireJoiner::WireJoinerP::splitEdges().
    wjSplitEdges.Build();
    wjSplitEdges.getOpenWires(wireSplitEdges, nullptr, false);

    // Assert

    // Without calling wjNoBuild.Build() the value of wjNoBuild.IsDone() should be false even if we
    // only changed the value of setSplitEdges()
    EXPECT_FALSE(wjNoBuild.IsDone());

    // In this case the number of edges is equal to the number of edges added with
    // wjNoSplitEdges.addShape() because none of them is used for the creation of a closed wire.
    EXPECT_EQ(wireNoSplitEdges.getSubTopoShapes(TopAbs_EDGE).size(), 2);

    // In this case the number of those edges is equal to 4 because both the edges added with
    // wjSplitEdges.addShape() have been split at the intersection point (0.5, 0.5, 0.0)
    EXPECT_EQ(wireSplitEdges.getSubTopoShapes(TopAbs_EDGE).size(), 4);
}

TEST_F(WireJoinerTest, setMergeEdges)
{
    // Arrange

    // Create various edges that will be used for the WireJoiner objects tests

    auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 0.0, 0.0), gp_Pnt(1.1, 0.0, 0.0)).Edge()};
    auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, -0.1, 0.0), gp_Pnt(1.0, 1.1, 0.0)).Edge()};
    auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()};

    // A vector of edges used as argument for wjNoMergeEdges.addShape()
    std::vector<TopoDS_Shape> edgesNoMergeEdges {edge1, edge2, edge3};
    // A vector of edges used as argument for wjMergeEdges.addShape()
    std::vector<TopoDS_Shape> edgesMergeEdges {edge1, edge2, edge3};

    // A WireJoiner object where the value of setMergeEdges() will be changed but no shapes will be
    // built
    auto wjNoBuild {WireJoiner()};

    // A WireJoiner object where setMergeEdges() will be set to false
    auto wjNoMergeEdges {WireJoiner()};
    // To see it's effect it's necessary also to call setTightBound(false) otherwise
    // WireJoiner::WireJoinerP::MergeEdges() will be called in any case
    wjNoMergeEdges.setTightBound(false);

    // A WireJoiner object where setMergeEdges() will be set to true
    auto wjMergeEdges {WireJoiner()};
    // To see it's effect it's necessary also to call setTightBound(false) otherwise
    // WireJoiner::WireJoinerP::MergeEdges() will be called in any case
    wjMergeEdges.setTightBound(false);

    // An empty TopoShape that will contain the shapes returned by wjNoMergeEdges.getOpenWires()
    auto wireNoMergeEdges {TopoShape(1)};
    // An empty TopoShape that will contain the shapes returned by wjMergeEdges.getOpenWires()
    auto wireMergeEdges {TopoShape(2)};

    // Act

    // Changing only the value of setMergeEdges(). This should set wjNoBuild.IsDone() to false
    wjNoBuild.setMergeEdges(false);

    // To see the effect of setMergeEdges() we call WireJoiner::Build() and then
    // WireJoiner::getOpenWires()

    wjNoMergeEdges.addShape(edgesNoMergeEdges);
    wjNoMergeEdges.setMergeEdges(false);
    // Calling wjNoMergeEdges.Build() will put all the edges produced by
    // WireJoiner::WireJoinerP::splitEdges() in the private object
    // WireJoiner::WireJoinerP::openWireCompound.
    wjNoMergeEdges.Build();
    wjNoMergeEdges.getOpenWires(wireNoMergeEdges, nullptr, false);

    wjMergeEdges.addShape(edgesMergeEdges);
    // same as wjMergeEdges.setMergeEdges(true);
    wjMergeEdges.setMergeEdges();
    // Calling wjMergeEdges.Build() will put, among the edges produced by
    // WireJoiner::WireJoinerP::splitEdges(), only the edges that are connected to only one of the
    // others by a single vertex in the private object WireJoiner::WireJoinerP::openWireCompound.
    // In the code those are called SuperEdges and are
    // processed by WireJoiner::WireJoinerP::findSuperEdges().
    wjMergeEdges.Build();
    wjMergeEdges.getOpenWires(wireMergeEdges, nullptr, false);

    // Assert

    // Without calling wjNoBuild.Build() the value of wjNoBuild.IsDone() should be false even if we
    // only changed the value of setMergeEdges()
    EXPECT_FALSE(wjNoBuild.IsDone());

    // In this case the number of edges is equal to 9 because all the 3 edges intersect the other 2
    // and are therefore split in 3 edges each.
    EXPECT_EQ(wireNoMergeEdges.getSubTopoShapes(TopAbs_EDGE).size(), 9);

    // In this case the number of edges is equal to 6 because, among the 9 produced by
    // WireJoiner::WireJoinerP::splitEdges(), 3 of them are connected to more than one other edge
    // and therefore aren't added by WireJoiner::WireJoinerP::findSuperEdges()
    EXPECT_EQ(wireMergeEdges.getSubTopoShapes(TopAbs_EDGE).size(), 6);
}

TEST_F(WireJoinerTest, setTolerance)
{
    // Arrange

    // Create various edges that will be used for the WireJoiner objects tests

    auto pi {acos(-1)};

    auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.1, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()};
    auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge()};
    auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()};
    auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(0.9, 0.0, 0.0)).Edge()};
    auto edge5 {BRepBuilderAPI_MakeEdge(
                    gp_Pnt(0.0, 0.0, 0.0),
                    gp_Pnt(0.9 * std::cos(pi / 18), 0.9 * std::sin(pi / 18), 0.0)
    )
                    .Edge()};

    // A vector of edges used as argument for wjNegtol.addShape()
    std::vector<TopoDS_Shape> edgesNegtol {edge1, edge2, edge3};
    // A vector of edges used as argument for wjtol.addShape()
    std::vector<TopoDS_Shape> edgestol {edge1, edge2, edge3};
    // A vector of edges used as argument for wjNegatol.addShape()
    std::vector<TopoDS_Shape> edgesNegatol {edge2, edge3, edge4, edge5};
    // A vector of edges used as argument for wjatol.addShape()
    std::vector<TopoDS_Shape> edgesatol {edge2, edge3, edge4, edge5};

    // A WireJoiner object where the value of setTolerance() will be changed but no shapes will be
    // built
    auto wjNoBuild {WireJoiner()};

    // A WireJoiner object where setTolerance() will be called passing to the argument tol a
    // negative value.
    auto wjNegtol {WireJoiner()};
    // A WireJoiner object where setTolerance() will be called passing to the argument tol a
    // value both positive and not equal to WireJoiner::WireJoinerP::myTol
    auto wjtol {WireJoiner()};

    // A WireJoiner object where setTolerance() will be called passing to the argument atol a
    // negative value.
    auto wjNegatol {WireJoiner()};
    // A WireJoiner object where setTolerance() will be called passing to the argument atol a
    // value both positive and not equal to WireJoiner::WireJoinerP::myAngularTol
    auto wjatol {WireJoiner()};

    // An empty TopoShape that will contain the shapes returned by wjNegtol.getOpenWires()
    auto wireNegtol {TopoShape(1)};
    // An empty TopoShape that will contain the shapes returned by wjtol.getOpenWires()
    auto wiretol {TopoShape(2)};

    // An empty TopoShape that will contain the shapes returned by wjNegatol.getOpenWires()
    auto wireNegatol {TopoShape(3)};
    // An empty TopoShape that will contain the shapes returned by wjatol.getOpenWires()
    auto wireatol {TopoShape(4)};

    // Act

    // Changing only the value of setTolerance(). This should set wjNoBuild.IsDone() to false
    wjNoBuild.setTolerance(0.1);

    // To see the effect of setTolerance() we call WireJoiner::Build() and then
    // WireJoiner::getOpenWires() to get the edges, if any, that aren't used to create a closed wire

    wjNegtol.addShape(edgesNegtol);
    // Setting tol to a negative value won't have effect and therefore wjNegtol.pimpl->myTol will
    // keep the default value.
    // It's better also to give a negative value for the argument atol otherwise setTolerance()
    // will set it to 0.0
    wjNegtol.setTolerance(-0.1, -pi);
    wjNegtol.Build();
    wjNegtol.getOpenWires(wireNegtol, nullptr, false);

    wjtol.addShape(edgestol);
    // Setting tol to a value that will change wjNegtol.pimpl->myTol.
    // It's better also to give a negative value for the argument atol otherwise setTolerance()
    // will set it to 0.0
    wjtol.setTolerance(0.2, -pi);
    wjtol.Build();
    wjtol.getOpenWires(wiretol, nullptr, false);

    wjNegatol.addShape(edgesNegatol);
    // Setting atol to a negative value won't have effect and therefore
    // wjNegatol.pimpl->myAngularTol will keep the default value. The tol value must be given in any
    // case.
    wjNegatol.setTolerance(-0.1, -pi);
    wjNegatol.Build();
    wjNegatol.getOpenWires(wireNegatol, nullptr, false);

    wjatol.addShape(edgesatol);
    // Setting atol to a negative value won't have effect and therefore
    // wjNegatol.pimpl->myAngularTol will keep the default value. We give also the tol value so that
    // a closed wire can be created.
    wjatol.setTolerance(0.2, pi / 9);
    wjatol.Build();
    wjatol.getOpenWires(wireatol, nullptr, false);

    // Assert

    // Without calling wjNoBuild.Build() the value of wjNoBuild.IsDone() should be false even if we
    // only changed the value of setTolerance()
    EXPECT_FALSE(wjNoBuild.IsDone());

    // In this case, as there's a gap between edge1 and edge3, no closed wires are created.
    EXPECT_TRUE(wjNegtol.Shape().IsNull());
    // All the edges added with wjNegtol.addShape() can be extracted with wjNegtol.getOpenWires()
    EXPECT_EQ(wireNegtol.getSubTopoShapes(TopAbs_EDGE).size(), 3);

    // In this case, as the gap between edge1 and edge3 is smaller than tol, a closed wire can be
    // created and it contains all the edges added with wjtol.addShape().
    EXPECT_EQ(TopoShape(wjtol.Shape()).getSubTopoShapes(TopAbs_EDGE).size(), 3);
    // There are no open wires and therefore no edges that create them
    EXPECT_EQ(wiretol.getSubTopoShapes(TopAbs_EDGE).size(), 0);

    // In this case, as there's a gap between edge2, edge4 and edge5, no closed wires are created.
    EXPECT_TRUE(wjNegatol.Shape().IsNull());
    // All the edges added with wjNegtol.addShape() can be extracted with wjNegatol.getOpenWires()
    EXPECT_EQ(wireNegatol.getSubTopoShapes(TopAbs_EDGE).size(), 4);

    // In this case, as the gap between edge2, edge4 and edge5 is smaller than tol, a closed wire
    // can be created.
    // Because of atol, edge4 and edge5 are considerated as duplicates and therefore one of them is
    // removed by WireJoiner::WireJoinerP::add().
    // The closed wire is then created using all the edges added with wjatol.addShape() except the
    // removed one
    EXPECT_EQ(TopoShape(wjatol.Shape()).getSubTopoShapes(TopAbs_EDGE).size(), 3);
    // There are no open wires and therefore no edges that create them
    EXPECT_EQ(wireatol.getSubTopoShapes(TopAbs_EDGE).size(), 0);
}

TEST_F(WireJoinerTest, getOpenWires)
{
    // Arrange

    // Create various edges that will be used for the WireJoiner objects tests

    auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()};
    auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge()};
    auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()};
    auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.5, 0.5, 0.0), gp_Pnt(1.5, 1.5, 0.0)).Edge()};

    // A vector of edges used as argument for wjNoOpenWires.addShape()
    std::vector<TopoDS_Shape> edgesNoOpenWires {edge1, edge2, edge3};
    // A vector of edges used as argument for wjOriginal.addShape()
    std::vector<TopoDS_Shape> edgesOriginal {edge1, edge2, edge4};
    // A vector of edges used as argument for wjNoOriginal.addShape()
    std::vector<TopoDS_Shape> edgesNoOriginal {edge1, edge2, edge4};
    // A vector of TopoShape edges used as argument for wjNoOp.addShape(). A Tag is needed for every
    // TopoShape, otherwise no element map will be created and no op can be found
    std::vector<TopoShape> edgesNoOp {TopoShape(edge2, 6), TopoShape(edge4, 7)};
    // A vector of TopoShape edges used as argument for wjOp.addShape(). A Tag is needed for every
    // TopoShape, otherwise no element map will be created and no op can be found
    std::vector<TopoShape> edgesOp {TopoShape(edge2, 8), TopoShape(edge4, 9)};

    // A WireJoiner object that will create a closed wire and no open wires
    auto wjNoOpenWires {WireJoiner()};

    // A WireJoiner object where the argument noOriginal will be set to false and that will create
    // an open wire
    auto wjOriginal {WireJoiner()};
    // A WireJoiner object where the argument noOriginal will be set to true and that will create an
    // open wire
    auto wjNoOriginal {WireJoiner()};

    // A WireJoiner object where the argument op won't be set and that will create an open wire
    auto wjNoOp {WireJoiner()};
    // A WireJoiner object where the argument op will be set and that will create an open wire
    auto wjOp {WireJoiner()};

    // An empty TopoShape that will contain the shapes returned by wjNoOpenWires.getOpenWires()
    auto wireNoOpenWires {TopoShape(1)};

    // An empty TopoShape that will contain the shapes returned by wjOriginal.getOpenWires()
    auto wireOriginal {TopoShape(2)};
    // An empty TopoShape that will contain the shapes returned by wjNoOriginal.getOpenWires()
    auto wireNoOriginal {TopoShape(3)};

    // An empty TopoShape that will contain the shapes returned by wjNoOp.getOpenWires()
    auto wireNoOp {TopoShape(4)};
    // An empty TopoShape that will contain the shapes returned by wjOp.getOpenWires()
    auto wireOp {TopoShape(5)};

    // Act

    wjNoOpenWires.addShape(edgesNoOpenWires);
    // wjNoOpenWires.Build() is called by wjNoOpenWires.getOpenWires()
    wjNoOpenWires.getOpenWires(wireNoOpenWires);

    wjOriginal.addShape(edgesOriginal);
    // wjOriginal.Build() is called by wjOriginal.getOpenWires()
    wjOriginal.getOpenWires(wireOriginal, nullptr, false);

    wjNoOriginal.addShape(edgesNoOriginal);
    // wjNoOriginal.Build() is called by wjNoOriginal.getOpenWires()
    wjNoOriginal.getOpenWires(wireNoOriginal);

    wjNoOp.addShape(edgesNoOp);
    // wjNoOp.Build() is called by wjNoOp.getOpenWires()
    wjNoOp.getOpenWires(wireNoOp, nullptr, false);

    wjOp.addShape(edgesOp);
    // wjOp.Build() is called by wjOp.getOpenWires()
    wjOp.getOpenWires(wireOp, "getOpenWires", false);

    // Assert

    // All the edges added with wjNoOpenWires.addShape() are used to create a closed wire, therefore
    // wireNoOpenWires should be null
    EXPECT_TRUE(wireNoOpenWires.isNull());

    // In this case wireOriginal should contain all the edges added with wjOriginal.addShape(),
    // except those ones that are split, and all the edges generated by splitting an edge with
    // another one.
    // edge1 and edge2 are left untouched, while edge4 is split in two at the intersection point
    // (1.0, 1.0, 0.0), therefore 4 edges.
    EXPECT_EQ(wireOriginal.getSubTopoShapes(TopAbs_EDGE).size(), 4);

    // In this case wireNoOriginal should contain only the edges generated by splitting one of them
    // with another one.
    // As edge1 and edge2 are left untouched, the only edges we should find are the ones generated
    // by splitting edge4 in two at the intersection point (1.0, 1.0, 0.0)
    EXPECT_EQ(wireNoOriginal.getSubTopoShapes(TopAbs_EDGE).size(), 2);

    // In this case, as we haven't set a value for op, WireJoiner::WireJoinerP::getOpenWires() will
    // call TopoShape::makeShapeWithElementMap() which, without a value for op, will use
    // Part::OpCodes::Maker as value for the various element maps
    // TODO  no longer works
    //    EXPECT_NE(wireNoOp.getElementMap()[0].name.find(Part::OpCodes::Maker), -1);
    // In this case WireJoiner::WireJoinerP::getOpenWires() will call
    // TopoShape::makeShapeWithElementMap() giving "getOpenWires" as value for the op argument.
    // That value should be found in the various element maps instead of Part::OpCodes::Maker
    EXPECT_EQ(wireOp.getElementMap()[0].name.find(Part::OpCodes::Maker), -1);
    // TODO  no longer works
    //    EXPECT_NE(wireOp.getElementMap()[0].name.find("getOpenWires"), -1);
}

TEST_F(WireJoinerTest, getResultWires)
{
    // Arrange

    // Create various edges that will be used for the WireJoiner objects tests
    // Unlike calling WireJoiner::Build(), WireJoiner::getResultWires() returns a shape with edges
    // only if those given as inputs crosses each others

    auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 0.0, 0.0), gp_Pnt(1.1, 0.0, 0.0)).Edge()};
    auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, -0.1, 0.0), gp_Pnt(1.0, 1.1, 0.0)).Edge()};
    auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(0.1, 0.1, 0.0)).Edge()};
    auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()};

    // A vector of edges used as argument for wjNoResultWires.addShape()
    std::vector<TopoDS_Shape> edgesNoResultWires {edge1, edge2, edge3};
    // A vector of TopoShape edges used as argument for wjNoOp.addShape(). A Tag is needed for every
    // TopoShape, otherwise no element map will be created and no op can be found
    std::vector<TopoShape> edgesNoOp {TopoShape(edge1, 4), TopoShape(edge2, 5), TopoShape(edge4, 6)};
    // A vector of TopoShape edges used as argument for wjOp.addShape(). A Tag is needed for every
    // TopoShape, otherwise no element map will be created and no op can be found
    std::vector<TopoShape> edgesOp {TopoShape(edge1, 7), TopoShape(edge2, 8), TopoShape(edge4, 9)};

    // A WireJoiner object that will create no closed wires
    auto wjNoResultWires {WireJoiner()};

    // A WireJoiner object where the argument op won't be set and that will create a closed wire
    auto wjNoOp {WireJoiner()};
    // A WireJoiner object where the argument op will be set and that will create a closed wire
    auto wjOp {WireJoiner()};

    // An empty TopoShape that will contain the shapes returned by
    // wireNoResultWires.getResultWires()
    auto wireNoResultWires {TopoShape(1)};

    // An empty TopoShape that will contain the shapes returned by wjNoOp.getResultWires()
    auto wireNoOp {TopoShape(2)};
    // An empty TopoShape that will contain the shapes returned by wjOp.getResultWires()
    auto wireOp {TopoShape(3)};


    // Act

    wjNoResultWires.addShape(edgesNoResultWires);
    // wjNoResultWires.Build() is called by wjNoResultWires.getResultWires()
    wjNoResultWires.getResultWires(wireNoResultWires);

    wjNoOp.addShape(edgesNoOp);
    // wjNoOp.Build() is called by wjNoOp.getResultWires()
    wjNoOp.getResultWires(wireNoOp, nullptr);

    wjOp.addShape(edgesOp);
    // wjOp.Build() is called by wjOp.getResultWires()
    wjOp.getResultWires(wireOp, "getResultWires");

    // Assert

    // All the edges added with wjNoResultWires.addShape() can't create a closed wire, therefore
    // wireNoResultWires shouldn't have any edges
    // It's not possible to get an useful result from wireNoResultWires.isNull() because
    // WireJoiner::WireJoinerP::compound is always created by
    // WireJoiner::WireJoinerP::builder.MakeCompound(), which doesn't create a null compound
    EXPECT_EQ(wireNoResultWires.getSubTopoShapes(TopAbs_EDGE).size(), 0);

    // In this case, as we haven't set a value for op, WireJoiner::WireJoinerP::getResultWires()
    // will call TopoShape::makeShapeWithElementMap() which, without a value for op, will use
    // Part::OpCodes::Maker as value for the various element maps
    EXPECT_NE(wireNoOp.getElementMap()[0].name.find(Part::OpCodes::Maker), -1);

    // In this case WireJoiner::WireJoinerP::getResultWires() will call
    // TopoShape::makeShapeWithElementMap() giving "getResultWires" as value for the op argument.
    // That value should be found in the various element maps instead of Part::OpCodes::Maker
    EXPECT_EQ(wireOp.getElementMap()[0].name.find(Part::OpCodes::Maker), -1);
    EXPECT_NE(wireOp.getElementMap()[0].name.find("getResultWires"), -1);
}

#if OCC_VERSION_HEX >= 0x070600
// WireJoiner::Build() has already been tested indirectly in the other tests.
// Here we check only the difference with OCCT versions >= 7.6.0 that add the parameter theRange
TEST_F(WireJoinerTest, Build)
{
    // Arrange

    // Create various edges that will be used for the WireJoiner objects tests

    auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 0.0, 0.0), gp_Pnt(1.1, 0.0, 0.0)).Edge()};
    auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, -0.1, 0.0), gp_Pnt(1.0, 1.1, 0.0)).Edge()};
    auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()};

    // A vector of edges used as argument for wjtheRange.addShape()
    std::vector<TopoDS_Shape> edgestheRange {edge1, edge2, edge3};

    // A WireJoiner object that will create no closed wires
    auto wjtheRange {WireJoiner()};

    // A Message_ProgressRange object that will be used as argument for wjtheRange.Build()
    auto mpr {Message_ProgressRange()};

    // Act

    wjtheRange.addShape(edgestheRange);
    wjtheRange.Build(mpr);

    // Assert

    // theRange isn't used in WireJoiner::Build() and therefore not attached to any indicator.
    // For more reference see
    // https://dev.opencascade.org/doc/occt-7.6.0/refman/html/class_message___progress_range.html
    // and
    // https://dev.opencascade.org/doc/occt-7.6.0/refman/html/class_message___progress_indicator.html
    EXPECT_FALSE(mpr.IsActive());
}
#endif

TEST_F(WireJoinerTest, Modified)
{
    // Arrange

    // Create various edges that will be used for the WireJoiner objects tests

    auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 0.0, 0.0), gp_Pnt(1.1, 0.0, 0.0)).Edge()};
    auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, -0.1, 0.0), gp_Pnt(1.0, 1.1, 0.0)).Edge()};
    auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()};

    // A vector of edges used as argument for wjModified.addShape()
    std::vector<TopoDS_Shape> edges {edge1, edge2, edge3};

    // A WireJoiner object that will have shapes modified by the added edges
    auto wjModified {WireJoiner()};

    // Act

    wjModified.addShape(edges);
    wjModified.Build();

    // Assert

    // In this case, every edge added with wjModified.addShape() modifies other shapes 3 times

    // edge1 modifies the edges with vertexes:
    // (0.0, 0.0, 0.0) - (1.0, 1.0, 0.0)
    // (-0.1, -0.1, 0.0) - (0.0, 0.0, 0.0)
    // (1.0, -0.1, 0.0) - (1.0, 0.0, 0.0)
    EXPECT_EQ(wjModified.Modified(edge1).Size(), 3);

    // edge2 modifies the edges with vertexes:
    // (1.0, 1.0, 0.0) - (1.1, 1.1, 0.0)
    // (0.0, 0.0, 0.0) - (1.0, 0.0, 0.0)
    // (1.0, 0.0, 0.0) - (1.1, 0.0, 0.0)
    EXPECT_EQ(wjModified.Modified(edge2).Size(), 3);

    // edge3 modifies the edges with vertexes:
    // (1.0, 0.0, 0.0) - (1.0, 1.0, 0.0)
    // (1.0, 1.0, 0.0) - (1.0, 1.1, 0.0)
    // (-0.1, 0.0, 0.0) - (0.0, 0.0, 0.0)
    EXPECT_EQ(wjModified.Modified(edge3).Size(), 3);
}

TEST_F(WireJoinerTest, Generated)
{
    // Arrange

    // Create various edges that will be used for the WireJoiner objects tests

    auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(-0.1, 0.0, 0.0), gp_Pnt(1.1, 0.0, 0.0)).Edge()};
    auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, -0.1, 0.0), gp_Pnt(1.0, 1.1, 0.0)).Edge()};
    auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.1, 1.1, 0.0), gp_Pnt(-0.1, -0.1, 0.0)).Edge()};

    // A vector of edges used as argument for wjGenerated.addShape()
    std::vector<TopoDS_Shape> edges {edge1, edge2, edge3};

    // A WireJoiner object that will have shapes generated by the added edges
    auto wjGenerated {WireJoiner()};

    // Act

    wjGenerated.addShape(edges);
    wjGenerated.Build();

    // Assert

    // There aren't calls to WireJoiner::WireJoinerP::aHistory->AddGenerated() or similar methods in
    // WireJoiner::WireJoinerP, therefore nothing is returned by calling
    // WireJoiner::WireJoinerP::aHistory->Generated().
    // There's a call to WireJoiner::WireJoinerP::aHistory->Merge() that uses the history produced
    // by a ShapeFix_Wire object in WireJoiner::WireJoinerP::makeCleanWire() that however looks
    // always empty as no methods in ShapeFix_Wire call AddGenerated() or similar methods
    // (checked in OCCT 7.3.0 source
    // https://git.dev.opencascade.org/gitweb/?p=occt.git;a=snapshot;h=42da0d5115bff683c6b596e66cdeaff957f81e7d;sf=tgz)
    EXPECT_EQ(wjGenerated.Generated(edge1).Size(), 0);
    EXPECT_EQ(wjGenerated.Generated(edge2).Size(), 0);
    EXPECT_EQ(wjGenerated.Generated(edge3).Size(), 0);
}

TEST_F(WireJoinerTest, IsDeleted)
{

    // Create various edges that will be used for the WireJoiner objects tests

    auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()};
    auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge()};
    auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()};
    auto edge4 {BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge()};
    auto edge5 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.49, 0.0, 0.0), gp_Pnt(0.51, 0.0, 0.0)).Edge()};

    // A vector of edges used as argument for wjIsDeleted.addShape()
    std::vector<TopoDS_Shape> edges {edge1, edge2, edge3, edge4, edge5};

    // A WireJoiner object that will have shapes deleted by the added edges
    auto wjIsDeleted {WireJoiner()};
    // To get all the deleted shapes in this case we also need to set the tolerance value
    wjIsDeleted.setTolerance(0.03);

    // Act

    wjIsDeleted.addShape(edges);
    wjIsDeleted.Build();

    // Assert

    // In this case, edge1, edge2 and edge3 don't meet the conditions for deletion
    EXPECT_FALSE(wjIsDeleted.IsDeleted(edge1));
    EXPECT_FALSE(wjIsDeleted.IsDeleted(edge2));
    EXPECT_FALSE(wjIsDeleted.IsDeleted(edge3));

    // edge4 is a duplicate of edge3 and therefore deleted
    EXPECT_TRUE(wjIsDeleted.IsDeleted(edge4));

    // edge5 is smaller that the smallest shape that can be considered with the given value of
    // tolerance and therefore deleted
    EXPECT_TRUE(wjIsDeleted.IsDeleted(edge5));
}

// NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers)