Moves LightInjectValidation to common and validates the container as part of new tests

This commit is contained in:
Shannon
2020-03-13 15:59:04 +11:00
parent f57a2ec576
commit c9913f45a0
9 changed files with 181 additions and 151 deletions
@@ -91,7 +91,7 @@ namespace Umbraco.Core.Composing.LightInject
/// <summary>
/// Gets the LightInject container.
/// </summary>
protected ServiceContainer Container { get; }
public ServiceContainer Container { get; }
/// <inheritdoc cref="IRegister"/>
/// <inheritdoc cref="IFactory"/>
@@ -20,9 +20,9 @@ namespace Umbraco.Core.Composing
/// <returns></returns>
public static IRegister CreateFrom(IServiceCollection services, out IServiceProvider serviceProvider)
{
var liContainer = new ServiceContainer(ContainerOptions.Default.WithMicrosoftSettings());
serviceProvider = liContainer.CreateServiceProvider(services);
return new LightInjectContainer(liContainer);
var lightInjectContainer = new ServiceContainer(ContainerOptions.Default.WithMicrosoftSettings());
serviceProvider = lightInjectContainer.CreateServiceProvider(services);
return new LightInjectContainer(lightInjectContainer);
}
//TODO: The following can die when net framework is gone
+35
View File
@@ -0,0 +1,35 @@
using LightInject;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Umbraco.Tests.Common.Composing;
namespace Umbraco.Tests.Common
{
public class Assertions
{
public static void AssertContainer(ServiceContainer container, bool reportOnly = false)
{
var results = container.Validate().ToList();
foreach (var resultGroup in results.GroupBy(x => x.Severity).OrderBy(x => x.Key))
{
Console.WriteLine($"{resultGroup.Key}: {resultGroup.Count()}");
}
foreach (var resultGroup in results.GroupBy(x => x.Severity).OrderBy(x => x.Key))
{
foreach (var result in resultGroup)
{
Console.WriteLine();
Console.Write(result.ToText());
}
}
if (!reportOnly)
Assert.AreEqual(0, results.Count);
}
}
}
@@ -35,7 +35,7 @@ using ServiceMap = System.Collections.Generic.Dictionary<System.Type, System.Col
http://twitter.com/bernhardrichter
******************************************************************************/
namespace Umbraco.Tests.Composing
namespace Umbraco.Tests.Common.Composing
{
public static class LightInjectValidation
{
@@ -110,7 +110,6 @@ Ensure that 'NameSpace.IBar' is registered with a lifetime that is equal to or h
{
var registration = GetServiceRegistration(serviceMap, validationTarget);
if (registration == null)
{
if (validationTarget.ServiceType.IsFunc() || validationTarget.ServiceType.IsLazy())
{
var serviceType = validationTarget.ServiceType.GenericTypeArguments[0];
@@ -118,17 +117,13 @@ Ensure that 'NameSpace.IBar' is registered with a lifetime that is equal to or h
registration = GetServiceRegistration(serviceMap, underlyingvalidationTarget);
if (registration != null)
{
return;
}
if (serviceMap.ContainsAmbiguousRegistrationFor(serviceType))
{
result.Add(new ValidationResult("", ValidationSeverity.Ambiguous, underlyingvalidationTarget));
}
else
{
string message = string.Format(MissingDeferredDependency, validationTarget.ServiceType, underlyingvalidationTarget.ServiceType);
var message = string.Format(MissingDeferredDependency, validationTarget.ServiceType, underlyingvalidationTarget.ServiceType);
result.Add(new ValidationResult(message, ValidationSeverity.MissingDependency, underlyingvalidationTarget));
}
}
@@ -140,21 +135,14 @@ Ensure that 'NameSpace.IBar' is registered with a lifetime that is equal to or h
if (registrations.Any()) return;
// strict: there has to be at least 1
string message = string.Format(MissingDeferredDependency, validationTarget.ServiceType, underlyingvalidationTarget.ServiceType);
var message = string.Format(MissingDeferredDependency, validationTarget.ServiceType, underlyingvalidationTarget.ServiceType);
result.Add(new ValidationResult(message, ValidationSeverity.MissingDependency, underlyingvalidationTarget));
}
else
{
if (serviceMap.ContainsAmbiguousRegistrationFor(validationTarget.ServiceType))
{
result.Add(new ValidationResult("", ValidationSeverity.Ambiguous, validationTarget));
}
else
{
result.Add(new ValidationResult("", ValidationSeverity.MissingDependency, validationTarget));
}
}
}
result.Add(new ValidationResult("", ValidationSeverity.Ambiguous, validationTarget));
else
result.Add(new ValidationResult("", ValidationSeverity.MissingDependency, validationTarget));
else
{
ValidateDisposable(validationTarget, result, registration);
@@ -206,24 +194,16 @@ Ensure that 'NameSpace.IBar' is registered with a lifetime that is equal to or h
private static ServiceRegistration GetServiceRegistration(ServiceMap serviceMap, ValidationTarget validationTarget)
{
if (!serviceMap.TryGetValue(validationTarget.ServiceType, out var registrations))
{
return null;
}
if (registrations.TryGetValue(string.Empty, out var registration))
{
return registration;
}
if (registrations.Count == 1)
{
return registrations.Values.First();
}
if (registrations.TryGetValue(validationTarget.ServiceName, out registration))
{
return registration;
}
return null;
}
@@ -231,22 +211,16 @@ Ensure that 'NameSpace.IBar' is registered with a lifetime that is equal to or h
private static string GetLifetimeName(ILifetime lifetime)
{
if (lifetime == null)
{
return "Transient";
}
return lifetime.GetType().Name;
}
private static int GetLifespan(ILifetime lifetime)
{
if (lifetime == null)
{
return 0;
}
if (LifeSpans.TryGetValue(lifetime.GetType(), out var lifespan))
{
return lifespan;
}
return 0;
}
}
@@ -274,9 +248,7 @@ Ensure that 'NameSpace.IBar' is registered with a lifetime that is equal to or h
if (serviceType.GetTypeInfo().IsGenericType && serviceType.GetTypeInfo().ContainsGenericParameters)
{
ServiceType = serviceType.GetGenericTypeDefinition();
}
}
@@ -340,9 +312,7 @@ Ensure that 'NameSpace.IBar' is registered with a lifetime that is equal to or h
public static bool ContainsAmbiguousRegistrationFor(this ServiceMap serviceMap, Type serviceType)
{
if (!serviceMap.TryGetValue(serviceType, out var registrations))
{
return false;
}
return registrations.Count > 1;
}
}
@@ -0,0 +1,106 @@
using System;
using System.Text;
using Umbraco.Core;
namespace Umbraco.Tests.Common.Composing
{
public static class ValidationResultExtensions
{
public static string ToText(this ValidationResult result)
{
var text = new StringBuilder();
text.AppendLine($"{result.Severity}: {WordWrap(result.Message, 120)}");
var target = result.ValidationTarget;
text.Append("\tsvce: ");
text.Append(target.ServiceName);
text.Append(target.DeclaringService.ServiceType);
if (!target.DeclaringService.ServiceName.IsNullOrWhiteSpace())
{
text.Append(" '");
text.Append(target.DeclaringService.ServiceName);
text.Append("'");
}
text.Append(" (");
if (target.DeclaringService.Lifetime == null)
text.Append("Transient");
else
text.Append(target.DeclaringService.Lifetime.ToString().TrimStart("LightInject.").TrimEnd("Lifetime"));
text.AppendLine(")");
text.Append("\timpl: ");
text.Append(target.DeclaringService.ImplementingType);
text.AppendLine();
text.Append("\tparm: ");
text.Append(target.Parameter);
text.AppendLine();
return text.ToString();
}
private static string WordWrap(string text, int width)
{
int pos, next;
var sb = new StringBuilder();
var nl = Environment.NewLine;
// Lucidity check
if (width < 1)
return text;
// Parse each line of text
for (pos = 0; pos < text.Length; pos = next)
{
// Find end of line
var eol = text.IndexOf(nl, pos, StringComparison.Ordinal);
if (eol == -1)
next = eol = text.Length;
else
next = eol + nl.Length;
// Copy this line of text, breaking into smaller lines as needed
if (eol > pos)
{
do
{
var len = eol - pos;
if (len > width)
len = BreakLine(text, pos, width);
if (pos > 0)
sb.Append("\t\t");
sb.Append(text, pos, len);
sb.Append(nl);
// Trim whitespace following break
pos += len;
while (pos < eol && char.IsWhiteSpace(text[pos]))
pos++;
} while (eol > pos);
}
else sb.Append(nl); // Empty line
}
return sb.ToString();
}
private static int BreakLine(string text, int pos, int max)
{
// Find last whitespace in line
var i = max - 1;
while (i >= 0 && !char.IsWhiteSpace(text[pos + i]))
i--;
if (i < 0)
return max; // No whitespace found; break at maximum length
// Find start of whitespace
while (i >= 0 && char.IsWhiteSpace(text[pos + i]))
i--;
// Return length of text before whitespace
return i + 1;
}
}
}
@@ -8,6 +8,7 @@ using Umbraco.Core.Composing;
using Umbraco.Core.Composing.LightInject;
using Umbraco.Core.Configuration;
using Umbraco.Core.Persistence;
using Umbraco.Tests.Common;
using Umbraco.Tests.Integration.Implementations;
namespace Umbraco.Tests.Integration
@@ -56,6 +57,8 @@ namespace Umbraco.Tests.Integration
Assert.AreNotSame(foo1, foo2);
// These are the same because the umbraco container wraps the light inject container
Assert.AreSame(foo2, foo3);
Assertions.AssertContainer(umbracoContainer.Container);
}
private class Foo
+18 -10
View File
@@ -1,10 +1,14 @@
using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework;
using System;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Composing.LightInject;
using Umbraco.Core.Configuration;
using Umbraco.Core.Runtime;
using Umbraco.Tests.Common;
using Umbraco.Tests.Common.Composing;
using Umbraco.Tests.Integration.Implementations;
namespace Umbraco.Tests.Integration
@@ -19,7 +23,7 @@ namespace Umbraco.Tests.Integration
var services = new ServiceCollection();
// LightInject / Umbraco
var umbracoContainer = RegisterFactory.CreateFrom(services, out var lightInjectServiceProvider);
var umbracoContainer = (LightInjectContainer)RegisterFactory.CreateFrom(services, out var lightInjectServiceProvider);
// Dependencies needed for Core Runtime
var testHelper = new TestHelper();
@@ -35,17 +39,21 @@ namespace Umbraco.Tests.Integration
Assert.IsNull(coreRuntime.State.BootFailedException);
Assert.AreEqual(RuntimeLevel.Install, coreRuntime.State.Level);
Assert.IsTrue(MyComposer.IsComposed);
}
}
[RuntimeLevel(MinLevel = RuntimeLevel.Install)]
public class MyComposer : IUserComposer
{
public void Compose(Composition composition)
{
IsComposed = true;
Assertions.AssertContainer(umbracoContainer.Container, reportOnly: true); // TODO Change that to false eventually when we clean up the container
}
public static bool IsComposed { get; private set; }
[RuntimeLevel(MinLevel = RuntimeLevel.Install)]
public class MyComposer : IUserComposer
{
public void Compose(Composition composition)
{
IsComposed = true;
}
public static bool IsComposed { get; private set; }
}
}
}
+9 -100
View File
@@ -25,7 +25,6 @@ using Umbraco.Core.Runtime;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
using Umbraco.Core.Sync;
using Umbraco.Tests.Composing;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.Cache;
@@ -36,6 +35,7 @@ using Umbraco.Web.Runtime;
using File = System.IO.File;
using Current = Umbraco.Web.Composing.Current;
using Umbraco.Tests.Common;
using Umbraco.Tests.Common.Composing;
namespace Umbraco.Tests.Runtimes
{
@@ -63,7 +63,7 @@ namespace Umbraco.Tests.Runtimes
var profilingLogger = new ProfilingLogger(logger, profiler);
var appCaches = AppCaches.Disabled;
var databaseFactory = new UmbracoDatabaseFactory(logger, new Lazy<IMapperCollection>(() => factory.GetInstance<IMapperCollection>()), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator);
var typeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
var ioHelper = TestHelper.IOHelper;
var hostingEnvironment = Mock.Of<IHostingEnvironment>();
var typeLoader = new TypeLoader(ioHelper, typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger);
@@ -73,6 +73,7 @@ namespace Umbraco.Tests.Runtimes
var runtimeState = new RuntimeState(logger, null, new Lazy<IMainDom>(() => mainDom), new Lazy<IServerRegistrar>(() => factory.GetInstance<IServerRegistrar>()), umbracoVersion, hostingEnvironment, backOfficeInfo);
var configs = TestHelper.GetConfigs();
var variationContextAccessor = TestHelper.VariationContextAccessor;
// create the register and the composition
var register = TestHelper.GetRegister();
@@ -80,7 +81,8 @@ namespace Umbraco.Tests.Runtimes
composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState, typeFinder, ioHelper, umbracoVersion, TestHelper.DbProviderFactoryCreator);
// create the core runtime and have it compose itself
var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom);coreRuntime.Compose(composition);
var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom, typeFinder);
coreRuntime.Compose(composition);
// determine actual runtime level
runtimeState.DetermineRuntimeLevel(databaseFactory, logger);
@@ -274,7 +276,7 @@ namespace Umbraco.Tests.Runtimes
composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState, typeFinder, ioHelper, umbracoVersion, TestHelper.DbProviderFactoryCreator);
// create the core runtime and have it compose itself
var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom);
var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom, typeFinder);
coreRuntime.Compose(composition);
// get the components
@@ -313,107 +315,14 @@ namespace Umbraco.Tests.Runtimes
foreach (var result in resultGroup)
{
Console.WriteLine();
Console.Write(ToText(result));
Console.Write(result.ToText());
}
Assert.AreEqual(0, results.Count);
}
private static string ToText(ValidationResult result)
{
var text = new StringBuilder();
text.AppendLine($"{result.Severity}: {WordWrap(result.Message, 120)}");
var target = result.ValidationTarget;
text.Append("\tsvce: ");
text.Append(target.ServiceName);
text.Append(target.DeclaringService.ServiceType);
if (!target.DeclaringService.ServiceName.IsNullOrWhiteSpace())
{
text.Append(" '");
text.Append(target.DeclaringService.ServiceName);
text.Append("'");
}
text.Append(" (");
if (target.DeclaringService.Lifetime == null)
text.Append("Transient");
else
text.Append(target.DeclaringService.Lifetime.ToString().TrimStart("LightInject.").TrimEnd("Lifetime"));
text.AppendLine(")");
text.Append("\timpl: ");
text.Append(target.DeclaringService.ImplementingType);
text.AppendLine();
text.Append("\tparm: ");
text.Append(target.Parameter);
text.AppendLine();
return text.ToString();
}
private static string WordWrap(string text, int width)
{
int pos, next;
var sb = new StringBuilder();
var nl = Environment.NewLine;
// Lucidity check
if (width < 1)
return text;
// Parse each line of text
for (pos = 0; pos < text.Length; pos = next)
{
// Find end of line
var eol = text.IndexOf(nl, pos, StringComparison.Ordinal);
if (eol == -1)
next = eol = text.Length;
else
next = eol + nl.Length;
// Copy this line of text, breaking into smaller lines as needed
if (eol > pos)
{
do
{
var len = eol - pos;
if (len > width)
len = BreakLine(text, pos, width);
if (pos > 0)
sb.Append("\t\t");
sb.Append(text, pos, len);
sb.Append(nl);
// Trim whitespace following break
pos += len;
while (pos < eol && char.IsWhiteSpace(text[pos]))
pos++;
} while (eol > pos);
}
else sb.Append(nl); // Empty line
}
return sb.ToString();
}
private static int BreakLine(string text, int pos, int max)
{
// Find last whitespace in line
var i = max - 1;
while (i >= 0 && !char.IsWhiteSpace(text[pos + i]))
i--;
if (i < 0)
return max; // No whitespace found; break at maximum length
// Find start of whitespace
while (i >= 0 && char.IsWhiteSpace(text[pos + i]))
i--;
// Return length of text before whitespace
return i + 1;
}
}
}
-1
View File
@@ -120,7 +120,6 @@
<Compile Include="Clr\ReflectionUtilitiesTests.cs" />
<Compile Include="Collections\OrderedHashSetTests.cs" />
<Compile Include="Composing\CompositionTests.cs" />
<Compile Include="Composing\LightInjectValidation.cs" />
<Compile Include="Composing\ContainerConformingTests.cs" />
<Compile Include="Configurations\GlobalSettingsTests.cs" />
<Compile Include="CoreThings\CallContextTests.cs" />