authorgravatar for saurabhm@proton.mesaurabh <saurabhm@proton.me> 2025-02-11 11:34:56+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-02-11 13:34:56+02:00
log4162f401cbaa2013eae8fa518cf255ce4c5c952b
tree4d83a61cf1f7513bb6c7f2cec3fb1b4cb5aa8272
parent8d914ea7340c920c1929c5d666fc802b15f49a64
signaturebadge-check Signed by PGP key B5690EEEBB952194

std.fmt: Add unit tests for all methods in the Parser struct


1 files changed, 244 insertions(+), 5 deletions(-)

lib/std/fmt.zig+244-5
......@@ -11,6 +11,7 @@ const unicode = std.unicode;
1111const meta = std.meta;
1212const lossyCast = math.lossyCast;
1313const expectFmt = std.testing.expectFmt;
14const testing = std.testing;
1415
1516pub const default_max_depth = 3;
1617
......@@ -319,7 +320,6 @@ pub const Specifier = union(enum) {
319320/// Allows to implement formatters compatible with std.fmt without replicating
320321/// the standard library behavior.
321322pub const Parser = struct {
322 pos: usize = 0,
323323 iter: std.unicode.Utf8Iterator,
324324
325325 // Returns a decimal number or null if the current character is not a
......@@ -345,16 +345,17 @@ pub const Parser = struct {
345345 // Returns a substring of the input starting from the current position
346346 // and ending where `ch` is found or until the end if not found
347347 pub fn until(self: *@This(), ch: u21) []const u8 {
348 var result: []const u8 = &[_]u8{};
348 const start = self.iter.i;
349349 while (self.peek(0)) |code_point| {
350350 if (code_point == ch)
351351 break;
352 result = result ++ (self.iter.nextCodepointSlice() orelse &[_]u8{});
352 _ = self.iter.nextCodepoint();
353353 }
354 return result;
354 return self.iter.bytes[start..self.iter.i];
355355 }
356356
357 // Returns one character, if available
357 // Returns the character pointed to by the iterator if available, or
358 // null otherwise
358359 pub fn char(self: *@This()) ?u21 {
359360 if (self.iter.nextCodepoint()) |code_point| {
360361 return code_point;
......@@ -362,6 +363,8 @@ pub const Parser = struct {
362363 return null;
363364 }
364365
366 // Returns true if the iterator points to an existing character and
367 // false otherwise
365368 pub fn maybe(self: *@This(), val: u21) bool {
366369 if (self.peek(0) == val) {
367370 _ = self.iter.nextCodepoint();
......@@ -2779,3 +2782,239 @@ test hex {
27792782 try std.testing.expectEqualStrings("[00efcdab78563412]", s);
27802783 }
27812784}
2785
2786test "parser until" {
2787 { // return substring till ':'
2788 var parser: Parser = .{
2789 .iter = .{ .bytes = "abc:1234", .i = 0 },
2790 };
2791 try testing.expectEqualStrings("abc", parser.until(':'));
2792 }
2793
2794 { // return the entire string - `ch` not found
2795 var parser: Parser = .{
2796 .iter = .{ .bytes = "abc1234", .i = 0 },
2797 };
2798 try testing.expectEqualStrings("abc1234", parser.until(':'));
2799 }
2800
2801 { // substring is empty - `ch` is the only character
2802 var parser: Parser = .{
2803 .iter = .{ .bytes = ":", .i = 0 },
2804 };
2805 try testing.expectEqualStrings("", parser.until(':'));
2806 }
2807
2808 { // empty string and `ch` not found
2809 var parser: Parser = .{
2810 .iter = .{ .bytes = "", .i = 0 },
2811 };
2812 try testing.expectEqualStrings("", parser.until(':'));
2813 }
2814
2815 { // substring starts at index 2 and goes upto `ch`
2816 var parser: Parser = .{
2817 .iter = .{ .bytes = "abc:1234", .i = 2 },
2818 };
2819 try testing.expectEqualStrings("c", parser.until(':'));
2820 }
2821
2822 { // substring starts at index 4 and goes upto the end - `ch` not found
2823 var parser: Parser = .{
2824 .iter = .{ .bytes = "abc1234", .i = 4 },
2825 };
2826 try testing.expectEqualStrings("234", parser.until(':'));
2827 }
2828}
2829
2830test "parser peek" {
2831 { // start iteration from the first index
2832 var parser: Parser = .{
2833 .iter = .{ .bytes = "hello world", .i = 0 },
2834 };
2835
2836 try testing.expectEqual('h', parser.peek(0));
2837 try testing.expectEqual('e', parser.peek(1));
2838 try testing.expectEqual(' ', parser.peek(5));
2839 try testing.expectEqual('d', parser.peek(10));
2840 try testing.expectEqual(null, parser.peek(11));
2841 }
2842
2843 { // start iteration from the second last index
2844 var parser: Parser = .{
2845 .iter = .{ .bytes = "hello world!", .i = 10 },
2846 };
2847
2848 try testing.expectEqual('d', parser.peek(0));
2849 try testing.expectEqual('!', parser.peek(1));
2850 try testing.expectEqual(null, parser.peek(5));
2851 }
2852
2853 { // start iteration beyond the length of the string
2854 var parser: Parser = .{
2855 .iter = .{ .bytes = "hello", .i = 5 },
2856 };
2857
2858 try testing.expectEqual(null, parser.peek(0));
2859 try testing.expectEqual(null, parser.peek(1));
2860 }
2861
2862 { // empty string
2863 var parser: Parser = .{
2864 .iter = .{ .bytes = "", .i = 0 },
2865 };
2866
2867 try testing.expectEqual(null, parser.peek(0));
2868 try testing.expectEqual(null, parser.peek(2));
2869 }
2870}
2871
2872test "parser char" {
2873 // character exists - iterator at 0
2874 var parser: Parser = .{ .iter = .{ .bytes = "~~hello", .i = 0 } };
2875 try testing.expectEqual('~', parser.char());
2876
2877 // character exists - iterator in the middle
2878 parser = .{ .iter = .{ .bytes = "~~hello", .i = 3 } };
2879 try testing.expectEqual('e', parser.char());
2880
2881 // character exists - iterator at the end
2882 parser = .{ .iter = .{ .bytes = "~~hello", .i = 6 } };
2883 try testing.expectEqual('o', parser.char());
2884
2885 // character doesn't exist - iterator beyond the length of the string
2886 parser = .{ .iter = .{ .bytes = "~~hello", .i = 7 } };
2887 try testing.expectEqual(null, parser.char());
2888}
2889
2890test "parser maybe" {
2891 // character exists - iterator at 0
2892 var parser: Parser = .{ .iter = .{ .bytes = "hello world", .i = 0 } };
2893 try testing.expect(parser.maybe('h'));
2894
2895 // character exists - iterator at space
2896 parser = .{ .iter = .{ .bytes = "hello world", .i = 5 } };
2897 try testing.expect(parser.maybe(' '));
2898
2899 // character exists - iterator at the end
2900 parser = .{ .iter = .{ .bytes = "hello world", .i = 10 } };
2901 try testing.expect(parser.maybe('d'));
2902
2903 // character doesn't exist - iterator beyond the length of the string
2904 parser = .{ .iter = .{ .bytes = "hello world", .i = 11 } };
2905 try testing.expect(!parser.maybe('e'));
2906}
2907
2908test "parser number" {
2909 // input is a single digit natural number - iterator at 0
2910 var parser: Parser = .{ .iter = .{ .bytes = "7", .i = 0 } };
2911 try testing.expect(7 == parser.number());
2912
2913 // input is a two digit natural number - iterator at 1
2914 parser = .{ .iter = .{ .bytes = "29", .i = 1 } };
2915 try testing.expect(9 == parser.number());
2916
2917 // input is a two digit natural number - iterator beyond the length of the string
2918 parser = .{ .iter = .{ .bytes = "32", .i = 2 } };
2919 try testing.expectEqual(null, parser.number());
2920
2921 // input is an integer
2922 parser = .{ .iter = .{ .bytes = "0", .i = 0 } };
2923 try testing.expect(0 == parser.number());
2924
2925 // input is a negative integer
2926 parser = .{ .iter = .{ .bytes = "-2", .i = 0 } };
2927 try testing.expectEqual(null, parser.number());
2928
2929 // input is a string
2930 parser = .{ .iter = .{ .bytes = "no_number", .i = 2 } };
2931 try testing.expectEqual(null, parser.number());
2932
2933 // input is a single character string
2934 parser = .{ .iter = .{ .bytes = "n", .i = 0 } };
2935 try testing.expectEqual(null, parser.number());
2936
2937 // input is an empty string
2938 parser = .{ .iter = .{ .bytes = "", .i = 0 } };
2939 try testing.expectEqual(null, parser.number());
2940}
2941
2942test "parser specifier" {
2943 { // input string is a digit; iterator at 0
2944 const expected: Specifier = Specifier{ .number = 1 };
2945 var parser: Parser = .{ .iter = .{ .bytes = "1", .i = 0 } };
2946
2947 const result = try parser.specifier();
2948 try testing.expect(expected.number == result.number);
2949 }
2950
2951 { // input string is a two digit number; iterator at 0
2952 const digit: Specifier = Specifier{ .number = 42 };
2953 var parser: Parser = .{ .iter = .{ .bytes = "42", .i = 0 } };
2954
2955 const result = try parser.specifier();
2956 try testing.expect(digit.number == result.number);
2957 }
2958
2959 { // input string is a two digit number digit; iterator at 1
2960 const digit: Specifier = Specifier{ .number = 8 };
2961 var parser: Parser = .{ .iter = .{ .bytes = "28", .i = 1 } };
2962
2963 const result = try parser.specifier();
2964 try testing.expect(digit.number == result.number);
2965 }
2966
2967 { // input string is a two digit number with square brackets; iterator at 0
2968 const digit: Specifier = Specifier{ .named = "15" };
2969 var parser: Parser = .{ .iter = .{ .bytes = "[15]", .i = 0 } };
2970
2971 const result = try parser.specifier();
2972 try testing.expectEqualStrings(digit.named, result.named);
2973 }
2974
2975 { // input string is not a number and contains square brackets; iterator at 0
2976 const digit: Specifier = Specifier{ .named = "hello" };
2977 var parser: Parser = .{ .iter = .{ .bytes = "[hello]", .i = 0 } };
2978
2979 const result = try parser.specifier();
2980 try testing.expectEqualStrings(digit.named, result.named);
2981 }
2982
2983 { // input string is not a number and doesn't contain closing square bracket; iterator at 0
2984 var parser: Parser = .{ .iter = .{ .bytes = "[hello", .i = 0 } };
2985
2986 const result = parser.specifier();
2987 try testing.expectError(@field(anyerror, "Expected closing ]"), result);
2988 }
2989
2990 { // input string is not a number and doesn't contain closing square bracket; iterator at 2
2991 var parser: Parser = .{ .iter = .{ .bytes = "[[[[hello", .i = 2 } };
2992
2993 const result = parser.specifier();
2994 try testing.expectError(@field(anyerror, "Expected closing ]"), result);
2995 }
2996
2997 { // input string is not a number and contains unbalanced square brackets; iterator at 0
2998 const digit: Specifier = Specifier{ .named = "[[hello" };
2999 var parser: Parser = .{ .iter = .{ .bytes = "[[[hello]", .i = 0 } };
3000
3001 const result = try parser.specifier();
3002 try testing.expectEqualStrings(digit.named, result.named);
3003 }
3004
3005 { // input string is not a number and contains unbalanced square brackets; iterator at 1
3006 const digit: Specifier = Specifier{ .named = "[[hello" };
3007 var parser: Parser = .{ .iter = .{ .bytes = "[[[[hello]]]]]", .i = 1 } };
3008
3009 const result = try parser.specifier();
3010 try testing.expectEqualStrings(digit.named, result.named);
3011 }
3012
3013 { // input string is neither a digit nor a named argument
3014 const char: Specifier = Specifier{ .none = {} };
3015 var parser: Parser = .{ .iter = .{ .bytes = "hello", .i = 0 } };
3016
3017 const result = try parser.specifier();
3018 try testing.expectEqual(char.none, result.none);
3019 }
3020}