Skip to content

Commit 37c75b3

Browse files
committed
Fixes #338: Need a way to generally support platform-specific extensions
1 parent 047cd97 commit 37c75b3

10 files changed

Lines changed: 77 additions & 24 deletions

File tree

src/common/ExecutionHelper.cs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,40 @@
1+
using System;
2+
13
namespace Xunit
24
{
35
internal static class ExecutionHelper
46
{
57
/// <summary>
6-
/// Gets the name of the execution DLL used to run xUnit.net v2 tests.
8+
/// Gets the file name of the execution DLL (with extension) used to run xUnit.net v2 tests.
9+
/// </summary>
10+
public static string AssemblyFileName
11+
{
12+
get { return String.Format("xunit.execution.{0}.dll", PlatformSpecificAssemblySuffix); }
13+
}
14+
15+
/// <summary>
16+
/// Gets the file name suffix used to construct platform-specific DLL names.
717
/// </summary>
818
#if ANDROID
9-
public static readonly string AssemblyName = "xunit.execution.MonoAndroid";
10-
#elif __IOS__ && ! __UNIFIED__
11-
public static readonly string AssemblyName = "xunit.execution.MonoTouch";
19+
public static readonly string PlatformSpecificAssemblySuffix = "MonoAndroid";
20+
#elif __IOS__ && !__UNIFIED__
21+
public static readonly string PlatformSpecificAssemblySuffix = "MonoTouch";
1222
#elif __IOS__
13-
public static readonly string AssemblyName = "xunit.execution.iOS-Universal";
23+
public static readonly string PlatformSpecificAssemblySuffix = "iOS-Universal";
1424
#elif WINDOWS_PHONE_APP
15-
public static readonly string AssemblyName = "xunit.execution.universal";
25+
public static readonly string PlatformSpecificAssemblySuffix = "universal";
1626
#elif WINDOWS_PHONE
17-
public static readonly string AssemblyName = "xunit.execution.wp8";
27+
public static readonly string PlatformSpecificAssemblySuffix = "wp8";
1828
#elif NO_APPDOMAIN
19-
public static readonly string AssemblyName = "xunit.execution.win8";
29+
public static readonly string PlatformSpecificAssemblySuffix = "win8";
2030
#else
21-
public static readonly string AssemblyName = "xunit.execution.desktop";
31+
public static readonly string PlatformSpecificAssemblySuffix = "desktop";
2232
#endif
2333

24-
public static readonly string AssemblyFileName = AssemblyName + ".dll";
34+
/// <summary>
35+
/// Gets the substitution token used as assembly name suffix to indicate that the assembly is
36+
/// a generalized reference to the platform-specific assembly.
37+
/// </summary>
38+
public static readonly string SubstitutionToken = ".{Platform}";
2539
}
2640
}

src/common/NewReflectionExtensions.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23
using System.Reflection;
34

45
/// <summary>
@@ -17,6 +18,15 @@ public static Assembly GetAssembly(this Type type)
1718
#endif
1819
}
1920

21+
public static Attribute[] GetCustomAttributes(this Assembly assembly)
22+
{
23+
#if NEW_REFLECTION
24+
return assembly.GetCustomAttributes<Attribute>().ToArray();
25+
#else
26+
return assembly.GetCustomAttributes(inherit: false).Cast<Attribute>().ToArray();
27+
#endif
28+
}
29+
2030
public static bool IsEnum(this Type type)
2131
{
2232
#if NEW_REFLECTION

src/common/SerializationHelper.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,8 @@ public static Type GetType(string assemblyQualifiedTypeName)
150150
/// <returns>The instance of the <see cref="Type"/>, if available; <c>null</c>, otherwise.</returns>
151151
public static Type GetType(string assemblyName, string typeName)
152152
{
153-
// Take a generic reference to xunit.execution.dll and swap it out for the current execution library
154-
if (String.Equals(assemblyName, "xunit.execution", StringComparison.OrdinalIgnoreCase))
155-
assemblyName = ExecutionHelper.AssemblyName;
153+
if (assemblyName.EndsWith(ExecutionHelper.SubstitutionToken))
154+
assemblyName = assemblyName.Substring(0, assemblyName.Length - ExecutionHelper.SubstitutionToken.Length + 1) + ExecutionHelper.PlatformSpecificAssemblySuffix;
156155

157156
#if WINDOWS_PHONE_APP || WINDOWS_PHONE
158157
Assembly assembly = null;
@@ -220,15 +219,16 @@ public static string GetTypeNameForSerialization(Type type)
220219
if (String.Equals(assemblyName, "mscorlib", StringComparison.OrdinalIgnoreCase))
221220
return typeName;
222221

223-
if (String.Equals(assemblyName, ExecutionHelper.AssemblyName, StringComparison.OrdinalIgnoreCase))
224-
assemblyName = "xunit.execution";
222+
// If this is a platform specific assembly, strip off the trailing . and name and replace it with the token
223+
if (type.GetAssembly().GetCustomAttributes().FirstOrDefault(a => a != null && a.GetType().FullName == "Xunit.Sdk.PlatformSpecificAssemblyAttribute") != null)
224+
assemblyName = assemblyName.Substring(0, assemblyName.LastIndexOf('.')) + ExecutionHelper.SubstitutionToken;
225225

226226
return String.Format("{0}, {1}", typeName, assemblyName);
227227
}
228228

229229
private static IList<string> SplitAtOuterCommas(string value)
230230
{
231-
List<string> results = new List<string>();
231+
var results = new List<string>();
232232

233233
var startIndex = 0;
234234
var endIndex = 0;

src/xunit.core/FactAttribute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Xunit
99
/// by the test runner. It can also be extended to support a customized definition of a
1010
/// test method.
1111
/// </summary>
12-
[XunitTestCaseDiscoverer("Xunit.Sdk.FactDiscoverer", "xunit.execution")]
12+
[XunitTestCaseDiscoverer("Xunit.Sdk.FactDiscoverer", "xunit.execution.{Platform}")]
1313
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
1414
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "This attribute is designed as an extensibility point.")]
1515
public class FactAttribute : Attribute
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
3+
namespace Xunit.Sdk
4+
{
5+
/// <summary>
6+
/// Marks an assembly as a platform specific assembly for use with xUnit.net. Type references from
7+
/// such assemblies are allowed to use a special suffix ("My.Assembly.{Platform}"), which will
8+
/// automatically be translated into the correct platform-specific name ("My.Assembly.desktop",
9+
/// "My.Assembly.win8", etc.). This affects both extensibility points which require specifying
10+
/// a string-based type name and assembly, as well as serialization. The supported platform target
11+
/// names include:
12+
/// "desktop" (for desktop and PCL tests),
13+
/// "iOS-Universal" (for Xamarin test projects targeting iOS),
14+
/// "MonoAndroid" (for Xamarin MonoAndroid tests),
15+
/// "MonoTouch" (for Xamarin MonoTouch tests),
16+
/// "universal" (for Windows Phone 8.1 and Windows 8.1 tests),
17+
/// "win8" (for Modern Windows 8 tests), and
18+
/// "wp8" (for Windows Phone 8 Silverlight tests).
19+
/// Note that file names may be case sensitive (when running on platforms with case sensitive
20+
/// file systems like Linux), so ensure that your assembly file name casing is consistent, and
21+
/// that you use the suffixes here with the exact case shown.
22+
/// </summary>
23+
24+
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)]
25+
public sealed class PlatformSpecificAssemblyAttribute : Attribute { }
26+
}

src/xunit.core/TestFrameworkAttribute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Xunit
66
/// <summary>
77
/// Used to decorate an assembly to allow the use a custom <see cref="T:Xunit.Sdk.ITestFramework"/>.
88
/// </summary>
9-
[TestFrameworkDiscoverer("Xunit.Sdk.TestFrameworkTypeDiscoverer", "xunit.execution")]
9+
[TestFrameworkDiscoverer("Xunit.Sdk.TestFrameworkTypeDiscoverer", "xunit.execution.{Platform}")]
1010
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
1111
public sealed class TestFrameworkAttribute : Attribute, ITestFrameworkAttribute
1212
{

src/xunit.core/TheoryAttribute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace Xunit
1111
/// derive from <see cref="DataAttribute"/> (notably, <see cref="InlineDataAttribute"/> and
1212
/// <see cref="MemberDataAttribute"/>).
1313
/// </summary>
14-
[XunitTestCaseDiscoverer("Xunit.Sdk.TheoryDiscoverer", "xunit.execution")]
14+
[XunitTestCaseDiscoverer("Xunit.Sdk.TheoryDiscoverer", "xunit.execution.{Platform}")]
1515
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
1616
public class TheoryAttribute : FactAttribute { }
1717
}

src/xunit.core/xunit.core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
<Compile Include="Sdk\IXunitTestCaseDiscoverer.cs" />
8989
<Compile Include="Sdk\IXunitTestCase.cs" />
9090
<Compile Include="Sdk\IXunitTestCollectionFactory.cs" />
91+
<Compile Include="Sdk\PlatformSpecificAssemblyAttribute.cs" />
9192
<Compile Include="Sdk\RunSummary.cs" />
9293
<Compile Include="Sdk\TestFrameworkDiscovererAttribute.cs" />
9394
<Compile Include="Sdk\XunitTestCaseDiscovererAttribute.cs" />

src/xunit.execution/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Reflection;
3+
using Xunit.Sdk;
34

45
#if ANDROID
56
[assembly: AssemblyTitle("xUnit.net Execution (MonoAndroid)")]
@@ -18,3 +19,4 @@
1819
#endif
1920

2021
[assembly: CLSCompliant(true)]
22+
[assembly: PlatformSpecificAssembly]

test/test.xunit.execution/Common/SerializationHelperTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,20 @@ public class SerializationHelperTests
99
[InlineData(typeof(object), "System.Object")]
1010
// Types outside of mscorlib include their assembly name
1111
[InlineData(typeof(FactAttribute), "Xunit.FactAttribute, xunit.core")]
12-
// Types in device-specific execution library show up in xunit.execution
13-
[InlineData(typeof(XunitTestFramework), "Xunit.Sdk.XunitTestFramework, xunit.execution")]
12+
// Types in platform-specific libraries show up with substitution tokens
13+
[InlineData(typeof(XunitTestFramework), "Xunit.Sdk.XunitTestFramework, xunit.execution.{Platform}")]
1414
// Array types
1515
[InlineData(typeof(FactAttribute[]), "Xunit.FactAttribute[], xunit.core")]
1616
// Array of arrays with multi-dimensions
1717
[InlineData(typeof(FactAttribute[][,]), "Xunit.FactAttribute[,][], xunit.core")]
1818
// Single-nested generic type (both in mscorlib)
1919
[InlineData(typeof(Action<object>), "System.Action`1[[System.Object]]")]
2020
// Single-nested generic type (non-mscorlib)
21-
[InlineData(typeof(TestMethodRunner<XunitTestCase>), "Xunit.Sdk.TestMethodRunner`1[[Xunit.Sdk.XunitTestCase, xunit.execution]], xunit.execution")]
21+
[InlineData(typeof(TestMethodRunner<XunitTestCase>), "Xunit.Sdk.TestMethodRunner`1[[Xunit.Sdk.XunitTestCase, xunit.execution.{Platform}]], xunit.execution.{Platform}")]
2222
// Multiply-nested generic types
23-
[InlineData(typeof(Action<Tuple<object, FactAttribute>, XunitTestFramework>), "System.Action`2[[System.Tuple`2[[System.Object],[Xunit.FactAttribute, xunit.core]]],[Xunit.Sdk.XunitTestFramework, xunit.execution]]")]
23+
[InlineData(typeof(Action<Tuple<object, FactAttribute>, XunitTestFramework>), "System.Action`2[[System.Tuple`2[[System.Object],[Xunit.FactAttribute, xunit.core]]],[Xunit.Sdk.XunitTestFramework, xunit.execution.{Platform}]]")]
2424
// Generics and arrays, living together, like cats and dogs
25-
[InlineData(typeof(Action<XunitTestCase[,][]>[][,]), "System.Action`1[[Xunit.Sdk.XunitTestCase[][,], xunit.execution]][,][]")]
25+
[InlineData(typeof(Action<XunitTestCase[,][]>[][,]), "System.Action`1[[Xunit.Sdk.XunitTestCase[][,], xunit.execution.{Platform}]][,][]")]
2626
public static void CanRoundTripSerializedTypeNames(Type type, string expectedName)
2727
{
2828
var name = SerializationHelper.GetTypeNameForSerialization(type);

0 commit comments

Comments
 (0)