From 18ec38b87301a6ba291e2c890da465e6e680e66a Mon Sep 17 00:00:00 2001 From: Axemasta <33064621+Axemasta@users.noreply.github.com> Date: Thu, 21 Dec 2023 10:51:26 +0000 Subject: [PATCH 1/5] Fix crash when not using external provider I never tested this but the initialization crashes when opting out of external providers because no IExternalLocalizationProvider interface is registered in the translator resolver. I have fixed this and added some config points in the main manager. --- src/Mocale/AppBuilderExtensions.cs | 7 ++++++ src/Mocale/Managers/LocalizationManager.cs | 24 ++++++++++++------- .../InactiveExternalLocalizationProvider.cs | 15 ++++++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 src/Mocale/Providers/InactiveExternalLocalizationProvider.cs diff --git a/src/Mocale/AppBuilderExtensions.cs b/src/Mocale/AppBuilderExtensions.cs index 2d4f7ba..7e7e847 100644 --- a/src/Mocale/AppBuilderExtensions.cs +++ b/src/Mocale/AppBuilderExtensions.cs @@ -1,6 +1,8 @@ +using System.Globalization; using Mocale.Exceptions; using Mocale.Helper; using Mocale.Managers; +using Mocale.Providers; using Mocale.Wrappers; namespace Mocale; @@ -52,6 +54,11 @@ public static MauiAppBuilder UseMocale( throw new InitializationException($"No external provider was registered when mocale was configured to use one. Please register an external provider or set {nameof(IMocaleConfiguration.UseExternalProvider)} to false."); } + if (!config.UseExternalProvider) + { + mauiAppBuilder.Services.AddTransient(); + } + if (!mocaleBuilder.CacheProviderRegistered) { // TODO: Initialize in memory provider diff --git a/src/Mocale/Managers/LocalizationManager.cs b/src/Mocale/Managers/LocalizationManager.cs index e4b6b13..3d20cdb 100644 --- a/src/Mocale/Managers/LocalizationManager.cs +++ b/src/Mocale/Managers/LocalizationManager.cs @@ -6,6 +6,7 @@ public class LocalizationManager : ILocalizationManager { private readonly ICurrentCultureManager currentCultureManager; private readonly ILogger logger; + private readonly IMocaleConfiguration mocaleConfiguration; private readonly ITranslationResolver translationResolver; private readonly ITranslationUpdater translationUpdater; @@ -13,6 +14,7 @@ public class LocalizationManager : ILocalizationManager public LocalizationManager( ICurrentCultureManager currentCultureManager, + IConfigurationManager configurationManager, ILogger logger, ITranslationResolver translationResolver, ITranslationUpdater translationUpdater) @@ -22,6 +24,9 @@ public LocalizationManager( this.translationResolver = Guard.Against.Null(translationResolver, nameof(translationResolver)); this.translationUpdater = Guard.Against.Null(translationUpdater, nameof(this.translationUpdater)); + configurationManager = Guard.Against.Null(configurationManager, nameof(configurationManager)); + this.mocaleConfiguration = configurationManager.Configuration; + CurrentCulture = currentCultureManager.GetActiveCulture(); } @@ -29,12 +34,17 @@ public async Task SetCultureAsync(CultureInfo culture) { try { - var result = await translationResolver.LoadTranslations(culture); - - if (!result.Loaded) + if (mocaleConfiguration.UseExternalProvider) { - logger.LogWarning("Unable to load culture {CultureName}, no localizations found", culture.Name); - return false; + var result = await translationResolver.LoadTranslations(culture); + + if (!result.Loaded) + { + logger.LogWarning("Unable to load culture {CultureName}, no localizations found", culture.Name); + return false; + } + + translationUpdater.UpdateTranslations(result.Localization, result.Source); } var localTranslations = translationResolver.LoadLocalTranslations(culture); @@ -48,8 +58,6 @@ public async Task SetCultureAsync(CultureInfo culture) logger.LogInformation("No internal translations found for culture: {CultureName}, consider adding them as a backup", culture.Name); } - translationUpdater.UpdateTranslations(result.Localization, result.Source); - CurrentCulture = culture; currentCultureManager.SetActiveCulture(culture); @@ -93,7 +101,7 @@ private Task InitializeInternal() logger.LogTrace("Loaded local translations from source: {TranslationSource}", localTranslations.Source); - if (localTranslations.Source is TranslationSource.Internal or TranslationSource.ColdCache) + if ((localTranslations.Source is TranslationSource.Internal or TranslationSource.ColdCache) && mocaleConfiguration.UseExternalProvider) { logger.LogInformation("External translations can be updated, checking for newer copy..."); diff --git a/src/Mocale/Providers/InactiveExternalLocalizationProvider.cs b/src/Mocale/Providers/InactiveExternalLocalizationProvider.cs new file mode 100644 index 0000000..9c97a95 --- /dev/null +++ b/src/Mocale/Providers/InactiveExternalLocalizationProvider.cs @@ -0,0 +1,15 @@ +using System.Globalization; +namespace Mocale.Providers; + +internal sealed class InactiveExternalLocalizationProvider : IExternalLocalizationProvider +{ + public Task GetValuesForCultureAsync(CultureInfo cultureInfo) + { + IExternalLocalizationResult blankResult = new ExternalLocalizationResult() + { + Success = false, + }; + + return Task.FromResult(blankResult); + } +} From 3f5611cc642ce4231604a12e5a4f6a498c82af99 Mon Sep 17 00:00:00 2001 From: Axemasta <33064621+Axemasta@users.noreply.github.com> Date: Thu, 21 Dec 2023 11:28:47 +0000 Subject: [PATCH 2/5] Updated tests for localization manager We now have test coverage for the config changes I made to support local only mode --- src/Mocale/Managers/LocalizationManager.cs | 5 + .../Managers/LocalizationManagerTests.cs | 285 ++++++++++++++++++ 2 files changed, 290 insertions(+) diff --git a/src/Mocale/Managers/LocalizationManager.cs b/src/Mocale/Managers/LocalizationManager.cs index 3d20cdb..4dce1db 100644 --- a/src/Mocale/Managers/LocalizationManager.cs +++ b/src/Mocale/Managers/LocalizationManager.cs @@ -56,6 +56,11 @@ public async Task SetCultureAsync(CultureInfo culture) else { logger.LogInformation("No internal translations found for culture: {CultureName}, consider adding them as a backup", culture.Name); + + if (!mocaleConfiguration.UseExternalProvider) + { + return false; + } } CurrentCulture = culture; diff --git a/tests/Mocale.UnitTests/Managers/LocalizationManagerTests.cs b/tests/Mocale.UnitTests/Managers/LocalizationManagerTests.cs index 1559a82..e830d68 100644 --- a/tests/Mocale.UnitTests/Managers/LocalizationManagerTests.cs +++ b/tests/Mocale.UnitTests/Managers/LocalizationManagerTests.cs @@ -13,22 +13,35 @@ public class LocalizationManagerTests : FixtureBase #region Setup private readonly Mock currentCultureManager; + private readonly Mock> configurationManager; private readonly Mock> logger; + private readonly Mock mocaleConfiguration; private readonly Mock translationResolver; private readonly Mock translationUpdater; public LocalizationManagerTests() { currentCultureManager = new Mock(); + configurationManager = new Mock>(); logger = new Mock>(); + mocaleConfiguration = new Mock(); translationResolver = new Mock(); translationUpdater = new Mock(); + + mocaleConfiguration = new Mock(); + + mocaleConfiguration.SetupGet(m => m.UseExternalProvider) + .Returns(true); + + configurationManager.SetupGet(m => m.Configuration) + .Returns(mocaleConfiguration.Object); } public override ILocalizationManager CreateSystemUnderTest() { return new LocalizationManager( currentCultureManager.Object, + configurationManager.Object, logger.Object, translationResolver.Object, translationUpdater.Object @@ -382,6 +395,180 @@ public async Task Initialize_WhenTranslationsCanBeUpdatedAndExternalLoads_Should Times.Never()); } + [Fact] + public async Task Initialize_WhenExternalProviderNotEnabledAndLocalProviderFailsToLoad_ShouldLogAndReturnFalse() + { + // Arrange + mocaleConfiguration.SetupGet(m => m.UseExternalProvider) + .Returns(false); + + var activeCulture = new CultureInfo("fr-FR"); + + currentCultureManager.Setup(m => m.GetActiveCulture()) + .Returns(activeCulture); + + translationResolver.Setup(m => m.LoadLocalTranslations(activeCulture)) + .Returns(new TranslationLoadResult() + { + Loaded = false, + Localization = Localization.Invariant, + }); + + // Act + var initialized = await Sut.Initialize(); + + // Assert + Assert.False(initialized); + + logger.VerifyLog( + log => log.LogWarning("Unable to load translations for culture: {CultureName}", activeCulture.Name), + Times.Once()); + } + + [Fact] + public async Task Initialize_WhenExternalProviderNotEnabledAndLocalProviderLoadsFromInternal_ShouldUpdateAndReturnTrueWithoutUpdatingExternal() + { + // Arrange + mocaleConfiguration.SetupGet(m => m.UseExternalProvider) + .Returns(false); + + var activeCulture = new CultureInfo("it-IT"); + + currentCultureManager.Setup(m => m.GetActiveCulture()) + .Returns(activeCulture); + + var loadResult = new TranslationLoadResult() + { + Loaded = true, + Localization = new Localization() + { + CultureInfo = activeCulture, + Translations = new Dictionary() + { + { "KeyOne", "Ciao mondo!" }, + } + }, + Source = TranslationSource.Internal, + }; + + translationResolver.Setup(m => m.LoadLocalTranslations(activeCulture)) + .Returns(loadResult); + + // Act + var initialized = await Sut.Initialize(); + + // Assert + Assert.True(initialized); + + translationUpdater.Verify( + m => m.UpdateTranslations(loadResult.Localization, TranslationSource.Internal), + Times.Once()); + + logger.VerifyLog( + log => log.LogTrace("Loaded local translations from source: {TranslationSource}", TranslationSource.Internal), + Times.Once()); + + logger.VerifyLog( + log => log.LogInformation("External translations can be updated, checking for newer copy..."), + Times.Never()); + } + + [Fact] + public async Task Initialize_WhenExternalProviderNotEnabledAndLocalProviderLoadsFromColdCache_ShouldUpdateAndReturnTrueWithoutUpdatingExternal() + { + // Arrange + mocaleConfiguration.SetupGet(m => m.UseExternalProvider) + .Returns(false); + + var activeCulture = new CultureInfo("it-IT"); + + currentCultureManager.Setup(m => m.GetActiveCulture()) + .Returns(activeCulture); + + var loadResult = new TranslationLoadResult() + { + Loaded = true, + Localization = new Localization() + { + CultureInfo = activeCulture, + Translations = new Dictionary() + { + { "KeyOne", "Ciao mondo!" }, + } + }, + Source = TranslationSource.ColdCache, + }; + + translationResolver.Setup(m => m.LoadLocalTranslations(activeCulture)) + .Returns(loadResult); + + // Act + var initialized = await Sut.Initialize(); + + // Assert + Assert.True(initialized); + + translationUpdater.Verify( + m => m.UpdateTranslations(loadResult.Localization, TranslationSource.ColdCache), + Times.Once()); + + logger.VerifyLog( + log => log.LogTrace("Loaded local translations from source: {TranslationSource}", TranslationSource.ColdCache), + Times.Once()); + + logger.VerifyLog( + log => log.LogInformation("External translations can be updated, checking for newer copy..."), + Times.Never()); + } + + [Fact] + public async Task Initialize_WhenExternalProviderNotEnabledAndLocalProviderLoadsFromHotCache_ShouldUpdateAndReturnTrueWithoutUpdatingExternal() + { + // Arrange + mocaleConfiguration.SetupGet(m => m.UseExternalProvider) + .Returns(false); + + var activeCulture = new CultureInfo("it-IT"); + + currentCultureManager.Setup(m => m.GetActiveCulture()) + .Returns(activeCulture); + + var loadResult = new TranslationLoadResult() + { + Loaded = true, + Localization = new Localization() + { + CultureInfo = activeCulture, + Translations = new Dictionary() + { + { "KeyOne", "Ciao mondo!" }, + } + }, + Source = TranslationSource.WarmCache, + }; + + translationResolver.Setup(m => m.LoadLocalTranslations(activeCulture)) + .Returns(loadResult); + + // Act + var initialized = await Sut.Initialize(); + + // Assert + Assert.True(initialized); + + translationUpdater.Verify( + m => m.UpdateTranslations(loadResult.Localization, TranslationSource.WarmCache), + Times.Once()); + + logger.VerifyLog( + log => log.LogTrace("Loaded local translations from source: {TranslationSource}", TranslationSource.WarmCache), + Times.Once()); + + logger.VerifyLog( + log => log.LogInformation("External translations can be updated, checking for newer copy..."), + Times.Never()); + } + [Fact] public async Task SetCultureAsync_WhenSomethingThrows_ShouldLogAndReturnFalse() { @@ -574,6 +761,104 @@ public async Task SetCultureAsync_WhenCultureCanBeLoadedAndLocalTranslationsDont Times.Never()); } + [Fact] + public async Task SetCultureAsync_WhenExternalProviderNotEnabledAndLocalTranslationsNotLoaded_ShouldReturnFalseAndNotSetCulture() + { + mocaleConfiguration.SetupGet(m => m.UseExternalProvider) + .Returns(false); + + var activeCulture = new CultureInfo("it-IT"); + + currentCultureManager.Setup(m => m.GetActiveCulture()) + .Returns(activeCulture); + + var newCulture = new CultureInfo("fr-FR"); + + var localLoadResult = new TranslationLoadResult() + { + Loaded = false, + Localization = Localization.Invariant, + Source = TranslationSource.Internal, + }; + + translationResolver.Setup(m => m.LoadLocalTranslations(newCulture)) + .Returns(localLoadResult); + + // Act + var loaded = await Sut.SetCultureAsync(newCulture); + + // Assert + Assert.False(loaded); + Assert.Equal(activeCulture, Sut.CurrentCulture); + Assert.NotEqual(newCulture, Sut.CurrentCulture); + + currentCultureManager.Verify( + m => m.SetActiveCulture(It.IsAny()), + Times.Never); + + logger.VerifyLog( + log => log.LogDebug("Updated localization culture to {CultureName}", newCulture.Name), + Times.Never()); + } + + [Fact] + public async Task SetCultureAsync_WhenExternalProviderNotEnabledAndLocalTranslationsLoaded_ShouldReturnTrue() + { + mocaleConfiguration.SetupGet(m => m.UseExternalProvider) + .Returns(false); + + var activeCulture = new CultureInfo("it-IT"); + + currentCultureManager.Setup(m => m.GetActiveCulture()) + .Returns(activeCulture); + + var newCulture = new CultureInfo("fr-FR"); + + var localLoadResult = new TranslationLoadResult() + { + Loaded = true, + Localization = new Localization() + { + CultureInfo = newCulture, + Translations = new Dictionary() + { + { "KeyOne", "Bonjour le monde" }, + }, + }, + Source = TranslationSource.Internal, + }; + + translationResolver.Setup(m => m.LoadLocalTranslations(newCulture)) + .Returns(localLoadResult); + + // Act + var loaded = await Sut.SetCultureAsync(newCulture); + + // Assert + Assert.True(loaded); + Assert.Equal(newCulture, Sut.CurrentCulture); + + currentCultureManager.Verify( + m => m.SetActiveCulture(newCulture), + Times.Once()); + + logger.VerifyLog( + log => log.LogDebug("Updated localization culture to {CultureName}", newCulture.Name), + Times.Once()); + + logger.VerifyLog( + log => log.LogInformation("No internal translations found for culture: {CultureName}, consider adding them as a backup", newCulture.Name), + Times.Never); + + translationUpdater.Verify( + m => m.UpdateTranslations(It.IsAny(), TranslationSource.External), + Times.Never()); + + translationUpdater.Verify( + m => m.UpdateTranslations(localLoadResult.Localization, TranslationSource.Internal), + Times.Once()); + } + #endregion Tests } From 58901c59ce7ca1bed265b3e0a35c1d4725e99f26 Mon Sep 17 00:00:00 2001 From: Axemasta <33064621+Axemasta@users.noreply.github.com> Date: Thu, 21 Dec 2023 14:10:23 +0000 Subject: [PATCH 3/5] Added Tests For EmbeddedResourceProvider Since it was returning new collections when it should have been returning null, causing issues upstream --- .../Providers/EmbeddedResourceProvider.cs | 23 +-- .../Mocale.UnitTests/Mocale.UnitTests.csproj | 5 + .../EmbeddedResourceProviderTests.cs | 180 ++++++++++++++++++ .../Resources/Invalid/en-GB.json | 9 + .../Resources/Misc/RandomFile.txt | 5 + .../Resources/Misc/TestFile.json | 22 +++ tests/Mocale.UnitTests/Resources/en-GB.json | 8 + 7 files changed, 238 insertions(+), 14 deletions(-) create mode 100644 tests/Mocale.UnitTests/Providers/EmbeddedResourceProviderTests.cs create mode 100644 tests/Mocale.UnitTests/Resources/Invalid/en-GB.json create mode 100644 tests/Mocale.UnitTests/Resources/Misc/RandomFile.txt create mode 100644 tests/Mocale.UnitTests/Resources/Misc/TestFile.json create mode 100644 tests/Mocale.UnitTests/Resources/en-GB.json diff --git a/src/Mocale/Providers/EmbeddedResourceProvider.cs b/src/Mocale/Providers/EmbeddedResourceProvider.cs index 6706a36..18e8a30 100644 --- a/src/Mocale/Providers/EmbeddedResourceProvider.cs +++ b/src/Mocale/Providers/EmbeddedResourceProvider.cs @@ -16,23 +16,17 @@ public EmbeddedResourceProvider( this.logger = logger; } - public Dictionary GetValuesForCulture(CultureInfo cultureInfo) + public Dictionary? GetValuesForCulture(CultureInfo cultureInfo) { // read assembly if (localConfig.ResourcesAssembly is null) { logger.LogWarning("Configured resource assembly was null"); - return new Dictionary(); + return null; } var resources = localConfig.ResourcesAssembly.GetManifestResourceNames(); - if (resources is null) - { - logger.LogWarning("No embedded resources found in assembly {ResourceAssembly}", localConfig.ResourcesAssembly); - return new Dictionary(); - } - // look for the right folder var relativeFolder = localConfig.UseResourceFolder ? $"Resources.{localConfig.ResourcesPath}" @@ -40,12 +34,13 @@ public Dictionary GetValuesForCulture(CultureInfo cultureInfo) var folderPrefix = localConfig.ResourcesAssembly.GetName().Name + "." + relativeFolder; - var localesFolderResources = resources.Where(r => r.StartsWith(folderPrefix, StringComparison.InvariantCultureIgnoreCase)); + var localesFolderResources = resources.Where(r => r.StartsWith(folderPrefix, StringComparison.InvariantCultureIgnoreCase)) + .ToList(); if (!localesFolderResources.Any()) { logger.LogWarning("No assembly resources found with prefix: {FolderPrefix}", folderPrefix); - return new Dictionary(); + return null; } // check if filenames match @@ -59,7 +54,7 @@ public Dictionary GetValuesForCulture(CultureInfo cultureInfo) logger.LogWarning("Unable to find resource for selected culture: {CultureName}", cultureInfo.Name); - return new Dictionary(); + return null; } private static bool FileMatchesCulture(string resourceName, CultureInfo culture) @@ -73,14 +68,14 @@ private static bool FileMatchesCulture(string resourceName, CultureInfo culture) return fileName.Equals(culture.Name, StringComparison.OrdinalIgnoreCase); } - private Dictionary ParseFile(string filePath, Assembly assembly) + private Dictionary? ParseFile(string filePath, Assembly assembly) { using var fileStream = assembly.GetManifestResourceStream(filePath); if (fileStream is null) { logger.LogWarning("File stream was null for assembly resource: {FilePath}", filePath); - return new Dictionary(); + return null; } try @@ -92,7 +87,7 @@ private Dictionary ParseFile(string filePath, Assembly assembly) { logger.LogError(ex, "An exception occurred loading & parsing assembly resource {FilePath}", filePath); - return new Dictionary(); + return null; } } } diff --git a/tests/Mocale.UnitTests/Mocale.UnitTests.csproj b/tests/Mocale.UnitTests/Mocale.UnitTests.csproj index d9941c9..9cce3e6 100644 --- a/tests/Mocale.UnitTests/Mocale.UnitTests.csproj +++ b/tests/Mocale.UnitTests/Mocale.UnitTests.csproj @@ -33,5 +33,10 @@ + + + + + diff --git a/tests/Mocale.UnitTests/Providers/EmbeddedResourceProviderTests.cs b/tests/Mocale.UnitTests/Providers/EmbeddedResourceProviderTests.cs new file mode 100644 index 0000000..025363b --- /dev/null +++ b/tests/Mocale.UnitTests/Providers/EmbeddedResourceProviderTests.cs @@ -0,0 +1,180 @@ +using System.Globalization; +using Microsoft.Extensions.Logging; +using Mocale.Abstractions; +using Mocale.Providers; +namespace Mocale.UnitTests.Providers; + +public class EmbeddedResourceProviderTests : FixtureBase +{ + #region Setup + + private readonly Mock> configurationManager; + private readonly Mock embeddedResourcesConfig; + private readonly Mock> logger; + + public EmbeddedResourceProviderTests() + { + configurationManager = new Mock>(); + embeddedResourcesConfig = new Mock(); + logger = new Mock>(); + + configurationManager.SetupGet(m => m.Configuration) + .Returns(embeddedResourcesConfig.Object); + } + + public override IInternalLocalizationProvider CreateSystemUnderTest() + { + return new EmbeddedResourceProvider( + configurationManager.Object, + logger.Object); + } + + #endregion Setup + + #region Tests + + [Fact] + public void GetValuesForCulture_WhenResourceAssemblyIsNull_ShouldReturnNull() + { + // Arrange + embeddedResourcesConfig.SetupGet(m => m.ResourcesAssembly) + .Returns(() => null); + + var culture = new CultureInfo("en-GB"); + + // Act + var values = Sut.GetValuesForCulture(culture); + + // Assert + Assert.Null(values); + + logger.VerifyLog(log => log.LogWarning("Configured resource assembly was null"), + Times.Once()); + } + + [Fact] + public void GetValuesForCulture_WhenResourceFolderNotFound_ShouldReturnNull() + { + // Arrange + embeddedResourcesConfig.SetupGet(m => m.ResourcesAssembly) + .Returns(typeof(EmbeddedResourceProviderTests).Assembly); + + embeddedResourcesConfig.SetupGet(m => m.UseResourceFolder) + .Returns(true); + + embeddedResourcesConfig.SetupGet(m => m.ResourcesPath) + .Returns("Locales"); + + var culture = new CultureInfo("en-GB"); + + // Act + var values = Sut.GetValuesForCulture(culture); + + // Assert + Assert.Null(values); + + logger.VerifyLog(log => log.LogWarning("No assembly resources found with prefix: {FolderPrefix}", "Mocale.UnitTests.Resources.Locales"), + Times.Once()); + } + + [Fact] + public void GetValuesForCulture_WhenResourceFolderFoundButNoMatchingFiles_ShouldReturnNull() + { + // Arrange + embeddedResourcesConfig.SetupGet(m => m.ResourcesAssembly) + .Returns(typeof(EmbeddedResourceProviderTests).Assembly); + + embeddedResourcesConfig.SetupGet(m => m.UseResourceFolder) + .Returns(false); + + embeddedResourcesConfig.SetupGet(m => m.ResourcesPath) + .Returns("Resources.Misc"); + + var culture = new CultureInfo("en-GB"); + + // Act + var values = Sut.GetValuesForCulture(culture); + + // Assert + Assert.Null(values); + + logger.VerifyLog(log => log.LogWarning("Unable to find resource for selected culture: {CultureName}", "en-GB"), + Times.Once()); + } + + [Fact] + public void GetValuesForCulture_WhenResourceFolderContainsInvalidCultureFiles_ShouldReturnNull() + { + // Arrange + embeddedResourcesConfig.SetupGet(m => m.ResourcesAssembly) + .Returns(typeof(EmbeddedResourceProviderTests).Assembly); + + embeddedResourcesConfig.SetupGet(m => m.UseResourceFolder) + .Returns(true); + + embeddedResourcesConfig.SetupGet(m => m.ResourcesPath) + .Returns("Invalid"); + + var culture = new CultureInfo("en-GB"); + + // Act + var values = Sut.GetValuesForCulture(culture); + + // Assert + Assert.Null(values); + + logger.VerifyLog( + log => log.LogError( + It.IsAny(), + "An exception occurred loading & parsing assembly resource {FilePath}", + It.IsAny()), + Times.Once()); + } + + [Fact] + public void GetValuesForCulture_WhenResourceFolderContainsValidCultureFiles_ShouldReturnLocalizations() + { + // Arrange + embeddedResourcesConfig.SetupGet(m => m.ResourcesAssembly) + .Returns(typeof(EmbeddedResourceProviderTests).Assembly); + + embeddedResourcesConfig.SetupGet(m => m.UseResourceFolder) + .Returns(true); + + var culture = new CultureInfo("en-GB"); + + // Act + var values = Sut.GetValuesForCulture(culture); + + // Assert + Assert.NotNull(values); + Assert.NotEmpty(values); + + var expectedLocalizations = new Dictionary() + { + { + "CurrentLocaleName", "English" + }, + { + "LocalizationCurrentProviderIs", "The current localization provider is:" + }, + { + "LocalizationProviderName", "Json" + }, + { + "MocaleDescription", "Localization framework for .NET Maui" + }, + { + "MocaleTitle", "Mocale" + }, + { + "ExternalPrefixExplanation", "Strings prefixed with GR_ indicate they have been pulled from the external provider (GitHub.Raw), when the local cache expires if these values change, so will the text displayed!" + }, + }; + + values.Should() + .BeEquivalentTo(expectedLocalizations); + } + + #endregion Tests +} diff --git a/tests/Mocale.UnitTests/Resources/Invalid/en-GB.json b/tests/Mocale.UnitTests/Resources/Invalid/en-GB.json new file mode 100644 index 0000000..385906e --- /dev/null +++ b/tests/Mocale.UnitTests/Resources/Invalid/en-GB.json @@ -0,0 +1,9 @@ + + + English + The current localization provider is: + Json + Localization framework for .NET Maui + Mocale + Strings prefixed with GR_ indicate they have been pulled from the external provider (GitHub.Raw), when the local cache expires if these values change, so will the text displayed! + diff --git a/tests/Mocale.UnitTests/Resources/Misc/RandomFile.txt b/tests/Mocale.UnitTests/Resources/Misc/RandomFile.txt new file mode 100644 index 0000000..7bb0eab --- /dev/null +++ b/tests/Mocale.UnitTests/Resources/Misc/RandomFile.txt @@ -0,0 +1,5 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed facilisis tempus nisl, et faucibus elit gravida eleifend. Aliquam consectetur nibh nec nisl malesuada, nec rhoncus nulla aliquet. Sed dictum tortor id auctor consectetur. Quisque ante nisi, iaculis non euismod et, sodales et nisl. Quisque laoreet eu mauris eget aliquam. Suspendisse nec sem non quam tristique fermentum eu at odio. Nullam pharetra non augue quis egestas. Proin pulvinar et neque id congue. Integer congue, nunc nec elementum vulputate, tortor arcu congue dui, a pulvinar odio ante at orci. + +Pellentesque tellus felis, congue ac mollis sed, venenatis vel erat. Cras blandit lectus sit amet magna euismod tempus. Nullam porttitor sollicitudin eleifend. Nullam scelerisque finibus mollis. Maecenas feugiat, urna condimentum iaculis laoreet, massa ligula venenatis libero, ut sodales nunc turpis vel tortor. Nullam vitae justo porta, congue ipsum vitae, ornare lacus. Sed condimentum interdum nisl, eget commodo arcu pellentesque quis. + +Integer interdum rutrum risus, iaculis condimentum tellus auctor eget. Quisque ut dignissim orci. In sit amet augue magna. Etiam neque orci, lobortis rhoncus sagittis non, aliquam ut nulla. Proin quis ex id lorem cursus rutrum. Ut iaculis lorem dolor, vehicula dictum nunc commodo eget. Maecenas fringilla mauris sit amet molestie viverra. Curabitur pretium lacus leo, at auctor tellus blandit quis. Nam vitae nisi gravida, bibendum nunc vel, venenatis tortor. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec vel arcu posuere, congue erat a, finibus tortor. Fusce pellentesque lorem vitae arcu pulvinar, vitae semper est commodo. Duis rhoncus leo ultricies ex gravida, nec elementum odio sollicitudin. Proin sollicitudin diam dui, id gravida sapien imperdiet scelerisque. diff --git a/tests/Mocale.UnitTests/Resources/Misc/TestFile.json b/tests/Mocale.UnitTests/Resources/Misc/TestFile.json new file mode 100644 index 0000000..cfe3476 --- /dev/null +++ b/tests/Mocale.UnitTests/Resources/Misc/TestFile.json @@ -0,0 +1,22 @@ +{ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML"] + }, + "GlossSee": "markup" + } + } + } + } +} diff --git a/tests/Mocale.UnitTests/Resources/en-GB.json b/tests/Mocale.UnitTests/Resources/en-GB.json new file mode 100644 index 0000000..adaaecf --- /dev/null +++ b/tests/Mocale.UnitTests/Resources/en-GB.json @@ -0,0 +1,8 @@ +{ + "CurrentLocaleName": "English", + "LocalizationCurrentProviderIs": "The current localization provider is:", + "LocalizationProviderName": "Json", + "MocaleDescription": "Localization framework for .NET Maui", + "MocaleTitle": "Mocale", + "ExternalPrefixExplanation": "Strings prefixed with GR_ indicate they have been pulled from the external provider (GitHub.Raw), when the local cache expires if these values change, so will the text displayed!" +} From 33e9b359a301b1c71479d1c883345e5536167102 Mon Sep 17 00:00:00 2001 From: Axemasta <33064621+Axemasta@users.noreply.github.com> Date: Thu, 21 Dec 2023 14:29:47 +0000 Subject: [PATCH 4/5] Add maui workload install to ci I managed to get away without using it, but here we go. Kiss goodbye to fast builds! --- .github/workflows/mocale-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/mocale-ci.yml b/.github/workflows/mocale-ci.yml index d337dab..86d18a1 100644 --- a/.github/workflows/mocale-ci.yml +++ b/.github/workflows/mocale-ci.yml @@ -25,6 +25,8 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: 7.0.x + - name: Install Maui Workloads + run: dotnet workload install maui --source https://api.nuget.org/v3/index.json - name: Restore dependencies run: dotnet restore - name: Build From a3dd715047f4edba1098e687039424549fa94961 Mon Sep 17 00:00:00 2001 From: Axemasta <33064621+Axemasta@users.noreply.github.com> Date: Thu, 21 Dec 2023 14:41:02 +0000 Subject: [PATCH 5/5] Explicit Java Version For CI Now it all wants to stop working, crickey --- .github/workflows/mocale-ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/mocale-ci.yml b/.github/workflows/mocale-ci.yml index 86d18a1..821d205 100644 --- a/.github/workflows/mocale-ci.yml +++ b/.github/workflows/mocale-ci.yml @@ -25,6 +25,12 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: 7.0.x + - name: Setup Java JDK + uses: actions/setup-java@v4.0.0 + with: + distribution: 'microsoft' + java-version: '11' + architecture: 'x64' - name: Install Maui Workloads run: dotnet workload install maui --source https://api.nuget.org/v3/index.json - name: Restore dependencies