winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

Versions.cpp (16363B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "TestCommon.h"
      5 #include <AppInstallerRuntime.h>
      6 #include <AppInstallerVersions.h>
      7 
      8 using namespace AppInstaller;
      9 using namespace AppInstaller::Utility;
     10 
     11 
     12 TEST_CASE("VersionParse", "[versions]")
     13 {
     14     Version version("1.2.3.4-alpha");
     15     const auto& parts = version.GetParts();
     16     REQUIRE(parts.size() == 4);
     17     for (size_t i = 0; i < parts.size(); ++i)
     18     {
     19         INFO(i);
     20         REQUIRE(parts[i].Integer == static_cast<uint64_t>(i + 1));
     21         if (i != 3)
     22         {
     23             REQUIRE(parts[i].Other == "");
     24         }
     25         else
     26         {
     27             REQUIRE(parts[i].Other == "-alpha");
     28         }
     29     }
     30 }
     31 
     32 TEST_CASE("VersionParsePlusDash", "[versions]")
     33 {
     34     Version version("1.2.3.4-alpha", ".-");
     35     const auto& parts = version.GetParts();
     36     REQUIRE(parts.size() == 5);
     37     for (size_t i = 0; i < 4; ++i)
     38     {
     39         INFO(i);
     40         REQUIRE(parts[i].Integer == static_cast<uint64_t>(i + 1));
     41         REQUIRE(parts[i].Other == "");
     42     }
     43     REQUIRE(parts[4].Other == "alpha");
     44 }
     45 
     46 TEST_CASE("VersionParseWithWhitespace", "[versions]")
     47 {
     48     Version version("1. 2.3 . 4 ");
     49     const auto& parts = version.GetParts();
     50     REQUIRE(parts.size() == 4);
     51     for (size_t i = 0; i < parts.size(); ++i)
     52     {
     53         INFO(i);
     54         REQUIRE(parts[i].Integer == static_cast<uint64_t>(i + 1));
     55         REQUIRE(parts[i].Other == "");
     56     }
     57 }
     58 
     59 TEST_CASE("VersionParseWithPreamble", "[versions]")
     60 {
     61     Version version("v1.2.3.4");
     62     const auto& parts = version.GetParts();
     63     REQUIRE(parts.size() == 4);
     64     for (size_t i = 0; i < parts.size(); ++i)
     65     {
     66         INFO(i);
     67         REQUIRE(parts[i].Integer == static_cast<uint64_t>(i + 1));
     68         REQUIRE(parts[i].Other == "");
     69     }
     70 }
     71 
     72 TEST_CASE("VersionParseCorner", "[versions]")
     73 {
     74     Version version1("");
     75     auto parts = version1.GetParts();
     76     REQUIRE(parts.size() == 0);
     77 
     78     Version version2(".");
     79     parts = version2.GetParts();
     80     REQUIRE(parts.size() == 0);
     81 
     82     Version version3(".0");
     83     parts = version3.GetParts();
     84     REQUIRE(parts.size() == 0);
     85 
     86     Version version4(".1");
     87     parts = version4.GetParts();
     88     REQUIRE(parts.size() == 2);
     89     REQUIRE(parts[0].Integer == 0);
     90     REQUIRE(parts[0].Other == "");
     91     REQUIRE(parts[1].Integer == 1);
     92     REQUIRE(parts[1].Other == "");
     93 
     94     Version version5("version");
     95     parts = version5.GetParts();
     96     REQUIRE(parts.size() == 1);
     97     REQUIRE(parts[0].Integer == 0);
     98     REQUIRE(parts[0].Other == "version");
     99 
    100     Version version6(". 1 ");
    101     parts = version6.GetParts();
    102     REQUIRE(parts.size() == 2);
    103     REQUIRE(parts[0].Integer == 0);
    104     REQUIRE(parts[0].Other == "");
    105     REQUIRE(parts[1].Integer == 1);
    106     REQUIRE(parts[1].Other == "");
    107 
    108     Version version7("v1.2a");
    109     parts = version7.GetParts();
    110     REQUIRE(parts.size() == 2);
    111     REQUIRE(parts[0].Integer == 1);
    112     REQUIRE(parts[0].Other == "");
    113     REQUIRE(parts[1].Integer == 2);
    114     REQUIRE(parts[1].Other == "a");
    115 }
    116 
    117 void RequireLessThan(std::string_view a, std::string_view b)
    118 {
    119     Version vA{ std::string(a) };
    120     Version vB{ std::string(b) };
    121 
    122     REQUIRE(vA < vB);
    123     REQUIRE_FALSE(vB < vA);
    124     REQUIRE(vA <= vB);
    125     REQUIRE_FALSE(vB <= vA);
    126     REQUIRE(vB > vA);
    127     REQUIRE_FALSE(vA > vB);
    128     REQUIRE(vB >= vA);
    129     REQUIRE_FALSE(vA >= vB);
    130     REQUIRE_FALSE(vA == vB);
    131     REQUIRE(vA != vB);
    132 }
    133 
    134 void RequireEqual(std::string_view a, std::string_view b)
    135 {
    136     Version vA{ std::string(a) };
    137     Version vB{ std::string(b) };
    138 
    139     REQUIRE(vA == vB);
    140     REQUIRE_FALSE(vA != vB);
    141     REQUIRE(vA <= vB);
    142     REQUIRE(vA >= vB);
    143     REQUIRE_FALSE(vA < vB);
    144     REQUIRE_FALSE(vA > vB);
    145 }
    146 
    147 TEST_CASE("VersionCompare", "[versions]")
    148 {
    149     RequireLessThan("1", "2");
    150     RequireLessThan("1.0.0", "2.0.0");
    151     RequireLessThan("0.0.1", "0.0.2");
    152     RequireLessThan("0.0.1-alpha", "0.0.2-alpha");
    153     RequireLessThan("0.0.1-beta", "0.0.2-alpha");
    154     RequireLessThan("0.0.1-beta", "0.0.2-alpha");
    155     RequireLessThan("13.9.8", "14.1");
    156 
    157     // Ensure that versions with non-digit characters in their parts are sorted correctly
    158     RequireLessThan("1-rc", "1");
    159     RequireLessThan("1.2-rc", "1.2");
    160     RequireLessThan("1.0-rc", "1.0");
    161     RequireLessThan("1.0.0-rc", "1");
    162     RequireLessThan("22.0.0-rc.1", "22.0.0");
    163     RequireLessThan("22.0.0-rc.1", "22.0.0.1");
    164     RequireLessThan("22.0.0-rc.1", "22.0.0.1-rc");
    165 
    166     // Ensure that Sub-RC versions are sorted correctly
    167     RequireLessThan("22.0.0-rc.1", "22.0.0-rc.1.1");
    168     RequireLessThan("22.0.0-rc.1.1", "22.0.0-rc.1.2");
    169     RequireLessThan("22.0.0-rc.1.2", "22.0.0-rc.2");
    170 
    171     RequireEqual("1.0", "1.0.0");
    172 
    173     // Ensure that integers are parsed correctly when there is a leading zero
    174     RequireEqual("1.2.00.3", "1.2.0.3");
    175     RequireEqual("1.2.003.4", "1.2.3.4");
    176     RequireEqual("01.02.03.04", "1.2.3.4");
    177     RequireEqual("1.2.03-beta", "1.2.3-beta");
    178 
    179     // Ensure whitespace doesn't affect equality
    180     RequireEqual("1.0", "1.0 ");
    181     RequireEqual("1.0", "1. 0");
    182     RequireEqual("1.0", "1.0.");
    183 
    184     // Ensure versions with preambles are sorted correctly
    185     RequireEqual("1.0", "Version 1.0");
    186     RequireEqual("foo1", "bar1");
    187     RequireLessThan("v0.0.1", "0.0.2");
    188     RequireLessThan("v0.0.1", "v0.0.2");
    189     RequireLessThan("1.a2", "1.b1");
    190     RequireLessThan("alpha", "beta");
    191 }
    192 
    193 TEST_CASE("VersionAndChannelSort", "[versions]")
    194 {
    195     std::vector<VersionAndChannel> sortedList =
    196     {
    197         { Version("15.0.0"), Channel("") },
    198         { Version("14.0.0"), Channel("") },
    199         { Version("13.2.1-bugfix"), Channel("") },
    200         { Version("13.2.0"), Channel("") },
    201         { Version("13.2.0-rc"), Channel("") },
    202         { Version("13.0.0"), Channel("") },
    203         { Version("16.0.0"), Channel("alpha") },
    204         { Version("15.8.0"), Channel("alpha") },
    205         { Version("15.1.0"), Channel("beta") },
    206     };
    207 
    208     std::vector<size_t> reorderList = { 4, 2, 1, 7, 6, 3, 8, 5, 0 };
    209     REQUIRE(sortedList.size() == reorderList.size());
    210 
    211     std::vector<VersionAndChannel> jumbledList;
    212     for (auto i : reorderList)
    213     {
    214         jumbledList.emplace_back(sortedList[i]);
    215     }
    216 
    217     std::sort(jumbledList.begin(), jumbledList.end());
    218 
    219     for (size_t i = 0; i < jumbledList.size(); ++i)
    220     {
    221         const VersionAndChannel& sortedVAC = sortedList[i];
    222         const VersionAndChannel& jumbledVAC = jumbledList[i];
    223 
    224         INFO(i);
    225         REQUIRE(sortedVAC.GetVersion().ToString() == jumbledVAC.GetVersion().ToString());
    226         REQUIRE(sortedVAC.GetChannel().ToString() == jumbledVAC.GetChannel().ToString());
    227     }
    228 }
    229 
    230 TEST_CASE("MinOsVersion_Check", "[versions]")
    231 {
    232     // Just verify that we are greater than Win 7 and less than far future Win 10.
    233     // Unfortunately, an unmanifested process will also pass these validations,
    234     // but an unmanifested process also can't use Windows APIs to determine the actual version.
    235     REQUIRE(Runtime::IsCurrentOSVersionGreaterThanOrEqual(Version("6.1")));
    236     REQUIRE(!Runtime::IsCurrentOSVersionGreaterThanOrEqual(Version("10.0.65535")));
    237 }
    238 
    239 TEST_CASE("VersionLatest", "[versions]")
    240 {
    241     REQUIRE(Version::CreateLatest().IsLatest());
    242     REQUIRE(Version("latest").IsLatest());
    243     REQUIRE(Version("LATEST").IsLatest());
    244     REQUIRE(!Version("1.0").IsLatest());
    245 
    246     RequireLessThan("1.0", "latest");
    247     RequireLessThan("100", "latest");
    248     RequireLessThan("943849587389754876.1", "latest");
    249 
    250     RequireEqual("latest", "LATEST");
    251 }
    252 
    253 TEST_CASE("VersionUnknown", "[versions]")
    254 {
    255     REQUIRE(Version::CreateUnknown().IsUnknown());
    256     REQUIRE(Version("unknown").IsUnknown());
    257     REQUIRE(Version("UNKNOWN").IsUnknown());
    258     REQUIRE(!Version("1.0").IsUnknown());
    259 
    260     RequireLessThan("unknown", "1.0");
    261     RequireLessThan("unknown", "1.fork");
    262 
    263     RequireEqual("unknown", "UNKNOWN");
    264 }
    265 
    266 TEST_CASE("VersionUnknownLessThanLatest", "[versions]")
    267 {
    268     REQUIRE(Version::CreateUnknown() < Version::CreateLatest());
    269 }
    270 
    271 TEST_CASE("VersionIsEmpty", "[versions]")
    272 {
    273     REQUIRE(Version{}.IsEmpty());
    274     REQUIRE(Version{""}.IsEmpty());
    275     REQUIRE(!Version{"1"}.IsEmpty());
    276     REQUIRE(!Version{"0"}.IsEmpty());
    277 
    278     Version v{ "1" };
    279     REQUIRE(!v.IsEmpty());
    280     v.Assign("");
    281     REQUIRE(v.IsEmpty());
    282 }
    283 
    284 TEST_CASE("VersionPartAt", "[versions]")
    285 {
    286     REQUIRE(Version{}.PartAt(0).Integer == 0);
    287     REQUIRE(Version{"1"}.PartAt(0).Integer == 1);
    288     REQUIRE(Version{"1"}.PartAt(1).Integer == 0);
    289     REQUIRE(Version{"1"}.PartAt(9999).Integer == 0);
    290 }
    291 
    292 TEST_CASE("UInt64Version_Success_FourParts", "[versions]")
    293 {
    294     Version expectedVersion("1.2.3.4");
    295     UInt64Version versionNumberFromNumber(0x0001000200030004);
    296     UInt64Version versionNumberFromString("1.2.3.4");
    297     REQUIRE(expectedVersion == versionNumberFromNumber);
    298     REQUIRE(expectedVersion == versionNumberFromString);
    299     REQUIRE(expectedVersion.ToString() == versionNumberFromNumber.ToString());
    300     REQUIRE(expectedVersion.ToString() == versionNumberFromString.ToString());
    301 }
    302 
    303 TEST_CASE("UInt64Version_Success_LessThanFourParts", "[versions]")
    304 {
    305     UInt64Version versionNumberFromNumber(0x0001000200030000);
    306     UInt64Version versionNumberFromString("1.2.3");
    307     REQUIRE(versionNumberFromNumber == versionNumberFromString);
    308 }
    309 
    310 TEST_CASE("UInt64Version_Success_NoOverflow", "[versions]")
    311 {
    312     REQUIRE_NOTHROW(UInt64Version("65535.65535.65535.65535")); // 65535 => 0xffff
    313     REQUIRE_NOTHROW(UInt64Version(0xffffffffffffffff));
    314 }
    315 
    316 TEST_CASE("UInt64Version_Fail_Overflow", "[versions]")
    317 {
    318     REQUIRE_THROWS(UInt64Version("1.0.0.65536")); // 65536 => 0x10000
    319 }
    320 
    321 TEST_CASE("UInt64Version_Fail_MoreThanFourParts", "[versions]")
    322 {
    323     REQUIRE_THROWS(UInt64Version("1.0.0.0.1"));
    324 }
    325 
    326 TEST_CASE("UInt64Version_Fail_NonNumeric", "[versions]")
    327 {
    328     REQUIRE_THROWS(UInt64Version("1.0.0.a"));
    329 }
    330 
    331 TEST_CASE("ApproximateVersionParse", "[versions]")
    332 {
    333     Version v1_0{ "1.0" };
    334     Version v1_0_LessThan{ v1_0, Version::ApproximateComparator::LessThan };
    335     Version v1_0_GreaterThan{ v1_0, Version::ApproximateComparator::GreaterThan };
    336 
    337     Version v1_0_LessThanFromString = Version{ "< 1.0" };
    338     Version v1_0_GreaterThanFromString = Version{ "> 1.0" };
    339 
    340     REQUIRE_FALSE(v1_0.IsApproximate());
    341     REQUIRE(v1_0_LessThanFromString.IsApproximate());
    342     REQUIRE(v1_0_GreaterThanFromString.IsApproximate());
    343 
    344     REQUIRE(v1_0_LessThan == v1_0_LessThanFromString);
    345     REQUIRE(v1_0_GreaterThan == v1_0_GreaterThanFromString);
    346 
    347     REQUIRE_THROWS(Version{ "< Unknown" });
    348     REQUIRE_THROWS(Version{ v1_0_LessThan, Version::ApproximateComparator::LessThan });
    349     REQUIRE_THROWS(Version{ Version::CreateUnknown(), Version::ApproximateComparator::LessThan });
    350 }
    351 
    352 TEST_CASE("ApproximateVersionCompare", "[versions]")
    353 {
    354     RequireEqual("< 1.0", "< 1.0");
    355     RequireEqual("< 1.0", "< 1.0.0");
    356     RequireEqual("> 1.0", "> 1.0");
    357     RequireEqual("> 1.0", "> 1.0.0");
    358 
    359     RequireLessThan("< 1.0", "1.0");
    360     RequireLessThan("< 1.0", "> 1.0");
    361     RequireLessThan("1.0", "> 1.0");
    362     RequireLessThan("0.9", "< 1.0");
    363     RequireLessThan("> 1.0", "1.1");
    364 
    365     // With latest
    366     RequireLessThan("< latest", "latest");
    367     RequireLessThan("latest", "> latest");
    368     RequireLessThan("9999", "< latest");
    369 }
    370 
    371 TEST_CASE("VersionRange", "[versions]")
    372 {
    373     // Create
    374     REQUIRE_NOTHROW(VersionRange{ Version{ "1.0" }, Version{ "2.0" } });
    375     REQUIRE_NOTHROW(VersionRange{ Version{ "1.0" }, Version{ "1.0" } });
    376     REQUIRE_NOTHROW(VersionRange{ Version{ "2.0" }, Version{ "1.0" } });
    377 
    378     // Overlaps
    379     REQUIRE(VersionRange{ Version{ "1.0" }, Version{ "2.0" } }.Overlaps(VersionRange{ Version{ "2.0" }, Version{ "3.0" } }));
    380     REQUIRE(VersionRange{ Version{ "1.0" }, Version{ "2.0" } }.Overlaps(VersionRange{ Version{ "1.0" }, Version{ "1.0" } }));
    381     REQUIRE(VersionRange{ Version{ "1.0" }, Version{ "2.0" } }.Overlaps(VersionRange{ Version{ "0.5" }, Version{ "1.5" } }));
    382     REQUIRE_FALSE(VersionRange{ Version{ "1.0" }, Version{ "2.0" } }.Overlaps(VersionRange{ Version{ "2.1" }, Version{ "3.0" } }));
    383     REQUIRE_FALSE(VersionRange{ Version{ "1.0" }, Version{ "2.0" } }.Overlaps(VersionRange{}));
    384 
    385     // Empty
    386     REQUIRE(VersionRange{}.IsEmpty());
    387     REQUIRE_THROWS(VersionRange{}.GetMinVersion());
    388     REQUIRE_THROWS(VersionRange{}.GetMaxVersion());
    389 
    390     // Less than compare
    391     REQUIRE_THROWS(VersionRange{ Version{ "0.5" }, Version{ "1.0" } } < VersionRange{ Version{ "1.0" }, Version{ "2.0" } });
    392     REQUIRE_THROWS(VersionRange{} < VersionRange{ Version{ "1.0" }, Version{ "2.0" } });
    393     REQUIRE(VersionRange{ Version{ "0.5" }, Version{ "1.0" } } < VersionRange{ Version{ "1.5" }, Version{ "2.0" } });
    394     REQUIRE_FALSE(VersionRange{ Version{ "1.5" }, Version{ "2.0" } } < VersionRange{ Version{ "0.5" }, Version{ "1.0" } });
    395 }
    396 
    397 TEST_CASE("GatedVersion", "[versions]")
    398 {
    399     REQUIRE(GatedVersion("1.0.*"sv).IsValidVersion({ "1.0.1" }));
    400     REQUIRE(GatedVersion("1.0.*"sv).IsValidVersion({ "1.0" }));
    401     REQUIRE(GatedVersion("1.0.*"sv).IsValidVersion({ "1" }));
    402     REQUIRE(GatedVersion("1.0.*"sv).IsValidVersion({ "1.0.alpha" }));
    403     REQUIRE(GatedVersion("1.0.*"sv).IsValidVersion({ "1.0.1.2.3" }));
    404     REQUIRE(GatedVersion("1.0.*"sv).IsValidVersion({ "1.0.*" }));
    405     REQUIRE_FALSE(GatedVersion("1.0.*"sv).IsValidVersion({ "1.1.1" }));
    406 
    407     REQUIRE(GatedVersion("1.*.*"sv).IsValidVersion({ "1.*.1" }));
    408     REQUIRE(GatedVersion("1.*.*"sv).IsValidVersion({ "1.*.*" }));
    409     REQUIRE_FALSE(GatedVersion("1.*.*"sv).IsValidVersion({ "1.1.1" }));
    410 
    411     REQUIRE(GatedVersion("1.0.1"sv).IsValidVersion({ "1.0.1" }));
    412     REQUIRE_FALSE(GatedVersion("1.0.1"sv).IsValidVersion({ "1.1.1" }));
    413 }
    414 
    415 TEST_CASE("SemanticVersion", "[versions]")
    416 {
    417     REQUIRE_THROWS_HR(SemanticVersion("1.2.3.4"), E_INVALIDARG);
    418     REQUIRE_THROWS_HR(SemanticVersion("1.2abc.3"), E_INVALIDARG);
    419 
    420     SemanticVersion version = SemanticVersion("1.2.3-alpha");
    421     REQUIRE(version.IsPrerelease());
    422     REQUIRE(version.PrereleaseVersion() == Version("alpha"));
    423     REQUIRE(!version.HasBuildMetadata());
    424     REQUIRE(version.PartAt(2).Other == "-alpha");
    425 
    426     version = SemanticVersion("1.2.3-4.5.6");
    427     REQUIRE(version.IsPrerelease());
    428     REQUIRE(version.PrereleaseVersion() == Version("4.5.6"));
    429     REQUIRE(!version.HasBuildMetadata());
    430     REQUIRE(version.PartAt(2).Other == "-4.5.6");
    431 
    432     // Really shouldn't be allowed, but we are loose here
    433     version = SemanticVersion("1.2+build");
    434     REQUIRE(!version.IsPrerelease());
    435     REQUIRE(version.HasBuildMetadata());
    436     REQUIRE(version.BuildMetadata() == Version("build"));
    437     REQUIRE(version.PartAt(2).Other == "+build");
    438 
    439     version = SemanticVersion("1.2.3-beta+4.5.6");
    440     REQUIRE(version.IsPrerelease());
    441     REQUIRE(version.PrereleaseVersion() == Version("beta"));
    442     REQUIRE(version.HasBuildMetadata());
    443     REQUIRE(version.BuildMetadata() == Version("4.5.6"));
    444     REQUIRE(version.PartAt(2).Other == "-beta+4.5.6");
    445 }
    446 
    447 TEST_CASE("OpenTypeFontVersion", "[versions]")
    448 {
    449     // Valid font version.
    450     OpenTypeFontVersion version = OpenTypeFontVersion("Version 1.234");
    451     REQUIRE(version.ToString() == "1.234");
    452     REQUIRE(version.GetParts().size() == 2);
    453     REQUIRE(version.PartAt(0).Integer == 1);
    454     REQUIRE(version.PartAt(1).Integer == 234);
    455 
    456     // Font version with additional metadata.
    457     version = OpenTypeFontVersion("Version 9.876.54 ;2024");
    458     REQUIRE(version.ToString() == "9.876");
    459     REQUIRE(version.GetParts().size() == 2);
    460     REQUIRE(version.PartAt(0).Integer == 9);
    461     REQUIRE(version.PartAt(1).Integer == 876);
    462 
    463     // Invalid version. Font version must have at least 2 parts.
    464     REQUIRE_NOTHROW(version = OpenTypeFontVersion("1234567"));
    465     REQUIRE(version.IsUnknown());
    466     REQUIRE(version.ToString() == "Unknown");
    467 
    468     // Major and minor parts must have digits.
    469     REQUIRE_NOTHROW(version = OpenTypeFontVersion(" abc.def "));
    470     REQUIRE(version.IsUnknown());
    471     REQUIRE(version.ToString() == "Unknown");
    472 }