Skip to content
This repository has been archived by the owner on Jan 24, 2021. It is now read-only.

Scanning changes #953

Merged
merged 9 commits into from
Feb 19, 2013
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
8 changes: 6 additions & 2 deletions src/Nancy.Testing.Tests/ConfigurableBootstrapperFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,18 @@ public void Should_throw_exceptions_if_any_occur_in_route()
public void Should_run_application_startup_closure()
{
var date = new DateTime(2112,10,31);
var bootstrapper = new ConfigurableBootstrapper(with => with.ApplicationStartup((container, pipelines) =>

var bootstrapper = new ConfigurableBootstrapper(with =>
{
with.ApplicationStartup((container, pipelines) =>
{
pipelines.BeforeRequest += ctx =>
{
ctx.Items.Add("date", date);
return null;
};
}));
});
});

bootstrapper.Initialise();

Expand Down
40 changes: 26 additions & 14 deletions src/Nancy.Testing/ConfigurableBootstrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public class ConfigurableBootstrapper : NancyBootstrapperWithRequestContainerBas
/// </summary>
public static IList<string> TestAssemblySuffixes = new[] { "test", "tests", "unittests", "specs", "specifications" };

private bool allDiscoveredModules;

/// <summary>
/// Initializes a new instance of the <see cref="ConfigurableBootstrapper"/> class.
/// </summary>
Expand Down Expand Up @@ -223,7 +225,12 @@ protected override IEnumerable<ModuleRegistration> Modules
var moduleRegistrations =
this.GetModuleRegistrations().ToList();

return (moduleRegistrations.Any()) ? moduleRegistrations : base.Modules;
if (moduleRegistrations.Any())
{
return moduleRegistrations;
}

return this.allDiscoveredModules ? base.Modules : new ModuleRegistration[] { };
}
}

Expand Down Expand Up @@ -332,7 +339,16 @@ protected override TinyIoCContainer GetApplicationContainer()
/// <returns>INancyEngine implementation</returns>
protected override INancyEngine GetEngineInternal()
{
return this.ApplicationContainer.Resolve<INancyEngine>();
try
{
return this.ApplicationContainer.Resolve<INancyEngine>();
}
catch (InvalidOperationException ex)
{
throw new InvalidOperationException(
"Something went wrong when trying to satisfy one of the dependencies during composition, make sure that you've registered all new dependencies in the container and specified either a module to test, or set AllDiscoveredModules in the ConfigurableBootstrapper. Inspect the innerexception for more details.",
ex.InnerException);
}
}

/// <summary>
Expand Down Expand Up @@ -536,6 +552,13 @@ public ConfigurableBootstrapperConfigurator(ConfigurableBootstrapper bootstrappe
this.Diagnostics<DisabledDiagnostics>();
}

public ConfigurableBootstrapperConfigurator AllDiscoveredModules()
{
this.bootstrapper.allDiscoveredModules = true;

return this;
}

public ConfigurableBootstrapperConfigurator Binder(IBinder binder)
{
this.bootstrapper.registeredInstances.Add(
Expand Down Expand Up @@ -1620,17 +1643,6 @@ public ConfigurableBootstrapperConfigurator TrieNodeFactory<T>() where T : ITrie
return this;
}

/// <summary>
/// Configures the bootstrapper to add an assembly ignore predicate to the list
/// </summary>
/// <param name="ignoredPredicate">Ignore predicate</param>
/// <returns>A reference to the current <see cref="ConfigurableBootstrapperConfigurator"/>.</returns>
public ConfigurableBootstrapperConfigurator IgnoredAssembly(Func<Assembly, bool> ignoredPredicate)
{
this.bootstrapper.configuration.WithIgnoredAssembly(ignoredPredicate);
return this;
}

public ConfigurableBootstrapperConfigurator ApplicationStartup(Action<TinyIoCContainer, IPipelines> action)
{
this.bootstrapper.applicationStartupActions.Add(action);
Expand Down Expand Up @@ -1691,4 +1703,4 @@ public void RegisterModuleInstance(INancyModule module, string moduleKey)
}
}
}
}
}
10 changes: 8 additions & 2 deletions src/Nancy.Tests/Fakes/FakeDefaultNancyBootstrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,16 @@ public FakeDefaultNancyBootstrapper()

}

protected override IEnumerable<Func<Assembly, bool>> AutoRegisterIgnoredAssemblies
{
get
{
return base.AutoRegisterIgnoredAssemblies.Union(new Func<Assembly, bool>[] { asm => asm.FullName.StartsWith("TestAssembly") });
}
}
public FakeDefaultNancyBootstrapper(NancyInternalConfiguration configuration)
{
this.configuration = configuration.WithIgnoredAssembly(asm => asm.FullName.StartsWith("TestAssembly"));

this.configuration = configuration;
this.RequestContainerInitialisations = new Dictionary<NancyContext, int>();
}

Expand Down
15 changes: 0 additions & 15 deletions src/Nancy.Tests/Unit/Bootstrapper/NancyBootstrapperBaseFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,21 +217,6 @@ public void Should_register_application_registration_instance_registrations_into
this.bootstrapper.InstanceRegistrations.ShouldBeSameAs(instanceRegistrations);
}

[Fact]
public void Should_ingore_assemblies_specified_in_AppDomainAssemblyTypeScanner()
{
// Given
// When
AppDomainAssemblyTypeScanner.IgnoredAssemblies =
new Func<Assembly, bool>[]
{
asm => asm.FullName.StartsWith("mscorlib")
};

// Then
AppDomainAssemblyTypeScanner.TypesOf<IEnumerable>().Where(t => t.Assembly.FullName.StartsWith("mscorlib")).Count().ShouldEqual(0);
}

[Fact]
public void Should_allow_favicon_override()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,27 +66,5 @@ public void Should_not_be_valid_if_any_types_null()

result.ShouldBeFalse();
}

[Fact]
public void Should_allow_additional_ignored_assemblies()
{
Func<Assembly, bool> predicate = asm => asm.FullName.StartsWith("moo");
var config = NancyInternalConfiguration.Default.WithIgnoredAssembly(predicate);

var result = config.IgnoredAssemblies;

result.Any(p => p.Equals(predicate)).ShouldBeTrue();
}

[Fact]
public void Should_append_ignored_assembly_to_default()
{
Func<Assembly, bool> predicate = asm => asm.FullName.StartsWith("moo");
var config = NancyInternalConfiguration.Default.WithIgnoredAssembly(predicate);

var result = config.IgnoredAssemblies.Count();

result.ShouldEqual(NancyInternalConfiguration.DefaultIgnoredAssemblies.Count() + 1);
}
}
}
20 changes: 17 additions & 3 deletions src/Nancy.ViewEngines.Razor.Tests/RazorViewEngineFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;

using FakeItEasy;
using Xunit;

using Nancy.Bootstrapper;
using Nancy.Tests;
using Nancy.ViewEngines.Razor.Tests.Models;
using System.Threading;
using Nancy.Localization;

using Xunit;

public class RazorViewEngineFixture
{
Expand Down Expand Up @@ -156,6 +160,8 @@ public void RenderView_csharp_should_use_model_directive_for_strongly_typed_view
[Fact]
public void RenderView_csharp_should_be_able_to_use_a_model_from_another_assembly()
{
AppDomainAssemblyTypeScanner.AddAssembliesToScan("Nancy.ViewEngines.Razor.Tests.Models.dll");

// Given
var view = new StringBuilder()
.AppendLine("@model Nancy.ViewEngines.Razor.Tests.Models.Person")
Expand Down Expand Up @@ -183,6 +189,8 @@ public void RenderView_csharp_should_be_able_to_use_a_model_from_another_assembl
[Fact]
public void RenderView_csharp_should_be_able_to_use_a_using_statement()
{
AppDomainAssemblyTypeScanner.AddAssembliesToScan("Nancy.ViewEngines.Razor.Tests.Models");

// Given
var view = new StringBuilder()
.AppendLine("@model Nancy.ViewEngines.Razor.Tests.Models.Person")
Expand Down Expand Up @@ -213,6 +221,12 @@ public void RenderView_csharp_should_be_able_to_use_a_using_statement()
public void RenderView_csharp_should_be_able_to_find_the_model_when_a_null_model_is_passed()
{
// Given
AppDomainAssemblyTypeScanner.AssembliesToScan =
AppDomainAssemblyTypeScanner.DefaultAssembliesToScan.Union(new Func<Assembly, bool>[]
{
x =>
x.GetName().Name.StartsWith("Nancy")
});
var view = new StringBuilder()
.AppendLine("@model Nancy.ViewEngines.Razor.Tests.Models.Person")
.AppendLine(@"@{ var hobby = new Hobby { Name = ""Music"" }; }")
Expand Down
92 changes: 84 additions & 8 deletions src/Nancy/Bootstrapper/AppDomainAssemblyTypeScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,35 @@ static AppDomainAssemblyTypeScanner()
/// </summary>
private static bool nancyAssembliesLoaded;

private static IEnumerable<Func<Assembly, bool>> assembliesToScan;

/// <summary>
///
/// The default assemblies for scanning.
/// Includes the nancy assembly and anythign referencing a nancy assembly
/// </summary>
private static IEnumerable<Func<Assembly, bool>> ignoredAssemblies;
public static Func<Assembly, bool>[] DefaultAssembliesToScan = new Func<Assembly, bool>[]
{
x => x == nancyAssembly,
x => x.GetReferencedAssemblies().Any(r => r.Name.StartsWith("Nancy", StringComparison.OrdinalIgnoreCase))
};

/// <summary>
/// Gets or sets a set of rules for ignoring assemblies while scanning through them.
/// Gets or sets a set of rules for which assemblies are scanned
/// Defaults to just assemblies that have references to nancy, and nancy
/// itself.
/// Each item in the enumerable is a delegate that takes the assembly and
/// returns true if it is to be included. Returning false doesn't mean it won't
/// be included as a true from another delegate will take precedence.
/// </summary>
public static IEnumerable<Func<Assembly, bool>> IgnoredAssemblies
public static IEnumerable<Func<Assembly, bool>> AssembliesToScan
{
private get
{
return ignoredAssemblies;
return assembliesToScan ?? (assembliesToScan = DefaultAssembliesToScan);
}
set
{
ignoredAssemblies = value;
assembliesToScan = value;
UpdateTypes ();
}
}
Expand All @@ -81,6 +93,54 @@ public static IEnumerable<Assembly> Assemblies
}
}

/// <summary>
/// Add assemblies to the list of assemblies to scan for Nancy types
/// </summary>
/// <param name="assemblyNames">One or more assembly names</param>
public static void AddAssembliesToScan(params string[] assemblyNames)
{
var normalisedNames = GetNormalisedAssemblyNames(assemblyNames).ToArray();

foreach (var assemblyName in normalisedNames)
{
LoadAssemblies(assemblyName + ".dll");
LoadAssemblies(assemblyName + ".exe");
}

var scanningPredicates = normalisedNames.Select(s =>
{
return (Func<Assembly, bool>)(a => a.GetName().Name == s);
});

AssembliesToScan = AssembliesToScan.Union(scanningPredicates);
}

/// <summary>
/// Add assemblies to the list of assemblies to scan for Nancy types
/// </summary>
/// <param name="assemblies">One of more assemblies</param>
public static void AddAssembliesToScan(params Assembly[] assemblies)
{
foreach (var assembly in assemblies)
{
LoadAssemblies(assembly.GetName() + ".dll");
LoadAssemblies(assembly.GetName() + ".exe");
}

var scanningPredicates = assemblies.Select(an => (Func<Assembly, bool>)(a => a == an));

AssembliesToScan = AssembliesToScan.Union(scanningPredicates);
}

/// <summary>
/// Add predicates for determining which assemblies to scan for Nancy types
/// </summary>
/// <param name="predicates">One or more predicates</param>
public static void AddAssembliesToScan(params Func<Assembly, bool>[] predicates)
{
AssembliesToScan = AssembliesToScan.Union(predicates);
}

/// <summary>
/// Load assemblies from a the app domain base directory matching a given wildcard.
/// Assemblies will only be loaded if they aren't already in the appdomain.
Expand Down Expand Up @@ -108,7 +168,8 @@ public static void LoadAssemblies(string containingDirectory, string wildcardFil

var unloadedAssemblies =
Directory.GetFiles(containingDirectory, wildcardFilename).Where(
f => !existingAssemblyPaths.Contains(f, StringComparer.InvariantCultureIgnoreCase));
f => !existingAssemblyPaths.Contains(f, StringComparer.InvariantCultureIgnoreCase)).ToArray();


foreach (var unloadedAssembly in unloadedAssemblies)
{
Expand Down Expand Up @@ -138,7 +199,7 @@ from type in assembly.SafeGetExportedTypes()
private static void UpdateAssemblies()
{
assemblies = (from assembly in AppDomain.CurrentDomain.GetAssemblies()
where IgnoredAssemblies != null ? !IgnoredAssemblies.Any(asm => asm(assembly)) : true
where AssembliesToScan.Any(asm => asm(assembly))
where !assembly.IsDynamic
where !assembly.ReflectionOnly
select assembly).ToArray();
Expand Down Expand Up @@ -255,6 +316,21 @@ private static IEnumerable<string> GetAssemblyDirectories()
yield return AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
}
}

private static IEnumerable<string> GetNormalisedAssemblyNames(string[] assemblyNames)
{
foreach (var assemblyName in assemblyNames)
{
if (assemblyName.EndsWith(".dll") || assemblyName.EndsWith(".exe"))
{
yield return Path.GetFileNameWithoutExtension(assemblyName);
}
else
{
yield return assemblyName;
}
}
}
}

public static class AppDomainAssemblyTypeScannerExtensions
Expand Down
1 change: 0 additions & 1 deletion src/Nancy/Bootstrapper/NancyBootstrapperBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ protected virtual DiagnosticsConfiguration DiagnosticsConfiguration
/// </summary>
public void Initialise()
{
AppDomainAssemblyTypeScanner.IgnoredAssemblies = this.InternalConfiguration.IgnoredAssemblies;
AppDomainAssemblyTypeScanner.LoadNancyAssemblies();

if (this.InternalConfiguration == null)
Expand Down
Loading