From e28beff3e6b0518fbd64f23b53d7d4f384c0f501 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Fri, 15 Oct 2021 16:37:58 -0500 Subject: [PATCH 01/14] Improve test code consistency --- test/version_sorter_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/version_sorter_test.rb b/test/version_sorter_test.rb index 1fbf478..eea787f 100644 --- a/test/version_sorter_test.rb +++ b/test/version_sorter_test.rb @@ -13,7 +13,7 @@ def setup end def test_sorts_versions_correctly - versions = %w(1.0.9 1.0.10 2.0 3.1.4.2 1.0.9a) + versions = %w( 1.0.9 1.0.10 2.0 3.1.4.2 1.0.9a ) sorted_versions = %w( 1.0.9a 1.0.9 1.0.10 2.0 3.1.4.2 ) assert_equal sorted_versions, VersionSorter.sort(versions) @@ -38,7 +38,7 @@ def test_returns_same_object end def test_reverse_sorts_versions_correctly - versions = %w(1.0.9 1.0.10 2.0 3.1.4.2 1.0.9a) + versions = %w( 1.0.9 1.0.10 2.0 3.1.4.2 1.0.9a ) sorted_versions = %w( 3.1.4.2 2.0 1.0.10 1.0.9 1.0.9a ) assert_equal sorted_versions, VersionSorter.rsort(versions) @@ -50,7 +50,7 @@ def test_does_not_raise_on_number_overflow (2**32 + 1).to_s, (2**32 + 2).to_s, (2**32 - 2).to_s, - (2**32 - 1).to_s, + (2**32 - 1).to_s ] randomized = shuffle big_numbers From cab24dd6bb63e67590f45ad7d97b6942cf523363 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Fri, 15 Oct 2021 16:39:38 -0500 Subject: [PATCH 02/14] Add tests for sorting version numbers with v/V prefixes --- test/version_sorter_test.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/version_sorter_test.rb b/test/version_sorter_test.rb index eea787f..77dd539 100644 --- a/test/version_sorter_test.rb +++ b/test/version_sorter_test.rb @@ -19,6 +19,20 @@ def test_sorts_versions_correctly assert_equal sorted_versions, VersionSorter.sort(versions) end + def test_sorts_versions_correctly_with_lowercase_prefixes + versions = %w( v1.0.9 v1.0.10 v2.0 v3.1.4.2 v1.0.9a ) + sorted_versions = %w( v1.0.9a v1.0.9 v1.0.10 v2.0 v3.1.4.2 ) + + assert_equal sorted_versions, VersionSorter.sort(versions) + end + + def test_sorts_versions_correctly_with_uppercase_prefixes + versions = %w( V1.0.9 V1.0.10 V2.0 V3.1.4.2 V1.0.9a ) + sorted_versions = %w( V1.0.9a V1.0.9 V1.0.10 V2.0 V3.1.4.2 ) + + assert_equal sorted_versions, VersionSorter.sort(versions) + end + def test_sorts_versions_like_rubygems versions = %w(1.0.9.b 1.0.9 1.0.10 2.0 3.1.4.2 1.0.9a 2.0rc2 2.0-rc1) if !Gem.respond_to?(:rubygems_version) || Gem.rubygems_version < Gem::Version.new('2.1.0') From 8d253ca001a3a6de8b67d944763af51c4eb42ca1 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Fri, 15 Oct 2021 16:41:03 -0500 Subject: [PATCH 03/14] Add test for sorting non-version data with trailing numbers alphabetically --- test/version_sorter_test.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/version_sorter_test.rb b/test/version_sorter_test.rb index 77dd539..6534ef8 100644 --- a/test/version_sorter_test.rb +++ b/test/version_sorter_test.rb @@ -85,6 +85,17 @@ def test_handles_non_version_data assert_equal sorted, VersionSorter.sort(non_versions) end + def test_sorts_non_version_data_with_trailing_numbers + non_versions = [ + "my-patch-2", "my-patch1", "my-patch2", "my-patch", "my-patch-1" + ] + sorted = [ + "my-patch", "my-patch-1", "my-patch-2", "my-patch1", "my-patch2" + ] + + assert_equal sorted, VersionSorter.sort(non_versions) + end + def test_sort_bang versions = ["10.0", "1.0", "2.0"] VersionSorter.sort! versions From adba5ad5255cd218570e8890cfe3e6f185707f00 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Fri, 15 Oct 2021 16:44:00 -0500 Subject: [PATCH 04/14] Update test for sorting non-version data to a more alphabetically-inclined order --- test/version_sorter_test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/version_sorter_test.rb b/test/version_sorter_test.rb index 6534ef8..5cd036b 100644 --- a/test/version_sorter_test.rb +++ b/test/version_sorter_test.rb @@ -73,13 +73,13 @@ def test_does_not_raise_on_number_overflow def test_handles_non_version_data non_versions = [ - "", " ", ".", "-", "ćevapčići", "The Quick Brown Fox", '!@#$%^&*()', + " ", ".", "-", "", "ćevapčići", "The Quick Brown Fox", '!@#$%^&*()', "<--------->", "a12a8a4a22122d01541b62193e9bdad7f5eda552", "1." * 65 ] sorted = [ - "<--------->", "-", "The Quick Brown Fox", - "a12a8a4a22122d01541b62193e9bdad7f5eda552", "ćevapčići", - "", " ", ".", '!@#$%^&*()', "1." * 65 + "", " ", '!@#$%^&*()', "-", ".", "<--------->", + "The Quick Brown Fox", "a12a8a4a22122d01541b62193e9bdad7f5eda552", + "ćevapčići", "1." * 65 ] assert_equal sorted, VersionSorter.sort(non_versions) From 684011425a7ba176ac19c233d6286e2387bb397b Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Fri, 15 Oct 2021 20:37:18 -0500 Subject: [PATCH 05/14] Add test for yui style tags to match benchmark fixtures --- test/version_sorter_test.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/version_sorter_test.rb b/test/version_sorter_test.rb index 5cd036b..d712efe 100644 --- a/test/version_sorter_test.rb +++ b/test/version_sorter_test.rb @@ -96,6 +96,19 @@ def test_sorts_non_version_data_with_trailing_numbers assert_equal sorted, VersionSorter.sort(non_versions) end + def test_yui_style_tags + yui_tags = [ + "yui3-571", "yui3-309", "yui3-1405", "yui3-1537", "yui3-440", + "yui3-572", "yui3-1406", "yui3-1538", "yui3-441", "yui3-573" + ] + sorted = [ + "yui3-309", "yui3-440", "yui3-441", "yui3-571", "yui3-572", + "yui3-573", "yui3-1405", "yui3-1406", "yui3-1537", "yui3-1538" + ] + + assert_equal sorted, VersionSorter.sort(yui_tags) + end + def test_sort_bang versions = ["10.0", "1.0", "2.0"] VersionSorter.sort! versions From 27ec38a7777399610a017cab14e5debace174cbd Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Fri, 15 Oct 2021 20:15:08 -0500 Subject: [PATCH 06/14] Update parse_version_number to short-circuit on non-versions --- ext/version_sorter/version_sorter.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ext/version_sorter/version_sorter.c b/ext/version_sorter/version_sorter.c index 081e3f2..bd93457 100644 --- a/ext/version_sorter/version_sorter.c +++ b/ext/version_sorter/version_sorter.c @@ -114,6 +114,24 @@ parse_version_number(const char *string) version = grow_version_number(version, comp_alloc); + // Does this input look like a version? e.g. "1.0", "1.2.3", "v1", "V1" + int non_version = !( + isdigit(string[0]) || + ( + (string[0] == 'v' || string[0] == 'V') && isdigit(string[1]) + ) + ); + + // If it does NOT look like a version, short-circuit to sort as a string + if (non_version) { + version->comp[0].string.offset = 0; + version->comp[0].string.len = strlen(string); + version->original = string; + version->num_flags = num_flags; + version->size = 1; + return version; + } + for (offset = 0; string[offset] && comp_n < 64;) { if (comp_n >= comp_alloc) { comp_alloc += 4; From eaa0275d280ce0bccfb51d346aefb3a059b021b2 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Fri, 15 Oct 2021 21:12:13 -0500 Subject: [PATCH 07/14] Add a test to verify the README example --- test/version_sorter_test.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/version_sorter_test.rb b/test/version_sorter_test.rb index d712efe..0c9cd9b 100644 --- a/test/version_sorter_test.rb +++ b/test/version_sorter_test.rb @@ -109,6 +109,13 @@ def test_yui_style_tags assert_equal sorted, VersionSorter.sort(yui_tags) end + def test_readme_examples + readme_versions = ["1.0.9", "2.0", "1.0.10", "1.0.3", "2.0.pre"] + sorted = ["1.0.3", "1.0.9", "1.0.10", "2.0.pre", "2.0"] + + assert_equal sorted, VersionSorter.sort(readme_versions) + end + def test_sort_bang versions = ["10.0", "1.0", "2.0"] VersionSorter.sort! versions From 718e23b17d19570317590fdd1c66d6b490b40653 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Fri, 15 Oct 2021 21:46:03 -0500 Subject: [PATCH 08/14] Slight ordering expectation adjustment to tests --- test/version_sorter_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/version_sorter_test.rb b/test/version_sorter_test.rb index 0c9cd9b..d7c7eff 100644 --- a/test/version_sorter_test.rb +++ b/test/version_sorter_test.rb @@ -90,7 +90,7 @@ def test_sorts_non_version_data_with_trailing_numbers "my-patch-2", "my-patch1", "my-patch2", "my-patch", "my-patch-1" ] sorted = [ - "my-patch", "my-patch-1", "my-patch-2", "my-patch1", "my-patch2" + "my-patch", "my-patch1", "my-patch2", "my-patch-1", "my-patch-2" ] assert_equal sorted, VersionSorter.sort(non_versions) From b5babfff355d6104eced0f46f75efc76f44a071c Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Fri, 15 Oct 2021 21:47:02 -0500 Subject: [PATCH 09/14] Handle special case for zero recognized chunks with alphabetical sorting --- ext/version_sorter/version_sorter.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/version_sorter/version_sorter.c b/ext/version_sorter/version_sorter.c index bd93457..77e9930 100644 --- a/ext/version_sorter/version_sorter.c +++ b/ext/version_sorter/version_sorter.c @@ -71,6 +71,10 @@ compare_version_number(const struct version_number *a, } } + if (a->size == 0 || b->size == 0) { + return strcmp(a->original, b->original); + } + if (a->size < b->size) return (b->num_flags & (1ull << n)) ? -1 : 1; From 269b7b65f28689f6c49997f008adcb527945446a Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Fri, 15 Oct 2021 21:47:50 -0500 Subject: [PATCH 10/14] Undo short-circuiting code in parse_version_number --- ext/version_sorter/version_sorter.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/ext/version_sorter/version_sorter.c b/ext/version_sorter/version_sorter.c index 77e9930..02a2375 100644 --- a/ext/version_sorter/version_sorter.c +++ b/ext/version_sorter/version_sorter.c @@ -118,24 +118,6 @@ parse_version_number(const char *string) version = grow_version_number(version, comp_alloc); - // Does this input look like a version? e.g. "1.0", "1.2.3", "v1", "V1" - int non_version = !( - isdigit(string[0]) || - ( - (string[0] == 'v' || string[0] == 'V') && isdigit(string[1]) - ) - ); - - // If it does NOT look like a version, short-circuit to sort as a string - if (non_version) { - version->comp[0].string.offset = 0; - version->comp[0].string.len = strlen(string); - version->original = string; - version->num_flags = num_flags; - version->size = 1; - return version; - } - for (offset = 0; string[offset] && comp_n < 64;) { if (comp_n >= comp_alloc) { comp_alloc += 4; From b214d73f8d92a1a9aa4487c73e60b772799554da Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Fri, 15 Oct 2021 21:49:26 -0500 Subject: [PATCH 11/14] Adjust string chunk building logic to include as many hyphenated portions as possible --- ext/version_sorter/version_sorter.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ext/version_sorter/version_sorter.c b/ext/version_sorter/version_sorter.c index 02a2375..7de3e75 100644 --- a/ext/version_sorter/version_sorter.c +++ b/ext/version_sorter/version_sorter.c @@ -153,10 +153,7 @@ parse_version_number(const char *string) if (string[offset] == '-' || isalpha(string[offset])) { uint16_t start = offset; - if (string[offset] == '-') - offset++; - - while (isalpha(string[offset])) + while (string[offset] == '-' || isalpha(string[offset])) offset++; version->comp[comp_n].string.offset = start; From 3c8b797e7ebafe698d03f82c9026907d95ee98ed Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Sat, 16 Oct 2021 08:52:23 -0500 Subject: [PATCH 12/14] Reduce size comparisons from 2 to 1 --- ext/version_sorter/version_sorter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/version_sorter/version_sorter.c b/ext/version_sorter/version_sorter.c index 7de3e75..4bdac33 100644 --- a/ext/version_sorter/version_sorter.c +++ b/ext/version_sorter/version_sorter.c @@ -71,7 +71,7 @@ compare_version_number(const struct version_number *a, } } - if (a->size == 0 || b->size == 0) { + if (max_n == 0) { return strcmp(a->original, b->original); } From d63ec1729f7178efdeabd3333ee497bd0e518ec1 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Mon, 18 Oct 2021 13:01:30 -0500 Subject: [PATCH 13/14] Revert stylistic changes to test code --- test/version_sorter_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/version_sorter_test.rb b/test/version_sorter_test.rb index d7c7eff..978df1c 100644 --- a/test/version_sorter_test.rb +++ b/test/version_sorter_test.rb @@ -13,7 +13,7 @@ def setup end def test_sorts_versions_correctly - versions = %w( 1.0.9 1.0.10 2.0 3.1.4.2 1.0.9a ) + versions = %w(1.0.9 1.0.10 2.0 3.1.4.2 1.0.9a) sorted_versions = %w( 1.0.9a 1.0.9 1.0.10 2.0 3.1.4.2 ) assert_equal sorted_versions, VersionSorter.sort(versions) @@ -64,7 +64,7 @@ def test_does_not_raise_on_number_overflow (2**32 + 1).to_s, (2**32 + 2).to_s, (2**32 - 2).to_s, - (2**32 - 1).to_s + (2**32 - 1).to_s, ] randomized = shuffle big_numbers From 489a1a1ffea2c180652d4f119eb669a86298f35e Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 19 Oct 2021 13:24:43 -0500 Subject: [PATCH 14/14] Add comments explaining test intent --- test/version_sorter_test.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/version_sorter_test.rb b/test/version_sorter_test.rb index 978df1c..c033174 100644 --- a/test/version_sorter_test.rb +++ b/test/version_sorter_test.rb @@ -96,6 +96,7 @@ def test_sorts_non_version_data_with_trailing_numbers assert_equal sorted, VersionSorter.sort(non_versions) end + # This verifies the sort order of a subset of `test/tags.txt` def test_yui_style_tags yui_tags = [ "yui3-571", "yui3-309", "yui3-1405", "yui3-1537", "yui3-440", @@ -109,6 +110,7 @@ def test_yui_style_tags assert_equal sorted, VersionSorter.sort(yui_tags) end + # This verifies the sort order of the example in `README.md` def test_readme_examples readme_versions = ["1.0.9", "2.0", "1.0.10", "1.0.3", "2.0.pre"] sorted = ["1.0.3", "1.0.9", "1.0.10", "2.0.pre", "2.0"]