diff --git a/src/Yarhl.IntegrationTests/AssemblyLoadContextExtensionsTests.cs b/src/Yarhl.IntegrationTests/AssemblyLoadContextExtensionsTests.cs index 51b825b..c78851a 100644 --- a/src/Yarhl.IntegrationTests/AssemblyLoadContextExtensionsTests.cs +++ b/src/Yarhl.IntegrationTests/AssemblyLoadContextExtensionsTests.cs @@ -128,7 +128,7 @@ public void FindFormatFromPluginsDir() string pluginDir = Path.Combine(programDir, "Plugins"); TypeLocator.Instance.LoadContext.TryLoadFromDirectory(pluginDir, false); - var formats = ConvertersLocator.Instance.Formats; + var formats = ConverterLocator.Instance.Formats; Assert.That(formats, Is.Not.Empty); Assert.That( formats.Select(t => t.Name), @@ -142,11 +142,11 @@ public void FindConverterFromPluginsDir() string pluginDir = Path.Combine(programDir, "Plugins"); TypeLocator.Instance.LoadContext.TryLoadFromDirectory(pluginDir, false); - Type poType = ConvertersLocator.Instance.Formats + Type poType = ConverterLocator.Instance.Formats .Single(f => f.Name == "Yarhl.Media.Text.Po") .Type; - var converters = ConvertersLocator.Instance.Converters + var converters = ConverterLocator.Instance.Converters .Where(f => f.CanConvert(poType)); Assert.That(converters, Is.Not.Empty); Assert.That( diff --git a/src/Yarhl.Plugins/FileFormat/ConvertersLocator.cs b/src/Yarhl.Plugins/FileFormat/ConverterLocator.cs similarity index 86% rename from src/Yarhl.Plugins/FileFormat/ConvertersLocator.cs rename to src/Yarhl.Plugins/FileFormat/ConverterLocator.cs index 0de2d04..8cc4b42 100644 --- a/src/Yarhl.Plugins/FileFormat/ConvertersLocator.cs +++ b/src/Yarhl.Plugins/FileFormat/ConverterLocator.cs @@ -25,18 +25,18 @@ namespace Yarhl.Plugins.FileFormat; /// /// Locates converter types across assemblies and provide their information. /// -public sealed class ConvertersLocator +public sealed class ConverterLocator { private static readonly object LockObj = new(); - private static ConvertersLocator? singleInstance; + private static ConverterLocator? singleInstance; private readonly List formatsMetadata; private readonly List convertersMetadata; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - private ConvertersLocator() + private ConverterLocator() { formatsMetadata = new List(); Formats = formatsMetadata; @@ -51,11 +51,11 @@ private ConvertersLocator() /// Gets the plugin manager instance. /// /// It initializes the manager if needed. - public static ConvertersLocator Instance { + public static ConverterLocator Instance { get { if (singleInstance == null) { lock (LockObj) { - singleInstance ??= new ConvertersLocator(); + singleInstance ??= new ConverterLocator(); } } @@ -76,6 +76,10 @@ private ConvertersLocator() /// /// Scan the assemblies from the load context to look for formats and converters. /// + /// + /// This method is already called when the instance is created. Only needed + /// after loading additional assemblies. + /// public void ScanAssemblies() { formatsMetadata.Clear(); diff --git a/src/Yarhl.UnitTests/FileFormat/BaseGeneralTests.cs b/src/Yarhl.UnitTests/FileFormat/BaseGeneralTests.cs index b9acb8b..2cc40a6 100644 --- a/src/Yarhl.UnitTests/FileFormat/BaseGeneralTests.cs +++ b/src/Yarhl.UnitTests/FileFormat/BaseGeneralTests.cs @@ -32,7 +32,7 @@ public abstract class BaseGeneralTests [Test] public void FormatIsFoundAndIsUnique() { - var formats = ConvertersLocator.Instance.Formats + var formats = ConverterLocator.Instance.Formats .Select(f => f.Type); Assert.That(formats, Does.Contain(typeof(T))); Assert.That(formats, Is.Unique); @@ -41,7 +41,7 @@ public void FormatIsFoundAndIsUnique() [Test] public void FormatNameMatchAndIsUnique() { - var names = ConvertersLocator.Instance.Formats + var names = ConverterLocator.Instance.Formats .Select(f => f.Name); Assert.That(names, Does.Contain(Name)); Assert.That(names, Is.Unique); diff --git a/src/Yarhl.UnitTests/Plugins/FileFormat/ConvertersLocatorTests.cs b/src/Yarhl.UnitTests/Plugins/FileFormat/ConvertersLocatorTests.cs index 1ff08d0..ce2fff0 100644 --- a/src/Yarhl.UnitTests/Plugins/FileFormat/ConvertersLocatorTests.cs +++ b/src/Yarhl.UnitTests/Plugins/FileFormat/ConvertersLocatorTests.cs @@ -23,122 +23,173 @@ namespace Yarhl.UnitTests.Plugins.FileFormat; using System.Linq; using NUnit.Framework; using Yarhl.FileFormat; +using Yarhl.FileSystem; +using Yarhl.IO; using Yarhl.Plugins; +using Yarhl.Plugins.FileFormat; [TestFixture] public class ConvertersLocatorTests { - /* [Test] - public void FormatMetadataContainsNameAndType() + public void InstanceIsSingleton() { - var format = TypeLocator.Instance.GetFormats() - .Single(p => p.Metadata.Type == typeof(StringFormat)); - Assert.That( - format.Metadata.Name, - Is.EqualTo(typeof(StringFormat).FullName)); + ConverterLocator instance1 = ConverterLocator.Instance; + ConverterLocator instance2 = ConverterLocator.Instance; + + Assert.That(instance1, Is.SameAs(instance2)); } [Test] - public void FormatsAreNotDuplicated() + public void InstanceIsInitialized() { - Assert.That( - TypeLocator.Instance.GetFormats().Select(f => f.Metadata.Type), - Is.Unique); + ConverterLocator instance = ConverterLocator.Instance; + + Assert.That(instance.Formats, Is.Not.Null); + Assert.That(instance.Converters, Is.Not.Null); } [Test] - public void GetFormatsReturnsKnownFormats() + public void LocateFormatsWithTypeInfo() { - Assert.That( - TypeLocator.Instance.GetFormats().Select(f => f.Metadata.Name), - Does.Contain(typeof(BinaryFormat).FullName)); + InterfaceImplementationInfo myFormat = ConverterLocator.Instance.Formats + .FirstOrDefault(i => i.Type == typeof(DerivedSourceFormat)); + + Assert.That(myFormat, Is.Not.Null); + Assert.That(myFormat.InterfaceImplemented, Is.EqualTo(typeof(IFormat))); + Assert.That(myFormat.Name, Is.EqualTo(typeof(DerivedSourceFormat).FullName)); + } + + [Test] + public void FormatsAreNotDuplicated() + { + InterfaceImplementationInfo[] formats = ConverterLocator.Instance.Formats + .Where(f => f.Type == typeof(MySourceFormat)) + .ToArray(); + + Assert.That(formats, Has.Length.EqualTo(1)); } [Test] - public void FindSingleInnerConverter() + public void LocateFormatsFindYarhlBaseFormats() { - IConverter converter = null; Assert.That( - () => converter = TypeLocator.Instance - .FindExtensions>() - .Single(), - Throws.Nothing); + ConverterLocator.Instance.Formats.Select(f => f.Type), + Does.Contain(typeof(BinaryFormat))); + Assert.That( - converter, - Is.InstanceOf()); - Assert.That(converter.Convert("4"), Is.EqualTo(4)); + ConverterLocator.Instance.Formats.Select(f => f.Type), + Does.Contain(typeof(NodeContainerFormat))); } [Test] - public void FindSingleOuterConverter() + public void LocateConvertersWithTypeInfo() { - IConverter converter = null; - Assert.That( - () => converter = TypeLocator.Instance - .FindExtensions>() - .Single(), - Throws.Nothing); - Assert.That(converter, Is.InstanceOf()); - Assert.That(converter.Convert("5"), Is.EqualTo(5)); + ConverterTypeInfo result = ConverterLocator.Instance.Converters + .FirstOrDefault(i => i.Type == typeof(MyConverter)); + + Assert.That(result, Is.Not.Null); + Assert.That(result.InterfaceImplemented, Is.EqualTo(typeof(IConverter))); + Assert.That(result.Name, Is.EqualTo(typeof(MyConverter).FullName)); + Assert.That(result.SourceType, Is.EqualTo(typeof(MySourceFormat))); + Assert.That(result.DestinationType, Is.EqualTo(typeof(MyDestFormat))); } [Test] - public void FindTwoConvertersInSameClass() + public void ConvertersAreNotDuplicated() { - var converter1 = TypeLocator.Instance - .FindExtensions>(); - Assert.IsInstanceOf(converter1.Single()); - - Assert.DoesNotThrow(() => - converter1.Single(t => - Array.Exists(t.GetType().GetInterfaces(), i => - i.IsGenericType && - i.GenericTypeArguments.Length == 2 && - i.GenericTypeArguments[0] == typeof(string) && - i.GenericTypeArguments[1] == typeof(int)))); - - var converter2 = TypeLocator.Instance - .FindExtensions>(); - Assert.IsInstanceOf(converter2.Single()); - - Assert.DoesNotThrow(() => - converter2.Single(t => - Array.Exists(t.GetType().GetInterfaces(), i => - i.IsGenericType && - i.GenericTypeArguments.Length == 2 && - i.GenericTypeArguments[0] == typeof(int) && - i.GenericTypeArguments[1] == typeof(string)))); + ConverterTypeInfo[] results = ConverterLocator.Instance.Converters + .Where(f => f.Type == typeof(MyConverter)) + .ToArray(); + + Assert.That(results, Has.Length.EqualTo(1)); } [Test] - public void FindDerivedConverter() + public void ScanAssembliesDoesNotDuplicateFindings() { - var converters = TypeLocator.Instance - .FindExtensions>(); - IConverter converter = null; - Assert.That( - () => converter = converters.Single(), - Throws.Nothing); - Assert.IsInstanceOf(converter); - Assert.IsInstanceOf(converter); - Assert.That(converter.Convert("3"), Is.EqualTo(3)); + ConverterLocator.Instance.ScanAssemblies(); + + FormatsAreNotDuplicated(); + ConvertersAreNotDuplicated(); + } + + [Test] + public void LocateConverterWithParameters() + { + ConverterTypeInfo[] results = ConverterLocator.Instance.Converters + .Where(f => f.Type == typeof(MyConverterParametrized)) + .ToArray(); + + Assert.That(results.Length, Is.EqualTo(1)); } [Test] - public void FindConvertsWithOtherInterfaces() + public void LocateSingleInnerConverter() { - IConverter converter = null; + ConverterTypeInfo converter = ConverterLocator.Instance.Converters + .FirstOrDefault(c => c.Type == typeof(SingleOuterConverter.SingleInnerConverter)); + + Assert.That(converter, Is.Not.Null); + } + + [Test] + public void LocateSingleOuterConverter() + { + ConverterTypeInfo converter = ConverterLocator.Instance.Converters + .FirstOrDefault(c => c.Type == typeof(SingleOuterConverter)); + + Assert.That(converter, Is.Not.Null); + } + + [Test] + public void LocateTwoConvertersInSameClass() + { + ConverterTypeInfo[] converters = ConverterLocator.Instance.Converters + .Where(c => c.Type == typeof(TwoConverters)) + .ToArray(); + + Assert.That(converters.Length, Is.EqualTo(2)); + Assert.That( + Array.Exists( + converters, + c => c.InterfaceImplemented == typeof(IConverter)), + Is.True); Assert.That( - () => converter = TypeLocator.Instance - .FindExtensions>() - .Single(), - Throws.Nothing); - Assert.That(converter, Is.InstanceOf()); - Assert.That(converter.Convert("3"), Is.EqualTo(3)); + Array.Exists( + converters, + c => c.SourceType == typeof(MySourceFormat) && c.DestinationType == typeof(MyDestFormat)), + Is.True); + + Assert.That( + Array.Exists( + converters, + c => c.InterfaceImplemented == typeof(IConverter)), + Is.True); Assert.That( - ((ConverterAndOtherInterface)converter).Dispose, - Throws.Nothing); + Array.Exists( + converters, + c => c.SourceType == typeof(MyDestFormat) && c.DestinationType == typeof(MySourceFormat)), + Is.True); + } + + [Test] + public void LocateDerivedConverter() + { + ConverterTypeInfo[] converters = ConverterLocator.Instance.Converters + .Where(c => c.Type == typeof(DerivedConverter)) + .ToArray(); + + Assert.That(converters.Length, Is.EqualTo(1)); + } + + [Test] + public void LocateConvertsWithOtherInterfaces() + { + ConverterTypeInfo[] converters = ConverterLocator.Instance.Converters + .Where(c => c.Type == typeof(ConverterAndOtherInterface)) + .ToArray(); + + Assert.That(converters.Length, Is.EqualTo(1)); } - */ } diff --git a/src/Yarhl.UnitTests/Plugins/FileFormat/TestConvertersDefinition.cs b/src/Yarhl.UnitTests/Plugins/FileFormat/TestConvertersDefinition.cs index c37af95..1effa18 100644 --- a/src/Yarhl.UnitTests/Plugins/FileFormat/TestConvertersDefinition.cs +++ b/src/Yarhl.UnitTests/Plugins/FileFormat/TestConvertersDefinition.cs @@ -41,74 +41,57 @@ public MyDestFormat Convert(MySourceFormat source) } } -public class SingleOuterConverterExample : IConverter +public class SingleOuterConverter : IConverter { - public uint Convert(string source) + public MyDestFormat Convert(MySourceFormat source) { - return System.Convert.ToUInt32(source); + return new MyDestFormat(); } - public class SingleInnerConverterExample : IConverter + public class SingleInnerConverter : IConverter { - public ulong Convert(string source) + public MyDestFormat Convert(MySourceFormat source) { - return System.Convert.ToUInt64(source); + return new MyDestFormat(); } } } public sealed class ConverterAndOtherInterface : - IConverter, + IConverter, IDisposable { - public short Convert(string source) + public MyDestFormat Convert(MySourceFormat source) { - return System.Convert.ToInt16(source); + return new MyDestFormat(); } public void Dispose() { - // Test dispose - } -} - -public class TwoConvertersExample : - IConverter, IConverter -{ - public int Convert(string source) - { - return System.Convert.ToInt32(source); - } - - public string Convert(int source) - { - return source.ToString(); + GC.SuppressFinalize(this); } } -public class DuplicatedConverter1 : - IConverter +public class TwoConverters : + IConverter, + IConverter { - public sbyte Convert(string source) + public MyDestFormat Convert(MySourceFormat source) { - return System.Convert.ToSByte(source); + return new MyDestFormat(); } -} -public class DuplicatedConverter2 : - IConverter -{ - public sbyte Convert(string source) + public MySourceFormat Convert(MyDestFormat source) { - return System.Convert.ToSByte(source); + return new MySourceFormat(); } } -public abstract class BaseAbstractConverter : IConverter +public abstract class BaseAbstractConverter : IConverter { - public long Convert(string source) + public MyDestFormat Convert(MySourceFormat source) { - return System.Convert.ToInt64(source); + return new MyDestFormat(); } }