IMetadataEntity not returning useful custom attributes

Hi folks. As part of the xunitcontrib project on CodePlex, I'm writing the unit test runner for xUnit.net. I've run across something a little odd in my implementation of IUnitTestProvider.ExploreAssembly. Given an IMetadataAssembly, I'm getting all public types, and for each type, I get it's custom attributes by calling IMetadataTypeInfo.CustomAttributes. This returns me back an array of IMetadataCustomAttribute. Phew.

Now, to get the type of the attribute, you need to navigate through the UsedConstructor property and get it's DeclaringType property.

This works fine for a custom attribute that has a constructor argument of (for example) a string.

I've got a problem with a constructor argument of System.Type, such as RunWith(typeof(ThingCommand)).

If the attribute is defined in the same assembly as the class it's applied to, then the UsedConstructor property is populated, and I can get the DeclaringType and all is good.

However, if the attribute is defined in a separate assembly, then UsedConstructor is always null, and I now don't know how to get the type of the attribute, or the value passed to the constructor.

Again, a custom attribute with a string constructor will work fine, if the attribute is defined in the same assembly or a referenced assembly.

Is this a ReSharper bug? I've tried it in 4.5 and 4.1 with the same results.

Any ideas?
Matt

5 comments

Hello Matt,

First, short note why is it done this way. With reflection we experienced
lots of performance issues, issues with locking files and other, and finaly
decided to base assembly exploration on raw metadata. It has some drawbacks,
but otherwise it is much faster and give us full control about what to load
and what not. For example, loading mscorlib as a referenced assembly took
significant amount of time, so we decided we don't load system assemblies
when exploring user code. After all, with NUnit (which was the only popular
testing framework at the time of decision) it is almost never needed. May
be we need to revisit it for v5, since situation changed.

As for UsedConstructor, it shouldn't be that way, however it could happen
if assembly with attribute is indeed system one. We will have to look at
it with 4.5 source code, and probably debug it. Do you have source code which
has this problem already checked in to xunitcontrib? What would be the best
way to reproduce it?

Sincerely,
Ilya Ryzhenkov

JetBrains, Inc
http://www.jetbrains.com
"Develop with pleasure!"


ME> Hi folks. As part of the xunitcontrib project on CodePlex, I'm
ME> writing the unit test runner for xUnit.net. I've run across
ME> something a little odd in my implementation of
ME> IUnitTestProvider.ExploreAssembly. Given an IMetadataAssembly, I'm
ME> getting all public types, and for each type, I get it's custom
ME> attributes by calling IMetadataTypeInfo.CustomAttributes. This
ME> returns me back an array of IMetadataCustomAttribute. Phew.
ME>
ME> Now, to get the type of the attribute, you need to navigate through
ME> the UsedConstructor property and get it's DeclaringType property.
ME>
ME> This works fine for a custom attribute that has a constructor
ME> argument of (for example) a string.
ME>
ME> I've got a problem with a constructor argument of System.Type, such
ME> as RunWith(typeof(ThingCommand)).
ME>
ME> If the attribute is defined in the same assembly as the class it's
ME> applied to, then the UsedConstructor property is populated, and I
ME> can get the DeclaringType and all is good.
ME>
ME> However, if the attribute is defined in a separate assembly, then
ME> UsedConstructor is always null, and I now don't know how to get the
ME> type of the attribute, or the value passed to the constructor.
ME>
ME> Again, a custom attribute with a string constructor will work fine,
ME> if the attribute is defined in the same assembly or a referenced
ME> assembly.
ME>
ME> Is this a ReSharper bug? I've tried it in 4.5 and 4.1 with the same
ME> results.
ME>
ME> Any ideas?
ME> Matt
ME> ---
ME> Original message URL:
ME> http://www.jetbrains.net/devnet/message/5241253#5241253


0

Hi. Thanks for the reply.

The currently checked in source code can demonstrate the problem, but it's  (of course) not terribly straightforward, so I've come up with a simpler repro.  If you get the current xunitcontrib source code from codeplex  (xunitcontrib.codeplex.com) and then completely replace the implementation of  XunitTestProvider.ExploreAssembly with: (using either the 4.1 or 4.5 projects)

foreach (IMetadataTypeInfo type in GetExportedTypes(assembly.GetTypes())) {     // The code in the sample doesn't have RunWithAttribute,     // but this allows you to step into HasAttribute     ITypeInfo typeInfo = TypeWrapper.Wrap(type);     if (typeInfo.HasAttribute(typeof (Xunit.RunWithAttribute)))     {         Console.WriteLine("Cool!");     } }

Then debug the CustomAttributes solution attached to this post. When you hit refresh in the unit test explorer, ExploreAssembly is called, and you get to step into HasAttribute. (I should point out that I've just spotted a few bugs in other methods of this class, but they don't get hit - and the problem can be shown with just the implementation of HasAttribute). Step into HasAttribute for Class1 and Class2.

The IMetadataTypeInfo.CustomAttributes for Class1 has two attributes, both with a System.Type constructor argument. The attribute in the current assembly has a populated UsedConstructor. The attribute defined in a different assembly (included in the solution) has null for UsedConstructor.

Class2 also has two attributes, this time with a string constructor args. Both the attribute defined in the current assembly and in the other assembly have populated UsedConstructor properties.

Cheers
Matt

Message was edited by: Matt Ellis (trying to format code snippet)



Attachment(s):
CustomAttributes.zip
0

Hi guys. Any news on this? It's been a while...

Thanks

Matt

0

Hello Matt!


Behaviour of MetadataLoader used in UnitTestManager for assembly exploration is changed in R# 5.0, now it loads all referenced assemblies.

We won't change its behaviour in R# 4.5 due to perfomance issues Ilya described above.

0

Hi,
It doesn't seem to have changed with version 5.0 ... I was waiting for it, as you can see.

Regards
Xavier

0

Please sign in to leave a comment.