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