@samitouri / QOSAMI-WSL / commits / 9b5659d4

Respect the distribution manifest ordering when listing distributions (#13561)

* Respect the distribution manifest ordering when listing distributions * Fix test * Update test/windows/UnitTests.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

Blue committed Oct 6, 2025 at 12:42 UTC 9b5659d4b9805084afcf2abb88796cf45dbf88b9
5 files changed +98 -10
CMakeLists.txt
+2 -2
@@ -34,8 +34,8 @@ FetchContent_GetProperties(GSL SOURCE_DIR GSL_SOURCE_DIR)
34
35
36 FetchContent_Declare(nlohmannjson
37 - URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz
38 - URL_HASH SHA256=d6c65aca6b1ed68e7a182f4757257b107ae403032760ed6ef121c9d55e81757d)
37 + URL https://github.com/nlohmann/json/releases/download/v3.12.0/json.tar.xz
38 + URL_HASH SHA256=42f6e95cad6ec532fd372391373363b62a14af6d771056dbfc86160e6dfff7aa )
39
40 FetchContent_MakeAvailable(nlohmannjson)
41 FetchContent_GetProperties(nlohmannjson SOURCE_DIR NLOHMAN_JSON_SOURCE_DIR)
src/shared/inc/JsonUtils.h
+5 -5
@@ -46,18 +46,18 @@ std::wstring ToJsonW(const T& Value)
46 return wsl::shared::string::MultiByteToWide(ToJson(Value));
47 }
48
49 -template <typename T>
49 +template <typename T, typename TJson = nlohmann::json>
50 T FromJson(const char* Value)
51 {
52 try
53 {
54 - auto json = nlohmann::json::parse(Value);
54 + auto json = TJson::parse(Value);
55 T object{};
56 from_json(json, object);
57
58 return object;
59 }
60 - catch (const nlohmann::json::exception& e)
60 + catch (const TJson::exception& e)
61 {
62
63 #ifdef WIN32
@@ -72,10 +72,10 @@ T FromJson(const char* Value)
72 }
73 }
74
75 -template <typename T>
75 +template <typename T, typename TJson = nlohmann::json>
76 T FromJson(const wchar_t* Value)
77 {
78 - return FromJson<T>(wsl::shared::string::WideToMultiByte(Value).c_str());
78 + return FromJson<T, TJson>(wsl::shared::string::WideToMultiByte(Value).c_str());
79 }
80
81 template <typename T>
src/windows/common/Distribution.cpp
+1 -1
@@ -112,7 +112,7 @@ DistributionList ReadFromManifest(const std::wstring& url)
112 content = response.Content().ReadAsStringAsync().get();
113 }
114
115 - auto distros = wsl::shared::FromJson<DistributionList>(content.c_str());
115 + auto distros = wsl::shared::FromJson<DistributionList, nlohmann::ordered_json>(content.c_str());
116
117 if (distros.Distributions.has_value())
118 {
src/windows/common/Distribution.h
+20 -2
@@ -58,10 +58,28 @@ struct Distribution
58 struct DistributionList
59 {
60 std::optional<std::vector<Distribution>> Distributions;
61 - std::optional<std::map<std::string, std::vector<ModernDistributionVersion>>> ModernDistributions;
61 + std::optional<nlohmann::ordered_map<std::string, std::vector<ModernDistributionVersion>>> ModernDistributions;
62 std::optional<std::wstring> Default;
63
64 - NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(DistributionList, Distributions, ModernDistributions, Default)
64 + friend void from_json(const nlohmann::ordered_json& nlohmann_json_j, DistributionList& nlohmann_json_t)
65 + {
66 + const DistributionList nlohmann_json_default_obj{};
67 + NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, Distributions, Default));
68 +
69 + auto modernDistributions = nlohmann_json_j.find("ModernDistributions");
70 + if (modernDistributions != nlohmann_json_j.end())
71 + {
72 + nlohmann_json_t.ModernDistributions.emplace();
73 +
74 + for (const auto& e : modernDistributions->items())
75 + {
76 + std::vector<ModernDistributionVersion> distros;
77 + from_json(e.value(), distros);
78 +
79 + nlohmann_json_t.ModernDistributions->emplace_back(e.key(), std::move(distros));
80 + }
81 + }
82 + }
83 };
84
85 constexpr inline auto c_distroUrlRegistryValue = L"DistributionListUrl";
test/windows/UnitTests.cpp
+70
@@ -5326,6 +5326,76 @@ Error code: Wsl/InstallDistro/WSL_E_INVALID_JSON\r\n",
5326
5327 VERIFY_ARE_EQUAL(error, L"");
5328 }
5329 +
5330 + // Validate that manifest distribution ordering is preserved.
5331 + {
5332 + auto validateOrder = [](const std::vector<LPCWSTR>& expected) {
5333 + auto [out, _] = LxsstuLaunchWslAndCaptureOutput(L"--list --online");
5334 +
5335 + auto lines = wsl::shared::string::Split<wchar_t>(out, '\n');
5336 +
5337 + for (size_t i = 0; i < expected.size(); i++)
5338 + {
5339 + auto end = lines[i + 4].find_first_of(L" \t");
5340 + VERIFY_ARE_NOT_EQUAL(end, std::wstring::npos);
5341 +
5342 + auto distro = lines[i + 4].substr(0, end);
5343 +
5344 + VERIFY_ARE_EQUAL(expected[i], distro);
5345 + }
5346 + };
5347 +
5348 + {
5349 + auto manifest =
5350 + R"({
5351 + "ModernDistributions": {
5352 + "distro1": [
5353 + {
5354 + "Name": "distro1",
5355 + "FriendlyName": "distro1Name",
5356 + "Amd64Url": {"Url": "","Sha256": ""}
5357 + }
5358 + ],
5359 + "distro2": [
5360 + {
5361 + "Name": "distro2",
5362 + "FriendlyName": "distro2Name",
5363 + "Amd64Url": {"Url": "","Sha256": ""}
5364 + }
5365 + ]
5366 + }
5367 +})";
5368 +
5369 + auto restore = SetManifest(manifest);
5370 + validateOrder({L"distro1", L"distro2"});
5371 + }
5372 +
5373 + {
5374 + auto manifest =
5375 + R"({
5376 + "ModernDistributions": {
5377 + "distro2": [
5378 + {
5379 + "Name": "distro2",
5380 + "FriendlyName": "distro2Name",
5381 + "Amd64Url": {"Url": "","Sha256": ""}
5382 + }
5383 + ],
5384 + "distro1": [
5385 + {
5386 + "Name": "distro1",
5387 + "FriendlyName": "distro1Name",
5388 + "Amd64Url": {"Url": "","Sha256": ""}
5389 + }
5390 + ]
5391 + }
5392 +})";
5393 +
5394 + auto restore = SetManifest(manifest);
5395 +
5396 + validateOrder({L"distro2", L"distro1"});
5397 + }
5398 + }
5399 }
5400
5401 TEST_METHOD(ModernInstallEndToEnd)