using DodoSSH.Client.App.Platform;
namespace DodoSSH.Client.App.Tests;
///
/// RpmVersion.Compare against the answers rpm itself gives.
///
///
///
/// Every expected value here was produced by rpm and not by reading the algorithm. They came out of
/// rpm --eval "%{lua:print(rpm.vercmp(a, b))}" in a Fedora-era container, which is the same
/// comparison PackageKit will make when it decides whether to accept the update this class caused to be
/// offered. A test written from the specification would agree with a misreading of the specification.
///
///
/// The awkward cases are the point of the list. 1a equals 1.a because separators are skipped
/// on each side independently; 1.01 equals 1.1 because leading zeros are stripped;
/// 1.0_1 equals 1.0.1 for the first reason again; and a is older than 1 because
/// rpm calls a numeric segment newer than an alphabetic one whatever the letters say.
///
///
/// The four rows that carry the feature are the ~ ones. 0.0.1~alpha.0.143 < 0.0.1 is what
/// makes every nightly older than the first real release, and ~alpha.0.9 < ~alpha.0.10 is what
/// keeps them in order among themselves — a string comparison would get that backwards and a client would
/// stop offering updates after the ninth nightly of a version.
///
///
public sealed class RpmVersionTests
{
[Theory]
// Ordinary
[InlineData("1.2.3", "1.2.3", 0)]
[InlineData("1.2.3", "1.2.4", -1)]
[InlineData("1.2.3", "1.2.3.1", -1)]
[InlineData("1.2.10", "1.2.9", 1)]
[InlineData("2.0", "10.0", -1)]
[InlineData("1.0.0", "1.0", 1)]
[InlineData("1.0.0.1", "1.0.0", 1)]
[InlineData("20101121", "20101122", -1)]
// Tilde: the prerelease ordering this feature depends on
[InlineData("0.0.1~alpha.0.143", "0.0.1", -1)]
[InlineData("0.0.1~alpha.0.143", "0.0.1~alpha.0.144", -1)]
[InlineData("0.0.1~alpha.0.143", "0.0.1~alpha.0.99", 1)]
[InlineData("0.0.1~alpha.0.9", "0.0.1~alpha.0.10", -1)]
[InlineData("1.0", "1.0~rc1", 1)]
[InlineData("1~~", "1~", -1)]
[InlineData("1.0~beta", "1.0~alpha", 1)]
// Caret: after everything except the end of the string
[InlineData("1.0^post", "1.0", 1)]
[InlineData("1.0^post", "1.0^post2", -1)]
[InlineData("1.2.3", "1.2.3^", -1)]
// Separators and leading zeros, which are the surprising ones
[InlineData("1a", "1.a", 0)]
[InlineData("1.01", "1.1", 0)]
[InlineData("1.0_1", "1.0.1", 0)]
[InlineData("1.0-1", "1.0-2", -1)]
// Segments of different kinds
[InlineData("1.a", "1.b", -1)]
[InlineData("a", "1", -1)]
[InlineData("1", "a", 1)]
[InlineData("5.5p1", "5.5p2", -1)]
[InlineData("xyz10", "xyz10.1", -1)]
[InlineData("MDK", "MDK1", -1)]
public void ItAgreesWithRpm(string left, string right, int expected) =>
RpmVersion.Compare(left, right).ShouldBe(expected);
///
/// The reverse of every case above, which rpm does not need asserting and this does.
///
///
/// The marker handling is the half of the algorithm with a left and a right branch for each case, so an
/// asymmetry there would pass every row above and reverse the answer for a client whose installed
/// version happened to be the newer one — which is the direction that offers a downgrade.
///
[Theory]
[InlineData("0.0.1~alpha.0.143", "0.0.1")]
[InlineData("1.0^post", "1.0")]
[InlineData("1.2.3", "1.2.3^")]
[InlineData("a", "1")]
[InlineData("1.2.3", "1.2.4")]
public void ItIsAntisymmetric(string left, string right) =>
RpmVersion.Compare(right, left).ShouldBe(-RpmVersion.Compare(left, right));
///
/// Nothing and an empty string are older than anything, and equal to each other.
///
///
/// rpm's own binding refuses these outright. Here they arrive off a feed rather than out of a spec
/// file, and the answer that leaves a client on the build it already has is better than an exception
/// thrown on a six-hourly timer.
///
[Theory]
[InlineData(null, "1.0", -1)]
[InlineData("", "1.0", -1)]
[InlineData("1.0", null, 1)]
[InlineData(null, null, 0)]
[InlineData("", null, 0)]
public void ItTreatsNothingAsOldest(string? left, string? right, int expected) =>
RpmVersion.Compare(left, right).ShouldBe(expected);
}