Accessing AppDomain.Evidence throws exception in test cases.

I ran into a strange issue when running my test cases using ReSharper (8.2.2) with VS 2013. The issue can be illustrated using the simple below.

Running the following test case from VS2013 would result in a SerializationException:

    [Serializable]
    public class ActivityStack
    {
        public string Name { get; set; }
    }

    [TestFixture]
    public class TestClass
    {
         [Test]
         public void MyRestCase()
         {      
            CallContext.LogicalSetData("MyData", new ActivityStack());
            var evidence = AppDomain.CurrentDomain.Evidence;
         }
    }

System.Runtime.Serialization.SerializationException : Type is not resolved for member 'CSG.Framework.UnitTest.Operations.ActivityStack,CSG.Framework.UnitTest, Version=15.2.0.0, Culture=neutral, PublicKeyToken=e7ab1d859f54b223'.
   at System.AppDomain.get_Evidence()
   at System.AppDomain.get_Evidence()
   at CSG.Framework.UnitTest.Operations.TestClass.MyRestCase() in ReSharperTest.cs: line 20

Fusion log:

=== Pre-bind state information ===
LOG: DisplayName = CSG.Framework.UnitTest, Version=15.2.0.0, Culture=neutral, PublicKeyToken=e7ab1d859f54b223
 (Fully-specified)
LOG: Appbase = file:///C:/Program Files (x86)/JetBrains/ReSharper/v8.2.Qiwabic/Bin/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = JetBrains.ReSharper.TaskRunner.CLR45.x64.exe
Calling assembly : (Unknown).
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\Program Files (x86)\JetBrains\ReSharper\v8.2.Qiwabic\Bin\JetBrains.ReSharper.TaskRunner.CLR45.x64.exe.Config
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
LOG: Post-policy reference: CSG.Framework.UnitTest, Version=15.2.0.0, Culture=neutral, PublicKeyToken=e7ab1d859f54b223
LOG: GAC Lookup was unsuccessful.
LOG: Attempting download of new URL file:///C:/Program Files (x86)/JetBrains/ReSharper/v8.2.Qiwabic/Bin/CSG.Framework.UnitTest.DLL.
LOG: Attempting download of new URL file:///C:/Program Files (x86)/JetBrains/ReSharper/v8.2.Qiwabic/Bin/CSG.Framework.UnitTest/CSG.Framework.UnitTest.DLL.
LOG: Attempting download of new URL file:///C:/Program Files (x86)/JetBrains/ReSharper/v8.2.Qiwabic/Bin/CSG.Framework.UnitTest.EXE.
LOG: Attempting download of new URL file:///C:/Program Files (x86)/JetBrains/ReSharper/v8.2.Qiwabic/Bin/CSG.Framework.UnitTest/CSG.Framework.UnitTest.EXE.
LOG: All probing URLs attempted and failed.


https://msdn.microsoft.com/en-us/library/dn458353(v=vs.110).aspx) might have explained the root cause: There are two app domains involved: the default app domain (ReSharper runner), and the current app domain (test case). When AppDomain.Evidence is executed in the current app domain:

  • It looks for evidence for the current app domain.
  • It tries to calculate the evidence for the current app domain based on the current domain.
  • The call to get evidence for the current domain triggers a cross-app domain call from the current app domain to the default app domain.
  • As part of the cross-app domain contract in the .NET Framework, the contents of the logical call context also have to be marshaled across app domain boundaries. Because the type (ActivityStack) that are in the logical call context cannot be resolved in the default app domain, an exception is thrown.

It is a pain to get all test cases work as some seemingly benign code such as NHibernate initialization or instantiating XmlSerializer would result in AppDomain.Evidence being accessed and would fail because of this issue. While we tried to remove the custom object from CallContext before hitting those code and were able to get most of test cases work, there are some test case code auto-generated from Specflow that is triggering this error and we cannot bypass except for putting our assembly in the GAC.

If possible we really like to see ReSharper test runner creates test app domains in such a way that won't cause it to go back to get the default AppDomain's evidence. Below code is from the AppDomain implementation - it looks like if evidence is explicitly provided when child app domains are created, the child app domains won't go back to get the default app domain's evidence later.

       internal Evidence EvidenceNoDemand {
            [SecurityCritical]
            get {
                if (_SecurityIdentity == null) {
                    if (!IsDefaultAppDomain() && nIsDefaultAppDomainForEvidence()) {
#if !FEATURE_CORECLR                         
                        //
                        // V1.x compatibility: If this is an AppDomain created 
                        // by the default appdomain without an explicit evidence
                        // then reuse the evidence of the default AppDomain.
                        //
                        return GetDefaultDomain().Evidence;
#else
                       Contract.Assert(false,"This code should not be called for core CLR");
                        
                        // This operation is not allowed
                        throw new InvalidOperationException();
#endif
                    }
                    else {
                        // We can't cache this value, since the VM needs to differentiate between AppDomains
                        // which have no user supplied evidence and those which do and it uses the presence
                        // of Evidence on the domain to make that switch.
                        return new Evidence(new AppDomainEvidenceFactory(this));
                    }
                }
                else {
                    return _SecurityIdentity.Clone();
                }
            }
        }
1 comment
Comment actions Permalink

Hello

  Please try enabling ReSharper | OPtions | Tools | Unit Testing | Use separate AppDomain for each assemblies with test.

Thanks!

0

Please sign in to leave a comment.