Welcome Guest! To enable all features please Login or Register.

Notification

Icon
Error

Testing code that creates application domains.
Micah71381
#1 Posted : Friday, December 12, 2014 1:24:04 AM(UTC)
Rank: Member

Groups: Registered
Joined: 10/18/2013(UTC)
Posts: 27
Location: United States of America

Was thanked: 2 time(s) in 2 post(s)
A while back I wrote a library that did some AppDomain creation. I wrote tests for it and set them up to work with NCrunch by following the guide here:
troubleshooting_tests-that-build-their-own-appdomains

It is now many months later and I went back to the project to make a couple changes but my tests are not running anymore. The biggest difference I can think of is that I upgraded the version of NCrunch I am using. I am pulling in the NCrunch reference via NuGet, so my first step towards a solution was to update that. I did, though it only brought it up to 2.8 (I'm on NCrunch 2.10), but the tests still fail.

The symptom is a stack overflow when attempting to resolve NCrunch.Framework. I don't remember exactly how this worked before, but I vaguely remember that somehow the application domains were pre-loaded with NCrunch.Framework so that they never failed to resolve them (or something like that). Looking at the modules list in a debugger while the stack is overflowing, I can see that NCrunch.Framework is *not* loaded into the application domain. I do see nCrunch.TestRuntime.dll loaded, but that doesn't help me.

Was there a recent (within the last n months) change to NCrunch that would effect this behavior? Is there a work around?
Remco
#2 Posted : Friday, December 12, 2014 2:30:09 AM(UTC)
Rank: NCrunch Developer

Groups: Administrators
Joined: 4/16/2011(UTC)
Posts: 7,144

Thanks: 959 times
Was thanked: 1290 time(s) in 1196 post(s)
Hi,

I can't think of any known issue that would cause this kind of behaviour - nor any change in upgrade that would trigger it. NCrunch itself has no special handling for NCrunch.Framework - this DLL is simply another referenced library and the behaviours it unlocks work entirely through string matching off attribute names (you can declare the attributes within your own code and they will also work).

NCrunch.Framework rarely sees changes and the updates via Nuget are usually just a version number adjustment. I think that there is something else happening with your code that is causing this. Do you have any code inside your application domain that adds event handlers onto the AppDomain's AssemblyResolve property? If you manage to isolate the problem in a code sample, please feel free to send it to me via the (- BROKEN LINK -)contact form [/url].
Micah71381
#3 Posted : Friday, December 12, 2014 5:24:43 PM(UTC)
Rank: Member

Groups: Registered
Joined: 10/18/2013(UTC)
Posts: 27
Location: United States of America

Was thanked: 2 time(s) in 2 post(s)
The code in Zoltu/NCrunch.StackOverflow.ReproCase reproduces the issue. This worked in older versions of NCrunch (perhaps circa 2.4 or 2.6, hard to remember exactly) but does not work now.

The reason for it is obvious and easy to see if you debug the test. It first tries to load ThirdPartyClassLibrary, but in order to do so it must first load NCrunch.Framework so it can get the list of NCrunch assembly locations. This fails so the AssemblyResolve event handler is triggered again for NCrunch.Framework, which then tries to load NCrunch.Framework again.

I'm not sure what magic made it work previously, I have also updated my version of Visual Studio (still 2013, but different update version) I believe so it could be that but NCrunch seems like a more likely culprit here.
Remco
#4 Posted : Friday, December 12, 2014 8:22:11 PM(UTC)
Rank: NCrunch Developer

Groups: Administrators
Joined: 4/16/2011(UTC)
Posts: 7,144

Thanks: 959 times
Was thanked: 1290 time(s) in 1196 post(s)
Thanks for the repro. I've taken it for a spin and may be able to give some answers on this. The difference in behaviour you've observed between versions is caused by the presence of NCrunch.Framework.dll in the GAC. Where the version of NCrunch.Framework.dll referenced by the project is consistent with the one installed in the GAC, the CLR will automatically find the GAC assembly prior to invoking the assemblyresolver. Where NCrunch.Framework.dll is not present in the GAC, or the GAC version is different to the one referenced in the project (which may be handled using Nuget), the CLR won't find it immediately and will attempt to resolve it using the assemblyresolver. Naturally, the assemblyresolver requires the assembly itself in order to perform the resolution ... and so you get a stack overflow.

In the code sample you've given me, there is a defect in the PathPointsAtFile method. This method is calling Path.GetFileNameWithoutExtension on the wrong parameter (should be against fileName instead of path). This is causing in inconsistent comparison and causing the loader to always fail. On fixing this problem, the code started passing for me where the GAC version was consistent.

NCrunch itself ships with NCrunch.Framework and will install this in the GAC for convenience. It's a bit of a legacy action as most people these days will reference the assembly using Nuget. In this case, I don't think the GAC dependency in your code was intentional .. so it isn't a good idea to rely on NCrunch.Framework inside an assembly resolver. There are a couple of options available for you in fixing this problem:

1. Use NCrunchEnvironment.GetAllAssemblyLocations() from outside your custom application domain. Create a 'loader' type inside the application domain and pass the results of this method call into the application domain as a string array, prior to setting up the assembly resolver.
2. Implement NCrunchEnvironment.GetAllAssemblyLocations() in your own code. This method basically just backs onto an environment variable, so you can just copy/paste this into your own source:

public static string[] GetAllAssemblyLocations()
{
var dependencies = Environment.GetEnvironmentVariable("NCrunch.AllAssemblyLocations");
if (dependencies == null)
return null;

return dependencies.Split(';');
}

Micah71381
#5 Posted : Friday, December 12, 2014 9:02:33 PM(UTC)
Rank: Member

Groups: Registered
Joined: 10/18/2013(UTC)
Posts: 27
Location: United States of America

Was thanked: 2 time(s) in 2 post(s)
Sorry about the bug, I refactored out PathPointsAtFile before pushing it, just put in the parameters backwards (fixed now).

I think writing my own GetAllAssemblyLocations is the right way for me to go. I agree, relying on it in the path doesn't work since me and my co-workers don't always have the same version of NCrunch installed, and some don't have it installed at all (so they can't build if there is a GAC referenced NCrunch.Framework.dll).

Thanks for the help!
Users browsing this topic
Guest
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.

YAF | YAF © 2003-2011, Yet Another Forum.NET
This page was generated in 0.038 seconds.
Trial NCrunch
Take NCrunch for a spin
Do your fingers a favour and supercharge your testing workflow
Free Download