Skip to content

Instantly share code, notes, and snippets.

@camilajenny
Created June 20, 2025 14:20
Show Gist options
  • Save camilajenny/2059145ad34e4e5c3adbff47ccca7657 to your computer and use it in GitHub Desktop.
Save camilajenny/2059145ad34e4e5c3adbff47ccca7657 to your computer and use it in GitHub Desktop.
test program for comparing debian versions in the project Eddy
void main () {
// test("31.2.3", "31.2.10");
// test("31.2.3", "31.2");
// test_greater_method("31.2.10", "31.2.3", compare_versions_contributor);
// test_greater_method("31.2.3", "31.2", compare_versions_contributor);
test_greater_method("1.2.3", "1.2.2", compare_versions_improved);
test_greater_method("1.2.10", "1.2.2", compare_versions_improved);
test_greater_method("1.2.3-2", "1.2.3-1", compare_versions_improved);
test_greater_method("2.4.5", "2.4", compare_versions_improved);
test_greater_method("1:1.0", "0:2.0", compare_versions_improved); // Epoch wins
test_greater_method("10:2.0", "3:2.0", compare_versions_improved); // Epoch wins
test_greater_method("1:1.2.3", "1.2.3", compare_versions_improved); // Epoch vs no epoch
test_greater_method("2.0", "2.0~beta1", compare_versions_improved); // ~ sorts lower
test_greater_method("2.1~beta", "2.0", compare_versions_improved); // but upstream version wins
test_greater_method("1.0+git20240501", "1.0", compare_versions_improved);
test_greater_method("1.0.0-1ubuntu2", "1.0.0-1ubuntu1", compare_versions_improved);
test_greater_method("1.0.0-1ubuntu2", "1.0.0-1", compare_versions_improved); // ubuntu suffix is still >=
test_greater_method("2.0~rc2", "2.0~rc1", compare_versions_improved);
test_greater_method("3.4.1", "3.4.1~rc1", compare_versions_improved);
test_greater_method("1.0.0+build2", "1.0.0+build1", compare_versions_improved);
test_greater_method("1.0.0", "1.0.0~", compare_versions_improved); // trailing tilde means it's earlier
test_greater_method("1.0.0-1", "1.0.0~rc1-1", compare_versions_improved);
test_greater_method("1.0.0-10", "1.0.0-3", compare_versions_improved); // debian revision higher
test_greater_method("1.0.0-0ubuntu2", "1.0.0-0ubuntu1", compare_versions_improved);
test_greater_method("1.0.0-0ubuntu1", "1.0.0-0", compare_versions_improved); // ubuntu suffix greater than none
test_greater_method("1.2.3-4", "1.2.3-4~beta1", compare_versions_improved);
test_greater_method("1.0.0-0ubuntu2build2", "1.0.0-0ubuntu2build1", compare_versions_improved);
println("");
test_equal_method("0:2.4.1", "2.4.1", compare_versions_improved); // Explicit vs implicit epoch
test_equal_method("1.2.3", "1.2.3", compare_versions_improved);
test_equal_method("1.0.0-1", "1.0.0-1", compare_versions_improved);
test_equal_method("1.2.3+git", "1.2.3+git", compare_versions_improved);
}
delegate int Compare(string a, string b);
int compare_versions_improved (string v1, string v2) {
// https://www.debian.org/doc/debian-policy/ch-controlfields.html#version
string a = v1;
string b = v2;
// check for empty strings - later we split the strings and it would break on an empty array
if (a == "")
return -1;
if (b == "")
return 1;
// check epochs
string[] atok = a.split(":");
string[] btok = b.split(":");
int a_epoch, b_epoch;
if (atok.length >= 2 ) {
bool a_is_epoch = int.try_parse(atok[0], out a_epoch);
if(!a_is_epoch)
a_epoch = 0;
} else {
a_epoch = 0;
}
if (btok.length >= 2) {
bool b_is_epoch = int.try_parse(btok[0], out b_epoch);
if(!b_is_epoch)
b_epoch = 0;
} else {
b_epoch = 0;
}
// print(@"epochs: $a_epoch, $b_epoch ");
if (a_epoch != b_epoch)
return a_epoch - b_epoch;
// trim epochs when the same (including assumed 0 when none)
a = join_sliced_from_1(atok);
b = join_sliced_from_1(btok);
//check upstream version
Regex regex = new Regex("[+~-]");
atok = regex.split(a);
btok = regex.split(b);
string[] aparts = atok[0].split (".");
string[] bparts = btok[0].split (".");
// int at = atok.length, bt = btok.length;
// print(@"upstream $a, $b ");
int length = int.min (aparts.length, bparts.length);
// print(@"$length ");
for (int i = 0; i < length; i++) {
int a_num, b_num;
bool a_is_num = int.try_parse(aparts[i], out a_num);
bool b_is_num = int.try_parse(bparts[i], out b_num);
if (a_is_num && b_is_num) {
int diff = a_num - b_num;
if (diff != 0) {
return diff;
}
} else {
int rc = strcmp (aparts[i], bparts[i]);
if (rc != 0) {
return rc;
}
}
if (i == length - 1) {
if(aparts.length != bparts.length) {
return aparts.length - bparts.length;
}
}
}
// print(@"tilde $a, $b ");
// a tilde sorts before anything, even the end of a part
atok = a.split ("~");
btok = b.split ("~");
if (atok.length < 2 && btok.length >= 2) {
return 1;
} else if (atok.length >= 2 && btok.length < 2) {
return -1;
}
// check debian revision if present
atok = a.split("-");
btok = b.split("-");
int a_dr, b_dr;
bool a_dr_is_num = int.try_parse(atok[atok.length - 1], out a_dr);
bool b_dr_is_num = int.try_parse(btok[btok.length - 1], out b_dr);
if (a_dr_is_num && b_dr_is_num) {
if (a_dr != b_dr)
return a_dr - b_dr;
}
return strcmp(a, b);
}
string join_sliced_from_1(string[] arr) {
if (arr.length == 0)
return "";
if (arr.length == 1)
return arr[0];
string result = "";
for (int i = 1; i < arr.length; i++) {
result += arr[i];
}
return result;
}
int compare_versions_original (string a, string b) {
if (a == b) {
return 0;
}
string[] atok = a.split ("~");
string[] btok = b.split ("~");
if (atok.length < 2 && btok.length >= 2) {
return -1;
} else if (atok.length >= 2 && btok.length < 2) {
return 1;
}
string aver = atok[0];
string bver = btok[0];
string[] aparts = aver.split (".");
string[] bparts = bver.split (".");
int length = int.max (aparts.length, bparts.length);
for (int i = 0; i < length; i++) {
int rc = strcmp (aparts[i], bparts[i]);
if (i == length - 1) {
if (bparts[i] > aparts[i]) {
return 1;
}
else if (bparts[i] < aparts[i]) {
return -1;
}
}
if (rc < 0) {
return -1;
} else if (rc > 0) {
return 1;
}
}
return 0;
}
int compare_versions_contributor (string a, string b) {
if (a == b) {
return 0;
}
string[] atok = a.split ("~");
string[] btok = b.split ("~");
if (atok.length < 2 && btok.length >= 2) {
return -1;
} else if (atok.length >= 2 && btok.length < 2) {
return 1;
}
string aver = atok[0];
string bver = btok[0];
string[] aparts = aver.split (".");
string[] bparts = bver.split (".");
int length = int.max (aparts.length, bparts.length);
// printlni(length);
for (int i = 0; i < length; i++) {
int a_num, b_num;
bool a_is_num = int.try_parse(aparts[i], out a_num);
bool b_is_num = int.try_parse(bparts[i], out b_num);
// print(a_is_num.to_string());
// print(b_is_num.to_string());
if (a_is_num && b_is_num) {
if (a_num < b_num) {
return -1;
} else if (a_num > b_num) {
return 1;
}
return 0;
} else {
return strcmp (aparts[i], bparts[i]);
}
}
return 0;
}
void println(string message) {
print("%s\n", message);
}
void printlni(int message) {
print("%d\n", message);
}
void test_greater_method(string left, string right, Compare compare) {
string result = test_greater(
compare(left, right)
);
println(@"$left, $right$SEPARATOR$result");
}
void test_equal_method(string left, string right, Compare compare) {
string result = test_equal(
compare(left, right)
);
println(@"$left, $right$SEPARATOR$result");
}
const string OK = "ok";
const string FAILED = "failed";
const string SEPARATOR = " ======> ";
string test_greater(int val) {
return val > 0 ? OK : FAILED;
}
string test_equal(int val) {
return val == 0 ? OK : FAILED;
}
// ------------------------------------------------------
void test(string left, string right) {
string result = test_equal_versions(
compare_versions_original(left, right),
compare_versions_contributor(left, right)
);
println(@"$left, $right: $result");
}
string test_equal_versions(int left, int right) {
return left == right ? OK : FAILED;
}
@camilajenny
Copy link
Author

Result:

1.2.3, 1.2.2    ======>   ok
1.2.10, 1.2.2    ======>   ok
1.2.3-2, 1.2.3-1    ======>   ok
2.4.5, 2.4    ======>   ok
1:1.0, 0:2.0    ======>   ok
10:2.0, 3:2.0    ======>   ok
1:1.2.3, 1.2.3    ======>   ok
2.0, 2.0~beta1    ======>   ok
2.1~beta, 2.0    ======>   ok
1.0+git20240501, 1.0    ======>   ok
1.0.0-1ubuntu2, 1.0.0-1ubuntu1    ======>   ok
1.0.0-1ubuntu2, 1.0.0-1    ======>   ok
2.0~rc2, 2.0~rc1    ======>   ok
3.4.1, 3.4.1~rc1    ======>   ok
1.0.0+build2, 1.0.0+build1    ======>   ok
1.0.0, 1.0.0~    ======>   ok
1.0.0-1, 1.0.0~rc1-1    ======>   ok
1.0.0-10, 1.0.0-3    ======>   ok
1.0.0-0ubuntu2, 1.0.0-0ubuntu1    ======>   ok
1.0.0-0ubuntu1, 1.0.0-0    ======>   ok
1.2.3-4, 1.2.3-4~beta1    ======>   ok
1.0.0-0ubuntu2build2, 1.0.0-0ubuntu2build1    ======>   ok

0:2.4.1, 2.4.1    ======>   ok
1.2.3, 1.2.3    ======>   ok
1.0.0-1, 1.0.0-1    ======>   ok
1.2.3+git, 1.2.3+git    ======>   ok

For testing of: donadigo/eddy#140

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment