From b3d8706a2c684acd57983d5a74a2acc1c25ff547 Mon Sep 17 00:00:00 2001 From: ksgk1 <29155932+ksgk1@users.noreply.github.com> Date: Thu, 30 Apr 2026 10:33:48 +0200 Subject: [PATCH] sort: fix inconsistent sort ordering under i18n-collator with equal sorting keys (#12013) * sort: Fix inconsistent sort orderg under i18n-collator with equal sorting keys. * Test cases for fix #11980 * Simplyfing fix for #11980 * Fix clippy lint and rename test files. * Remove old test files * Update tests/by-util/test_sort.rs Co-authored-by: Daniel Hofstetter * Update tests/by-util/test_sort.rs Co-authored-by: Daniel Hofstetter * Removing redundant test and swapping default order for sort to match sort's ordering. * Comment for clarification. --------- Co-authored-by: Daniel Hofstetter --- src/uu/sort/src/sort.rs | 8 ++++++- tests/by-util/test_sort.rs | 21 +++++++++++++++++++ .../sort/fix_i18n_collate_inconsistency_1.txt | 3 +++ .../sort/fix_i18n_collate_inconsistency_2.txt | 3 +++ 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/sort/fix_i18n_collate_inconsistency_1.txt create mode 100644 tests/fixtures/sort/fix_i18n_collate_inconsistency_2.txt diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index a62e17edd..a360f5f95 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -2655,7 +2655,13 @@ fn compare_by<'a>( if global_settings.precomputed.fast_locale_collation { let a_key = a_line_data.collation_key(a.index); let b_key = b_line_data.collation_key(b.index); - let cmp = a_key.cmp(b_key); + let mut cmp = a_key.cmp(b_key); + // If collation keys are equal, fall back to lexicographic comparison + // This can be the case for inputs like `01` and `0_1`, which have equal keys + if cmp == Ordering::Equal { + // Reversing the order to match sort's sorting behaviour + cmp = b.line.cmp(a.line); + } return if global_settings.reverse { cmp.reverse() } else { diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index b0e7602fd..1e3bc8842 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -2960,4 +2960,25 @@ e f 5436 down data path1 path2 path3 path4 path5\n"; .stdout_is(input); } +#[test] +fn test_consistent_sorting_with_i18n_collate() { + // Regression test for issue #11980 + // Lexicographic fallback sorting for equal sorting keys for 01 and 0_1 + let expected_output = "0_1\n0_1\n01\n01\n02\n02\n"; + new_ucmd!() + .env("LC_ALL", "en_US.UTF-8") + .arg("fix_i18n_collate_inconsistency_1.txt") + .arg("fix_i18n_collate_inconsistency_2.txt") + .succeeds() + .stdout_is(expected_output); + + let expected_output = "01\n01\n02\n02\n0_1\n0_1\n"; + new_ucmd!() + .env("LC_ALL", "C") + .arg("fix_i18n_collate_inconsistency_1.txt") + .arg("fix_i18n_collate_inconsistency_2.txt") + .succeeds() + .stdout_is(expected_output); +} + /* spell-checker: enable */ diff --git a/tests/fixtures/sort/fix_i18n_collate_inconsistency_1.txt b/tests/fixtures/sort/fix_i18n_collate_inconsistency_1.txt new file mode 100644 index 000000000..20a849540 --- /dev/null +++ b/tests/fixtures/sort/fix_i18n_collate_inconsistency_1.txt @@ -0,0 +1,3 @@ +01 +0_1 +02 \ No newline at end of file diff --git a/tests/fixtures/sort/fix_i18n_collate_inconsistency_2.txt b/tests/fixtures/sort/fix_i18n_collate_inconsistency_2.txt new file mode 100644 index 000000000..bafe91c4f --- /dev/null +++ b/tests/fixtures/sort/fix_i18n_collate_inconsistency_2.txt @@ -0,0 +1,3 @@ +0_1 +01 +02 \ No newline at end of file