Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions src/Cuemon.Core/Globalization/UnM49DataContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

internal Dictionary<string, StatisticalRegionInfo> CountriesByIsoAlpha2 { get; } = new(StringComparer.OrdinalIgnoreCase);

private (List<Unm49RegionData> Regions, List<Unm49CountryData> Countries) LoadUnM49Data()

Check warning on line 26 in src/Cuemon.Core/Globalization/UnM49DataContainer.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Refactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)

Check warning on line 26 in src/Cuemon.Core/Globalization/UnM49DataContainer.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Refactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)

Check warning on line 26 in src/Cuemon.Core/Globalization/UnM49DataContainer.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Refactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)
{
var resourceName = $"{nameof(Cuemon)}.{nameof(Globalization)}.unm49-data.csv";
var regions = new List<Unm49RegionData>();
Expand Down Expand Up @@ -96,7 +96,7 @@
{
// Escaped quote
current.Append('"');
i++;

Check warning on line 99 in src/Cuemon.Core/Globalization/UnM49DataContainer.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Do not update the loop counter 'i' within the loop body. (https://rules.sonarsource.com/csharp/RSPEC-127)

Check warning on line 99 in src/Cuemon.Core/Globalization/UnM49DataContainer.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Do not update the loop counter 'i' within the loop body. (https://rules.sonarsource.com/csharp/RSPEC-127)

Check warning on line 99 in src/Cuemon.Core/Globalization/UnM49DataContainer.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Do not update the loop counter 'i' within the loop body. (https://rules.sonarsource.com/csharp/RSPEC-127)
}
else
{
Expand All @@ -118,7 +118,7 @@
return parts.ToArray();
}

private void BuildHierarchy(List<Unm49RegionData> regions, List<Unm49CountryData> countries)

Check warning on line 121 in src/Cuemon.Core/Globalization/UnM49DataContainer.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Refactor this method to reduce its Cognitive Complexity from 18 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)

Check warning on line 121 in src/Cuemon.Core/Globalization/UnM49DataContainer.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Refactor this method to reduce its Cognitive Complexity from 18 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)
{
// First pass: Create all region objects
foreach (var regionData in regions)
Expand All @@ -142,6 +142,13 @@
}

// Third pass: Create country objects as children of their parent regions
var regionsByIsoAlpha2 = new Dictionary<string, RegionInfo>(StringComparer.OrdinalIgnoreCase);
var seenIsoAlpha2 = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var r in World.Regions.Where(r => seenIsoAlpha2.Add(r.TwoLetterISORegionName)))
{
regionsByIsoAlpha2[r.TwoLetterISORegionName] = r;
}

foreach (var countryData in countries)
{
if (RegionsByCode.TryGetValue(countryData.ParentCode, out var parent))
Expand All @@ -154,15 +161,10 @@
$"Country {countryData.Name} ({countryData.Code}) must have kind 'CountryOrTerritory', but was '{countryData.Kind}'.");
}

// Try to find matching RegionInfo
RegionInfo regionInfo = null;
try
{
regionInfo = World.Regions.FirstOrDefault(r => string.Equals(r.TwoLetterISORegionName, countryData.IsoAlpha2, StringComparison.OrdinalIgnoreCase));
}
catch
if (!string.IsNullOrEmpty(countryData.IsoAlpha2))
{
// Some territories may not be supported by the OS
regionsByIsoAlpha2.TryGetValue(countryData.IsoAlpha2, out regionInfo);
}

var country = new StatisticalRegionInfo(
Expand Down Expand Up @@ -207,7 +209,7 @@
return kind;
}

private void ValidateHierarchy()

Check warning on line 212 in src/Cuemon.Core/Globalization/UnM49DataContainer.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Refactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)

Check warning on line 212 in src/Cuemon.Core/Globalization/UnM49DataContainer.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Refactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)
{
// Validate World has no parent
if (!RegionsByCode.TryGetValue("001", out var world) || world == null)
Expand Down
10 changes: 6 additions & 4 deletions src/Cuemon.Core/Globalization/World.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Cuemon.Collections.Generic;

namespace Cuemon.Globalization
{
Expand All @@ -14,9 +15,9 @@ public static class World
{
var cultures = new SortedList<string, CultureInfo>();
var specificCultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures);
foreach (var c in specificCultures.Where(ci => ci.LCID != 127))
foreach (var c in specificCultures.Where(ci => ci.LCID != 127)) // *should* not happen for specific cultures, but on some linux based systems there are some cultures with LCID 127 (invariant culture) that are incorrectly categorized as specific cultures, so we need to filter those out.
{
if (!cultures.ContainsKey(c.DisplayName)) { cultures.Add(c.DisplayName, c); }
Decorator.Enclose(cultures).TryAdd(c.Name, c);
}
return cultures.Values;
});
Expand All @@ -27,9 +28,10 @@ public static class World
foreach (var c in SpecificCultures.Value)
{
var region = new RegionInfo(c.Name);
if (!regions.ContainsKey(region.EnglishName)) { regions.Add(region.EnglishName, region); }
if (int.TryParse(region.Name, out _)) { continue; } // Skip statistical regions (why would Microsoft even consider having these as part of RegionInfo? No valid ISO 3166-1 alpha-2/3 code can be all digits, so these are not actual regions or countries.)
Decorator.Enclose(regions).TryAdd($"{region.Name}:{region.NativeName}", region);
}
return regions.Values;
return regions.Values.OrderBy(info => info.Name).ThenBy(info => info.NativeName);
});

private static readonly Lazy<UnM49DataContainer> UnM49Data = new(() => new UnM49DataContainer());
Expand Down
89 changes: 84 additions & 5 deletions test/Cuemon.Core.Tests/Globalization/WorldTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Codebelt.Extensions.Xunit;
Expand All @@ -13,17 +14,95 @@ public WorldTest(ITestOutputHelper output) : base(output)
}

[Fact]
public void Regions_ShouldReturnAllRegions()
public void Regions_ShouldContainAllExpectedIsoRegionCodes_ForBackwardCompatibility()
{
var expectedTwoLetterIsoCodes = new HashSet<string>(StringComparer.Ordinal)
{
"AD", "AE", "AF", "AG", "AI", "AL", "AM", "AO", "AR", "AS", "AT", "AU", "AW", "AX", "AZ",
"BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BL", "BM", "BN", "BO", "BQ", "BR", "BS", "BT", "BW", "BY", "BZ",
"CA", "CC", "CD", "CF", "CG", "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", "CU", "CV", "CW", "CX", "CY", "CZ",
"DE", "DJ", "DK", "DM", "DO", "DZ",
"EC", "EE", "EG", "ER", "ES", "ET",
"FI", "FJ", "FK", "FM", "FO", "FR",
"GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL", "GM", "GN", "GP", "GQ", "GR", "GT", "GU", "GW", "GY",
"HK", "HN", "HR", "HT", "HU",
"ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS", "IT",
"JE", "JM", "JO", "JP",
"KE", "KG", "KH", "KI", "KM", "KN", "KP", "KR", "KW", "KY", "KZ",
"LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY",
"MA", "MC", "MD", "ME", "MF", "MG", "MH", "MK", "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", "MT", "MU", "MV", "MW", "MX", "MY", "MZ",
"NA", "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", "NR", "NU", "NZ",
"OM",
"PA", "PE", "PF", "PG", "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", "PW", "PY",
"QA",
"RE", "RO", "RS", "RU", "RW",
"SA", "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", "SK", "SL", "SM", "SN", "SO", "SR", "SS", "ST", "SV", "SX", "SY", "SZ",
"TC", "TD", "TG", "TH", "TJ", "TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV", "TW", "TZ",
"UA", "UG", "UM", "US", "UY", "UZ",
"VA", "VC", "VE", "VG", "VI", "VN", "VU",
"WF", "WS",
"XK",
"YE", "YT",
"ZA", "ZM", "ZW"
};

var sut1 = World.Regions.ToList();
var actualCodes = new HashSet<string>(sut1.Select(r => r.Name), StringComparer.Ordinal);

#if NET48_OR_GREATER
Assert.NotEmpty(actualCodes);
Assert.True(actualCodes.Count > 100, "actualCodes.Count > 100");
#else
var missing = expectedTwoLetterIsoCodes.Except(actualCodes).OrderBy(c => c).ToList();
var added = actualCodes.Except(expectedTwoLetterIsoCodes).OrderBy(c => c).ToList();
foreach (var code in missing)
{
TestOutput.WriteLine($"Missing: {code} - {World.Regions.SingleOrDefault(info => info.Name == code).EnglishName}");
}
foreach (var code in added)
{
TestOutput.WriteLine($"Added: {code} - {World.Regions.Last(info => info.Name == code).EnglishName}");
Comment thread
gimlichael marked this conversation as resolved.
}
Comment thread
gimlichael marked this conversation as resolved.
TestOutput.WriteLine($"Expected: {expectedTwoLetterIsoCodes.Count}, Actual: {actualCodes.Count}, Missing: {missing.Count}, Added: {added.Count}");
Assert.Empty(missing);
#endif
}

[Fact]
public void Regions_ShouldPrintAllRegionsAndHighlightIsoCodeFrequency()
{
var sut1 = World.Regions.ToList();

TestOutput.WriteLine(sut1.Count.ToString());
foreach (var r in sut1)
{
TestOutput.WriteLine($"{r.Name,-5} {r.EnglishName}");
}

var grouped = sut1.GroupBy(r => r.Name).OrderBy(g => g.Key).ToList();
var multiEntry = grouped.Where(g => g.Count() > 1).OrderByDescending(g => g.Count()).ThenBy(g => g.Key).ToList();

TestOutput.WriteLine($"Total: {sut1.Count}, Unique ISO codes: {grouped.Count}, ISO codes with multiple entries: {multiEntry.Count}");

foreach (var g in multiEntry)
{
var first = g.First();
var allEqual = g.All(r => r.Equals(first));
var distinctNativeNames = g.Select(r => r.NativeName).Distinct().ToList();
TestOutput.WriteLine($" {g.Key} ({first.EnglishName}): {g.Count()} entries | all Equals: {allEqual} | distinct NativeNames: {distinctNativeNames.Count}");
if (distinctNativeNames.Count > 1)
{
foreach (var name in distinctNativeNames)
{
TestOutput.WriteLine($" NativeName: {name}");
}
}
}

Assert.NotNull(sut1);
Assert.NotEmpty(sut1);
#if NET48_OR_GREATER
Assert.True(sut1.Count > 100, "sut1.Count > 100");
#else
Assert.True(sut1.Count > 220, "sut1.Count > 220");
Assert.True(sut1.Count > 400, "sut1.Count > 400");
#endif
}

Expand Down
Loading