Diff of /Implementation/APUnitTestFramework/DependencyResolver.cs [000000] .. [5a4bff]  Maximize  Restore

Switch to side-by-side view

--- a
+++ b/Implementation/APUnitTestFramework/DependencyResolver.cs
@@ -0,0 +1,437 @@
+using _3S.CoDeSys.Core;
+using _3S.CoDeSys.Core.ComponentModel;
+using _3S.CoDeSys.Core.Components;
+using _3S.CoDeSys.Core.Views;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Xml;
+
+namespace _3S.APUnitTestFramework
+{
+    class DependencyResolver
+    {
+        public DependencyResolver(Testbed testbed, string profileName, Profile profile)
+        {
+            if (testbed == null)
+                throw new ArgumentNullException(nameof(testbed));
+
+            _testbed = testbed;
+
+            var byTypeGuid = new Dictionary<Guid, IValueProvider>();
+            var byItfType = new Dictionary<Type, List<IValueProvider>>();
+            foreach (var kvp in testbed.Mocks)
+            {
+                var prototype = kvp.Value.CreatePrototypeFunc();
+                var valueProvider = new MockValueProvider(kvp.Key, prototype, kvp.Value);
+                byTypeGuid[kvp.Key] = valueProvider;
+
+                foreach (var itfType in prototype.GetType().GetInterfaces())
+                {
+                    List<IValueProvider> valueProviderList;
+                    byItfType.TryGetValue(itfType, out valueProviderList);
+                    if (valueProviderList == null)
+                    {
+                        valueProviderList = new List<IValueProvider>();
+                        byItfType[itfType] = valueProviderList;
+                    }
+                    valueProviderList.Add(valueProvider);
+                }
+            }
+
+            _mockFactoriesByTypeGuid = byTypeGuid;
+            _mockFactoriesByItfType = byItfType.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToArray());
+            _profileName = profileName ?? ComponentManager.Singleton.ActiveProfileName;
+            _profile = profile ?? ComponentManager.Singleton.ActiveProfile;
+        }
+
+        public IValueProvider ResolveSingle(Type itfType, bool optional, MemberInfo member)
+        {
+            IValueProvider[] results;
+            _mockFactoriesByItfType.TryGetValue(itfType, out results);
+
+            if (results == null)
+            {
+                // We did not find an implementation among the mocks.
+                // Try the additional plug-ins, if any. This discovery is similar to the original
+                // component model implementation. In the case of any doubts, look there.
+
+                if (_systemTypeInfos == null)
+                    _systemTypeInfos = ComponentManager.Singleton.GetSystemTypes();
+
+                var itfTypeFullName = itfType.FullName;
+                Guid systemTypeGuid;
+                try
+                {
+                    var enum1 = Enumerable.Where(_systemTypeInfos, typeInfo => typeInfo.TypeName == itfTypeFullName || typeInfo.ItfTypesFast.Contains(itfTypeFullName));
+                    var enum2 = Enumerable.Select(enum1, typeInfo => typeInfo.TypeGuid);
+                    systemTypeGuid = Enumerable.SingleOrDefault(enum2);
+                }
+                catch
+                {
+                    var exception = new MultipleInstancesForInjectionException(member);
+                    _testbed.RaiseDependencyError(exception, itfType, null);
+                    return new ExceptionValueProvider(exception);
+                }
+
+                if (systemTypeGuid != Guid.Empty)
+                    return new SystemInstanceValueProvider(systemTypeGuid);
+
+                if (itfType.IsAssignableFrom(SystemInstances.MessageService.GetType()))
+                    return new MessageServiceValueProvider();
+
+                IValueProvider vp = null;
+                foreach (var t in ComponentManager.Singleton.PlugInCache.GetTypesFast(itfTypeFullName, null))
+                {
+                    if (vp == null)
+                    {
+                        vp = new TypeGuidValueProvider(t);
+                    }
+                    else
+                    {
+                        var exception = new MultipleInstancesForInjectionException(member);
+                        _testbed.RaiseDependencyError(exception, itfType, null);
+                        return new ExceptionValueProvider(exception);
+                    }
+                }
+
+                if (vp != null)
+                    results = new[] { vp };
+            }
+
+            if (results == null && optional)
+            {
+                return new NullValueProvider();
+            }
+            else if (results == null)
+            {
+                var exception = new NoInstanceForInjectionException(member);
+                _testbed.RaiseDependencyError(exception, itfType, null);
+                return new ExceptionValueProvider(exception);
+            }
+            else if (results.Length == 1)
+            {
+                return results[0];
+            }
+            else
+            {
+                var exception = new MultipleInstancesForInjectionException(member);
+                _testbed.RaiseDependencyError(exception, itfType, null);
+                return new ExceptionValueProvider(exception);
+            }
+        }
+
+        public IValueProvider ResolveSpecific(Guid typeGuid, Type itfType, bool optional, MemberInfo member)
+        {
+            IValueProvider result;
+            _mockFactoriesByTypeGuid.TryGetValue(typeGuid, out result);
+
+            if (result == null)
+            {
+                // We did not find an implementation among the mocks.
+                // Try the additional plug-ins, if any. This discovery is similar to the original
+                // component model implementation. In the case of any doubts, look there.
+
+                var exists = false;
+                try
+                {
+                    exists = ComponentManager.Singleton.TryGetPlugInType(typeGuid, true) != null;
+                }
+                catch { }
+
+                if (exists)
+                    return new TypeGuidValueProvider(typeGuid);
+            }
+
+            if (result == null && optional)
+            {
+                return new NullValueProvider();
+            }
+            else if (result == null)
+            {
+                var exception = new NoInstanceForInjectionException(member);
+                _testbed.RaiseDependencyError(exception, itfType, typeGuid);
+                return new ExceptionValueProvider(exception);
+            }
+            else
+            {
+                return result;
+            }
+        }
+
+        public IEnumerable<IValueProvider> ResolveMultiple(Type itfType, bool optional, MemberInfo member)
+        {
+            IValueProvider[] results;
+            _mockFactoriesByItfType.TryGetValue(itfType, out results);
+
+            if (results == null)
+            {
+                // We did not find an implementation among the mocks.
+                // Try the additional plug-ins, if any. This discovery is similar to the original
+                // component model implementation. In the case of any doubts, look there.
+
+                var typeGuids = ComponentManager.Singleton.PlugInCache.GetTypesFast(itfType.FullName, null);
+                if (Enumerable.Any(typeGuids))
+                    results = typeGuids.Select(typeGuid => new TypeGuidValueProvider(typeGuid)).ToArray();
+            }
+
+            if (results == null && optional)
+            {
+                return new IValueProvider[0];
+            }
+            else if (results == null)
+            {
+                var exception = new NoInstanceForInjectionException(member);
+                _testbed.RaiseDependencyError(exception, itfType, null);
+                return new ExceptionValueProvider(exception);
+            }
+            else
+            {
+                return results;
+            }
+        }
+
+        public void ResolveProfile(MemberInfo member, out string profileName, out Profile profile)
+        {
+            profileName = _profileName;
+            profile = _profile;
+        }
+
+        public IWinFormWrapper ResolveFrameForm(MemberInfo member)
+        {
+            if (_frameForm == null)
+                _frameForm = ResolveSingle(typeof(IWinFormWrapper), true, member).CreateInstance<IWinFormWrapper>();
+
+            return _frameForm;
+        }
+
+        private Testbed _testbed;
+        private IDictionary<Guid, IValueProvider> _mockFactoriesByTypeGuid;
+        private IDictionary<Type, IValueProvider[]> _mockFactoriesByItfType;
+        private string _profileName;
+        private Profile _profile;
+        private IWinFormWrapper _frameForm;
+        private TypeInformation[] _systemTypeInfos;
+    }
+
+    interface IValueProvider
+    {
+        Guid TypeGuid { get; }
+        Type Type { get; }
+        TypeInformation TypeInformation { get; }
+        T CreateInstance<T>() where T : class;
+    }
+
+    class MockValueProvider : IValueProvider
+    {
+        public MockValueProvider(Guid typeGuid, object prototype, MockConstructor mockConstructor)
+        {
+            if (mockConstructor == null)
+                throw new ArgumentNullException(nameof(mockConstructor));
+
+            TypeGuid = typeGuid;
+            Type = prototype.GetType();
+            _mockConstructor = mockConstructor;
+
+            // As the TypeInformation type does not have any suitable public constructors for us,
+            // we must (partially) construct the instance using reflection and fake XML,
+            // unfortunately.
+
+            XmlDocument plugInInfoXmlDocument = new XmlDocument();
+            plugInInfoXmlDocument.AppendChild(plugInInfoXmlDocument.CreateElement("ROOT"));
+            plugInInfoXmlDocument.DocumentElement.SetAttribute("AssemblyName", Assembly.GetExecutingAssembly().FullName);
+            plugInInfoXmlDocument.DocumentElement.SetAttribute("CodeBase", Assembly.GetExecutingAssembly().CodeBase);
+            plugInInfoXmlDocument.DocumentElement.SetAttribute("GUID", XmlConvert.ToString(Guid.NewGuid()));
+            plugInInfoXmlDocument.DocumentElement.SetAttribute("Version", "1.0.0.0");
+            plugInInfoXmlDocument.DocumentElement.SetAttribute("System", XmlConvert.ToString(false));
+            plugInInfoXmlDocument.DocumentElement.SetAttribute("Company", "The Unit Test Company");
+            plugInInfoXmlDocument.DocumentElement.SetAttribute("Copyright", "Copyright © 2016 by The Unit Test Company. All rights reserved.");
+            plugInInfoXmlDocument.DocumentElement.SetAttribute("Description", "Generated mock plug-in for the Unit Test Framework.");
+            plugInInfoXmlDocument.DocumentElement.SetAttribute("Product", "CODESYS Automation Platform Unit Test Framework");
+            plugInInfoXmlDocument.DocumentElement.SetAttribute("Title", "CODESYS Automation Platform Unit Test Framework");
+            XmlElement typeElement = plugInInfoXmlDocument.CreateElement("Type");
+            plugInInfoXmlDocument.DocumentElement.AppendChild(typeElement);
+            typeElement.SetAttribute("GUID", XmlConvert.ToString(TypeGuid));
+            typeElement.SetAttribute("Name", Type.FullName);
+            typeElement.SetAttribute("ItfTypes", string.Join(";", prototype.GetType().GetInterfaces().Select(itf => itf.FullName)));
+
+            var plugInInfoCtor = typeof(PlugInInformation).GetConstructor(
+                BindingFlags.Instance | BindingFlags.NonPublic,
+                null,
+                new[] { typeof(XmlElement) },
+                null);
+            var plugInInfo = (PlugInInformation)plugInInfoCtor.Invoke(new object[] { plugInInfoXmlDocument.DocumentElement });
+
+            TypeInformation = plugInInfo.TypeInfosFast.First(); // We added exactly one type above, so we should able to get it back now.
+        }
+
+        public Guid TypeGuid { get; private set; }
+        public Type Type { get; private set; }
+        public TypeInformation TypeInformation { get; private set; }
+
+        public T CreateInstance<T>() where T : class
+        {
+            if (_mockConstructor.SystemInstance)
+            {
+                if (_systemInstance == null)
+                    _systemInstance = _mockConstructor.CreateFunc();
+                return (T)_systemInstance;
+            }
+            else
+            {
+                return (T)_mockConstructor.CreateFunc();
+            }
+        }
+
+        private MockConstructor _mockConstructor;
+        private object _systemInstance;
+    }
+
+    class NullValueProvider : IValueProvider
+    {
+        public Guid TypeGuid
+        {
+            get { return Guid.Empty; }
+        }
+
+        public Type Type
+        {
+            get { return null; }
+        }
+
+        public TypeInformation TypeInformation
+        {
+            get { return null; }
+        }
+
+        public T CreateInstance<T>() where T : class
+        {
+            return null;
+        }
+    }
+
+    class SystemInstanceValueProvider : IValueProvider
+    {
+        public SystemInstanceValueProvider(Guid typeGuid)
+        {
+            TypeGuid = typeGuid;
+        }
+
+        public Type Type
+        {
+            get { return ComponentManager.Singleton.TryGetPlugInType(TypeGuid, true); }
+        }
+
+        public Guid TypeGuid { get; private set; }
+
+        public TypeInformation TypeInformation
+        {
+            get { return ComponentManager.Singleton.PlugInCache.GetTypeInformation(TypeGuid, null); }
+        }
+
+        public T CreateInstance<T>() where T : class
+        {
+            return ComponentManager.Singleton.TryGetSystemInstance<T>();
+        }
+    }
+
+    class MessageServiceValueProvider : IValueProvider
+    {
+        public Type Type
+        {
+            get { return SystemInstances.MessageService.GetType(); }
+        }
+
+        public Guid TypeGuid
+        {
+            get
+            {
+                if (s_typeGuid == null)
+                {
+                    var attr = TypeGuidAttribute.FromObject(SystemInstances.MessageService);
+                    s_typeGuid = attr != null ? attr.Guid : Guid.Empty;
+                }
+                return s_typeGuid.Value;
+            }
+        }
+
+        private static Guid? s_typeGuid;
+
+        public TypeInformation TypeInformation
+        {
+            get { return ComponentManager.Singleton.PlugInCache.GetTypeInformation(TypeGuid, null); }
+        }
+
+        public T CreateInstance<T>() where T : class
+        {
+            return (T)SystemInstances.MessageService;
+        }
+    }
+
+    class TypeGuidValueProvider : IValueProvider
+    {
+        public TypeGuidValueProvider(Guid typeGuid)
+        {
+            TypeGuid = typeGuid;
+        }
+
+        public Type Type
+        {
+            get { return ComponentManager.Singleton.TryGetPlugInType(TypeGuid, true); }
+        }
+
+        public Guid TypeGuid { get; private set; }
+
+        public TypeInformation TypeInformation
+        {
+            get { return ComponentManager.Singleton.PlugInCache.GetTypeInformation(TypeGuid, null); }
+        }
+
+        public T CreateInstance<T>() where T : class
+        {
+            return ComponentManager.Singleton.TryCreateInstance<T>(TypeGuid);
+        }
+    }
+
+    class ExceptionValueProvider : IValueProvider, IEnumerable<IValueProvider>
+    {
+        public ExceptionValueProvider(Exception exception)
+        {
+            _exception = exception;
+        }
+
+        public Type Type
+        {
+            get { throw _exception; }
+        }
+
+        public Guid TypeGuid
+        {
+            get { throw _exception; }
+        }
+
+        public TypeInformation TypeInformation
+        {
+            get { throw _exception; }
+        }
+
+        public T CreateInstance<T>() where T : class
+        {
+            throw _exception;
+        }
+
+        public IEnumerator<IValueProvider> GetEnumerator()
+        {
+            throw _exception;
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            throw _exception;
+        }
+
+        private Exception _exception;
+    }
+}