unit test helper classes. Changed calls from old TypeFinder to new TypeFinder2. Created benchmark unit tests to test speed differences between them. Fixes: 30946 - IOHelper with HttpContext issue Fixes: 30945 - AppCodeFileExtensions issue with UmbracoSettings
293 lines
10 KiB
C#
293 lines
10 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security;
|
|
using System.Xml;
|
|
using System.Xml.Linq;
|
|
using NUnit.Framework;
|
|
using Umbraco.Core;
|
|
|
|
namespace Umbraco.Tests.PartialTrust
|
|
{
|
|
public static class PartialTrustHelper<T>
|
|
where T : class, new()
|
|
{
|
|
public const string PartialTrustAppDomainName = "Partial Trust AppDomain";
|
|
|
|
public static void RunInPartial(MethodInfo methodInfo)
|
|
{
|
|
AppDomain partiallyTrustedDomain = CreatePartialTrustDomain();
|
|
|
|
try
|
|
{
|
|
RunInPartial(methodInfo, partiallyTrustedDomain);
|
|
}
|
|
finally
|
|
{
|
|
AppDomain.Unload(partiallyTrustedDomain);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Runs the provided <paramref name="methodInfo"/> in the <paramref name="partiallyTrustedDomain"/>.
|
|
/// If <paramref name="marshaller"/> is provided, it will be used, otherwise a new one will be created.
|
|
/// </summary>
|
|
/// <param name="methodInfo">The method info.</param>
|
|
/// <param name="partiallyTrustedDomain">The partially trusted domain.</param>
|
|
/// <param name="marshaller">The marshaller.</param>
|
|
public static void RunInPartial(MethodInfo methodInfo, AppDomain partiallyTrustedDomain, PartialTrustMethodRunner<T> marshaller = null)
|
|
{
|
|
// Ensure no mistakes creep in and this code is actually running on the fully trusted AppDomain
|
|
Assert.That(AppDomain.CurrentDomain.FriendlyName, Is.Not.StringStarting(PartialTrustAppDomainName));
|
|
|
|
if (marshaller == null) marshaller = GenerateMarshaller(partiallyTrustedDomain);
|
|
|
|
try
|
|
{
|
|
marshaller.Run(methodInfo);
|
|
}
|
|
catch (PartialTrustTestException ex)
|
|
{
|
|
throw ex;
|
|
}
|
|
catch (TargetInvocationException ex)
|
|
{
|
|
// If this gets raised, it could be because the method failed, or an assertion was called (either pass or failure)
|
|
if (ex.InnerException != null)
|
|
{
|
|
if (ex.InnerException is SuccessException)
|
|
/* Do nothing but return - it's NUnit's way of exiting out of a method when Assert.Pass() is called */
|
|
return;
|
|
Assert.Fail(ex.InnerException.ToString());
|
|
}
|
|
Assert.Fail("Failed but InnerException was null; " + ex.Message);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Assert.Fail(ex.ToString());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a marshaller proxy of <see cref="PartialTrustMethodRunner{TFixture}"/> inside the provided <paramref name="partiallyTrustedDomain"/>.
|
|
/// </summary>
|
|
/// <param name="partiallyTrustedDomain">The partially trusted domain.</param>
|
|
/// <returns></returns>
|
|
public static PartialTrustMethodRunner<T> GenerateMarshaller(AppDomain partiallyTrustedDomain)
|
|
{
|
|
var marshallerType = typeof(PartialTrustMethodRunner<T>);
|
|
var marshaller =
|
|
partiallyTrustedDomain.CreateInstanceAndUnwrap(marshallerType.Assembly.FullName, marshallerType.FullName) as
|
|
PartialTrustMethodRunner<T>;
|
|
return marshaller;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates the an <see cref="AppDomain"/> with a partial trust <see cref="PermissionSet"/>.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static AppDomain CreatePartialTrustDomain()
|
|
{
|
|
var setup = new AppDomainSetup { ApplicationBase = AppDomain.CurrentDomain.BaseDirectory };
|
|
var permissions = MediumTrustConfigHelper.GetMediumTrustPermissionSet();
|
|
return AppDomain.CreateDomain("Partial Trust AppDomain: " + DateTime.Now.Ticks, null, setup, permissions);
|
|
}
|
|
|
|
public static void CheckRunNUnitTestInPartialTrust()
|
|
{
|
|
var findMethodInfo = FindCurrentTestMethodInfo();
|
|
|
|
try
|
|
{
|
|
RunInPartial(findMethodInfo);
|
|
}
|
|
catch (SuccessException assertPass)
|
|
{
|
|
/* This means the test passed with a direct call to Pass, so do nothing */
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public static MethodInfo FindCurrentTestMethodInfo()
|
|
{
|
|
var nameOfTest = TestContext.CurrentContext.Test.Name;
|
|
var findMethodInfo = typeof(T).GetMethod(nameOfTest);
|
|
return findMethodInfo;
|
|
}
|
|
|
|
public static void CheckRunNUnitTestInPartialTrust(AppDomain withAppDomain, PartialTrustMethodRunner<T> marshaller)
|
|
{
|
|
var findMethodInfo = FindCurrentTestMethodInfo();
|
|
|
|
try
|
|
{
|
|
RunInPartial(findMethodInfo, withAppDomain, marshaller);
|
|
}
|
|
catch (SuccessException assertPass)
|
|
{
|
|
/* This means the test passed with a direct call to Pass, so do nothing */
|
|
throw;
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
public class PartialTrustMethodRunner<TFixture> : MarshalByRefObject
|
|
where TFixture : class, new()
|
|
{
|
|
protected TFixture Instance { get; set; }
|
|
public Type TestClass { get { return typeof(TFixture); } }
|
|
|
|
public void Run(MethodInfo methodToRun)
|
|
{
|
|
// Verify that we are definitely running in an appdomain that we made ourselves
|
|
Assert.That(AppDomain.CurrentDomain.FriendlyName, Is.StringStarting(PartialTrustAppDomainName));
|
|
|
|
try
|
|
{
|
|
// Support having the marshaller run multiple methods on the same instance by storing the instance locally
|
|
if (Instance == null) Instance = new TFixture();
|
|
|
|
// Run the method on the instance we've either created or cached locally
|
|
methodToRun.Invoke(Instance, null);
|
|
}
|
|
catch (TargetInvocationException ex)
|
|
{
|
|
if (ex.InnerException != null && (ex.InnerException is SecurityException || ex.InnerException is MemberAccessException))
|
|
{
|
|
var inner = ex.InnerException;
|
|
throw new PartialTrustTestException("Test {0} fails in a partial trust environment, due to: {1}".InvariantFormat(methodToRun.Name, inner.Message), ex.InnerException);
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static class MediumTrustConfigHelper
|
|
{
|
|
/// <summary>
|
|
/// Gets the medium trust permission set from the default config path.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static NamedPermissionSet GetMediumTrustPermissionSet()
|
|
{
|
|
return GetMediumTrustPermissionSet(GetMediumTrustConfigPath());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the medium trust permission set.
|
|
/// </summary>
|
|
/// <param name="pathToConfigFile">The path to the config file.</param>
|
|
/// <returns></returns>
|
|
public static NamedPermissionSet GetMediumTrustPermissionSet(string pathToConfigFile)
|
|
{
|
|
// Load the config file trusting that it exists.
|
|
var xDocument = XDocument.Load(pathToConfigFile);
|
|
|
|
// Get all of the SecurityClass elements which we'll use later to look
|
|
// up a type strongname given a key
|
|
var securityClasses = xDocument.Descendants("SecurityClass").Select(
|
|
x => new
|
|
{
|
|
Name = (string)x.Attribute("Name"),
|
|
Type = (string)x.Attribute("Description")
|
|
});
|
|
|
|
// Get the first PermissionSet element where the Name attribute is "ASP.Net"
|
|
var namedSet = xDocument.Descendants("PermissionSet").Where(x => (string)x.Attribute("Name") == "ASP.Net").FirstOrDefault();
|
|
|
|
// If we didn't find it, that's a fail
|
|
Assert.NotNull(namedSet);
|
|
|
|
// Create a new SecurityElement class to mimic what is represented in Xml
|
|
var secElement = new SecurityElement("PermissionSet");
|
|
secElement.AddAttribute("Name", "ASP.Net");
|
|
secElement.AddAttribute("class", "NamedPermissionSet");
|
|
secElement.AddAttribute("version", "1");
|
|
|
|
// For each child of the ASP.Net PermissionSet, create a child SecurityElement representing the IPermission
|
|
foreach (var xElement in namedSet.Elements())
|
|
{
|
|
var child = new SecurityElement("IPermission");
|
|
|
|
// Check if we need to do any string replacement on the Xml values first
|
|
ProcessIPermissionAttributeValue(xElement);
|
|
|
|
// Get the attributes of the IPermission from Xml and put them onto our child SecurityElement
|
|
foreach (var xAttribute in xElement.Attributes())
|
|
{
|
|
var attribName = xAttribute.Name.LocalName;
|
|
var value = xAttribute.Value;
|
|
|
|
try
|
|
{
|
|
if (attribName == "class")
|
|
// This is the type key. Get the full type name from the SecurityClasses list we grabbed earlier
|
|
value = securityClasses.Where(x => x.Name == value).Select(x => x.Type).Single();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new XmlException("Could not find the fully-qualified type name for " + value, ex);
|
|
}
|
|
|
|
child.AddAttribute(attribName, value);
|
|
}
|
|
secElement.AddChild(child);
|
|
}
|
|
|
|
// Create a new NamedPermissionSet, pass in the SecurityElement class representing the Xml
|
|
var permissionSet = new NamedPermissionSet("ASP.Net");
|
|
permissionSet.FromXml(secElement);
|
|
return permissionSet;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Processes the custom attribute values of IPermission config elements to replicate what ASP.Net performs
|
|
/// when scanning the permission set. Primarily just replaces $AppDir$ on a FileIOPermission with the current
|
|
/// directory.
|
|
/// </summary>
|
|
/// <param name="element">The element.</param>
|
|
public static void ProcessIPermissionAttributeValue(XElement element)
|
|
{
|
|
var classKey = (string)element.Attribute("class");
|
|
if (classKey == null) return;
|
|
|
|
// These attributes are on every IPermission, we can ignore them later
|
|
var ignoredAttributes = new[] { "class", "version" };
|
|
|
|
// Get all the attribute on this IPermission that are "custom"
|
|
var customAttributes = element.Attributes().Where(x => !ignoredAttributes.Contains(x.Name.LocalName));
|
|
foreach (var customAttribute in customAttributes)
|
|
{
|
|
switch (classKey)
|
|
{
|
|
case "FileIOPermission":
|
|
switch (customAttribute.Name.LocalName)
|
|
{
|
|
case "Read":
|
|
case "Write":
|
|
case "Append":
|
|
case "PathDiscovery":
|
|
// Replace an $AppDir$ token with the current directory to mimic setting it to the IIS root
|
|
customAttribute.Value = customAttribute.Value.Replace("$AppDir$", Environment.CurrentDirectory);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the medium trust config path for the current runtime environment.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static string GetMediumTrustConfigPath()
|
|
{
|
|
var readFromDirectory = Path.GetDirectoryName(RuntimeEnvironment.SystemConfigurationFile);
|
|
var autoPath = Path.Combine(readFromDirectory, "web_mediumtrust.config");
|
|
return autoPath;
|
|
}
|
|
}
|
|
}
|
|
} |