diff --git a/src/xunit.core/Sdk/ITestCollectionOrderer.cs b/src/xunit.core/Sdk/ITestCollectionOrderer.cs
new file mode 100644
index 000000000..656f44e71
--- /dev/null
+++ b/src/xunit.core/Sdk/ITestCollectionOrderer.cs
@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+using System.Linq;
+using Xunit.Abstractions;
+
+namespace Xunit.Sdk
+{
+ ///
+ /// A class implements this interface to participate in ordering test collections
+ /// for the test runner. Test collection orderers are applied using the
+ /// , which can be applied at
+ /// the assembly level.
+ ///
+ public interface ITestCollectionOrderer
+ {
+ ///
+ /// Orders test collections for execution.
+ ///
+ /// The test collections to be ordered.
+ /// The test collections in the order to be run.
+ IEnumerable> OrderTestCollections(IEnumerable> testCollections) where TTestCase : ITestCase;
+ }
+}
\ No newline at end of file
diff --git a/src/xunit.core/TestCollectionOrdererAttribute.cs b/src/xunit.core/TestCollectionOrdererAttribute.cs
new file mode 100644
index 000000000..31b1a6a87
--- /dev/null
+++ b/src/xunit.core/TestCollectionOrdererAttribute.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Xunit
+{
+ ///
+ /// Used to decorate an assembly to allow
+ /// the use a custom .
+ ///
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = true, AllowMultiple = false)]
+ public sealed class TestCollectionOrdererAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The type name of the orderer class (that implements ).
+ /// The assembly that exists in.
+ public TestCollectionOrdererAttribute(string ordererTypeName, string ordererAssemblyName) { }
+ }
+}
\ No newline at end of file
diff --git a/src/xunit.core/xunit.core.csproj b/src/xunit.core/xunit.core.csproj
index 8e80244a6..9406f7550 100644
--- a/src/xunit.core/xunit.core.csproj
+++ b/src/xunit.core/xunit.core.csproj
@@ -56,6 +56,8 @@
+
+
diff --git a/src/xunit.execution/Sdk/ExtensibilityPointFactory.cs b/src/xunit.execution/Sdk/ExtensibilityPointFactory.cs
index 2b564ea2b..07c946332 100644
--- a/src/xunit.execution/Sdk/ExtensibilityPointFactory.cs
+++ b/src/xunit.execution/Sdk/ExtensibilityPointFactory.cs
@@ -78,6 +78,14 @@ public static ITestCaseOrderer GetTestCaseOrderer(Type ordererType)
return Get(ordererType);
}
+ ///
+ /// Gets a test collection orderer.
+ ///
+ public static ITestCollectionOrderer GetTestCollectionOrderer(Type ordererType)
+ {
+ return Get(ordererType);
+ }
+
///
/// Gets a test case orderer, as specified in a reflected .
///
@@ -93,6 +101,17 @@ public static ITestCaseOrderer GetTestCaseOrderer(IAttributeInfo testCaseOrderer
return GetTestCaseOrderer(ordererType);
}
+
+ public static ITestCollectionOrderer GetTestCollectionOrderer(IAttributeInfo testCollectionOrdererAttribute)
+ {
+ var args = testCollectionOrdererAttribute.GetConstructorArguments().Cast().ToList();
+ var ordererType = Reflector.GetType(args[1], args[0]);
+ if (ordererType == null)
+ return null;
+
+ return GetTestCollectionOrderer(ordererType);
+ }
+
///
/// Gets a trait discoverer.
///
diff --git a/src/xunit.execution/Sdk/Frameworks/Runners/TestAssemblyRunner.cs b/src/xunit.execution/Sdk/Frameworks/Runners/TestAssemblyRunner.cs
index e7ff1d8e8..5b3a51b2d 100644
--- a/src/xunit.execution/Sdk/Frameworks/Runners/TestAssemblyRunner.cs
+++ b/src/xunit.execution/Sdk/Frameworks/Runners/TestAssemblyRunner.cs
@@ -63,6 +63,11 @@ public abstract class TestAssemblyRunner : IDisposable
///
protected ITestCaseOrderer TestCaseOrderer { get; set; }
+ ///
+ /// Gets or sets the test collection orderer that will be used to decide how to order test collections.
+ ///
+ protected ITestCollectionOrderer TestCollectionOrderer { get; set; }
+
///
/// Gets or sets the test cases to be run.
///
diff --git a/src/xunit.execution/Sdk/Frameworks/Runners/XunitTestAssemblyRunner.cs b/src/xunit.execution/Sdk/Frameworks/Runners/XunitTestAssemblyRunner.cs
index 27e6e30c7..19f645a6c 100644
--- a/src/xunit.execution/Sdk/Frameworks/Runners/XunitTestAssemblyRunner.cs
+++ b/src/xunit.execution/Sdk/Frameworks/Runners/XunitTestAssemblyRunner.cs
@@ -92,20 +92,25 @@ protected override void OnAssemblyStarting()
scheduler = GetTaskScheduler(maxParallelThreads);
var ordererAttribute = AssemblyInfo.GetCustomAttributes(typeof(TestCaseOrdererAttribute)).SingleOrDefault();
- if (ordererAttribute != null)
- TestCaseOrderer = ExtensibilityPointFactory.GetTestCaseOrderer(ordererAttribute);
+ if (ordererAttribute != null) TestCaseOrderer = ExtensibilityPointFactory.GetTestCaseOrderer(ordererAttribute);
+
+ var collectionOrdererAttribute = AssemblyInfo.GetCustomAttributes(typeof(TestCollectionOrdererAttribute)).SingleOrDefault();
+ if (collectionOrdererAttribute != null) TestCollectionOrderer = ExtensibilityPointFactory.GetTestCollectionOrderer(collectionOrdererAttribute);
}
///
protected override async Task RunTestCollectionsAsync(IMessageBus messageBus, CancellationTokenSource cancellationTokenSource)
{
- if (disableParallelization)
- return await base.RunTestCollectionsAsync(messageBus, cancellationTokenSource);
- var testCollections = TestCases.Cast()
- .GroupBy(tc => tc.TestCollection, TestCollectionComparer.Instance)
- .ToArray();
+ if (disableParallelization) return await base.RunTestCollectionsAsync(messageBus, cancellationTokenSource);
+ var testCollections = TestCases.GroupBy(tc => tc.TestCollection, TestCollectionComparer.Instance).ToArray();
+ if (TestCollectionOrderer != null)
+ {
+ testCollections = TestCollectionOrderer.OrderTestCollections(testCollections).ToArray();
+ }
+ var order = testCollections.Aggregate(String.Empty, (value, group) => value + Environment.NewLine + group.Key.DisplayName);
Console.WriteLine("Queueing {0} test collections.", testCollections.Length);
+ Console.WriteLine("Test collection will be queued in the following order: {0}", order);
var tasks = testCollections.Select(collectionGroup => Task.Factory.StartNew(() => RunTestCollectionAsync(messageBus, collectionGroup.Key, collectionGroup, cancellationTokenSource),
cancellationTokenSource.Token,