1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// +build !go1.10
6
7package bidirule
8
9import (
10	"golang.org/x/text/transform"
11	"golang.org/x/text/unicode/bidi"
12)
13
14var testCases = [][]ruleTest{
15	// Go-specific rules.
16	// Invalid UTF-8 is invalid.
17	0: []ruleTest{{
18		in:  "",
19		dir: bidi.LeftToRight,
20	}, {
21		in:  "\x80",
22		dir: bidi.LeftToRight,
23		err: ErrInvalid,
24		n:   0,
25	}, {
26		in:  "\xcc",
27		dir: bidi.LeftToRight,
28		err: ErrInvalid,
29		n:   0,
30	}, {
31		in:  "abc\x80",
32		dir: bidi.LeftToRight,
33		err: ErrInvalid,
34		n:   3,
35	}, {
36		in:  "abc\xcc",
37		dir: bidi.LeftToRight,
38		err: ErrInvalid,
39		n:   3,
40	}, {
41		in:  "abc\xccdef",
42		dir: bidi.LeftToRight,
43		err: ErrInvalid,
44		n:   3,
45	}, {
46		in:  "\xccdef",
47		dir: bidi.LeftToRight,
48		err: ErrInvalid,
49		n:   0,
50	}, {
51		in:  strR + "\x80",
52		dir: bidi.RightToLeft,
53		err: ErrInvalid,
54		n:   len(strR),
55	}, {
56		in:  strR + "\xcc",
57		dir: bidi.RightToLeft,
58		err: ErrInvalid,
59		n:   len(strR),
60	}, {
61		in:  strAL + "\xcc" + strR,
62		dir: bidi.RightToLeft,
63		err: ErrInvalid,
64		n:   len(strAL),
65	}, {
66		in:  "\xcc" + strR,
67		dir: bidi.RightToLeft,
68		err: ErrInvalid,
69		n:   0,
70	}},
71
72	// Rule 2.1: The first character must be a character with Bidi property L,
73	// R, or AL.  If it has the R or AL property, it is an RTL label; if it has
74	// the L property, it is an LTR label.
75	1: []ruleTest{{
76		in:  strL,
77		dir: bidi.LeftToRight,
78	}, {
79		in:  strR,
80		dir: bidi.RightToLeft,
81	}, {
82		in:  strAL,
83		dir: bidi.RightToLeft,
84	}, {
85		in:  strAN,
86		dir: bidi.RightToLeft,
87		err: ErrInvalid,
88	}, {
89		in:  strEN,
90		dir: bidi.LeftToRight,
91		err: nil, // not an RTL string
92	}, {
93		in:  strES,
94		dir: bidi.LeftToRight,
95		err: nil, // not an RTL string
96	}, {
97		in:  strET,
98		dir: bidi.LeftToRight,
99		err: nil, // not an RTL string
100	}, {
101		in:  strCS,
102		dir: bidi.LeftToRight,
103		err: nil, // not an RTL string
104	}, {
105		in:  strNSM,
106		dir: bidi.LeftToRight,
107		err: nil, // not an RTL string
108	}, {
109		in:  strBN,
110		dir: bidi.LeftToRight,
111		err: nil, // not an RTL string
112	}, {
113		in:  strB,
114		dir: bidi.LeftToRight,
115		err: nil, // not an RTL string
116	}, {
117		in:  strS,
118		dir: bidi.LeftToRight,
119		err: nil, // not an RTL string
120	}, {
121		in:  strWS,
122		dir: bidi.LeftToRight,
123		err: nil, // not an RTL string
124	}, {
125		in:  strON,
126		dir: bidi.LeftToRight,
127		err: nil, // not an RTL string
128	}, {
129		in:  strEN + strR,
130		dir: bidi.RightToLeft,
131		err: ErrInvalid,
132		n:   3,
133	}, {
134		in:  strES + strR,
135		dir: bidi.RightToLeft,
136		err: ErrInvalid,
137		n:   2,
138	}, {
139		in:  strET + strR,
140		dir: bidi.RightToLeft,
141		err: ErrInvalid,
142		n:   1,
143	}, {
144		in:  strCS + strR,
145		dir: bidi.RightToLeft,
146		err: ErrInvalid,
147		n:   1,
148	}, {
149		in:  strNSM + strR,
150		dir: bidi.RightToLeft,
151		err: ErrInvalid,
152		n:   2,
153	}, {
154		in:  strBN + strR,
155		dir: bidi.RightToLeft,
156		err: ErrInvalid,
157		n:   3,
158	}, {
159		in:  strB + strR,
160		dir: bidi.RightToLeft,
161		err: ErrInvalid,
162		n:   3,
163	}, {
164		in:  strS + strR,
165		dir: bidi.RightToLeft,
166		err: ErrInvalid,
167		n:   1,
168	}, {
169		in:  strWS + strR,
170		dir: bidi.RightToLeft,
171		err: ErrInvalid,
172		n:   1,
173	}, {
174		in:  strON + strR,
175		dir: bidi.RightToLeft,
176		err: ErrInvalid,
177		n:   1,
178	}},
179
180	// Rule 2.2: In an RTL label, only characters with the Bidi properties R,
181	// AL, AN, EN, ES, CS, ET, ON, BN, or NSM are allowed.
182	2: []ruleTest{{
183		in:  strR + strR + strAL,
184		dir: bidi.RightToLeft,
185	}, {
186		in:  strR + strAL + strR,
187		dir: bidi.RightToLeft,
188	}, {
189		in:  strR + strAN + strAL,
190		dir: bidi.RightToLeft,
191	}, {
192		in:  strR + strEN + strR,
193		dir: bidi.RightToLeft,
194	}, {
195		in:  strR + strES + strR,
196		dir: bidi.RightToLeft,
197	}, {
198		in:  strR + strCS + strR,
199		dir: bidi.RightToLeft,
200	}, {
201		in:  strR + strET + strAL,
202		dir: bidi.RightToLeft,
203	}, {
204		in:  strR + strON + strR,
205		dir: bidi.RightToLeft,
206	}, {
207		in:  strR + strBN + strR,
208		dir: bidi.RightToLeft,
209	}, {
210		in:  strR + strNSM + strAL,
211		dir: bidi.RightToLeft,
212	}, {
213		in:  strR + strL + strR,
214		dir: bidi.RightToLeft,
215		n:   len(strR),
216		err: ErrInvalid,
217	}, {
218		in:  strR + strB + strR,
219		dir: bidi.RightToLeft,
220		n:   len(strR),
221		err: ErrInvalid,
222	}, {
223		in:  strR + strS + strAL,
224		dir: bidi.RightToLeft,
225		n:   len(strR),
226		err: ErrInvalid,
227	}, {
228		in:  strR + strWS + strAL,
229		dir: bidi.RightToLeft,
230		n:   len(strR),
231		err: ErrInvalid,
232	}, {
233		in:  strAL + strR + strAL,
234		dir: bidi.RightToLeft,
235	}, {
236		in:  strAL + strAL + strR,
237		dir: bidi.RightToLeft,
238	}, {
239		in:  strAL + strAN + strAL,
240		dir: bidi.RightToLeft,
241	}, {
242		in:  strAL + strEN + strR,
243		dir: bidi.RightToLeft,
244	}, {
245		in:  strAL + strES + strR,
246		dir: bidi.RightToLeft,
247	}, {
248		in:  strAL + strCS + strR,
249		dir: bidi.RightToLeft,
250	}, {
251		in:  strAL + strET + strAL,
252		dir: bidi.RightToLeft,
253	}, {
254		in:  strAL + strON + strR,
255		dir: bidi.RightToLeft,
256	}, {
257		in:  strAL + strBN + strR,
258		dir: bidi.RightToLeft,
259	}, {
260		in:  strAL + strNSM + strAL,
261		dir: bidi.RightToLeft,
262	}, {
263		in:  strAL + strL + strR,
264		dir: bidi.RightToLeft,
265		n:   len(strAL),
266		err: ErrInvalid,
267	}, {
268		in:  strAL + strB + strR,
269		dir: bidi.RightToLeft,
270		n:   len(strAL),
271		err: ErrInvalid,
272	}, {
273		in:  strAL + strS + strAL,
274		dir: bidi.RightToLeft,
275		n:   len(strAL),
276		err: ErrInvalid,
277	}, {
278		in:  strAL + strWS + strAL,
279		dir: bidi.RightToLeft,
280		n:   len(strAL),
281		err: ErrInvalid,
282	}},
283
284	// Rule 2.3: In an RTL label, the end of the label must be a character with
285	// Bidi property R, AL, EN, or AN, followed by zero or more characters with
286	// Bidi property NSM.
287	3: []ruleTest{{
288		in:  strR + strNSM,
289		dir: bidi.RightToLeft,
290	}, {
291		in:  strR + strR,
292		dir: bidi.RightToLeft,
293	}, {
294		in:  strR + strAL + strNSM,
295		dir: bidi.RightToLeft,
296	}, {
297		in:  strR + strEN + strNSM + strNSM,
298		dir: bidi.RightToLeft,
299	}, {
300		in:  strR + strAN,
301		dir: bidi.RightToLeft,
302	}, {
303		in:  strR + strES + strNSM,
304		dir: bidi.RightToLeft,
305		n:   len(strR + strES + strNSM),
306		err: ErrInvalid,
307	}, {
308		in:  strR + strCS + strNSM + strNSM,
309		dir: bidi.RightToLeft,
310		n:   len(strR + strCS + strNSM + strNSM),
311		err: ErrInvalid,
312	}, {
313		in:  strR + strET,
314		dir: bidi.RightToLeft,
315		n:   len(strR + strET),
316		err: ErrInvalid,
317	}, {
318		in:  strR + strON + strNSM,
319		dir: bidi.RightToLeft,
320		n:   len(strR + strON + strNSM),
321		err: ErrInvalid,
322	}, {
323		in:  strR + strBN + strNSM + strNSM,
324		dir: bidi.RightToLeft,
325		n:   len(strR + strBN + strNSM + strNSM),
326		err: ErrInvalid,
327	}, {
328		in:  strR + strL + strNSM,
329		dir: bidi.RightToLeft,
330		n:   len(strR),
331		err: ErrInvalid,
332	}, {
333		in:  strR + strB + strNSM + strNSM,
334		dir: bidi.RightToLeft,
335		n:   len(strR),
336		err: ErrInvalid,
337	}, {
338		in:  strR + strS,
339		dir: bidi.RightToLeft,
340		n:   len(strR),
341		err: ErrInvalid,
342	}, {
343		in:  strR + strWS,
344		dir: bidi.RightToLeft,
345		n:   len(strR),
346		err: ErrInvalid,
347	}, {
348		in:  strAL + strNSM,
349		dir: bidi.RightToLeft,
350	}, {
351		in:  strAL + strR,
352		dir: bidi.RightToLeft,
353	}, {
354		in:  strAL + strAL + strNSM,
355		dir: bidi.RightToLeft,
356	}, {
357		in:  strAL + strEN + strNSM + strNSM,
358		dir: bidi.RightToLeft,
359	}, {
360		in:  strAL + strAN,
361		dir: bidi.RightToLeft,
362	}, {
363		in:  strAL + strES + strNSM,
364		dir: bidi.RightToLeft,
365		n:   len(strAL + strES + strNSM),
366		err: ErrInvalid,
367	}, {
368		in:  strAL + strCS + strNSM + strNSM,
369		dir: bidi.RightToLeft,
370		n:   len(strAL + strCS + strNSM + strNSM),
371		err: ErrInvalid,
372	}, {
373		in:  strAL + strET,
374		dir: bidi.RightToLeft,
375		n:   len(strAL + strET),
376		err: ErrInvalid,
377	}, {
378		in:  strAL + strON + strNSM,
379		dir: bidi.RightToLeft,
380		n:   len(strAL + strON + strNSM),
381		err: ErrInvalid,
382	}, {
383		in:  strAL + strBN + strNSM + strNSM,
384		dir: bidi.RightToLeft,
385		n:   len(strAL + strBN + strNSM + strNSM),
386		err: ErrInvalid,
387	}, {
388		in:  strAL + strL + strNSM,
389		dir: bidi.RightToLeft,
390		n:   len(strAL),
391		err: ErrInvalid,
392	}, {
393		in:  strAL + strB + strNSM + strNSM,
394		dir: bidi.RightToLeft,
395		n:   len(strAL),
396		err: ErrInvalid,
397	}, {
398		in:  strAL + strS,
399		dir: bidi.RightToLeft,
400		n:   len(strAL),
401		err: ErrInvalid,
402	}, {
403		in:  strAL + strWS,
404		dir: bidi.RightToLeft,
405		n:   len(strAL),
406		err: ErrInvalid,
407	}},
408
409	// Rule 2.4: In an RTL label, if an EN is present, no AN may be present,
410	// and vice versa.
411	4: []ruleTest{{
412		in:  strR + strEN + strAN,
413		dir: bidi.RightToLeft,
414		n:   len(strR + strEN),
415		err: ErrInvalid,
416	}, {
417		in:  strR + strAN + strEN + strNSM,
418		dir: bidi.RightToLeft,
419		n:   len(strR + strAN),
420		err: ErrInvalid,
421	}, {
422		in:  strAL + strEN + strAN,
423		dir: bidi.RightToLeft,
424		n:   len(strAL + strEN),
425		err: ErrInvalid,
426	}, {
427		in:  strAL + strAN + strEN + strNSM,
428		dir: bidi.RightToLeft,
429		n:   len(strAL + strAN),
430		err: ErrInvalid,
431	}},
432
433	// Rule 2.5: In an LTR label, only characters with the Bidi properties L,
434	// EN, ES, CS, ET, ON, BN, or NSM are allowed.
435	5: []ruleTest{{
436		in:  strL + strL + strL,
437		dir: bidi.LeftToRight,
438	}, {
439		in:  strL + strEN + strL,
440		dir: bidi.LeftToRight,
441	}, {
442		in:  strL + strES + strL,
443		dir: bidi.LeftToRight,
444	}, {
445		in:  strL + strCS + strL,
446		dir: bidi.LeftToRight,
447	}, {
448		in:  strL + strET + strL,
449		dir: bidi.LeftToRight,
450	}, {
451		in:  strL + strON + strL,
452		dir: bidi.LeftToRight,
453	}, {
454		in:  strL + strBN + strL,
455		dir: bidi.LeftToRight,
456	}, {
457		in:  strL + strNSM + strL,
458		dir: bidi.LeftToRight,
459	}, {
460		in:  strL + strR + strL,
461		dir: bidi.RightToLeft,
462		n:   len(strL),
463		err: ErrInvalid,
464	}, {
465		in:  strL + strAL + strL,
466		dir: bidi.RightToLeft,
467		n:   len(strL),
468		err: ErrInvalid,
469	}, {
470		in:  strL + strAN + strL,
471		dir: bidi.RightToLeft,
472		n:   len(strL),
473		err: ErrInvalid,
474	}, {
475		in:  strL + strB + strL,
476		dir: bidi.LeftToRight,
477		n:   len(strL + strAN + strL),
478		err: nil,
479	}, {
480		in:  strL + strB + strL + strR,
481		dir: bidi.RightToLeft,
482		n:   len(strL + strB + strL),
483		err: ErrInvalid,
484	}, {
485		in:  strL + strS + strL,
486		dir: bidi.LeftToRight,
487		n:   len(strL + strS + strL),
488		err: nil,
489	}, {
490		in:  strL + strS + strL + strR,
491		dir: bidi.RightToLeft,
492		n:   len(strL + strS + strL),
493		err: ErrInvalid,
494	}, {
495		in:  strL + strWS + strL,
496		dir: bidi.LeftToRight,
497		n:   len(strL + strWS + strL),
498		err: nil,
499	}, {
500		in:  strL + strWS + strL + strR,
501		dir: bidi.RightToLeft,
502		n:   len(strL + strWS + strL),
503		err: ErrInvalid,
504	}},
505
506	// Rule 2.6: In an LTR label, the end of the label must be a character with
507	// Bidi property L or EN, followed by zero or more characters with Bidi
508	// property NSM.
509	6: []ruleTest{{
510		in:  strL,
511		dir: bidi.LeftToRight,
512	}, {
513		in:  strL + strNSM,
514		dir: bidi.LeftToRight,
515	}, {
516		in:  strL + strNSM + strNSM,
517		dir: bidi.LeftToRight,
518	}, {
519		in:  strL + strEN,
520		dir: bidi.LeftToRight,
521	}, {
522		in:  strL + strEN + strNSM,
523		dir: bidi.LeftToRight,
524	}, {
525		in:  strL + strEN + strNSM + strNSM,
526		dir: bidi.LeftToRight,
527	}, {
528		in:  strL + strES,
529		dir: bidi.LeftToRight,
530		n:   len(strL + strES),
531		err: nil,
532	}, {
533		in:  strL + strES + strR,
534		dir: bidi.RightToLeft,
535		n:   len(strL + strES),
536		err: ErrInvalid,
537	}, {
538		in:  strL + strCS,
539		dir: bidi.LeftToRight,
540		n:   len(strL + strCS),
541		err: nil,
542	}, {
543		in:  strL + strCS + strR,
544		dir: bidi.RightToLeft,
545		n:   len(strL + strCS),
546		err: ErrInvalid,
547	}, {
548		in:  strL + strET,
549		dir: bidi.LeftToRight,
550		n:   len(strL + strET),
551		err: nil,
552	}, {
553		in:  strL + strET + strR,
554		dir: bidi.RightToLeft,
555		n:   len(strL + strET),
556		err: ErrInvalid,
557	}, {
558		in:  strL + strON,
559		dir: bidi.LeftToRight,
560		n:   len(strL + strON),
561		err: nil,
562	}, {
563		in:  strL + strON + strR,
564		dir: bidi.RightToLeft,
565		n:   len(strL + strON),
566		err: ErrInvalid,
567	}, {
568		in:  strL + strBN,
569		dir: bidi.LeftToRight,
570		n:   len(strL + strBN),
571		err: nil,
572	}, {
573		in:  strL + strBN + strR,
574		dir: bidi.RightToLeft,
575		n:   len(strL + strBN),
576		err: ErrInvalid,
577	}, {
578		in:  strL + strR,
579		dir: bidi.RightToLeft,
580		n:   len(strL),
581		err: ErrInvalid,
582	}, {
583		in:  strL + strAL,
584		dir: bidi.RightToLeft,
585		n:   len(strL),
586		err: ErrInvalid,
587	}, {
588		in:  strL + strAN,
589		dir: bidi.RightToLeft,
590		n:   len(strL),
591		err: ErrInvalid,
592	}, {
593		in:  strL + strB,
594		dir: bidi.LeftToRight,
595		n:   len(strL + strB),
596		err: nil,
597	}, {
598		in:  strL + strB + strR,
599		dir: bidi.RightToLeft,
600		n:   len(strL + strB),
601		err: ErrInvalid,
602	}, {
603		in:  strL + strB,
604		dir: bidi.LeftToRight,
605		n:   len(strL + strB),
606		err: nil,
607	}, {
608		in:  strL + strB + strR,
609		dir: bidi.RightToLeft,
610		n:   len(strL + strB),
611		err: ErrInvalid,
612	}, {
613		in:  strL + strB,
614		dir: bidi.LeftToRight,
615		n:   len(strL + strB),
616		err: nil,
617	}, {
618		in:  strL + strB + strR,
619		dir: bidi.RightToLeft,
620		n:   len(strL + strB),
621		err: ErrInvalid,
622	}},
623
624	// Incremental processing.
625	9: []ruleTest{{
626		in:  "e\u0301", // é
627		dir: bidi.LeftToRight,
628
629		pSrc: 2,
630		nSrc: 1,
631		err0: transform.ErrShortSrc,
632	}, {
633		in:  "e\u1000f", // é
634		dir: bidi.LeftToRight,
635
636		pSrc: 3,
637		nSrc: 1,
638		err0: transform.ErrShortSrc,
639	}, {
640		// Remain invalid once invalid.
641		in:  strR + "ab",
642		dir: bidi.RightToLeft,
643		n:   len(strR),
644		err: ErrInvalid,
645
646		pSrc: len(strR) + 1,
647		nSrc: len(strR),
648		err0: ErrInvalid,
649	}, {
650		// Short destination
651		in:  "abcdefghij",
652		dir: bidi.LeftToRight,
653
654		pSrc:  10,
655		szDst: 5,
656		nSrc:  5,
657		err0:  transform.ErrShortDst,
658	}, {
659		// Short destination splitting input rune
660		in:  "e\u0301",
661		dir: bidi.LeftToRight,
662
663		pSrc:  3,
664		szDst: 2,
665		nSrc:  1,
666		err0:  transform.ErrShortDst,
667	}},
668}
669