TNG / ArchUnitNET

A C# architecture test library to specify and assert architecture rules in C# for automated testing.
Apache License 2.0
927 stars 61 forks source link

"Sequence contains no matching element" while trying to load recursively assembly that has c++ dependencies #218

Closed ivsavchenko closed 1 year ago

ivsavchenko commented 1 year ago

I get "Sequence contains no matching element" exception while trying to load recursively assembly that has c++ project dependencies.

The problem reside in TypeFactory class line 180 or so. typeDefinition.CustomAttributes.Any(att => att.AttributeType.FullName == typeof(UnsafeValueTypeAttribute).FullName) returns two attributes - UnsafeValueTypeAttribute and NativeCppClassAttribute typeDefinition FullName looks like .$ArrayType$$$BY0GC@$$CB_W

typeDefinition.Fields has no fields so trying to get them leads to an exception here: var arrayType = typeDefinition.Fields.First(field => field.Name == "FixedElementField").FieldType;

Stack trace looks like that: System.InvalidOperationException Sequence contains no matching element at System.Linq.ThrowHelper.ThrowNoMatchException() at System.Linq.Enumerable.First[TSource](IEnumerable1 source, Func2 predicate) at ArchUnitNET.Loader.TypeFactory.CreateTypeFromTypeReference(TypeReference typeReference, Boolean isStub) in \ArchUnitNET\Loader\TypeFactory.cs:line 188 at ArchUnitNET.Loader.TypeFactory.<>cDisplayClass7_0.b0(String s) in \ArchUnitNET\Loader\TypeFactory.cs:line 51 at ArchUnitNET.Loader.RegistryUtils.GetFromDictOrCreateAndAdd[T,TK](TK key, Dictionary2 dict, Func2 createFunc) in \ArchUnitNET\Loader\RegistryUtils.cs:line 21 at ArchUnitNET.Loader.TypeRegistry.GetOrCreateTypeFromTypeReference(TypeReference typeReference, Func2 createFunc) in \ArchUnitNET\Loader\TypeRegistry.cs:line 24 at ArchUnitNET.Loader.TypeFactory.GetOrCreateTypeFromTypeReference(TypeReference typeReference) in \ArchUnitNET\Loader\TypeFactory.cs:line 50 at ArchUnitNET.Loader.ArchBuilder.<>cDisplayClass15_0.b3(TypeDefinition typeDefinition) in \ArchUnitNET\Loader\ArchBuilder.cs:line 84 at ArchUnitNET.Domain.Extensions.EnumerableExtensions.ForEach[T](IEnumerable1 source, Action1[] actions) in \ArchUnitNET\Domain\Extensions\EnumerableExtensions.cs:line 21 at ArchUnitNET.Loader.ArchBuilder.LoadTypesForModule(ModuleDefinition module, String namespaceFilter) in \ArchUnitNET\Loader\ArchBuilder.cs:line 79 at ArchUnitNET.Loader.ArchLoader.LoadModule(String fileName, String nameSpace, Boolean includeDependencies, Boolean recursive, FilterFunc filterFunc) in \ArchUnitNET\Loader\ArchLoader.cs:line 144 at ArchUnitNET.Loader.ArchLoader.LoadAssembliesRecursively(IEnumerable1 assemblies, FilterFunc filterFunc) in \ArchUnitNET\Loader\ArchLoader.cs:line 205 at Architecture.Tests.ArchUnitExtensions.LoadAssemblyRecursively(ArchLoader loader, String assemblyName)

fgather commented 1 year ago

Hi @ivsavchenko ! Thanks for the PR ! It looks good to me. Could you also add a test reproducing the issue to prevent regressions?

ivsavchenko commented 1 year ago

Hi @fgather ! Thanks for a quick response. I've added test that validates the case. The only problem is that configured Travis can't build c++/cli projects and build with such project failed with error "MSB4019: The imported project "/Microsoft.Cpp.Default.props" was not found". So in the latest version I've just added prebuilt dll that contains managed c++ code (add added cpp code itself as a comment) into tests so no built action require. Is that suitable for you?

fgather commented 1 year ago

Hi @ivsavchenko ,

thanks for adding the test. The binary is not really beautiful, but let's be pragmatic :)

Best, Florian

ivsavchenko commented 1 year ago

@fgather I completely agree with you about the binary in tests, I've tried few approaches to keep c++ in separate project in solution instead of binary, but all in all only Travis changes would help, so... yeah. Thanks for the approved PR!