Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable .NET Standard 2.0 tests on non-Windows platforms #2609

Merged
merged 6 commits into from
Dec 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Feature constants are defined in [Common.props](src/NUnitFramework/Common.props)
- `PARALLEL` enables running tests in parallel
- `PLATFORM_DETECTION` enables platform detection
- `THREAD_ABORT` enables timeouts and forcible cancellation
- `APARTMENT_STATE` enables control of the thread apartment state

Platform constants are defined by convention by the csproj SDK, one per target framework.
For example, `NET20` or `NET45`, `NETSTANDARD1_6`, `NETCOREAPP2_0`, and so on.
Expand Down
1 change: 0 additions & 1 deletion build.cake
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,6 @@ Task("TestNetStandard16")

Task("TestNetStandard20")
.Description("Tests the .NET Standard 2.0 version of the framework")
.WithCriteria(IsRunningOnWindows())
.IsDependentOn("Build")
.OnError(exception => { ErrorDetail.Add(exception.Message); })
.Does(() =>
Expand Down
2 changes: 1 addition & 1 deletion src/NUnitFramework/Common.props
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<DefineConstants Condition="'$(TargetFramework)' != 'netstandard1.6'
and '$(TargetFramework)' != 'netcoreapp1.1'
and '$(TargetFramework)' != 'netstandard2.0'
and '$(TargetFramework)' != 'netcoreapp2.0'">$(DefineConstants);PLATFORM_DETECTION;THREAD_ABORT</DefineConstants>
and '$(TargetFramework)' != 'netcoreapp2.0'">$(DefineConstants);PLATFORM_DETECTION;THREAD_ABORT;APARTMENT_STATE</DefineConstants>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ***********************************************************************

#if !NETSTANDARD1_6
#if APARTMENT_STATE
using System;
using System.Threading;
using NUnit.Framework.Internal;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// ***********************************************************************
// ***********************************************************************
// Copyright (c) 2008 Charlie Poole, Rob Prouse
//
// Permission is hereby granted, free of charge, to any person obtaining
Expand Down Expand Up @@ -41,6 +41,7 @@ public class RequiresThreadAttribute : PropertyAttribute, IApplyToTest
public RequiresThreadAttribute()
: base(true) { }

#if APARTMENT_STATE
/// <summary>
/// Construct a RequiresThreadAttribute, specifying the apartment
/// </summary>
Expand All @@ -49,6 +50,7 @@ public RequiresThreadAttribute(ApartmentState apartment)
{
this.Properties.Add(PropertyNames.ApartmentState, apartment);
}
#endif

void IApplyToTest.ApplyToTest(Test test)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,13 @@ private void InitializeShifts()

// Assign queues to shifts
ParallelShift.AddQueue(ParallelQueue);
#if APARTMENT_STATE
ParallelShift.AddQueue(ParallelSTAQueue);
#endif
NonParallelShift.AddQueue(NonParallelQueue);
#if APARTMENT_STATE
NonParallelSTAShift.AddQueue(NonParallelSTAQueue);
#endif

// Create workers and assign to shifts and queues
// TODO: Avoid creating all the workers till needed
Expand All @@ -89,15 +93,19 @@ private void InitializeShifts()
ParallelShift.Assign(new TestWorker(ParallelQueue, name));
}

#if APARTMENT_STATE
ParallelShift.Assign(new TestWorker(ParallelSTAQueue, "ParallelSTAWorker"));
#endif

var worker = new TestWorker(NonParallelQueue, "NonParallelWorker");
worker.Busy += OnStartNonParallelWorkItem;
NonParallelShift.Assign(worker);

#if APARTMENT_STATE
worker = new TestWorker(NonParallelSTAQueue, "NonParallelSTAWorker");
worker.Busy += OnStartNonParallelWorkItem;
NonParallelSTAShift.Assign(worker);
#endif
}

private void OnStartNonParallelWorkItem(TestWorker worker, WorkItem work)
Expand All @@ -108,9 +116,9 @@ private void OnStartNonParallelWorkItem(TestWorker worker, WorkItem work)
IsolateQueues(work);
}

#endregion
#endregion

#region Properties
#region Properties

/// <summary>
/// Number of parallel worker threads
Expand All @@ -126,7 +134,9 @@ public IEnumerable<WorkShift> Shifts
{
yield return ParallelShift;
yield return NonParallelShift;
#if APARTMENT_STATE
yield return NonParallelSTAShift;
#endif
}
}

Expand All @@ -138,27 +148,36 @@ public IEnumerable<WorkItemQueue> Queues
get
{
yield return ParallelQueue;
#if APARTMENT_STATE
yield return ParallelSTAQueue;
#endif
yield return NonParallelQueue;
#if APARTMENT_STATE
yield return NonParallelSTAQueue;
#endif
}
}

// WorkShifts - Dispatcher processes tests in three non-overlapping shifts.
// See comment in Workshift.cs for a more detailed explanation.
private WorkShift ParallelShift { get; } = new WorkShift("Parallel");
private WorkShift NonParallelShift { get; } = new WorkShift("NonParallel");
#if APARTMENT_STATE
private WorkShift NonParallelSTAShift { get; } = new WorkShift("NonParallelSTA");

// WorkItemQueues
private WorkItemQueue ParallelQueue { get; } = new WorkItemQueue("ParallelQueue", true, ApartmentState.MTA);
private WorkItemQueue ParallelSTAQueue { get; } = new WorkItemQueue("ParallelSTAQueue", true, ApartmentState.STA);
private WorkItemQueue NonParallelQueue { get; } = new WorkItemQueue("NonParallelQueue", false, ApartmentState.MTA);
private WorkItemQueue NonParallelSTAQueue { get; } = new WorkItemQueue("NonParallelSTAQueue", false, ApartmentState.STA);
#else
// WorkItemQueues
private WorkItemQueue ParallelQueue { get; } = new WorkItemQueue("ParallelQueue", true);
private WorkItemQueue NonParallelQueue { get; } = new WorkItemQueue("NonParallelQueue", false);
#endif
#endregion

#endregion

#region IWorkItemDispatcher Members
#region IWorkItemDispatcher Members

/// <summary>
/// Start execution, setting the top level work,
Expand Down Expand Up @@ -209,15 +228,19 @@ private void Dispatch(WorkItem work, ParallelExecutionStrategy strategy)
work.Execute();
break;
case ParallelExecutionStrategy.Parallel:
#if APARTMENT_STATE
if (work.TargetApartment == ApartmentState.STA)
ParallelSTAQueue.Enqueue(work);
else
#endif
ParallelQueue.Enqueue(work);
break;
case ParallelExecutionStrategy.NonParallel:
#if APARTMENT_STATE
if (work.TargetApartment == ApartmentState.STA)
NonParallelSTAQueue.Enqueue(work);
else
#endif
NonParallelQueue.Enqueue(work);
break;
}
Expand Down Expand Up @@ -274,9 +297,9 @@ private void RestoreQueues()
}
}

#endregion
#endregion

#region Helper Methods
#region Helper Methods

private void OnEndOfShift(WorkShift endingShift)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@ public void Start(WorkItem topLevelWorkItem)
_topLevelWorkItem = topLevelWorkItem;
_runnerThread = new Thread(RunnerThreadProc);

#if APARTMENT_STATE
if (topLevelWorkItem.TargetApartment == ApartmentState.STA)
_runnerThread.SetApartmentState(ApartmentState.STA);
#endif

_runnerThread.Start();
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,9 @@ public void Start()
{
_workerThread = new Thread(new ThreadStart(TestWorkerThreadProc));
_workerThread.Name = Name;
#if APARTMENT_STATE
_workerThread.SetApartmentState(WorkQueue.TargetApartment);

#endif
log.Info("{0} starting on thread [{1}]", Name, _workerThread.ManagedThreadId);
_workerThread.Start();
}
Expand Down
24 changes: 19 additions & 5 deletions src/NUnitFramework/framework/Internal/Execution/WorkItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public WorkItem(Test test, ITestFilter filter)
? (ParallelScope)Test.Properties.Get(PropertyNames.ParallelScope)
: ParallelScope.Default;

#if !NETSTANDARD1_6
#if APARTMENT_STATE
TargetApartment = GetTargetApartment(Test);
#endif

Expand All @@ -91,7 +91,7 @@ public WorkItem(WorkItem wrappedItem)
#if PARALLEL
TestWorker = wrappedItem.TestWorker;
#endif
#if !NETSTANDARD1_6
#if APARTMENT_STATE
TargetApartment = wrappedItem.TargetApartment;
#endif

Expand Down Expand Up @@ -193,7 +193,7 @@ public virtual ParallelExecutionStrategy ExecutionStrategy
/// </summary>
public ParallelScope ParallelScope { get; private set; }

#if !NETSTANDARD1_6
#if APARTMENT_STATE
internal ApartmentState TargetApartment { get; set; }
private ApartmentState CurrentApartment { get; set; }
#endif
Expand Down Expand Up @@ -221,10 +221,14 @@ public virtual void Execute()
// (--workers=0 option) it occurs routinely whenever a
// different apartment is requested.

#if APARTMENT_STATE
CurrentApartment = Thread.CurrentThread.GetApartmentState();
var targetApartment = TargetApartment == ApartmentState.Unknown ? CurrentApartment : TargetApartment;

if (Test.RequiresThread || targetApartment != CurrentApartment)
#else
if (Test.RequiresThread)
#endif
{
// Handle error conditions in a single threaded fixture
if (Context.IsSingleThreaded)
Expand All @@ -242,12 +246,16 @@ public virtual void Execute()
log.Debug("Running on separate thread because {0} is specified.",
Test.RequiresThread ? "RequiresThread" : "different Apartment");

#if APARTMENT_STATE
RunOnSeparateThread(targetApartment);
#else
RunOnSeparateThread();
#endif
}
else
RunOnCurrentThread();
#else
RunOnCurrentThread();
RunOnCurrentThread();
#endif
}

Expand Down Expand Up @@ -459,7 +467,11 @@ protected void ChangeResult(ResultState resultState, string message)
#if !NETSTANDARD1_6
private Thread thread;

#if APARTMENT_STATE
private void RunOnSeparateThread(ApartmentState apartment)
#else
private void RunOnSeparateThread()
#endif
{
thread = new Thread(() =>
{
Expand All @@ -471,7 +483,9 @@ private void RunOnSeparateThread(ApartmentState apartment)
#endif
RunOnCurrentThread();
});
#if APARTMENT_STATE
thread.SetApartmentState(apartment);
#endif
thread.Start();
thread.Join();
}
Expand Down Expand Up @@ -531,7 +545,7 @@ private ParallelExecutionStrategy GetExecutionStrategy()
}
#endif

#if !NETSTANDARD1_6
#if APARTMENT_STATE
/// <summary>
/// Recursively walks up the test hierarchy to see if the
/// <see cref="ApartmentState"/> has been set on any of the parent tests.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// ***********************************************************************
// ***********************************************************************
// Copyright (c) 2017 Charlie Poole, Rob Prouse
//
// Permission is hereby granted, free of charge, to any person obtaining
Expand Down Expand Up @@ -59,7 +59,7 @@ static public WorkItem CreateWorkItem(ITest test, ITestFilter filter, bool recur
{
var childItem = CreateWorkItem(childTest, filter, recursive);

#if !NETSTANDARD1_6
#if APARTMENT_STATE
if (childItem.TargetApartment == ApartmentState.Unknown && work.TargetApartment != ApartmentState.Unknown)
childItem.TargetApartment = work.TargetApartment;
#endif
Expand Down
19 changes: 16 additions & 3 deletions src/NUnitFramework/framework/Internal/Execution/WorkItemQueue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,28 @@ public SavedState(WorkItemQueue queue)
private int _addId = int.MinValue;
private int _removeId = int.MinValue;

#if APARTMENT_STATE
/// <summary>
/// Initializes a new instance of the <see cref="WorkItemQueue"/> class.
/// </summary>
/// <param name="name">The name of the queue.</param>
/// <param name="isParallel">Flag indicating whether this is a parallel queue</param>
/// <param name="apartment">ApartmentState to use for items on this queue</param>
public WorkItemQueue(string name, bool isParallel, ApartmentState apartment)
#else
/// <summary>
/// Initializes a new instance of the <see cref="WorkItemQueue"/> class.
/// </summary>
/// <param name="name">The name of the queue.</param>
/// <param name="isParallel">Flag indicating whether this is a parallel queue</param>
public WorkItemQueue(string name, bool isParallel)
#endif
{
Name = name;
IsParallelQueue = isParallel;
#if APARTMENT_STATE
TargetApartment = apartment;
#endif
State = WorkItemQueueState.Paused;
ItemsProcessed = 0;

Expand All @@ -126,7 +137,7 @@ private void InitializeQueues()
_innerQueues[i] = new ConcurrentQueue<WorkItem>();
}

#region Properties
#region Properties

/// <summary>
/// Gets the name of the work item queue.
Expand All @@ -138,10 +149,12 @@ private void InitializeQueues()
/// </summary>
public bool IsParallelQueue { get; private set; }

#if APARTMENT_STATE
/// <summary>
/// Gets the target ApartmentState for work items on this queue
/// </summary>
public ApartmentState TargetApartment { get; private set; }
#endif

private int _itemsProcessed;
/// <summary>
Expand Down Expand Up @@ -178,9 +191,9 @@ public bool IsEmpty
}
}

#endregion
#endregion

#region Public Methods
#region Public Methods

/// <summary>
/// Enqueue a WorkItem to be processed
Expand Down
4 changes: 3 additions & 1 deletion src/NUnitFramework/testdata/ParallelExecutionData.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// ***********************************************************************
// ***********************************************************************
// Copyright (c) 2017 Charlie Poole, Rob Prouse
//
// Permission is hereby granted, free of charge, to any person obtaining
Expand Down Expand Up @@ -86,6 +86,7 @@ public void TestFixture3_Test()
}
}

#if APARTMENT_STATE
[Apartment(ApartmentState.STA)]
public class STAFixture
{
Expand All @@ -95,6 +96,7 @@ public void STAFixture_Test()
Thread.Sleep(100);
}
}
#endif

public class TestFixtureWithParallelParameterizedTest
{
Expand Down