| ... | @@ -608,313 +608,3 @@ fn expandVariablesCmake( | ... | @@ -608,313 +608,3 @@ fn expandVariablesCmake( |
| 608 | | 608 | |
| 609 | return result.toOwnedSliceAssert(); | 609 | return result.toOwnedSliceAssert(); |
| 610 | } | 610 | } |
| 611 | | | |
| 612 | fn testReplaceVariablesAutoconfAt( | | |
| 613 | arena: Allocator, | | |
| 614 | contents: []const u8, | | |
| 615 | expected: []const u8, | | |
| 616 | value_map: *const ValueMap, | | |
| 617 | ) !void { | | |
| 618 | var aw: Writer.Allocating = .init(arena); | | |
| 619 | defer aw.deinit(); | | |
| 620 | | | |
| 621 | const used = try arena.alloc(bool, value_map.count()); | | |
| 622 | for (used) |*u| u.* = false; | | |
| 623 | | | |
| 624 | try expandVariablesAutoconfAt(&aw.writer, contents, value_map, used); | | |
| 625 | | | |
| 626 | for (used) |u| if (!u) return error.UnusedValue; | | |
| 627 | try std.testing.expectEqualStrings(expected, aw.written()); | | |
| 628 | } | | |
| 629 | | | |
| 630 | fn testReplaceVariablesCMake( | | |
| 631 | arena: Allocator, | | |
| 632 | contents: []const u8, | | |
| 633 | expected: []const u8, | | |
| 634 | value_map: *const ValueMap, | | |
| 635 | ) !void { | | |
| 636 | const actual = try expandVariablesCmake(arena, contents, value_map); | | |
| 637 | | | |
| 638 | try std.testing.expectEqualStrings(expected, actual); | | |
| 639 | } | | |
| 640 | | | |
| 641 | test "expandVariablesAutoconfAt simple cases" { | | |
| 642 | const allocator = std.testing.allocator; | | |
| 643 | var value_map: ValueMap = .empty; | | |
| 644 | defer value_map.deinit(); | | |
| 645 | | | |
| 646 | // empty strings are preserved | | |
| 647 | try testReplaceVariablesAutoconfAt(allocator, "", "", value_map); | | |
| 648 | | | |
| 649 | // line with misc content is preserved | | |
| 650 | try testReplaceVariablesAutoconfAt(allocator, "no substitution", "no substitution", value_map); | | |
| 651 | | | |
| 652 | // empty @ sigils are preserved | | |
| 653 | try testReplaceVariablesAutoconfAt(allocator, "@", "@", value_map); | | |
| 654 | try testReplaceVariablesAutoconfAt(allocator, "@@", "@@", value_map); | | |
| 655 | try testReplaceVariablesAutoconfAt(allocator, "@@@", "@@@", value_map); | | |
| 656 | try testReplaceVariablesAutoconfAt(allocator, "@@@@", "@@@@", value_map); | | |
| 657 | | | |
| 658 | // simple substitution | | |
| 659 | try value_map.putNoClobber("undef", .undef); | | |
| 660 | try testReplaceVariablesAutoconfAt(allocator, "@undef@", "", value_map); | | |
| 661 | value_map.clearRetainingCapacity(); | | |
| 662 | | | |
| 663 | try value_map.putNoClobber("defined", .defined); | | |
| 664 | try testReplaceVariablesAutoconfAt(allocator, "@defined@", "", value_map); | | |
| 665 | value_map.clearRetainingCapacity(); | | |
| 666 | | | |
| 667 | try value_map.putNoClobber("true", Value{ .boolean = true }); | | |
| 668 | try testReplaceVariablesAutoconfAt(allocator, "@true@", "1", value_map); | | |
| 669 | value_map.clearRetainingCapacity(); | | |
| 670 | | | |
| 671 | try value_map.putNoClobber("false", Value{ .boolean = false }); | | |
| 672 | try testReplaceVariablesAutoconfAt(allocator, "@false@", "0", value_map); | | |
| 673 | value_map.clearRetainingCapacity(); | | |
| 674 | | | |
| 675 | try value_map.putNoClobber("int", Value{ .int = 42 }); | | |
| 676 | try testReplaceVariablesAutoconfAt(allocator, "@int@", "42", value_map); | | |
| 677 | value_map.clearRetainingCapacity(); | | |
| 678 | | | |
| 679 | try value_map.putNoClobber("ident", Value{ .string = "value" }); | | |
| 680 | try testReplaceVariablesAutoconfAt(allocator, "@ident@", "value", value_map); | | |
| 681 | value_map.clearRetainingCapacity(); | | |
| 682 | | | |
| 683 | try value_map.putNoClobber("string", Value{ .string = "text" }); | | |
| 684 | try testReplaceVariablesAutoconfAt(allocator, "@string@", "text", value_map); | | |
| 685 | value_map.clearRetainingCapacity(); | | |
| 686 | | | |
| 687 | // double packed substitution | | |
| 688 | try value_map.putNoClobber("string", Value{ .string = "text" }); | | |
| 689 | try testReplaceVariablesAutoconfAt(allocator, "@string@@string@", "texttext", value_map); | | |
| 690 | value_map.clearRetainingCapacity(); | | |
| 691 | | | |
| 692 | // triple packed substitution | | |
| 693 | try value_map.putNoClobber("int", Value{ .int = 42 }); | | |
| 694 | try value_map.putNoClobber("string", Value{ .string = "text" }); | | |
| 695 | try testReplaceVariablesAutoconfAt(allocator, "@string@@int@@string@", "text42text", value_map); | | |
| 696 | value_map.clearRetainingCapacity(); | | |
| 697 | | | |
| 698 | // double separated substitution | | |
| 699 | try value_map.putNoClobber("int", Value{ .int = 42 }); | | |
| 700 | try testReplaceVariablesAutoconfAt(allocator, "@int@.@int@", "42.42", value_map); | | |
| 701 | value_map.clearRetainingCapacity(); | | |
| 702 | | | |
| 703 | // triple separated substitution | | |
| 704 | try value_map.putNoClobber("true", Value{ .boolean = true }); | | |
| 705 | try value_map.putNoClobber("int", Value{ .int = 42 }); | | |
| 706 | try testReplaceVariablesAutoconfAt(allocator, "@int@.@true@.@int@", "42.1.42", value_map); | | |
| 707 | value_map.clearRetainingCapacity(); | | |
| 708 | | | |
| 709 | // misc prefix is preserved | | |
| 710 | try value_map.putNoClobber("false", Value{ .boolean = false }); | | |
| 711 | try testReplaceVariablesAutoconfAt(allocator, "false is @false@", "false is 0", value_map); | | |
| 712 | value_map.clearRetainingCapacity(); | | |
| 713 | | | |
| 714 | // misc suffix is preserved | | |
| 715 | try value_map.putNoClobber("true", Value{ .boolean = true }); | | |
| 716 | try testReplaceVariablesAutoconfAt(allocator, "@true@ is true", "1 is true", value_map); | | |
| 717 | value_map.clearRetainingCapacity(); | | |
| 718 | | | |
| 719 | // surrounding content is preserved | | |
| 720 | try value_map.putNoClobber("int", Value{ .int = 42 }); | | |
| 721 | try testReplaceVariablesAutoconfAt(allocator, "what is 6*7? @int@!", "what is 6*7? 42!", value_map); | | |
| 722 | value_map.clearRetainingCapacity(); | | |
| 723 | | | |
| 724 | // incomplete key is preserved | | |
| 725 | try testReplaceVariablesAutoconfAt(allocator, "@undef", "@undef", value_map); | | |
| 726 | | | |
| 727 | // unknown key leads to an error | | |
| 728 | try std.testing.expectError(error.MissingValue, testReplaceVariablesAutoconfAt(allocator, "@bad@", "", value_map)); | | |
| 729 | | | |
| 730 | // unused key leads to an error | | |
| 731 | try value_map.putNoClobber("int", Value{ .int = 42 }); | | |
| 732 | try value_map.putNoClobber("false", Value{ .boolean = false }); | | |
| 733 | try std.testing.expectError(error.UnusedValue, testReplaceVariablesAutoconfAt(allocator, "@int", "", value_map)); | | |
| 734 | value_map.clearRetainingCapacity(); | | |
| 735 | } | | |
| 736 | | | |
| 737 | test "expandVariablesAutoconfAt edge cases" { | | |
| 738 | const allocator = std.testing.allocator; | | |
| 739 | var value_map: std.array_hash_map.String(Value) = .init(allocator); | | |
| 740 | defer value_map.deinit(); | | |
| 741 | | | |
| 742 | // @-vars resolved only when they wrap valid characters, otherwise considered literals | | |
| 743 | try value_map.putNoClobber("string", Value{ .string = "text" }); | | |
| 744 | try testReplaceVariablesAutoconfAt(allocator, "@@string@@", "@text@", value_map); | | |
| 745 | value_map.clearRetainingCapacity(); | | |
| 746 | | | |
| 747 | // expanded variables are considered strings after expansion | | |
| 748 | try value_map.putNoClobber("string_at", Value{ .string = "@string@" }); | | |
| 749 | try testReplaceVariablesAutoconfAt(allocator, "@string_at@", "@string@", value_map); | | |
| 750 | value_map.clearRetainingCapacity(); | | |
| 751 | } | | |
| 752 | | | |
| 753 | test "expandVariablesCmake simple cases" { | | |
| 754 | const allocator = std.testing.allocator; | | |
| 755 | var value_map: std.array_hash_map.String(Value) = .init(allocator); | | |
| 756 | defer value_map.deinit(); | | |
| 757 | | | |
| 758 | try value_map.putNoClobber("undef", .undef); | | |
| 759 | try value_map.putNoClobber("defined", .defined); | | |
| 760 | try value_map.putNoClobber("true", Value{ .boolean = true }); | | |
| 761 | try value_map.putNoClobber("false", Value{ .boolean = false }); | | |
| 762 | try value_map.putNoClobber("int", Value{ .int = 42 }); | | |
| 763 | try value_map.putNoClobber("ident", Value{ .string = "value" }); | | |
| 764 | try value_map.putNoClobber("string", Value{ .string = "text" }); | | |
| 765 | | | |
| 766 | // empty strings are preserved | | |
| 767 | try testReplaceVariablesCMake(allocator, "", "", value_map); | | |
| 768 | | | |
| 769 | // line with misc content is preserved | | |
| 770 | try testReplaceVariablesCMake(allocator, "no substitution", "no substitution", value_map); | | |
| 771 | | | |
| 772 | // empty ${} wrapper leads to an error | | |
| 773 | try std.testing.expectError(error.MissingKey, testReplaceVariablesCMake(allocator, "${}", "", value_map)); | | |
| 774 | | | |
| 775 | // empty @ sigils are preserved | | |
| 776 | try testReplaceVariablesCMake(allocator, "@", "@", value_map); | | |
| 777 | try testReplaceVariablesCMake(allocator, "@@", "@@", value_map); | | |
| 778 | try testReplaceVariablesCMake(allocator, "@@@", "@@@", value_map); | | |
| 779 | try testReplaceVariablesCMake(allocator, "@@@@", "@@@@", value_map); | | |
| 780 | | | |
| 781 | // simple substitution | | |
| 782 | try testReplaceVariablesCMake(allocator, "@undef@", "", value_map); | | |
| 783 | try testReplaceVariablesCMake(allocator, "${undef}", "", value_map); | | |
| 784 | try testReplaceVariablesCMake(allocator, "@defined@", "", value_map); | | |
| 785 | try testReplaceVariablesCMake(allocator, "${defined}", "", value_map); | | |
| 786 | try testReplaceVariablesCMake(allocator, "@true@", "1", value_map); | | |
| 787 | try testReplaceVariablesCMake(allocator, "${true}", "1", value_map); | | |
| 788 | try testReplaceVariablesCMake(allocator, "@false@", "0", value_map); | | |
| 789 | try testReplaceVariablesCMake(allocator, "${false}", "0", value_map); | | |
| 790 | try testReplaceVariablesCMake(allocator, "@int@", "42", value_map); | | |
| 791 | try testReplaceVariablesCMake(allocator, "${int}", "42", value_map); | | |
| 792 | try testReplaceVariablesCMake(allocator, "@ident@", "value", value_map); | | |
| 793 | try testReplaceVariablesCMake(allocator, "${ident}", "value", value_map); | | |
| 794 | try testReplaceVariablesCMake(allocator, "@string@", "text", value_map); | | |
| 795 | try testReplaceVariablesCMake(allocator, "${string}", "text", value_map); | | |
| 796 | | | |
| 797 | // double packed substitution | | |
| 798 | try testReplaceVariablesCMake(allocator, "@string@@string@", "texttext", value_map); | | |
| 799 | try testReplaceVariablesCMake(allocator, "${string}${string}", "texttext", value_map); | | |
| 800 | | | |
| 801 | // triple packed substitution | | |
| 802 | try testReplaceVariablesCMake(allocator, "@string@@int@@string@", "text42text", value_map); | | |
| 803 | try testReplaceVariablesCMake(allocator, "@string@${int}@string@", "text42text", value_map); | | |
| 804 | try testReplaceVariablesCMake(allocator, "${string}@int@${string}", "text42text", value_map); | | |
| 805 | try testReplaceVariablesCMake(allocator, "${string}${int}${string}", "text42text", value_map); | | |
| 806 | | | |
| 807 | // double separated substitution | | |
| 808 | try testReplaceVariablesCMake(allocator, "@int@.@int@", "42.42", value_map); | | |
| 809 | try testReplaceVariablesCMake(allocator, "${int}.${int}", "42.42", value_map); | | |
| 810 | | | |
| 811 | // triple separated substitution | | |
| 812 | try testReplaceVariablesCMake(allocator, "@int@.@true@.@int@", "42.1.42", value_map); | | |
| 813 | try testReplaceVariablesCMake(allocator, "@int@.${true}.@int@", "42.1.42", value_map); | | |
| 814 | try testReplaceVariablesCMake(allocator, "${int}.@true@.${int}", "42.1.42", value_map); | | |
| 815 | try testReplaceVariablesCMake(allocator, "${int}.${true}.${int}", "42.1.42", value_map); | | |
| 816 | | | |
| 817 | // misc prefix is preserved | | |
| 818 | try testReplaceVariablesCMake(allocator, "false is @false@", "false is 0", value_map); | | |
| 819 | try testReplaceVariablesCMake(allocator, "false is ${false}", "false is 0", value_map); | | |
| 820 | | | |
| 821 | // misc suffix is preserved | | |
| 822 | try testReplaceVariablesCMake(allocator, "@true@ is true", "1 is true", value_map); | | |
| 823 | try testReplaceVariablesCMake(allocator, "${true} is true", "1 is true", value_map); | | |
| 824 | | | |
| 825 | // surrounding content is preserved | | |
| 826 | try testReplaceVariablesCMake(allocator, "what is 6*7? @int@!", "what is 6*7? 42!", value_map); | | |
| 827 | try testReplaceVariablesCMake(allocator, "what is 6*7? ${int}!", "what is 6*7? 42!", value_map); | | |
| 828 | | | |
| 829 | // incomplete key is preserved | | |
| 830 | try testReplaceVariablesCMake(allocator, "@undef", "@undef", value_map); | | |
| 831 | try testReplaceVariablesCMake(allocator, "${undef", "${undef", value_map); | | |
| 832 | try testReplaceVariablesCMake(allocator, "{undef}", "{undef}", value_map); | | |
| 833 | try testReplaceVariablesCMake(allocator, "undef@", "undef@", value_map); | | |
| 834 | try testReplaceVariablesCMake(allocator, "undef}", "undef}", value_map); | | |
| 835 | | | |
| 836 | // unknown key leads to an error | | |
| 837 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "@bad@", "", value_map)); | | |
| 838 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${bad}", "", value_map)); | | |
| 839 | } | | |
| 840 | | | |
| 841 | test "expandVariablesCmake edge cases" { | | |
| 842 | const allocator = std.testing.allocator; | | |
| 843 | var value_map: std.array_hash_map.String(Value) = .init(allocator); | | |
| 844 | defer value_map.deinit(); | | |
| 845 | | | |
| 846 | // special symbols | | |
| 847 | try value_map.putNoClobber("at", Value{ .string = "@" }); | | |
| 848 | try value_map.putNoClobber("dollar", Value{ .string = "$" }); | | |
| 849 | try value_map.putNoClobber("underscore", Value{ .string = "_" }); | | |
| 850 | | | |
| 851 | // basic value | | |
| 852 | try value_map.putNoClobber("string", Value{ .string = "text" }); | | |
| 853 | | | |
| 854 | // proxy case value_map | | |
| 855 | try value_map.putNoClobber("string_proxy", Value{ .string = "string" }); | | |
| 856 | try value_map.putNoClobber("string_at", Value{ .string = "@string@" }); | | |
| 857 | try value_map.putNoClobber("string_curly", Value{ .string = "{string}" }); | | |
| 858 | try value_map.putNoClobber("string_var", Value{ .string = "${string}" }); | | |
| 859 | | | |
| 860 | // stack case value_map | | |
| 861 | try value_map.putNoClobber("nest_underscore_proxy", Value{ .string = "underscore" }); | | |
| 862 | try value_map.putNoClobber("nest_proxy", Value{ .string = "nest_underscore_proxy" }); | | |
| 863 | | | |
| 864 | // @-vars resolved only when they wrap valid characters, otherwise considered literals | | |
| 865 | try testReplaceVariablesCMake(allocator, "@@string@@", "@text@", value_map); | | |
| 866 | try testReplaceVariablesCMake(allocator, "@${string}@", "@text@", value_map); | | |
| 867 | | | |
| 868 | // @-vars are resolved inside ${}-vars | | |
| 869 | try testReplaceVariablesCMake(allocator, "${@string_proxy@}", "text", value_map); | | |
| 870 | | | |
| 871 | // expanded variables are considered strings after expansion | | |
| 872 | try testReplaceVariablesCMake(allocator, "@string_at@", "@string@", value_map); | | |
| 873 | try testReplaceVariablesCMake(allocator, "${string_at}", "@string@", value_map); | | |
| 874 | try testReplaceVariablesCMake(allocator, "$@string_curly@", "${string}", value_map); | | |
| 875 | try testReplaceVariablesCMake(allocator, "$${string_curly}", "${string}", value_map); | | |
| 876 | try testReplaceVariablesCMake(allocator, "${string_var}", "${string}", value_map); | | |
| 877 | try testReplaceVariablesCMake(allocator, "@string_var@", "${string}", value_map); | | |
| 878 | try testReplaceVariablesCMake(allocator, "${dollar}{${string}}", "${text}", value_map); | | |
| 879 | try testReplaceVariablesCMake(allocator, "@dollar@{${string}}", "${text}", value_map); | | |
| 880 | try testReplaceVariablesCMake(allocator, "@dollar@{@string@}", "${text}", value_map); | | |
| 881 | | | |
| 882 | // when expanded variables contain invalid characters, they prevent further expansion | | |
| 883 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${${string_var}}", "", value_map)); | | |
| 884 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${@string_var@}", "", value_map)); | | |
| 885 | | | |
| 886 | // nested expanded variables are expanded from the inside out | | |
| 887 | try testReplaceVariablesCMake(allocator, "${string${underscore}proxy}", "string", value_map); | | |
| 888 | try testReplaceVariablesCMake(allocator, "${string@underscore@proxy}", "string", value_map); | | |
| 889 | | | |
| 890 | // nested vars are only expanded when ${} is closed | | |
| 891 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "@nest@underscore@proxy@", "", value_map)); | | |
| 892 | try testReplaceVariablesCMake(allocator, "${nest${underscore}proxy}", "nest_underscore_proxy", value_map); | | |
| 893 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "@nest@@nest_underscore@underscore@proxy@@proxy@", "", value_map)); | | |
| 894 | try testReplaceVariablesCMake(allocator, "${nest${${nest_underscore${underscore}proxy}}proxy}", "nest_underscore_proxy", value_map); | | |
| 895 | | | |
| 896 | // invalid characters lead to an error | | |
| 897 | try std.testing.expectError(error.InvalidCharacter, testReplaceVariablesCMake(allocator, "${str*ing}", "", value_map)); | | |
| 898 | try std.testing.expectError(error.InvalidCharacter, testReplaceVariablesCMake(allocator, "${str$ing}", "", value_map)); | | |
| 899 | try std.testing.expectError(error.InvalidCharacter, testReplaceVariablesCMake(allocator, "${str@ing}", "", value_map)); | | |
| 900 | } | | |
| 901 | | | |
| 902 | test "expandVariablesCmake escaped characters" { | | |
| 903 | const allocator = std.testing.allocator; | | |
| 904 | var value_map: std.array_hash_map.String(Value) = .init(allocator); | | |
| 905 | defer value_map.deinit(); | | |
| 906 | | | |
| 907 | try value_map.putNoClobber("string", Value{ .string = "text" }); | | |
| 908 | | | |
| 909 | // backslash is an invalid character for @ lookup | | |
| 910 | try testReplaceVariablesCMake(allocator, "\\@string\\@", "\\@string\\@", value_map); | | |
| 911 | | | |
| 912 | // backslash is preserved, but doesn't affect ${} variable expansion | | |
| 913 | try testReplaceVariablesCMake(allocator, "\\${string}", "\\text", value_map); | | |
| 914 | | | |
| 915 | // backslash breaks ${} opening bracket identification | | |
| 916 | try testReplaceVariablesCMake(allocator, "$\\{string}", "$\\{string}", value_map); | | |
| 917 | | | |
| 918 | // backslash is skipped when checking for invalid characters, yet it mangles the key | | |
| 919 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${string\\}", "", value_map)); | | |
| 920 | } | | |