kintsugi 0.4.3 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,6 +8,7 @@ require "tempfile"
8
8
  require "tmpdir"
9
9
 
10
10
  require "kintsugi/apply_change_to_project"
11
+ require "kintsugi/error"
11
12
 
12
13
  require_relative "be_equivalent_to_project"
13
14
 
@@ -50,6 +51,20 @@ describe Kintsugi, :apply_change_to_project do
50
51
  expect(base_project).to be_equivalent_to_project(theirs_project)
51
52
  end
52
53
 
54
+ it "adds package reference" do
55
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
56
+
57
+ theirs_project.root_object.package_references <<
58
+ create_remote_swift_package_reference(theirs_project)
59
+
60
+ changes_to_apply = get_diff(theirs_project, base_project)
61
+
62
+ described_class.apply_change_to_project(base_project, changes_to_apply)
63
+ base_project.save
64
+
65
+ expect(base_project).to be_equivalent_to_project(theirs_project)
66
+ end
67
+
53
68
  it "adds new subproject" do
54
69
  theirs_project = create_copy_of_project(base_project.path, "theirs")
55
70
  add_new_subproject_to_project(theirs_project, "foo", "foo")
@@ -100,6 +115,23 @@ describe Kintsugi, :apply_change_to_project do
100
115
  expect(base_project).to be_equivalent_to_project(theirs_project, ignore_keys: ["containerPortal"])
101
116
  end
102
117
 
118
+ it "raises if adding subproject whose file reference isn't found" do
119
+ ours_project = create_copy_of_project(base_project.path, "ours")
120
+
121
+ add_new_subproject_to_project(base_project, "foo", "foo")
122
+ base_project.save
123
+
124
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
125
+
126
+ base_project.root_object.project_references.pop
127
+
128
+ changes_to_apply = get_diff(theirs_project, base_project)
129
+
130
+ expect {
131
+ described_class.apply_change_to_project(ours_project, changes_to_apply)
132
+ }.to raise_error(Kintsugi::MergeError)
133
+ end
134
+
103
135
  describe "file related changes" do
104
136
  let(:filepath) { "foo" }
105
137
 
@@ -370,6 +402,19 @@ describe Kintsugi, :apply_change_to_project do
370
402
  expect(base_project).to be_equivalent_to_project(theirs_project)
371
403
  end
372
404
 
405
+ it "adds package product dependency to target" do
406
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
407
+ theirs_project.targets[0].package_product_dependencies <<
408
+ create_swift_package_product_dependency(theirs_project)
409
+
410
+ changes_to_apply = get_diff(theirs_project, base_project)
411
+
412
+ described_class.apply_change_to_project(base_project, changes_to_apply)
413
+ base_project.save
414
+
415
+ expect(base_project).to be_equivalent_to_project(theirs_project)
416
+ end
417
+
373
418
  it "changes framework from reference proxy to file reference" do
374
419
  framework_filename = "baz"
375
420
 
@@ -382,7 +427,8 @@ describe Kintsugi, :apply_change_to_project do
382
427
 
383
428
  theirs_project = create_copy_of_project(base_project.path, "theirs")
384
429
 
385
- file_reference = theirs_project.main_group.new_reference(framework_filename)
430
+ file_reference = theirs_project.main_group.new_reference("bar")
431
+ file_reference.name = framework_filename
386
432
  build_phase = theirs_project.targets[0].frameworks_build_phase
387
433
  build_phase.files[-1].remove_from_project
388
434
  theirs_project.targets[0].frameworks_build_phase.add_file_reference(file_reference)
@@ -390,6 +436,10 @@ describe Kintsugi, :apply_change_to_project do
390
436
  changes_to_apply = get_diff(theirs_project, base_project)
391
437
 
392
438
  described_class.apply_change_to_project(base_project, changes_to_apply)
439
+ # This verifies we haven't created a new file reference instead of reusing the one in the
440
+ # hierarchy.
441
+ base_project.files[-1].name = "foo"
442
+ theirs_project.files[-1].name = "foo"
393
443
  base_project.save
394
444
 
395
445
  expect(base_project).to be_equivalent_to_project(theirs_project)
@@ -466,9 +516,28 @@ describe Kintsugi, :apply_change_to_project do
466
516
  expect(base_project).to be_equivalent_to_project(theirs_project, ignore_keys: ["containerPortal"])
467
517
  end
468
518
 
469
- it "adds build file to a file reference that already exist" do
470
- file_reference = base_project.main_group.new_reference("bar")
471
- base_project.targets[0].frameworks_build_phase.add_file_reference(file_reference)
519
+ it "adds product ref to build file" do
520
+ base_project.main_group.new_reference("bar")
521
+ base_project.save
522
+
523
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
524
+
525
+ file_reference = theirs_project.main_group.files.find { |file| file.display_name == "bar" }
526
+ build_file =
527
+ theirs_project.targets[0].frameworks_build_phase.add_file_reference(file_reference)
528
+ build_file.product_ref =
529
+ create_swift_package_product_dependency(theirs_project)
530
+
531
+ changes_to_apply = get_diff(theirs_project, base_project)
532
+
533
+ described_class.apply_change_to_project(base_project, changes_to_apply)
534
+ base_project.save
535
+
536
+ expect(base_project).to be_equivalent_to_project(theirs_project, ignore_keys: ["containerPortal"])
537
+ end
538
+
539
+ it "adds build file to a file reference that already exists" do
540
+ base_project.main_group.new_reference("bar")
472
541
 
473
542
  base_project.main_group.new_reference("bar")
474
543
 
@@ -557,69 +626,427 @@ describe Kintsugi, :apply_change_to_project do
557
626
  expect(base_project).to be_equivalent_to_project(theirs_project, ignore_keys: ["containerPortal"])
558
627
  end
559
628
 
560
- it "adds new build setting" do
561
- theirs_project = create_copy_of_project(base_project.path, "theirs")
629
+ describe "build settings" do
630
+ it "adds new string build setting" do
631
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
562
632
 
563
- theirs_project.targets[0].build_configurations.each do |configuration|
564
- configuration.build_settings["HEADER_SEARCH_PATHS"] = [
565
- "$(SRCROOT)/../Foo",
566
- "$(SRCROOT)/../Bar"
567
- ]
633
+ theirs_project.targets[0].build_configurations.each do |configuration|
634
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = "$(SRCROOT)/../Bar"
635
+ end
636
+
637
+ changes_to_apply = get_diff(theirs_project, base_project)
638
+
639
+ described_class.apply_change_to_project(base_project, changes_to_apply)
640
+ base_project.save
641
+
642
+ expect(base_project).to be_equivalent_to_project(theirs_project)
568
643
  end
569
644
 
570
- changes_to_apply = get_diff(theirs_project, base_project)
645
+ it "adds new array build setting" do
646
+ base_project.targets[0].build_configurations.each do |configuration|
647
+ configuration.build_settings = {}
648
+ end
571
649
 
572
- described_class.apply_change_to_project(base_project, changes_to_apply)
573
- base_project.save
650
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
574
651
 
575
- expect(base_project).to be_equivalent_to_project(theirs_project)
576
- end
652
+ theirs_project.targets[0].build_configurations.each do |configuration|
653
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = [
654
+ "$(SRCROOT)/../Foo",
655
+ "$(SRCROOT)/../Bar"
656
+ ]
657
+ end
577
658
 
578
- it "adds values to existing build setting" do
579
- base_project.targets[0].build_configurations.each do |configuration|
580
- configuration.build_settings["HEADER_SEARCH_PATHS"] = [
581
- "$(SRCROOT)/../Foo"
582
- ]
659
+ changes_to_apply = get_diff(theirs_project, base_project)
660
+
661
+ described_class.apply_change_to_project(base_project, changes_to_apply)
662
+ base_project.save
663
+
664
+ expect(base_project).to be_equivalent_to_project(theirs_project)
583
665
  end
584
666
 
585
- theirs_project = create_copy_of_project(base_project.path, "theirs")
667
+ it "adds new hash build setting" do
668
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
586
669
 
587
- theirs_project.targets[0].build_configurations.each do |configuration|
588
- configuration.build_settings["HEADER_SEARCH_PATHS"] = [
589
- "$(SRCROOT)/../Foo",
590
- "$(SRCROOT)/../Bar"
591
- ]
670
+ theirs_project.targets[0].build_configurations.each do |configuration|
671
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = [
672
+ "$(SRCROOT)/../Foo",
673
+ "$(SRCROOT)/../Bar"
674
+ ]
675
+ end
676
+
677
+ changes_to_apply = get_diff(theirs_project, base_project)
678
+
679
+ described_class.apply_change_to_project(base_project, changes_to_apply)
680
+ base_project.save
681
+
682
+ expect(base_project).to be_equivalent_to_project(theirs_project)
592
683
  end
593
684
 
594
- changes_to_apply = get_diff(theirs_project, base_project)
685
+ it "adds values to existing array build setting" do
686
+ base_project.targets[0].build_configurations.each do |configuration|
687
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = [
688
+ "$(SRCROOT)/../Foo"
689
+ ]
690
+ end
595
691
 
596
- described_class.apply_change_to_project(base_project, changes_to_apply)
597
- base_project.save
692
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
598
693
 
599
- expect(base_project).to be_equivalent_to_project(theirs_project)
600
- end
694
+ theirs_project.targets[0].build_configurations.each do |configuration|
695
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = [
696
+ "$(SRCROOT)/../Foo",
697
+ "$(SRCROOT)/../Bar"
698
+ ]
699
+ end
601
700
 
602
- it "removes build setting" do
603
- base_project.targets[0].build_configurations.each do |configuration|
604
- configuration.build_settings["HEADER_SEARCH_PATHS"] = [
605
- "$(SRCROOT)/../Foo",
606
- "$(SRCROOT)/../Bar"
607
- ]
701
+ changes_to_apply = get_diff(theirs_project, base_project)
702
+
703
+ described_class.apply_change_to_project(base_project, changes_to_apply)
704
+ base_project.save
705
+
706
+ expect(base_project).to be_equivalent_to_project(theirs_project)
608
707
  end
609
708
 
610
- base_project.save
611
- theirs_project = create_copy_of_project(base_project.path, "theirs")
709
+ it "adds array value to an existing string if no removed value" do
710
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
612
711
 
613
- theirs_project.targets[0].build_configurations.each do |configuration|
614
- configuration.build_settings["HEADER_SEARCH_PATHS"] = nil
712
+ theirs_project.targets[0].build_configurations.each do |configuration|
713
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = %w[bar foo]
714
+ end
715
+ changes_to_apply = get_diff(theirs_project, base_project)
716
+
717
+ ours_project = create_copy_of_project(base_project.path, "ours")
718
+ ours_project.targets[0].build_configurations.each do |configuration|
719
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = "baz"
720
+ end
721
+ ours_project.save
722
+
723
+ described_class.apply_change_to_project(ours_project, changes_to_apply)
724
+ ours_project.save
725
+
726
+ expected_project = create_copy_of_project(base_project.path, "expected")
727
+ expected_project.targets[0].build_configurations.each do |configuration|
728
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = %w[bar foo baz]
729
+ end
730
+ expect(ours_project).to be_equivalent_to_project(expected_project)
615
731
  end
616
732
 
617
- changes_to_apply = get_diff(theirs_project, base_project)
733
+ it "adds string value to existing array value if no removed value" do
734
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
618
735
 
619
- described_class.apply_change_to_project(base_project, changes_to_apply)
620
- base_project.save
736
+ theirs_project.targets[0].build_configurations.each do |configuration|
737
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = "baz"
738
+ end
739
+ changes_to_apply = get_diff(theirs_project, base_project)
621
740
 
622
- expect(base_project).to be_equivalent_to_project(theirs_project)
741
+ ours_project = create_copy_of_project(base_project.path, "ours")
742
+ ours_project.targets[0].build_configurations.each do |configuration|
743
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = %w[bar foo]
744
+ end
745
+ ours_project.save
746
+
747
+ described_class.apply_change_to_project(ours_project, changes_to_apply)
748
+ ours_project.save
749
+
750
+ expected_project = create_copy_of_project(base_project.path, "expected")
751
+ expected_project.targets[0].build_configurations.each do |configuration|
752
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = %w[bar foo baz]
753
+ end
754
+ expect(ours_project).to be_equivalent_to_project(expected_project)
755
+ end
756
+
757
+ it "removes array build setting" do
758
+ base_project.targets[0].build_configurations.each do |configuration|
759
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = [
760
+ "$(SRCROOT)/../Foo",
761
+ "$(SRCROOT)/../Bar"
762
+ ]
763
+ end
764
+
765
+ base_project.save
766
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
767
+
768
+ theirs_project.targets[0].build_configurations.each do |configuration|
769
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = nil
770
+ end
771
+
772
+ changes_to_apply = get_diff(theirs_project, base_project)
773
+
774
+ described_class.apply_change_to_project(base_project, changes_to_apply)
775
+ base_project.save
776
+
777
+ expect(base_project).to be_equivalent_to_project(theirs_project)
778
+ end
779
+
780
+ it "removes string build setting" do
781
+ base_project.targets[0].build_configurations.each do |configuration|
782
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = "bar"
783
+ end
784
+
785
+ base_project.save
786
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
787
+
788
+ theirs_project.targets[0].build_configurations.each do |configuration|
789
+ configuration.build_settings =
790
+ configuration.build_settings.reject { |key, _| key == "HEADER_SEARCH_PATHS" }
791
+ end
792
+
793
+ changes_to_apply = get_diff(theirs_project, base_project)
794
+
795
+ described_class.apply_change_to_project(base_project, changes_to_apply)
796
+ base_project.save
797
+
798
+ expect(base_project).to be_equivalent_to_project(theirs_project)
799
+ end
800
+
801
+ it "removes hash build setting" do
802
+ base_project.targets[0].build_configurations.each do |configuration|
803
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = "bar"
804
+ end
805
+
806
+ base_project.save
807
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
808
+ theirs_project.targets[0].build_configurations.each do |configuration|
809
+ configuration.build_settings = nil
810
+ end
811
+
812
+ changes_to_apply = get_diff(theirs_project, base_project)
813
+
814
+ described_class.apply_change_to_project(base_project, changes_to_apply)
815
+ base_project.save
816
+
817
+ expect(base_project).to be_equivalent_to_project(theirs_project)
818
+ end
819
+
820
+ it "removes hash build setting if removed hash contains the existing hash" do
821
+ base_project.targets[0].build_configurations.each do |configuration|
822
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = "bar"
823
+ configuration.build_settings["foo"] = "baz"
824
+ end
825
+
826
+ base_project.save
827
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
828
+ theirs_project.targets[0].build_configurations.each do |configuration|
829
+ configuration.build_settings = nil
830
+ end
831
+
832
+ ours_project = create_copy_of_project(base_project.path, "theirs")
833
+ ours_project.targets[0].build_configurations.each do |configuration|
834
+ configuration.build_settings["foo"] = nil
835
+ end
836
+
837
+ changes_to_apply = get_diff(theirs_project, base_project)
838
+
839
+ described_class.apply_change_to_project(base_project, changes_to_apply)
840
+ base_project.save
841
+
842
+ expect(base_project).to be_equivalent_to_project(theirs_project)
843
+ end
844
+
845
+ it "removes value if existing is string and removed is array that contains it" do
846
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
847
+
848
+ base_project.targets[0].build_configurations.each do |configuration|
849
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = "bar"
850
+ end
851
+ base_project.save
852
+
853
+ before_theirs_project = create_copy_of_project(base_project.path, "before_theirs")
854
+ before_theirs_project.targets[0].build_configurations.each do |configuration|
855
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = ["bar"]
856
+ end
857
+
858
+ changes_to_apply = get_diff(theirs_project, before_theirs_project)
859
+
860
+ described_class.apply_change_to_project(base_project, changes_to_apply)
861
+ base_project.save
862
+
863
+ expect(base_project).to be_equivalent_to_project(theirs_project)
864
+ end
865
+
866
+ it "removes value if removed value is string and existing is array that contains it" do
867
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
868
+
869
+ base_project.targets[0].build_configurations.each do |configuration|
870
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = ["bar"]
871
+ end
872
+ base_project.save
873
+
874
+ before_theirs_project = create_copy_of_project(base_project.path, "before_theirs")
875
+ before_theirs_project.targets[0].build_configurations.each do |configuration|
876
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = "bar"
877
+ end
878
+
879
+ changes_to_apply = get_diff(theirs_project, before_theirs_project)
880
+
881
+ described_class.apply_change_to_project(base_project, changes_to_apply)
882
+ base_project.save
883
+
884
+ expect(base_project).to be_equivalent_to_project(theirs_project)
885
+ end
886
+
887
+ it "removes value if existing is string and removed is array that contains it among other " \
888
+ "values" do
889
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
890
+
891
+ base_project.targets[0].build_configurations.each do |configuration|
892
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = "bar"
893
+ end
894
+ base_project.save
895
+
896
+ before_theirs_project = create_copy_of_project(base_project.path, "before_theirs")
897
+ before_theirs_project.targets[0].build_configurations.each do |configuration|
898
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = %w[bar baz]
899
+ end
900
+
901
+ changes_to_apply = get_diff(theirs_project, before_theirs_project)
902
+
903
+ described_class.apply_change_to_project(base_project, changes_to_apply)
904
+ base_project.save
905
+
906
+ expected_project = create_copy_of_project(base_project.path, "expected")
907
+ expected_project.targets[0].build_configurations.each do |configuration|
908
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = nil
909
+ end
910
+ expect(base_project).to be_equivalent_to_project(expected_project)
911
+ end
912
+
913
+ it "changes to a single string value if removed is string and existing is array that " \
914
+ "contains it among another value" do
915
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
916
+
917
+ base_project.targets[0].build_configurations.each do |configuration|
918
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = %w[bar baz]
919
+ end
920
+ base_project.save
921
+
922
+ before_theirs_project = create_copy_of_project(base_project.path, "before_theirs")
923
+ before_theirs_project.targets[0].build_configurations.each do |configuration|
924
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = "bar"
925
+ end
926
+
927
+ changes_to_apply = get_diff(theirs_project, before_theirs_project)
928
+
929
+ described_class.apply_change_to_project(base_project, changes_to_apply)
930
+ base_project.save
931
+
932
+ expected_project = create_copy_of_project(base_project.path, "expected")
933
+ expected_project.targets[0].build_configurations.each do |configuration|
934
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = "baz"
935
+ end
936
+ expect(base_project).to be_equivalent_to_project(expected_project)
937
+ end
938
+
939
+ it "changes to string value if change contains removal of existing array" do
940
+ base_project.targets[0].build_configurations.each do |configuration|
941
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = %w[bar foo]
942
+ end
943
+
944
+ base_project.save
945
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
946
+
947
+ theirs_project.targets[0].build_configurations.each do |configuration|
948
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = "baz"
949
+ end
950
+
951
+ changes_to_apply = get_diff(theirs_project, base_project)
952
+
953
+ described_class.apply_change_to_project(base_project, changes_to_apply)
954
+ base_project.save
955
+
956
+ expect(base_project).to be_equivalent_to_project(theirs_project)
957
+ end
958
+
959
+ it "changes to array value if change contains removal of existing string" do
960
+ base_project.targets[0].build_configurations.each do |configuration|
961
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = "bar"
962
+ end
963
+
964
+ base_project.save
965
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
966
+
967
+ theirs_project.targets[0].build_configurations.each do |configuration|
968
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = %w[baz foo]
969
+ end
970
+
971
+ changes_to_apply = get_diff(theirs_project, base_project)
972
+
973
+ described_class.apply_change_to_project(base_project, changes_to_apply)
974
+ base_project.save
975
+
976
+ expect(base_project).to be_equivalent_to_project(theirs_project)
977
+ end
978
+
979
+ it "changes to array if added value is string and existing is another string and removal is" \
980
+ "nil for an array build setting" do
981
+ before_theirs_project = create_copy_of_project(base_project.path, "theirs")
982
+
983
+ base_project.targets[0].build_configurations.each do |configuration|
984
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = "bar"
985
+ end
986
+ base_project.save
987
+
988
+ theirs_project = create_copy_of_project(base_project.path, "before_theirs")
989
+ theirs_project.targets[0].build_configurations.each do |configuration|
990
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = "baz"
991
+ end
992
+
993
+ expected_project = create_copy_of_project(base_project.path, "expected")
994
+ expected_project.targets[0].build_configurations.each do |configuration|
995
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = %w[bar baz]
996
+ end
997
+
998
+ changes_to_apply = get_diff(theirs_project, before_theirs_project)
999
+
1000
+ described_class.apply_change_to_project(base_project, changes_to_apply)
1001
+ base_project.save
1002
+
1003
+ expect(base_project).to be_equivalent_to_project(expected_project)
1004
+ end
1005
+
1006
+ it "raises if added value is string and existing is another string and removal is nil for a " \
1007
+ "string build setting" do
1008
+ before_theirs_project = create_copy_of_project(base_project.path, "theirs")
1009
+
1010
+ base_project.targets[0].build_configurations.each do |configuration|
1011
+ configuration.build_settings["PRODUCT_NAME"] = "bar"
1012
+ end
1013
+ base_project.save
1014
+
1015
+ theirs_project = create_copy_of_project(base_project.path, "before_theirs")
1016
+ theirs_project.targets[0].build_configurations.each do |configuration|
1017
+ configuration.build_settings["PRODUCT_NAME"] = "baz"
1018
+ end
1019
+
1020
+ changes_to_apply = get_diff(theirs_project, before_theirs_project)
1021
+
1022
+ expect {
1023
+ described_class.apply_change_to_project(base_project, changes_to_apply)
1024
+ }.to raise_error(Kintsugi::MergeError)
1025
+ end
1026
+
1027
+ it "raises if trying to remove hash entry whose value changed" do
1028
+ base_project.targets[0].build_configurations.each do |configuration|
1029
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = "bar"
1030
+ end
1031
+
1032
+ base_project.save
1033
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
1034
+ theirs_project.targets[0].build_configurations.each do |configuration|
1035
+ configuration.build_settings = nil
1036
+ end
1037
+
1038
+ base_project.save
1039
+ before_theirs_project = create_copy_of_project(base_project.path, "theirs")
1040
+ before_theirs_project.targets[0].build_configurations.each do |configuration|
1041
+ configuration.build_settings["HEADER_SEARCH_PATHS"] = "baz"
1042
+ end
1043
+
1044
+ changes_to_apply = get_diff(theirs_project, before_theirs_project)
1045
+
1046
+ expect {
1047
+ described_class.apply_change_to_project(base_project, changes_to_apply)
1048
+ }.to raise_error(Kintsugi::MergeError)
1049
+ end
623
1050
  end
624
1051
 
625
1052
  it "adds build phases" do
@@ -640,6 +1067,20 @@ describe Kintsugi, :apply_change_to_project do
640
1067
  expect(base_project).to be_equivalent_to_project(theirs_project)
641
1068
  end
642
1069
 
1070
+ it "adds build phase with a simple attribute value that has non nil default" do
1071
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
1072
+
1073
+ theirs_project.targets[0].new_shell_script_build_phase("bar")
1074
+ theirs_project.targets[0].build_phases.last.shell_script = "Other value"
1075
+
1076
+ changes_to_apply = get_diff(theirs_project, base_project)
1077
+
1078
+ described_class.apply_change_to_project(base_project, changes_to_apply)
1079
+ base_project.save
1080
+
1081
+ expect(base_project).to be_equivalent_to_project(theirs_project)
1082
+ end
1083
+
643
1084
  it "removes build phase" do
644
1085
  base_project.targets[0].new_shell_script_build_phase("bar")
645
1086
 
@@ -752,7 +1193,7 @@ describe Kintsugi, :apply_change_to_project do
752
1193
  base_project.save
753
1194
  theirs_project = create_copy_of_project(base_project.path, "theirs")
754
1195
 
755
- theirs_project.root_object.known_regions += ["en"]
1196
+ theirs_project.root_object.known_regions += ["fr"]
756
1197
 
757
1198
  changes_to_apply = get_diff(theirs_project, base_project)
758
1199
 
@@ -763,12 +1204,9 @@ describe Kintsugi, :apply_change_to_project do
763
1204
  end
764
1205
 
765
1206
  it "removes known regions" do
766
- base_project.root_object.known_regions += ["en"]
767
-
768
- base_project.save
769
1207
  theirs_project = create_copy_of_project(base_project.path, "theirs")
770
1208
 
771
- theirs_project.root_object.known_regions = []
1209
+ theirs_project.root_object.known_regions = nil
772
1210
 
773
1211
  changes_to_apply = get_diff(theirs_project, base_project)
774
1212
 
@@ -927,6 +1365,41 @@ describe Kintsugi, :apply_change_to_project do
927
1365
  expect(base_project).to be_equivalent_to_project(theirs_project)
928
1366
  end
929
1367
 
1368
+ it "adds group to product group" do
1369
+ base_project_path = make_temp_directory("base", ".xcodeproj")
1370
+ base_project = Xcodeproj::Project.new(base_project_path)
1371
+ base_project.new_target("com.apple.product-type.library.static", "foo", :ios)
1372
+
1373
+ base_project.save
1374
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
1375
+
1376
+ theirs_project.root_object.product_ref_group.new_group("foo")
1377
+
1378
+ changes_to_apply = get_diff(theirs_project, base_project)
1379
+
1380
+ described_class.apply_change_to_project(base_project, changes_to_apply)
1381
+
1382
+ expect(base_project).to be_equivalent_to_project(theirs_project)
1383
+ end
1384
+
1385
+ it "adds localization files to product group" do
1386
+ base_project_path = make_temp_directory("base", ".xcodeproj")
1387
+ base_project = Xcodeproj::Project.new(base_project_path)
1388
+ base_project.new_target("com.apple.product-type.library.static", "foo", :ios)
1389
+
1390
+ base_project.save
1391
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
1392
+
1393
+ variant_group = theirs_project.root_object.product_ref_group.new_variant_group("foo.strings")
1394
+ variant_group.new_reference("Base").last_known_file_type = "text.plist.strings"
1395
+
1396
+ changes_to_apply = get_diff(theirs_project, base_project)
1397
+
1398
+ described_class.apply_change_to_project(base_project, changes_to_apply)
1399
+
1400
+ expect(base_project).to be_equivalent_to_project(theirs_project)
1401
+ end
1402
+
930
1403
  def create_copy_of_project(project_path, new_project_prefix)
931
1404
  copied_project_path = make_temp_directory(new_project_prefix, ".xcodeproj")
932
1405
  FileUtils.cp(File.join(project_path, "project.pbxproj"), copied_project_path)
@@ -934,7 +1407,15 @@ describe Kintsugi, :apply_change_to_project do
934
1407
  end
935
1408
 
936
1409
  def get_diff(first_project, second_project)
937
- Xcodeproj::Differ.project_diff(first_project, second_project, :added, :removed)
1410
+ diff = Xcodeproj::Differ.project_diff(first_project, second_project, :added, :removed)
1411
+
1412
+ diff_without_display_name =
1413
+ diff.merge("rootObject" => diff["rootObject"].reject { |key, _| key == "displayName" })
1414
+ if diff_without_display_name == {"rootObject" => {}}
1415
+ raise "Diff contains no changes. This probably means the test doesn't check anything."
1416
+ end
1417
+
1418
+ diff
938
1419
  end
939
1420
 
940
1421
  def add_new_subproject_to_project(project, subproject_name, subproject_product_name)
@@ -987,6 +1468,22 @@ describe Kintsugi, :apply_change_to_project do
987
1468
  reference_proxy
988
1469
  end
989
1470
 
1471
+ def create_swift_package_product_dependency(project)
1472
+ product_dependency = project.new(Xcodeproj::Project::XCSwiftPackageProductDependency)
1473
+ product_dependency.product_name = "foo"
1474
+ product_dependency.package = create_remote_swift_package_reference(project)
1475
+
1476
+ product_dependency
1477
+ end
1478
+
1479
+ def create_remote_swift_package_reference(project)
1480
+ package_reference = project.new(Xcodeproj::Project::XCRemoteSwiftPackageReference)
1481
+ package_reference.repositoryURL = "http://foo"
1482
+ package_reference.requirement = {"foo" => "bar"}
1483
+
1484
+ package_reference
1485
+ end
1486
+
990
1487
  def make_temp_directory(directory_prefix, directory_extension)
991
1488
  directory_path = Dir.mktmpdir([directory_prefix, directory_extension])
992
1489
  temporary_directories_paths << directory_path