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
|
const std = @import("std");
const testing = std.testing;
const expect = testing.expect;
const expectEqual = testing.expectEqual;
const expectEqualStrings = testing.expectEqualStrings;
const allocator = testing.allocator;
const GenCatData = @import("GenCatData");
test "General Category" {
const gcd = try GenCatData.init(allocator);
defer gcd.deinit();
try expect(gcd.gc('A') == .Lu); // Lu: uppercase letter
try expect(gcd.gc('3') == .Nd); // Nd: Decimal number
try expect(gcd.isControl(0));
try expect(gcd.isLetter('z'));
try expect(gcd.isMark('\u{301}'));
try expect(gcd.isNumber('3'));
try expect(gcd.isPunctuation('['));
try expect(gcd.isSeparator(' '));
try expect(gcd.isSymbol('©'));
}
const PropsData = @import("PropsData");
test "Properties" {
const pd = try PropsData.init(allocator);
defer pd.deinit();
try expect(pd.isMath('+'));
try expect(pd.isAlphabetic('Z'));
try expect(pd.isWhitespace(' '));
try expect(pd.isHexDigit('f'));
try expect(!pd.isHexDigit('z'));
try expect(pd.isDiacritic('\u{301}'));
try expect(pd.isIdStart('Z')); // Identifier start character
try expect(!pd.isIdStart('1'));
try expect(pd.isIdContinue('1'));
try expect(pd.isXidStart('\u{b33}')); // Extended identifier start character
try expect(pd.isXidContinue('\u{e33}'));
try expect(!pd.isXidStart('1'));
// Note surprising Unicode numeric types!
try expect(pd.isNumeric('\u{277f}'));
try expect(!pd.isNumeric('3'));
try expect(pd.isDigit('\u{2070}'));
try expect(!pd.isDigit('3'));
try expect(pd.isDecimal('3'));
}
const CaseData = @import("CaseData");
test "Case" {
const cd = try CaseData.init(allocator);
defer cd.deinit();
try expect(cd.isUpper('A'));
try expect('A' == cd.toUpper('a'));
try expect(cd.isLower('a'));
try expect('a' == cd.toLower('A'));
try expect(cd.isCased('É'));
try expect(!cd.isCased('3'));
try expect(cd.isUpperStr("HELLO 123!"));
const ucased = try cd.toUpperStr(allocator, "hello 123");
defer allocator.free(ucased);
try expectEqualStrings("HELLO 123", ucased);
try expect(cd.isLowerStr("hello 123!"));
const lcased = try cd.toLowerStr(allocator, "HELLO 123");
defer allocator.free(lcased);
try expectEqualStrings("hello 123", lcased);
}
const Normalize = @import("Normalize");
test "Normalization" {
var norm_data = try Normalize.NormData.init(allocator);
defer norm_data.deinit();
const n = Normalize{ .norm_data = &norm_data };
// NFD: Canonical decomposition
const nfd_result = try n.nfd(allocator, "Héllo World! \u{3d3}");
defer nfd_result.deinit();
try expectEqualStrings("He\u{301}llo World! \u{3d2}\u{301}", nfd_result.slice);
// NFKD: Compatibility decomposition
const nfkd_result = try n.nfkd(allocator, "Héllo World! \u{3d3}");
defer nfkd_result.deinit();
try expectEqualStrings("He\u{301}llo World! \u{3a5}\u{301}", nfkd_result.slice);
// NFC: Canonical composition
const nfc_result = try n.nfc(allocator, "Complex char: \u{3D2}\u{301}");
defer nfc_result.deinit();
try expectEqualStrings("Complex char: \u{3D3}", nfc_result.slice);
// NFKC: Compatibility composition
const nfkc_result = try n.nfkc(allocator, "Complex char: \u{03A5}\u{0301}");
defer nfkc_result.deinit();
try expectEqualStrings("Complex char: \u{038E}", nfkc_result.slice);
// Test for equality of two strings after normalizing to NFC.
try expect(try n.eql(allocator, "foé", "foe\u{0301}"));
try expect(try n.eql(allocator, "foϓ", "fo\u{03D2}\u{0301}"));
}
const CaseFold = @import("CaseFold");
test "Caseless matching" {
var norm_data = try Normalize.NormData.init(allocator);
defer norm_data.deinit();
const n = Normalize{ .norm_data = &norm_data };
const cfd = try CaseFold.FoldData.init(allocator);
defer cfd.deinit();
const cf = CaseFold{ .fold_data = &cfd };
// compatCaselessMatch provides the deepest level of caseless
// matching because it decomposes and composes fully to NFKC.
const a = "Héllo World! \u{3d3}";
const b = "He\u{301}llo World! \u{3a5}\u{301}";
try expect(try cf.compatCaselessMatch(allocator, &n, a, b));
const c = "He\u{301}llo World! \u{3d2}\u{301}";
try expect(try cf.compatCaselessMatch(allocator, &n, a, c));
// canonCaselessMatch isn't as comprehensive as compatCaselessMatch
// because it only decomposes and composes to NFC. But it's faster.
try expect(!try cf.canonCaselessMatch(allocator, &n, a, b));
try expect(try cf.canonCaselessMatch(allocator, &n, a, c));
}
const DisplayWidth = @import("DisplayWidth");
test "Display width" {
const dwd = try DisplayWidth.DisplayWidthData.init(allocator);
defer dwd.deinit();
const dw = DisplayWidth{ .data = &dwd };
// String display width
try expectEqual(@as(usize, 5), dw.strWidth("Hello\r\n"));
try expectEqual(@as(usize, 8), dw.strWidth("Hello 😊"));
try expectEqual(@as(usize, 8), dw.strWidth("Héllo 😊"));
try expectEqual(@as(usize, 9), dw.strWidth("Ẓ̌á̲l͔̝̞̄̑͌g̖̘̘̔̔͢͞͝o̪̔T̢̙̫̈̍͞e̬͈͕͌̏͑x̺̍ṭ̓̓ͅ"));
try expectEqual(@as(usize, 17), dw.strWidth("슬라바 우크라이나"));
// Centering text
const centered = try dw.center(allocator, "w😊w", 10, "-");
defer allocator.free(centered);
try expectEqualStrings("---w😊w---", centered);
// Pad left
const right_aligned = try dw.padLeft(allocator, "abc", 9, "*");
defer allocator.free(right_aligned);
try expectEqualStrings("******abc", right_aligned);
// Pad right
const left_aligned = try dw.padRight(allocator, "abc", 9, "*");
defer allocator.free(left_aligned);
try expectEqualStrings("abc******", left_aligned);
// Wrap text
const input = "The quick brown fox\r\njumped over the lazy dog!";
const wrapped = try dw.wrap(allocator, input, 10, 3);
defer allocator.free(wrapped);
const want =
\\The quick
\\brown fox
\\jumped
\\over the
\\lazy dog!
;
try expectEqualStrings(want, wrapped);
}
const code_point = @import("code_point");
test "Code point iterator" {
const str = "Hi 😊";
var iter = code_point.Iterator{ .bytes = str };
var i: usize = 0;
while (iter.next()) |cp| : (i += 1) {
if (i == 0) try expect(cp.code == 'H');
if (i == 1) try expect(cp.code == 'i');
if (i == 2) try expect(cp.code == ' ');
if (i == 3) {
try expect(cp.code == '😊');
try expect(cp.offset == 3);
try expect(cp.len == 4);
}
}
}
const grapheme = @import("grapheme");
test "Grapheme cluster iterator" {
const gd = try grapheme.GraphemeData.init(allocator);
defer gd.deinit();
const str = "He\u{301}"; // Hé
var iter = grapheme.Iterator.init(str, &gd);
var i: usize = 0;
while (iter.next()) |gc| : (i += 1) {
if (i == 0) try expect(gc.len == 1);
if (i == 1) {
try expect(gc.len == 3);
try expect(gc.offset == 1);
try expectEqualStrings("e\u{301}", gc.bytes(str));
}
}
}
const ScriptsData = @import("ScriptsData");
test "Scripts" {
const sd = try ScriptsData.init(allocator);
defer sd.deinit();
try expect(sd.script('A') == .Latin);
try expect(sd.script('Ω') == .Greek);
try expect(sd.script('צ') == .Hebrew);
}
|