authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2024-01-28 00:39:00 -08:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2024-01-28 00:39:00 -08:00
log3b1ee55372343807515c6a776f211471323804b7
tree8ab4b9216a76be7b5f659e406e5b6d03fd50be6d
parente8f81eb1d126e37dc5b57e796fd8ee5e10123312

add more tests


23 files changed, 387 insertions(+), 14 deletions(-)

src/BufIndexer.zig+39-1
......@@ -1,6 +1,8 @@
11const std = @import("std");
22const string = []const u8;
33const extras = @import("./lib.zig");
4const indexBufferT = extras.indexBufferT;
5const rawInt = extras.rawInt;
46
57pub fn BufIndexer(comptime T: type, comptime endian: std.builtin.Endian) type {
68 return struct {
......@@ -16,8 +18,44 @@ pub fn BufIndexer(comptime T: type, comptime endian: std.builtin.Endian) type {
1618 };
1719 }
1820
21 /// asserts 'idx' to be in bounds
1922 pub fn at(self: *const Self, idx: usize) T {
20 return extras.indexBufferT(self.bytes, T, endian, idx, self.max_len);
23 return indexBufferT(self.bytes, T, endian, idx, self.max_len);
2124 }
2225 };
2326}
27
28test {
29 const bytes = std.mem.toBytes(rawInt(u64, 0x4e5a7da9f3f1d132));
30 const bindex = BufIndexer(u64, .Big).init(&bytes, 1);
31 try std.testing.expect(bindex.at(0) == 0x4e5a7da9f3f1d132);
32}
33
34test {
35 const bytes = std.mem.toBytes(rawInt(u64, 0x4e5a7da9f3f1d132));
36 const bindex = BufIndexer(u32, .Big).init(&bytes, 2);
37 try std.testing.expect(bindex.at(0) == 0x4e5a7da9);
38 try std.testing.expect(bindex.at(1) == 0xf3f1d132);
39}
40
41test {
42 const bytes = std.mem.toBytes(rawInt(u64, 0x4e5a7da9f3f1d132));
43 const bindex = BufIndexer(u16, .Big).init(&bytes, 4);
44 try std.testing.expect(bindex.at(0) == 0x4e5a);
45 try std.testing.expect(bindex.at(1) == 0x7da9);
46 try std.testing.expect(bindex.at(2) == 0xf3f1);
47 try std.testing.expect(bindex.at(3) == 0xd132);
48}
49
50test {
51 const bytes = std.mem.toBytes(rawInt(u64, 0x4e5a7da9f3f1d132));
52 const bindex = BufIndexer(u8, .Big).init(&bytes, 8);
53 try std.testing.expect(bindex.at(0) == 0x4e);
54 try std.testing.expect(bindex.at(1) == 0x5a);
55 try std.testing.expect(bindex.at(2) == 0x7d);
56 try std.testing.expect(bindex.at(3) == 0xa9);
57 try std.testing.expect(bindex.at(4) == 0xf3);
58 try std.testing.expect(bindex.at(5) == 0xf1);
59 try std.testing.expect(bindex.at(6) == 0xd1);
60 try std.testing.expect(bindex.at(7) == 0x32);
61}
src/FixedMaxBuffer.zig+4
......@@ -46,3 +46,7 @@ pub fn FixedMaxBuffer(comptime max_len: usize) type {
4646 }
4747 };
4848}
49
50test {
51 std.testing.refAllDecls(FixedMaxBuffer(16));
52}
src/TagNameJsonStringifyMixin.zig+18-2
......@@ -4,8 +4,24 @@ const extras = @import("./lib.zig");
44
55pub fn TagNameJsonStringifyMixin(comptime S: type) type {
66 return struct {
7 pub fn jsonStringify(self: S, options: std.json.StringifyOptions, out_stream: anytype) !void {
8 try std.json.stringify(@tagName(self), options, out_stream);
7 pub fn jsonStringify(self: S, json_stream: anytype) !void {
8 try json_stream.write(@tagName(self));
99 }
1010 };
1111}
12
13test {
14 const E = enum {
15 windows,
16 linux,
17 macos,
18 freebsd,
19
20 pub usingnamespace TagNameJsonStringifyMixin(@This());
21 };
22 const allocator = std.testing.allocator;
23 var str = std.ArrayList(u8).init(allocator);
24 defer str.deinit();
25 try std.json.stringify(@as(E, .linux), .{}, str.writer());
26 try std.testing.expect(std.mem.eql(u8, str.items, "\"linux\""));
27}
src/containsString.zig+7
......@@ -10,3 +10,10 @@ pub fn containsString(haystack: []const string, needle: string) bool {
1010 }
1111 return false;
1212}
13
14test {
15 try std.testing.expect(containsString(&.{ "a", "b", "c", "d" }, "b"));
16}
17test {
18 try std.testing.expect(!containsString(&.{}, "b"));
19}
src/countScalar.zig+7
......@@ -12,3 +12,10 @@ pub fn countScalar(comptime T: type, haystack: []const T, needle: T) usize {
1212 }
1313 return found;
1414}
15
16test {
17 try std.testing.expect(countScalar(u8, "The lazy brown fox jumped over the lazy dog.", ' ') == 8);
18}
19test {
20 try std.testing.expect(countScalar(u8, "kljsklksldajvaskidjklvadv", 'z') == 0);
21}
src/ensureFieldSubset.zig+15-1
......@@ -2,8 +2,22 @@ const std = @import("std");
22const string = []const u8;
33const extras = @import("./lib.zig");
44
5/// Asserts at comptime that L ⊆ R.
56pub fn ensureFieldSubset(comptime L: type, comptime R: type) void {
67 for (std.meta.fields(L)) |item| {
7 if (!@hasField(R, item.name)) @compileError(std.fmt.comptimePrint("{s} is missing the {s} field from {s}", .{ R, item.name, L }));
8 if (!@hasField(R, item.name)) {
9 @compileError(std.fmt.comptimePrint("'{s}' field in {s} is not present in {s}", .{ item.name, @typeName(L), @typeName(R) }));
10 }
11 }
12}
13
14test {
15 comptime {
16 ensureFieldSubset(enum {
17 linux,
18 macos,
19 windows,
20 freebsd,
21 }, std.Target.Os.Tag);
822 }
923}
src/fmtReplacer.zig+13-5
......@@ -10,12 +10,14 @@ pub fn fmtReplacer(bytes: string, from: u8, to: u8) std.fmt.Formatter(formatRepl
1010 } };
1111}
1212
13const FormatData = struct {
14 bytes: string,
15 from: u8,
16 to: u8,
17};
18
1319fn formatReplacer(
14 self: struct {
15 bytes: string,
16 from: u8,
17 to: u8,
18 },
20 self: FormatData,
1921 comptime fmt: []const u8,
2022 options: std.fmt.FormatOptions,
2123 writer: anytype,
......@@ -26,3 +28,9 @@ fn formatReplacer(
2628 try writer.writeByte(if (c == self.from) self.to else @intCast(c));
2729 }
2830}
31
32test {
33 const in = "C:\\Program Files\\Custom Utilities\\StringFinder.exe";
34 const out = "C:/Program Files/Custom Utilities/StringFinder.exe";
35 try std.testing.expectFmt(out, "{}", .{fmtReplacer(in, '\\', '/')});
36}
src/hashBytes.zig+17
......@@ -1,6 +1,7 @@
11const std = @import("std");
22const string = []const u8;
33const extras = @import("./lib.zig");
4const rawInt = extras.rawInt;
45
56pub fn hashBytes(comptime Algo: type, bytes: []const u8) [Algo.digest_length]u8 {
67 var h = Algo.init(.{});
......@@ -9,3 +10,19 @@ pub fn hashBytes(comptime Algo: type, bytes: []const u8) [Algo.digest_length]u8
910 h.final(&out);
1011 return out;
1112}
13
14test {
15 const A = std.crypto.hash.Md5;
16 try std.testing.expect(std.mem.eql(u8, &hashBytes(A, "hello"), &std.mem.toBytes(rawInt(u128, 0x5d41402abc4b2a76b9719d911017c592))));
17}
18
19test {
20 const A = std.crypto.hash.Sha1;
21 // by default the array len is 24 for u160
22 try std.testing.expect(std.mem.eql(u8, &hashBytes(A, "hello"), std.mem.toBytes(rawInt(u160, 0xaaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d))[0..20]));
23}
24
25test {
26 const A = std.crypto.hash.sha2.Sha256;
27 try std.testing.expect(std.mem.eql(u8, &hashBytes(A, "hello"), &std.mem.toBytes(rawInt(u256, 0x2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824))));
28}
src/indexBufferT.zig+14-1
......@@ -1,11 +1,24 @@
11const std = @import("std");
22const string = []const u8;
33const extras = @import("./lib.zig");
4const readType = extras.readType;
5const rawInt = extras.rawInt;
46
57pub fn indexBufferT(bytes: [*]const u8, comptime T: type, endian: std.builtin.Endian, idx: usize, max_len: usize) T {
68 std.debug.assert(idx < max_len);
79 var fbs = std.io.fixedBufferStream((bytes + (idx * @sizeOf(T)))[0..@sizeOf(T)]);
8 return extras.readType(fbs.reader(), T, endian) catch |err| switch (err) {
10 return readType(fbs.reader(), T, endian) catch |err| switch (err) {
911 error.EndOfStream => unreachable, // assert above has been violated
1012 };
1113}
14
15test {
16 const bytes = std.mem.toBytes(rawInt(u32, 0x4e5a7da9));
17 try std.testing.expect(indexBufferT(&bytes, u16, .Big, 0, 2) == 0x4e5a);
18 try std.testing.expect(indexBufferT(&bytes, u16, .Big, 1, 2) == 0x7da9);
19}
20test {
21 const bytes = std.mem.toBytes(rawInt(u32, 0x4e5a7da9));
22 try std.testing.expect(indexBufferT(&bytes, u16, .Little, 0, 2) == 0x5a4e);
23 try std.testing.expect(indexBufferT(&bytes, u16, .Little, 1, 2) == 0xa97d);
24}
src/is.zig+14
......@@ -4,6 +4,20 @@ const extras = @import("./lib.zig");
44
55/// ?A == A fails
66/// ?A == @as(?A, b) works
7/// Sourced from https://github.com/ziglang/zig/issues/12609
78pub fn is(a: anytype, b: @TypeOf(a)) bool {
89 return a == b;
910}
11
12test {
13 try std.testing.expect(is(@as(u8, 5), 5));
14}
15test {
16 try std.testing.expect(is(@as(?u8, 5), 5));
17}
18test {
19 try std.testing.expect(!is(@as(u8, 5), 8));
20}
21test {
22 try std.testing.expect(!is(@as(?u8, 5), 8));
23}
src/lessThanSlice.zig+11
......@@ -3,6 +3,7 @@ const string = []const u8;
33const extras = @import("./lib.zig");
44
55pub fn lessThanSlice(comptime T: type) fn (void, T, T) bool {
6 comptime std.debug.assert(std.meta.trait.isSlice(T));
67 return struct {
78 fn f(_: void, lhs: T, rhs: T) bool {
89 const result = for (0..@min(lhs.len, rhs.len)) |i| {
......@@ -13,3 +14,13 @@ pub fn lessThanSlice(comptime T: type) fn (void, T, T) bool {
1314 }
1415 }.f;
1516}
17
18test {
19 try std.testing.expect(lessThanSlice(string)({}, "mouse", "possum"));
20}
21test {
22 try std.testing.expect(!lessThanSlice(string)({}, "mouse", "kangaroo"));
23}
24test {
25 try std.testing.expect(!lessThanSlice(string)({}, "mouse", "mouse"));
26}
src/ptrCast.zig+7
......@@ -6,3 +6,10 @@ pub fn ptrCast(comptime T: type, ptr: *anyopaque) *T {
66 if (@alignOf(T) == 0) @compileError(@typeName(T));
77 return @ptrCast(@alignCast(ptr));
88}
9
10test {
11 var a: u16 = 42;
12 const b: *anyopaque = &a;
13 const c: *u16 = ptrCast(u16, b);
14 try std.testing.expect(a == c.*);
15}
src/ptrCastConst.zig+7
......@@ -6,3 +6,10 @@ pub fn ptrCastConst(comptime T: type, ptr: *const anyopaque) *const T {
66 if (@alignOf(T) == 0) @compileError(@typeName(T));
77 return @ptrCast(@alignCast(ptr));
88}
9
10test {
11 const a: u16 = 42;
12 const b: *const anyopaque = &a;
13 const c: *const u16 = ptrCastConst(u16, b);
14 try std.testing.expect(a == c.*);
15}
src/randomBytes.zig+4
......@@ -7,3 +7,7 @@ pub fn randomBytes(comptime len: usize) [len]u8 {
77 std.crypto.random.bytes(&bytes);
88 return bytes;
99}
10
11test {
12 std.testing.refAllDecls(@This());
13}
src/readBytes.zig+4
......@@ -8,3 +8,7 @@ pub fn readBytes(reader: anytype, comptime len: usize) ![len]u8 {
88 assert(try reader.readAll(&bytes) == len);
99 return bytes;
1010}
11
12test {
13 std.testing.refAllDecls(@This());
14}
src/readBytesAlloc.zig+20-1
......@@ -3,10 +3,29 @@ const string = []const u8;
33const extras = @import("./lib.zig");
44
55pub fn readBytesAlloc(reader: anytype, alloc: std.mem.Allocator, len: usize) ![]u8 {
6 var list = std.ArrayListUnmanaged(u8){};
6 var list = try std.ArrayListUnmanaged(u8).initCapacity(alloc, len);
77 try list.ensureTotalCapacityPrecise(alloc, len);
88 errdefer list.deinit(alloc);
99 list.appendNTimesAssumeCapacity(0, len);
1010 try reader.readNoEof(list.items[0..len]);
1111 return list.items;
1212}
13
14test {
15 const array = [_]u8{ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
16 var fba = std.io.fixedBufferStream(&array);
17 const reader = fba.reader();
18 const alloc = std.testing.allocator;
19 const res = try readBytesAlloc(reader, alloc, 2);
20 defer alloc.free(res);
21 try std.testing.expect(res.len == 2);
22 try std.testing.expect(std.mem.eql(u8, res, &.{ 9, 8 }));
23}
24
25test {
26 const array = [_]u8{ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
27 var fba = std.io.fixedBufferStream(&array);
28 const reader = fba.reader();
29 const alloc = std.testing.allocator;
30 try std.testing.expect(readBytesAlloc(reader, alloc, 11) == error.EndOfStream);
31}
src/readExpected.zig+4
......@@ -11,3 +11,7 @@ pub fn readExpected(reader: anytype, expected: []const u8) !bool {
1111 }
1212 return true;
1313}
14
15test {
16 std.testing.refAllDecls(@This());
17}
src/readType.zig+64
......@@ -1,6 +1,9 @@
11const std = @import("std");
22const string = []const u8;
33const extras = @import("./lib.zig");
4const builtin = @import("builtin");
5const native_endian = builtin.target.cpu.arch.endian();
6const rawInt = extras.rawInt;
47
58pub fn readType(reader: anytype, comptime T: type, endian: std.builtin.Endian) !T {
69 if (T == u8) return reader.readByte(); // single bytes dont have an endianness
......@@ -29,3 +32,64 @@ pub fn readType(reader: anytype, comptime T: type, endian: std.builtin.Endian) !
2932 else => |e| @compileError(@tagName(e)),
3033 };
3134}
35
36test {
37 const bytes = std.mem.toBytes(@as(u32, 0x4e5a7da9));
38 var fba = std.io.fixedBufferStream(&bytes);
39 const I = u32;
40 const i = try readType(fba.reader(), I, native_endian);
41 try std.testing.expect(i == 1314553257);
42}
43
44test {
45 const bytes = std.mem.toBytes(@as(u32, 0x4e5a7da9));
46 var fba = std.io.fixedBufferStream(&bytes);
47 const E = enum(u32) {
48 a,
49 b,
50 c,
51 d = 0x4e5a7da9,
52 e,
53 };
54 const e = try readType(fba.reader(), E, native_endian);
55 try std.testing.expect(e == .d);
56}
57
58test {
59 const bytes = std.mem.toBytes(rawInt(u32, 0x4e5a7da9));
60 var fba = std.io.fixedBufferStream(&bytes);
61 const S = struct {
62 a: u16,
63 b: u8,
64 c: u8,
65 };
66 const s = try readType(fba.reader(), S, .Big);
67 try std.testing.expect(s.a == 0x4e5a);
68 try std.testing.expect(s.b == 0x7d);
69 try std.testing.expect(s.c == 0xa9);
70}
71
72test {
73 const bytes = std.mem.toBytes(rawInt(u32, 0x4e5a7da9));
74 var fba = std.io.fixedBufferStream(&bytes);
75 const A = [2]u16;
76 const a = try readType(fba.reader(), A, .Big);
77 try std.testing.expect(a[0] == 0x4e5a);
78 try std.testing.expect(a[1] == 0x7da9);
79}
80
81test {
82 const bytes = std.mem.toBytes(rawInt(u32, 0x4e5a7da9));
83 var fba = std.io.fixedBufferStream(&bytes);
84 const S = packed struct {
85 a: u16,
86 b: u4,
87 c: u4,
88 d: u8,
89 };
90 const s = try readType(fba.reader(), S, .Big);
91 try std.testing.expect(s.a == 0x7da9);
92 try std.testing.expect(s.b == 0xa);
93 try std.testing.expect(s.c == 0x5);
94 try std.testing.expect(s.d == 0x4e);
95}
src/safeAdd.zig+9-1
......@@ -1,11 +1,19 @@
11const std = @import("std");
22const string = []const u8;
33const extras = @import("./lib.zig");
4const OneBiggerInt = extras.OneBiggerInt;
45
56/// Allows u32 + i16 to work
67pub fn safeAdd(a: anytype, b: anytype) @TypeOf(a) {
78 if (b >= 0) {
89 return a + @as(@TypeOf(a), @intCast(b));
910 }
10 return a - @as(@TypeOf(a), @intCast(-@as(extras.OneBiggerInt(@TypeOf(b)), b)));
11 return a - @as(@TypeOf(a), @intCast(-@as(OneBiggerInt(@TypeOf(b)), b)));
12}
13
14test {
15 try std.testing.expect(safeAdd(@as(u16, 10), @as(i8, -2)) == 8);
16}
17test {
18 try std.testing.expect(safeAdd(@as(u16, 256), @as(i8, -128)) == 128);
1119}
src/safeAddWrap.zig+15-1
......@@ -1,11 +1,25 @@
11const std = @import("std");
22const string = []const u8;
33const extras = @import("./lib.zig");
4const OneBiggerInt = extras.OneBiggerInt;
45
56/// Allows u32 +% i16 to work
67pub fn safeAddWrap(a: anytype, b: anytype) @TypeOf(a) {
78 if (b >= 0) {
89 return a +% @as(@TypeOf(a), @intCast(b));
910 }
10 return a -% @as(@TypeOf(a), @intCast(-@as(extras.OneBiggerInt(@TypeOf(b)), b)));
11 return a -% @as(@TypeOf(a), @intCast(-@as(OneBiggerInt(@TypeOf(b)), b)));
12}
13
14test {
15 try std.testing.expect(safeAddWrap(@as(u16, 0), @as(i8, -2)) == 65534);
16}
17test {
18 try std.testing.expect(safeAddWrap(@as(u16, 10), @as(i8, -2)) == 8);
19}
20test {
21 try std.testing.expect(safeAddWrap(@as(u16, 65535), @as(i8, 1)) == 0);
22}
23test {
24 try std.testing.expect(safeAddWrap(@as(u16, 256), @as(i8, -128)) == 128);
1125}
src/skipToBoundary.zig+50
......@@ -9,3 +9,53 @@ pub fn skipToBoundary(pos: u64, boundary: u64, reader: anytype) !void {
99 const b = boundary;
1010 try reader.skipBytes(((a + (b - 1)) & ~(b - 1)) - a, .{});
1111}
12
13test {
14 const array = [_]u8{ 5, 4, 3, 2, 1, 0 };
15 var fba = std.io.fixedBufferStream(&array);
16 var counter = std.io.countingReader(fba.reader());
17 const reader = counter.reader();
18 try reader.skipBytes(0, .{});
19 try skipToBoundary(counter.bytes_read, 4, reader);
20 try std.testing.expect(counter.bytes_read == 0);
21}
22
23test {
24 const array = [_]u8{ 5, 4, 3, 2, 1, 0 };
25 var fba = std.io.fixedBufferStream(&array);
26 var counter = std.io.countingReader(fba.reader());
27 const reader = counter.reader();
28 try reader.skipBytes(1, .{});
29 try skipToBoundary(counter.bytes_read, 4, reader);
30 try std.testing.expect(counter.bytes_read == 4);
31}
32
33test {
34 const array = [_]u8{ 5, 4, 3, 2, 1, 0 };
35 var fba = std.io.fixedBufferStream(&array);
36 var counter = std.io.countingReader(fba.reader());
37 const reader = counter.reader();
38 try reader.skipBytes(2, .{});
39 try skipToBoundary(counter.bytes_read, 4, reader);
40 try std.testing.expect(counter.bytes_read == 4);
41}
42
43test {
44 const array = [_]u8{ 5, 4, 3, 2, 1, 0 };
45 var fba = std.io.fixedBufferStream(&array);
46 var counter = std.io.countingReader(fba.reader());
47 const reader = counter.reader();
48 try reader.skipBytes(3, .{});
49 try skipToBoundary(counter.bytes_read, 4, reader);
50 try std.testing.expect(counter.bytes_read == 4);
51}
52
53test {
54 const array = [_]u8{ 5, 4, 3, 2, 1, 0 };
55 var fba = std.io.fixedBufferStream(&array);
56 var counter = std.io.countingReader(fba.reader());
57 const reader = counter.reader();
58 try reader.skipBytes(4, .{});
59 try skipToBoundary(counter.bytes_read, 4, reader);
60 try std.testing.expect(counter.bytes_read == 4);
61}
src/sortBy.zig+21
......@@ -9,3 +9,24 @@ pub fn sortBy(comptime T: type, items: []T, comptime field: std.meta.FieldEnum(T
99 }
1010 }.f);
1111}
12
13test {
14 const Block = struct {
15 from: u21,
16 to: u21,
17 name: []const u8,
18 };
19 var data = [_]Block{
20 .{ .from = 0x0000, .to = 0x007F, .name = "Basic Latin" },
21 .{ .from = 0x0250, .to = 0x02AF, .name = "IPA Extensions" },
22 .{ .from = 0x0100, .to = 0x017F, .name = "Latin Extended-A" },
23 .{ .from = 0x0180, .to = 0x024F, .name = "Latin Extended-B" },
24 .{ .from = 0x0080, .to = 0x00FF, .name = "Latin-1 Supplement" },
25 };
26 sortBy(Block, &data, .from);
27 try std.testing.expect(std.mem.eql(u8, data[0].name, "Basic Latin"));
28 try std.testing.expect(std.mem.eql(u8, data[1].name, "Latin-1 Supplement"));
29 try std.testing.expect(std.mem.eql(u8, data[2].name, "Latin Extended-A"));
30 try std.testing.expect(std.mem.eql(u8, data[3].name, "Latin Extended-B"));
31 try std.testing.expect(std.mem.eql(u8, data[4].name, "IPA Extensions"));
32}
src/sortBySlice.zig+23-1
......@@ -1,11 +1,33 @@
11const std = @import("std");
22const string = []const u8;
33const extras = @import("./lib.zig");
4const lessThanSlice = extras.lessThanSlice;
45
56pub fn sortBySlice(comptime T: type, items: []T, comptime field: std.meta.FieldEnum(T)) void {
67 std.mem.sort(T, items, {}, struct {
78 fn f(_: void, lhs: T, rhs: T) bool {
8 return extras.lessThanSlice(std.meta.FieldType(T, field))({}, @field(lhs, @tagName(field)), @field(rhs, @tagName(field)));
9 return lessThanSlice(std.meta.FieldType(T, field))({}, @field(lhs, @tagName(field)), @field(rhs, @tagName(field)));
910 }
1011 }.f);
1112}
13
14test {
15 const Block = struct {
16 from: u21,
17 to: u21,
18 name: []const u8,
19 };
20 var data = [_]Block{
21 .{ .from = 0x0000, .to = 0x007F, .name = "Basic Latin" },
22 .{ .from = 0x0080, .to = 0x00FF, .name = "Latin-1 Supplement" },
23 .{ .from = 0x0180, .to = 0x024F, .name = "Latin Extended-B" },
24 .{ .from = 0x0100, .to = 0x017F, .name = "Latin Extended-A" },
25 .{ .from = 0x0250, .to = 0x02AF, .name = "IPA Extensions" },
26 };
27 sortBySlice(Block, &data, .name);
28 try std.testing.expect(std.mem.eql(u8, data[0].name, "Basic Latin"));
29 try std.testing.expect(std.mem.eql(u8, data[1].name, "IPA Extensions"));
30 try std.testing.expect(std.mem.eql(u8, data[2].name, "Latin Extended-A"));
31 try std.testing.expect(std.mem.eql(u8, data[3].name, "Latin Extended-B"));
32 try std.testing.expect(std.mem.eql(u8, data[4].name, "Latin-1 Supplement"));
33}