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 }