Skip to content

Commit

Permalink
Multiple assemblies (#5)
Browse files Browse the repository at this point in the history
* Allow multiple assemblies

* Add tests

* Add configuration helpers

* Generate diagrams

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
maisiesadler and github-actions[bot] authored Jun 24, 2021
1 parent 6cefad4 commit c3a06c1
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 20 deletions.
2 changes: 0 additions & 2 deletions example-outputs/mermaidmd.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
```mermaid
classDiagram
DependencyTree --> DependencyTreeConfig
DependencyTreeConfig --> Assembly
DependencyTreeConfig --> IConfiguration
```
3 changes: 0 additions & 3 deletions example-outputs/plantuml.puml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,5 @@

class DependencyTree
DependencyTree ---> DependencyTreeConfig
class DependencyTreeConfig
DependencyTreeConfig ---> Assembly
DependencyTreeConfig ---> IConfiguration

@enduml
2 changes: 0 additions & 2 deletions example-outputs/yuml.yuml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,3 @@
// {generate:true}

[DependencyTree]->[DependencyTreeConfig]
[DependencyTreeConfig]->[Assembly]
[DependencyTreeConfig]->[IConfiguration]
2 changes: 1 addition & 1 deletion example-outputs/yumlmd.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<img src="http://yuml.me/diagram/scruffy/class/[DependencyTree]-&gt;[DependencyTreeConfig], [DependencyTreeConfig]-&gt;[Assembly], [DependencyTreeConfig]-&gt;[IConfiguration]" />
<img src="http://yuml.me/diagram/scruffy/class/[DependencyTree]-&gt;[DependencyTreeConfig]" />
18 changes: 17 additions & 1 deletion src/Lively.Tests/ConfigurationToDependencyTreeConfigTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Xunit;
using System;
using System.Reflection;

namespace Lively.Tests
{
Expand All @@ -8,9 +9,24 @@ public class DependencyTreeConfigTests
[Fact]
public void NullAssemblyThrows()
{
Assert.Throws<ArgumentNullException>(() => new DependencyTreeConfig(null));
Assembly assembly = null;
Assert.Throws<ArgumentNullException>(() => new DependencyTreeConfig(assembly));
}

[Fact]
public void NoAssemblyThrows()
{
var assemblies = new Assembly[] { };
Assert.Throws<InvalidOperationException>(() => new DependencyTreeConfig(assemblies));
}

// [Fact]
// public void NoNonNullAssemblyThrows()
// {
// var assemblies = new Assembly[] { null };
// Assert.Throws<InvalidOperationException>(() => new DependencyTreeConfig(assemblies));
// }

[Fact]
public void StartupNotSpecifiedDefaultIsStartup()
{
Expand Down
42 changes: 42 additions & 0 deletions src/Lively.Tests/DependencyTreeTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using Lively.Resolvers;
using Lively.TypeDescriptions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
Expand Down Expand Up @@ -188,6 +189,47 @@ public void RecursiveGenericDependencyDoesNotStackOverflow()
Assert.Null(node.Children);
}

[Fact]
public void FindTypeInAnotherAssemblyNotReferenced_DoesNotWork()
{
var assembly = this.GetType().Assembly;
var config = new DependencyTreeConfig(assembly);
var testTypeName = "Lively.Tests.DependencyTreeTests+ExampleType";
var livelyTypeName = "Lively.DependencyTreeConfig";

var tree = new DependencyTree(config);
var testTypeDepTree = tree.GetDependencies(testTypeName);
var livelyTypeDepTree = tree.GetDependencies(livelyTypeName);

Assert.NotNull(testTypeDepTree);
var testTypeDescription = Assert.IsType<ConcreteTypeDescription>(testTypeDepTree.Type);
Assert.Equal("Lively.Tests.DependencyTreeTests+ExampleType", testTypeDescription.FullName);
Assert.NotNull(livelyTypeDepTree);
var livelyTypeDescription = Assert.IsType<UnknownTypeDescription>(livelyTypeDepTree.Type);
Assert.Equal("Lively.DependencyTreeConfig", livelyTypeDescription.FullName);
}

[Fact]
public void LoadMultipleAssembliesAndFindTypes()
{
var testAssembly = this.GetType().Assembly;
var livelyAssembly = typeof(DependencyTreeConfig).Assembly;
var config = new DependencyTreeConfig(new[] { testAssembly, livelyAssembly });
var testTypeName = "Lively.Tests.DependencyTreeTests+ExampleType";
var livelyTypeName = "Lively.DependencyTreeConfig";

var tree = new DependencyTree(config);
var testTypeDepTree = tree.GetDependencies(testTypeName);
var livelyTypeDepTree = tree.GetDependencies(livelyTypeName);

Assert.NotNull(testTypeDepTree);
var testTypeDescription = Assert.IsType<ConcreteTypeDescription>(testTypeDepTree.Type);
Assert.Equal("Lively.Tests.DependencyTreeTests+ExampleType", testTypeDescription.FullName);
Assert.NotNull(livelyTypeDepTree);
var livelyTypeDescription = Assert.IsType<ConcreteTypeDescription>(livelyTypeDepTree.Type);
Assert.Equal("Lively.DependencyTreeConfig", livelyTypeDescription.FullName);
}

public class ExampleTypeWithDeps
{
public ExampleTypeWithDeps(ExampleType example) { }
Expand Down
41 changes: 41 additions & 0 deletions src/Lively/Configuration/DependencyTreeConfigExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Lively.Resolvers;
using Microsoft.Extensions.Configuration;

namespace Lively
{
public static class DependencyTreeConfigExtensions
{
public static IConfiguration EmptyConfiguration()
{
var cfgBuilder = new ConfigurationBuilder();
return cfgBuilder.Build();
}

// public static IConfiguration ConfigurationFromJsonFile(string assemblyConfigLocation)
// {
// var cfgBuilder = new ConfigurationBuilder();
// cfgBuilder.AddJsonFile(assemblyConfigLocation, optional: false, reloadOnChange: false);
// return cfgBuilder.Build();
// }

public static IEnumerable<Assembly> GetAllAssembliesInDirectory(
string path,
Func<string, bool> patternMatchingFn = null)
{
patternMatchingFn ??= _ => true;
foreach (var file in Directory.GetFiles(path))
{
var split = file.Split(".");
var fileEnding = split[split.Length - 1];
if (fileEnding == "dll" && patternMatchingFn(file))
{
yield return Assembly.LoadFrom(file);
}
}
}
}
}
19 changes: 15 additions & 4 deletions src/Lively/DependencyTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ public class DependencyTree
{
private readonly int _maxDepth = 100;

public Assembly Assembly { get; }
public IReadOnlyList<Assembly> Assemblies { get; }
public IInterfaceResolver InterfaceResolver { get; }
public HashSet<string> SkipTypes { get; set; }

public DependencyTree(DependencyTreeConfig config)
{
if (config == null) throw new ArgumentNullException(nameof(config));
Assembly = config.Assembly ?? throw new ArgumentNullException(nameof(config.Assembly));
Assemblies = config.Assemblies ?? throw new ArgumentNullException(nameof(config.Assemblies));
SkipTypes = config.SkipTypes;
InterfaceResolver = config.CreateInterfaceResolver == null
? NoInterfaceResolver.Create()
Expand All @@ -31,8 +31,7 @@ public DependencyTreeNode GetDependencies(string typeName, string name = "root")

private DependencyTreeNode GetDependencies(string typeName, string name, int depth)
{
var type = Assembly.GetType(typeName);
if (type == null)
if (!TryFindType(typeName, out var type))
{
var td = new UnknownTypeDescription(typeName);
return new DependencyTreeNode(name, td, DependencyTreeError.UnknownType);
Expand Down Expand Up @@ -93,6 +92,18 @@ private DependencyTreeNode GetDependencies(string typeName, string name, int dep
return (children, null);
}

private bool TryFindType(string typeName, out Type type)
{
foreach (var assembly in Assemblies)
{
type = assembly.GetType(typeName);
if (type != null) return true;
}

type = null;
return false;
}

private static Type NonGenericTypeName(Type type)
{
return type.IsGenericType
Expand Down
12 changes: 9 additions & 3 deletions src/Lively/DependencyTreeConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Lively
{
public class DependencyTreeConfig
{
public Assembly Assembly { get; }
public IReadOnlyList<Assembly> Assemblies { get; }
public IConfiguration Configuration { get; }
public string StartupName { get; set; } = "Startup";
public HashSet<string> SkipTypes { get; set; }
Expand All @@ -17,14 +17,20 @@ public class DependencyTreeConfig
public DependencyTreeConfig(
Assembly assembly,
IConfiguration configuration = null)
: this(new[] { assembly ?? throw new ArgumentNullException(nameof(assembly)) }, configuration) { }

public DependencyTreeConfig(
IReadOnlyList<Assembly> assemblies,
IConfiguration configuration = null)
{
Assembly = assembly ?? throw new ArgumentNullException(nameof(assembly));
Assemblies = assemblies ?? throw new ArgumentNullException(nameof(assemblies));
if (Assemblies.Count == 0) throw new InvalidOperationException();
Configuration = configuration;
}

public StartupInterfaceResolverConfig StartupConfig => new StartupInterfaceResolverConfig
{
Assembly = Assembly,
Assemblies = Assemblies,
Configuration = Configuration,
StartupName = StartupName,
SkipTypes = SkipTypes,
Expand Down
7 changes: 4 additions & 3 deletions src/Lively/Resolvers/StartupInterfaceResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ public class StartupInterfaceResolver : IInterfaceResolver
public StartupInterfaceResolver(StartupInterfaceResolverConfig config)
{
if (config == null) throw new ArgumentNullException(nameof(config));
if (config.Assembly == null) throw new ArgumentNullException(nameof(config.Assembly));
if (config.Assemblies == null) throw new ArgumentNullException(nameof(config.Assemblies));

var startupType = config.Assembly.GetTypes().FirstOrDefault(x => x.FullName == config.StartupName);
var assemblyTypes = config.AssemblyTypes;
var startupType = assemblyTypes.FirstOrDefault(x => x.FullName == config.StartupName);
if (startupType == null)
{
startupType = config.Assembly.GetTypes().FirstOrDefault(x => x.Name == config.StartupName);
startupType = assemblyTypes.FirstOrDefault(x => x.Name == config.StartupName);
}
if (startupType == null)
{
Expand Down
6 changes: 5 additions & 1 deletion src/Lively/Resolvers/StartupInterfaceResolverConfig.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.Configuration;

namespace Lively.Resolvers
{
public class StartupInterfaceResolverConfig
{
public Assembly Assembly { get; set; }
public IReadOnlyList<Assembly> Assemblies { get; set; }
public IConfiguration Configuration { get; set; }
public HashSet<string> SkipTypes { get; set; }
public string StartupName { get; set; } = "Startup";

public IEnumerable<Type> AssemblyTypes => Assemblies.SelectMany(a => a.GetTypes());
}
}

0 comments on commit c3a06c1

Please sign in to comment.