Skip to content

Commit

Permalink
Now test collections can be specified on per test method basis.
Browse files Browse the repository at this point in the history
  • Loading branch information
pawelpabich committed May 28, 2014
1 parent 7737458 commit 7fa0f32
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 61 deletions.
8 changes: 7 additions & 1 deletion src/xunit.core/CollectionBehavior.cs
Expand Up @@ -16,6 +16,12 @@ public enum CollectionBehavior
/// By default, generates a collection per test class for any test classes that are not
/// decorated with <see cref="CollectionAttribute"/>.
/// </summary>
CollectionPerClass
CollectionPerClass,

/// <summary>
/// By default, generates a collection per test method for any test classes that are not
/// decorated with <see cref="CollectionAttribute"/>.
/// </summary>
CollectionPerMethod
}
}
6 changes: 3 additions & 3 deletions src/xunit.core/Sdk/IXunitTestCollectionFactory.cs
Expand Up @@ -16,10 +16,10 @@ public interface IXunitTestCollectionFactory
string DisplayName { get; }

/// <summary>
/// Gets the test collection for a given test class.
/// Gets the test collection for a given test method.
/// </summary>
/// <param name="testClass">The test class.</param>
/// <param name="testMethod">The test method.</param>
/// <returns>The test collection.</returns>
ITestCollection Get(ITypeInfo testClass);
ITestCollection Get(IMethodInfo testMethod);
}
}
7 changes: 6 additions & 1 deletion src/xunit.execution/Sdk/ExtensibilityPointFactory.cs
Expand Up @@ -154,9 +154,14 @@ static Type GetTestCollectionFactoryType(IAttributeInfo collectionBehaviorAttrib

if (ctorArgs.Count == 1)
{
if ((CollectionBehavior)ctorArgs[0] == CollectionBehavior.CollectionPerAssembly)
var collectionBehaviour = (CollectionBehavior) ctorArgs[0];

if (collectionBehaviour == CollectionBehavior.CollectionPerAssembly)
return typeof(CollectionPerAssemblyTestCollectionFactory);

if (collectionBehaviour == CollectionBehavior.CollectionPerMethod)
return typeof(CollectionPerMethodTestCollectionFactory);

return typeof(CollectionPerClassTestCollectionFactory);
}

Expand Down
Expand Up @@ -49,11 +49,11 @@ ITestCollection CreateTestCollection(string name)
}

/// <inheritdoc/>
public ITestCollection Get(ITypeInfo testClass)
public ITestCollection Get(IMethodInfo testMethod)
{
var testClass = testMethod.Type;
var collectionAttribute = testClass.GetCustomAttributes(typeof(CollectionAttribute)).SingleOrDefault();
if (collectionAttribute == null)
return defaultCollection;
if (collectionAttribute == null) return defaultCollection;

var collectionName = (string)collectionAttribute.GetConstructorArguments().First();
return testCollections.GetOrAdd(collectionName, CreateTestCollection);
Expand Down
Expand Up @@ -46,8 +46,9 @@ ITestCollection CreateCollection(string name)
}

/// <inheritdoc/>
public ITestCollection Get(ITypeInfo testClass)
public ITestCollection Get(IMethodInfo testMethod)
{
var testClass = testMethod.Type;
string collectionName;
var collectionAttribute = testClass.GetCustomAttributes(typeof(CollectionAttribute)).SingleOrDefault();

Expand Down
@@ -0,0 +1,62 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Xunit.Abstractions;

namespace Xunit.Sdk
{
/// <summary>
/// Implementation of <see cref="IXunitTestCollectionFactory"/> which creates a new test
/// collection for each test method that isn't decorated with <see cref="CollectionAttribute"/>.
/// </summary>
public class CollectionPerMethodTestCollectionFactory : IXunitTestCollectionFactory
{
readonly Dictionary<string, ITypeInfo> collectionDefinitions;
readonly ConcurrentDictionary<string, ITestCollection> testCollections = new ConcurrentDictionary<string, ITestCollection>();

/// <summary>
/// Initializes a new instance of the <see cref="CollectionPerMethodTestCollectionFactory" /> class.
/// </summary>
/// <param name="assemblyInfo">The assembly.</param>
public CollectionPerMethodTestCollectionFactory(IAssemblyInfo assemblyInfo)
: this(assemblyInfo, MessageAggregator.Instance) { }

/// <summary>
/// Initializes a new instance of the <see cref="CollectionPerMethodTestCollectionFactory" /> class.
/// </summary>
/// <param name="assemblyInfo">The assembly info.</param>
/// <param name="messageAggregator">The message aggregator used to report <see cref="EnvironmentalWarning"/> messages.</param>
public CollectionPerMethodTestCollectionFactory(IAssemblyInfo assemblyInfo, IMessageAggregator messageAggregator)
{
collectionDefinitions = TestCollectionFactoryHelper.GetTestCollectionDefinitions(assemblyInfo, messageAggregator);
}

/// <inheritdoc/>
public string DisplayName
{
get { return "collection-per-method"; }
}

ITestCollection CreateCollection(string name)
{
ITypeInfo definitionType;
collectionDefinitions.TryGetValue(name, out definitionType);
return new XunitTestCollection { CollectionDefinition = definitionType, DisplayName = name };
}

/// <inheritdoc/>
public ITestCollection Get(IMethodInfo testMethod)
{
string collectionName;
var collectionAttribute = testMethod.GetCustomAttributes(typeof(CollectionAttribute)).SingleOrDefault();

if (collectionAttribute == null)
collectionName = "Test collection for " + testMethod.Type.Name + "." + testMethod.Name;
else
collectionName = (string)collectionAttribute.GetConstructorArguments().First();

return testCollections.GetOrAdd(collectionName, CreateCollection);
}
}
}
Expand Up @@ -101,15 +101,18 @@ protected override async Task<RunSummary> RunTestCollectionsAsync(IMessageBus me
{
if (disableParallelization)
return await base.RunTestCollectionsAsync(messageBus, cancellationTokenSource);

var tasks = TestCases.Cast<IXunitTestCase>()
.GroupBy(tc => tc.TestCollection, TestCollectionComparer.Instance)
.Select(collectionGroup => Task.Factory.StartNew(() => RunTestCollectionAsync(messageBus, collectionGroup.Key, collectionGroup, cancellationTokenSource),
cancellationTokenSource.Token,
TaskCreationOptions.None,
scheduler))
.ToArray();

var testCollections = TestCases.Cast<IXunitTestCase>()
.GroupBy(tc => tc.TestCollection, TestCollectionComparer.Instance)
.ToArray();

Console.WriteLine("Queueing {0} test collections.", testCollections.Length);

var tasks = testCollections.Select(collectionGroup => Task.Factory.StartNew(() => RunTestCollectionAsync(messageBus, collectionGroup.Key, collectionGroup, cancellationTokenSource),
cancellationTokenSource.Token,
TaskCreationOptions.None,
scheduler))
.ToArray();

var summaries = await Task.WhenAll(tasks.Select(t => t.Unwrap()));

return new RunSummary()
Expand Down
Expand Up @@ -93,11 +93,11 @@ protected virtual bool FindTestsForMethod(ITestCollection testCollection, ITypeI
/// <inheritdoc/>
protected override bool FindTestsForType(ITypeInfo type, bool includeSourceInformation, IMessageBus messageBus)
{
var testCollection = TestCollectionFactory.Get(type);

foreach (var method in type.GetMethods(includePrivateMethods: true))
if (!FindTestsForMethod(testCollection, type, method, includeSourceInformation, messageBus))
return false;
{
var testCollection = TestCollectionFactory.Get(method);
if (!FindTestsForMethod(testCollection, type, method, includeSourceInformation, messageBus))return false;
}

return true;
}
Expand Down
3 changes: 2 additions & 1 deletion src/xunit.execution/xunit.execution.csproj
Expand Up @@ -77,6 +77,7 @@
<Compile Include="Extensions\ReflectionAbstractionExtensions.cs" />
<Compile Include="Sdk\AsyncTestSyncContext.cs" />
<Compile Include="Sdk\DefaultTestCaseOrderer.cs" />
<Compile Include="Sdk\Frameworks\CollectionPerClassTestCollectionFactory.cs" />
<Compile Include="Sdk\Frameworks\Runners\TestAssemblyRunner.cs" />
<Compile Include="Sdk\Frameworks\Runners\TestClassRunner.cs" />
<Compile Include="Sdk\Frameworks\Runners\TestCollectionRunner.cs" />
Expand All @@ -96,7 +97,7 @@
<Compile Include="Sdk\EnvironmentalWarning.cs" />
<Compile Include="Sdk\ExceptionUtility.cs" />
<Compile Include="Sdk\MessageAggregator.cs" />
<Compile Include="Sdk\Frameworks\CollectionPerClassTestCollectionFactory.cs" />
<Compile Include="Sdk\Frameworks\CollectionPerMethodTestCollectionFactory.cs" />
<Compile Include="Sdk\Frameworks\LambdaTestCase.cs" />
<Compile Include="Sdk\Frameworks\TestCollectionFactoryHelper.cs" />
<Compile Include="Sdk\Frameworks\XunitTestCollection.cs" />
Expand Down
Expand Up @@ -7,14 +7,14 @@ public class CollectionPerAssemblyTestCollectionFactoryTests
[Fact]
public void ReturnsDefaultTestCollectionForUndecoratedTestClass()
{
var type1 = Mocks.TypeInfo("type1");
var type2 = Mocks.TypeInfo("type2");
var method1 = Mocks.MethodInfo(type: Mocks.TypeInfo("type1"));
var method2 = Mocks.MethodInfo(type: Mocks.TypeInfo("type2"));
var assembly = Mocks.AssemblyInfo();
assembly.AssemblyPath.Returns(@"C:\Foo\bar.dll");
var factory = new CollectionPerAssemblyTestCollectionFactory(assembly);

var result1 = factory.Get(type1);
var result2 = factory.Get(type2);
var result1 = factory.Get(method1);
var result2 = factory.Get(method2);

Assert.Same(result1, result2);
Assert.Equal("Test collection for bar.dll", result1.DisplayName);
Expand All @@ -24,14 +24,14 @@ public void ReturnsDefaultTestCollectionForUndecoratedTestClass()
public void ClassesDecoratedWithSameCollectionNameAreInSameTestCollection()
{
var attr = Mocks.CollectionAttribute("My Collection");
var type1 = Mocks.TypeInfo("type1", attributes: new[] { attr });
var type2 = Mocks.TypeInfo("type2", attributes: new[] { attr });
var method1 = Mocks.MethodInfo(type: Mocks.TypeInfo("type1", attributes: new[] { attr }));
var method2 = Mocks.MethodInfo(type: Mocks.TypeInfo("type2", attributes: new[] { attr }));
var assembly = Mocks.AssemblyInfo();
assembly.AssemblyPath.Returns(@"C:\Foo\bar.dll");
var factory = new CollectionPerAssemblyTestCollectionFactory(assembly);

var result1 = factory.Get(type1);
var result2 = factory.Get(type2);
var result1 = factory.Get(method1);
var result2 = factory.Get(method2);

Assert.Same(result1, result2);
Assert.Equal("My Collection", result1.DisplayName);
Expand All @@ -40,14 +40,14 @@ public void ClassesDecoratedWithSameCollectionNameAreInSameTestCollection()
[Fact]
public void ClassesWithDifferentCollectionNamesHaveDifferentCollectionObjects()
{
var type1 = Mocks.TypeInfo("type1", attributes: new[] { Mocks.CollectionAttribute("Collection 1") });
var type2 = Mocks.TypeInfo("type2", attributes: new[] { Mocks.CollectionAttribute("Collection 2") });
var method1 = Mocks.MethodInfo(type: Mocks.TypeInfo("type1", attributes: new[] { Mocks.CollectionAttribute("Collection 1") }));
var method2 = Mocks.MethodInfo(type: Mocks.TypeInfo("type2", attributes: new[] { Mocks.CollectionAttribute("Collection 2") }));
var assembly = Mocks.AssemblyInfo();
assembly.AssemblyPath.Returns(@"C:\Foo\bar.dll");
var factory = new CollectionPerAssemblyTestCollectionFactory(assembly);

var result1 = factory.Get(type1);
var result2 = factory.Get(type2);
var result1 = factory.Get(method1);
var result2 = factory.Get(method2);

Assert.NotSame(result1, result2);
Assert.Equal("Collection 1", result1.DisplayName);
Expand All @@ -57,13 +57,13 @@ public void ClassesWithDifferentCollectionNamesHaveDifferentCollectionObjects()
[Fact]
public void UsingTestCollectionDefinitionSetsTypeInfo()
{
var testType = Mocks.TypeInfo("type", attributes: new[] { Mocks.CollectionAttribute("This is a test collection") });
var testMethod = Mocks.MethodInfo(type: Mocks.TypeInfo("type", attributes: new[] { Mocks.CollectionAttribute("This is a test collection") }));
var collectionDefinitionType = Mocks.TypeInfo("collectionDefinition", attributes: new[] { Mocks.CollectionDefinitionAttribute("This is a test collection") });
var assembly = Mocks.AssemblyInfo(new[] { collectionDefinitionType });
assembly.AssemblyPath.Returns(@"C:\Foo\bar.dll");
var factory = new CollectionPerAssemblyTestCollectionFactory(assembly);

var result = factory.Get(testType);
var result = factory.Get(testMethod);

Assert.Same(collectionDefinitionType, result.CollectionDefinition);
}
Expand All @@ -72,14 +72,14 @@ public void UsingTestCollectionDefinitionSetsTypeInfo()
public void MultiplyDeclaredCollectionsRaisesEnvironmentalWarning()
{
var broker = Substitute.For<IMessageAggregator>();
var testType = Mocks.TypeInfo("type", attributes: new[] { Mocks.CollectionAttribute("This is a test collection") });
var testMethod = Mocks.MethodInfo(type: Mocks.TypeInfo("type", attributes: new[] { Mocks.CollectionAttribute("This is a test collection") }));
var collectionDefinition1 = Mocks.TypeInfo("collectionDefinition1", attributes: new[] { Mocks.CollectionDefinitionAttribute("This is a test collection") });
var collectionDefinition2 = Mocks.TypeInfo("collectionDefinition2", attributes: new[] { Mocks.CollectionDefinitionAttribute("This is a test collection") });
var assembly = Mocks.AssemblyInfo(new[] { collectionDefinition1, collectionDefinition2 });
assembly.AssemblyPath.Returns(@"C:\Foo\bar.dll");
var factory = new CollectionPerAssemblyTestCollectionFactory(assembly, broker);

factory.Get(testType);
factory.Get(testMethod);

var captured = broker.Captured(b => b.Add<EnvironmentalWarning>(null));
var warning = captured.Arg<EnvironmentalWarning>();
Expand Down

0 comments on commit 7fa0f32

Please sign in to comment.