8
8
// SPDX-License-Identifier: MIT
9
9
//
10
10
11
- use plib:: testing:: { run_test, TestPlan } ;
11
+ use plib:: testing:: { run_test, run_test_u8, TestPlan , TestPlanU8 } ;
12
+
13
+ fn tr_test_binary ( args : & [ & str ] , test_data : & [ u8 ] , expected_output : & [ u8 ] ) {
14
+ let str_args = args
15
+ . iter ( )
16
+ . map ( |st| st. to_owned ( ) . to_owned ( ) )
17
+ . collect :: < Vec < _ > > ( ) ;
18
+
19
+ run_test_u8 ( TestPlanU8 {
20
+ cmd : "tr" . to_owned ( ) ,
21
+ args : str_args,
22
+ stdin_data : test_data. to_owned ( ) ,
23
+ expected_out : expected_output. to_owned ( ) ,
24
+ expected_err : Vec :: < u8 > :: new ( ) ,
25
+ expected_exit_code : 0_i32 ,
26
+ } ) ;
27
+ }
12
28
13
29
fn tr_test ( args : & [ & str ] , test_data : & str , expected_output : & str ) {
14
30
let str_args = args
15
31
. iter ( )
16
32
. map ( |st| st. to_owned ( ) . to_owned ( ) )
17
- . collect :: < Vec < String > > ( ) ;
33
+ . collect :: < Vec < _ > > ( ) ;
18
34
19
35
run_test ( TestPlan {
20
36
cmd : "tr" . to_owned ( ) ,
21
37
args : str_args,
22
38
stdin_data : test_data. to_owned ( ) ,
23
39
expected_out : expected_output. to_owned ( ) ,
24
40
expected_err : String :: new ( ) ,
25
- expected_exit_code : 0 ,
41
+ expected_exit_code : 0_i32 ,
26
42
} ) ;
27
43
}
28
44
@@ -38,7 +54,7 @@ fn tr_bad_arguments_failure_test(args: &[&str], expected_stderr: &str) {
38
54
stdin_data : String :: new ( ) ,
39
55
expected_out : String :: new ( ) ,
40
56
expected_err : expected_stderr. to_owned ( ) ,
41
- expected_exit_code : 1 ,
57
+ expected_exit_code : 1_i32 ,
42
58
} ) ;
43
59
}
44
60
@@ -482,23 +498,23 @@ fn tr_bad_octal_range() {
482
498
fn tr_bad_x_n_construct_decimal ( ) {
483
499
tr_bad_arguments_failure_test (
484
500
& [ "-d" , "[a*100000000000000000000]" ] ,
485
- "tr: invalid repeat count ‘ 100000000000000000000’ in [c*n] construct\n " ,
501
+ "tr: invalid repeat count ' 100000000000000000000' in [c*n] construct\n " ,
486
502
) ;
487
503
}
488
504
489
505
#[ test]
490
506
fn tr_bad_x_n_construct_octal ( ) {
491
507
tr_bad_arguments_failure_test (
492
508
& [ "-d" , "[a*010000000000000000000000]" ] ,
493
- "tr: invalid repeat count ‘ 010000000000000000000000’ in [c*n] construct\n " ,
509
+ "tr: invalid repeat count ' 010000000000000000000000' in [c*n] construct\n " ,
494
510
) ;
495
511
}
496
512
497
513
#[ test]
498
514
fn tr_bad_x_n_construct_non_decimal_non_octal ( ) {
499
515
tr_bad_arguments_failure_test (
500
516
& [ "-d" , "[a*a]" ] ,
501
- "tr: invalid repeat count ‘a’ in [c*n] construct\n " ,
517
+ "tr: invalid repeat count 'a' in [c*n] construct\n " ,
502
518
) ;
503
519
}
504
520
@@ -597,7 +613,7 @@ fn tr_equivalence_class_low_priority() {
597
613
fn tr_arguments_validation_error_message_format ( ) {
598
614
tr_bad_arguments_failure_test (
599
615
& [ "a" ] ,
600
- "tr: missing operand after ‘a’ . Two strings must be given when translating.\n " ,
616
+ "tr: missing operand after 'a' . Two strings must be given when translating.\n " ,
601
617
) ;
602
618
}
603
619
@@ -633,11 +649,147 @@ fn tr_minimal_d_s() {
633
649
fn tr_missing_equiv ( ) {
634
650
tr_bad_arguments_failure_test (
635
651
& [ "-d" , "[==]" ] ,
636
- "tr: missing equivalence class character '[==]'\n " ,
652
+ "tr: input '[==]' is invalid: missing equivalence class character \n " ,
637
653
) ;
638
654
}
639
655
640
656
#[ test]
641
657
fn tr_missing_character_class ( ) {
642
- tr_bad_arguments_failure_test ( & [ "-d" , "[::]" ] , "tr: missing character class name '[::]'\n " ) ;
658
+ tr_bad_arguments_failure_test (
659
+ & [ "-d" , "[::]" ] ,
660
+ "tr: input '[::]' is invalid: missing character class name\n " ,
661
+ ) ;
662
+ }
663
+
664
+ #[ test]
665
+ fn tr_8_bit ( ) {
666
+ tr_test_binary ( & [ r"\377" , "A" ] , b"\xFF " , b"A" ) ;
667
+ }
668
+
669
+ #[ test]
670
+ fn tr_multi_byte_utf_8 ( ) {
671
+ tr_test ( & [ "-d" , "ᛆᚠ" ] , "ᛆᚠᛏᚢᛆᛘᚢᚦᛌᛏᚭᚿᛏᛆᚱᚢᚿᛆᛧᚦᛆᛧ" , "ᛏᚢᛘᚢᚦᛌᛏᚭᚿᛏᚱᚢᚿᛧᚦᛧ" ) ;
672
+ }
673
+
674
+ #[ test]
675
+ fn tr_c_d_s_squeeze_not_complemented ( ) {
676
+ tr_test ( & [ "-c" , "-d" , "-s" , "D" , "D" ] , "DDD AAABBBCCC DDD" , "D" ) ;
677
+ }
678
+
679
+ #[ test]
680
+ fn tr_squeeze_independent_of_translation ( ) {
681
+ tr_test ( & [ "-s" , "1" , "23" ] , "111 222 333" , "2 2 3" ) ;
682
+ }
683
+
684
+ #[ test]
685
+ fn tr_complemented_squeeze_independent_of_translation ( ) {
686
+ tr_test ( & [ "-c" , "-s" , "1" , "23" ] , "111 222 333" , "1113" ) ;
687
+ }
688
+
689
+ #[ test]
690
+ fn tr_c_s_as_many_as_needed ( ) {
691
+ tr_test ( & [ "-c" , "-s" , "B" , "[d*]" ] , "AAA BBB CCC" , "dBBBd" ) ;
692
+ }
693
+
694
+ // Only BusyBox and uutils' coreutils handle this "correctly"
695
+ // bsdutils runs forever (or a very long time)
696
+ // GNU Core Utilities rejects this because of the "[d*] argument"
697
+ #[ test]
698
+ fn tr_non_standard_d_s ( ) {
699
+ tr_test ( & [ "-d" , "-s" , "B" , "[C*]" ] , "AAA BBB CCC DDD" , "AAA C DDD" ) ;
700
+ }
701
+
702
+ // Different from bsdutils, but bsdutils doesn't handle 8-bit non-UTF-8 data
703
+ #[ test]
704
+ fn tr_multi_byte_complement_translation ( ) {
705
+ tr_test ( & [ "-c" , "ᛏ" , "A" ] , "ᛆᚠᛏ" , "AAAAAAᛏ" ) ;
706
+ }
707
+
708
+ #[ test]
709
+ fn tr_multi_byte_indexing_check ( ) {
710
+ tr_test ( & [ "-c" , "ᛏ" , "B" ] , "ᛏA" , "ᛏB" ) ;
711
+ }
712
+
713
+ // BusyBox does not parse escape backslash/escape sequences inside [x*n] constructs
714
+ // Other implementations do
715
+ #[ test]
716
+ fn tr_slash_n_as_many_as_needed ( ) {
717
+ tr_test (
718
+ & [ "b-c" , r"[\n*]" ] ,
719
+ "The big black fox jumped over the fence" ,
720
+ "\
721
+ The
722
+ ig
723
+ la
724
+ k fox jumped over the fen
725
+ e" ,
726
+ ) ;
727
+ }
728
+
729
+ #[ test]
730
+ fn tr_slash_n_broken_x_n_construct ( ) {
731
+ tr_test (
732
+ & [ "b-e" , r"[\nZ]" ] ,
733
+ "The big black fox jumped over the fence" ,
734
+ "\
735
+ Th] [ig [la
736
+ k fox jump]Z ov]r th] f]n
737
+ ]" ,
738
+ ) ;
739
+ }
740
+
741
+ #[ test]
742
+ fn tr_octal_in_as_many_as_needed ( ) {
743
+ tr_test (
744
+ & [ "a-d" , r"[\123*2]9" ] ,
745
+ "The big black fox jumped over the fence" ,
746
+ "The Sig SlS9k fox jumpe9 over the fen9e" ,
747
+ ) ;
748
+ }
749
+
750
+ #[ test]
751
+ fn tr_invalid_octal_in_as_many_as_needed ( ) {
752
+ tr_test (
753
+ & [ "a-d" , r"[\128*2]" ] ,
754
+ "The big black fox jumped over the fence" ,
755
+ "\
756
+ The
757
+ ig
758
+ l[8k fox jumpe* over the fen8e" ,
759
+ ) ;
760
+ }
761
+
762
+ #[ test]
763
+ fn tr_invalid_multi_byte_range ( ) {
764
+ tr_bad_arguments_failure_test (
765
+ & [ "-d" , "ᛆ-ᚦ" ] ,
766
+ r"tr: range-endpoints of '\u{16c6}-\u{16a6}' are in reverse collating sequence order
767
+ " ,
768
+ ) ;
769
+ }
770
+
771
+ #[ test]
772
+ fn tr_multi_byte_range ( ) {
773
+ tr_test (
774
+ & [ "-d" , "ᚢ-ᛆ" ] ,
775
+ "A ᛆᚠᛏᚢᛆᛘᚢᚦᛌᛏᚭᚿᛏᛆᚱᚢᚿᛆᛧᚦᛆᛧ B" ,
776
+ "\
777
+ A ᚠᛏᛘᛌᛏᛏᛧᛧ B",
778
+ ) ;
779
+ }
780
+
781
+ #[ test]
782
+ fn tr_multi_byte_squeeze_translate ( ) {
783
+ tr_test ( & [ "-s" , "ᚢ" , "A" ] , "123 ᚢᚢᚢᚢᚢᚢ 456" , "123 A 456" ) ;
784
+ }
785
+
786
+ #[ test]
787
+ fn tr_dash_d_two_strings ( ) {
788
+ tr_bad_arguments_failure_test (
789
+ & [ "-d" , "A" , "B" ] ,
790
+ "\
791
+ tr: extra operand 'B'
792
+ Only one string may be given when deleting without squeezing repeats.
793
+ " ,
794
+ ) ;
643
795
}
0 commit comments