1
2
3
4
5 package runes
6
7 import (
8 "strings"
9 "testing"
10 "unicode"
11
12 "golang.org/x/text/cases"
13 "golang.org/x/text/language"
14 "golang.org/x/text/transform"
15 )
16
17 var (
18 toUpper = cases.Upper(language.Und)
19 toLower = cases.Lower(language.Und)
20 )
21
22 type spanformer interface {
23 transform.SpanningTransformer
24 }
25
26 func TestPredicate(t *testing.T) {
27 testConditional(t, func(rt *unicode.RangeTable, t, f spanformer) spanformer {
28 return If(Predicate(func(r rune) bool {
29 return unicode.Is(rt, r)
30 }), t, f)
31 })
32 }
33
34 func TestIn(t *testing.T) {
35 testConditional(t, func(rt *unicode.RangeTable, t, f spanformer) spanformer {
36 return If(In(rt), t, f)
37 })
38 }
39
40 func TestNotIn(t *testing.T) {
41 testConditional(t, func(rt *unicode.RangeTable, t, f spanformer) spanformer {
42 return If(NotIn(rt), f, t)
43 })
44 }
45
46 func testConditional(t *testing.T, f func(rt *unicode.RangeTable, t, f spanformer) spanformer) {
47 lower := f(unicode.Latin, toLower, toLower)
48
49 for i, tt := range []transformTest{{
50 desc: "empty",
51 szDst: large,
52 atEOF: true,
53 in: "",
54 out: "",
55 outFull: "",
56 t: lower,
57 }, {
58 desc: "small",
59 szDst: 1,
60 atEOF: true,
61 in: "B",
62 out: "b",
63 outFull: "b",
64 errSpan: transform.ErrEndOfSpan,
65 t: lower,
66 }, {
67 desc: "short dst",
68 szDst: 2,
69 atEOF: true,
70 in: "AAA",
71 out: "aa",
72 outFull: "aaa",
73 err: transform.ErrShortDst,
74 errSpan: transform.ErrEndOfSpan,
75 t: lower,
76 }, {
77 desc: "short dst writing error",
78 szDst: 1,
79 atEOF: false,
80 in: "A\x80",
81 out: "a",
82 outFull: "a\x80",
83 err: transform.ErrShortDst,
84 errSpan: transform.ErrEndOfSpan,
85 t: lower,
86 }, {
87 desc: "short dst writing incomplete rune",
88 szDst: 2,
89 atEOF: true,
90 in: "Σ\xc2",
91 out: "Σ",
92 outFull: "Σ\xc2",
93 err: transform.ErrShortDst,
94 t: f(unicode.Latin, toLower, nil),
95 }, {
96 desc: "short dst, longer",
97 szDst: 5,
98 atEOF: true,
99 in: "Hellø",
100 out: "Hell",
101 outFull: "Hellø",
102 err: transform.ErrShortDst,
103
104 t: f(unicode.Latin, Map(idem), nil),
105 }, {
106 desc: "short dst, longer, writing error",
107 szDst: 6,
108 atEOF: false,
109 in: "\x80Hello\x80",
110 out: "\x80Hello",
111 outFull: "\x80Hello\x80",
112 err: transform.ErrShortDst,
113 t: f(unicode.Latin, Map(idem), nil),
114 }, {
115 desc: "short src",
116 szDst: 2,
117 atEOF: false,
118 in: "A\xc2",
119 out: "a",
120 outFull: "a\xc2",
121 err: transform.ErrShortSrc,
122 errSpan: transform.ErrEndOfSpan,
123 t: lower,
124 }, {
125 desc: "short src no change",
126 szDst: 2,
127 atEOF: false,
128 in: "a\xc2",
129 out: "a",
130 outFull: "a\xc2",
131 err: transform.ErrShortSrc,
132 errSpan: transform.ErrShortSrc,
133 nSpan: 1,
134 t: lower,
135 }, {
136 desc: "invalid input, atEOF",
137 szDst: large,
138 atEOF: true,
139 in: "\x80",
140 out: "\x80",
141 outFull: "\x80",
142 t: lower,
143 }, {
144 desc: "invalid input, !atEOF",
145 szDst: large,
146 atEOF: false,
147 in: "\x80",
148 out: "\x80",
149 outFull: "\x80",
150 t: lower,
151 }, {
152 desc: "invalid input, incomplete rune atEOF",
153 szDst: large,
154 atEOF: true,
155 in: "\xc2",
156 out: "\xc2",
157 outFull: "\xc2",
158 t: lower,
159 }, {
160 desc: "nop",
161 szDst: large,
162 atEOF: true,
163 in: "Hello World!",
164 out: "Hello World!",
165 outFull: "Hello World!",
166 t: f(unicode.Latin, nil, nil),
167 }, {
168 desc: "nop in",
169 szDst: large,
170 atEOF: true,
171 in: "THIS IS α ΤΕΣΤ",
172 out: "this is α ΤΕΣΤ",
173 outFull: "this is α ΤΕΣΤ",
174 errSpan: transform.ErrEndOfSpan,
175 t: f(unicode.Greek, nil, toLower),
176 }, {
177 desc: "nop in latin",
178 szDst: large,
179 atEOF: true,
180 in: "THIS IS α ΤΕΣΤ",
181 out: "THIS IS α τεστ",
182 outFull: "THIS IS α τεστ",
183 errSpan: transform.ErrEndOfSpan,
184 t: f(unicode.Latin, nil, toLower),
185 }, {
186 desc: "nop not in",
187 szDst: large,
188 atEOF: true,
189 in: "THIS IS α ΤΕΣΤ",
190 out: "this is α ΤΕΣΤ",
191 outFull: "this is α ΤΕΣΤ",
192 errSpan: transform.ErrEndOfSpan,
193 t: f(unicode.Latin, toLower, nil),
194 }, {
195 desc: "pass atEOF is true when at end",
196 szDst: large,
197 atEOF: true,
198 in: "hello",
199 out: "HELLO",
200 outFull: "HELLO",
201 errSpan: transform.ErrEndOfSpan,
202 t: f(unicode.Latin, upperAtEOF{}, nil),
203 }, {
204 desc: "pass atEOF is true when at end of segment",
205 szDst: large,
206 atEOF: true,
207 in: "hello ",
208 out: "HELLO ",
209 outFull: "HELLO ",
210 errSpan: transform.ErrEndOfSpan,
211 t: f(unicode.Latin, upperAtEOF{}, nil),
212 }, {
213 desc: "don't pass atEOF is true when atEOF is false",
214 szDst: large,
215 atEOF: false,
216 in: "hello",
217 out: "",
218 outFull: "HELLO",
219 err: transform.ErrShortSrc,
220 errSpan: transform.ErrShortSrc,
221 t: f(unicode.Latin, upperAtEOF{}, nil),
222 }, {
223 desc: "pass atEOF is true when at end, no change",
224 szDst: large,
225 atEOF: true,
226 in: "HELLO",
227 out: "HELLO",
228 outFull: "HELLO",
229 t: f(unicode.Latin, upperAtEOF{}, nil),
230 }, {
231 desc: "pass atEOF is true when at end of segment, no change",
232 szDst: large,
233 atEOF: true,
234 in: "HELLO ",
235 out: "HELLO ",
236 outFull: "HELLO ",
237 t: f(unicode.Latin, upperAtEOF{}, nil),
238 }, {
239 desc: "large input ASCII",
240 szDst: 12000,
241 atEOF: false,
242 in: strings.Repeat("HELLO", 2000),
243 out: strings.Repeat("hello", 2000),
244 outFull: strings.Repeat("hello", 2000),
245 errSpan: transform.ErrEndOfSpan,
246 err: nil,
247 t: lower,
248 }, {
249 desc: "large input non-ASCII",
250 szDst: 12000,
251 atEOF: false,
252 in: strings.Repeat("\u3333", 2000),
253 out: strings.Repeat("\u3333", 2000),
254 outFull: strings.Repeat("\u3333", 2000),
255 err: nil,
256 t: lower,
257 }} {
258 tt.check(t, i)
259 }
260 }
261
262
263
264 type upperAtEOF struct{ transform.NopResetter }
265
266 func (upperAtEOF) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
267 if !atEOF {
268 return 0, 0, transform.ErrShortSrc
269 }
270 return toUpper.Transform(dst, src, atEOF)
271 }
272
273 func (upperAtEOF) Span(src []byte, atEOF bool) (n int, err error) {
274 if !atEOF {
275 return 0, transform.ErrShortSrc
276 }
277 return toUpper.Span(src, atEOF)
278 }
279
280 func BenchmarkConditional(b *testing.B) {
281 doBench(b, If(In(unicode.Hangul), transform.Nop, transform.Nop))
282 }
283
View as plain text