Skip to content
This repository was archived by the owner on Dec 12, 2017. It is now read-only.

Commit c9e1a0b

Browse files
committed
Fix for continuous testing
...and code coverage and dotMemoryUnit. Disables parallelisation and async test message reporting, so that the monitoring tools know which test executing code belongs to, and knows exactly when a tests starts/finishes.
1 parent e0a453d commit c9e1a0b

File tree

9 files changed

+78
-20
lines changed

9 files changed

+78
-20
lines changed

resharper/CommonAssemblyInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
[assembly : AssemblyDescription("xUnit.net unit test provider for " + ProductInfo.Product)]
77
[assembly : AssemblyCopyright("Copyright (C) Matt Ellis")]
88
[assembly : ComVisible(false)]
9-
[assembly : AssemblyVersion("2.3.1.0")]
9+
[assembly : AssemblyVersion("2.3.2.0")]

resharper/nuget/xunitcontrib.nuspec

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@
33
<metadata>
44
<id>CitizenMatt.Xunit</id>
55
<title>xUnit.net Test Support for ReSharper 10</title>
6-
<version>2.3.1</version>
6+
<version>2.3.2</version>
77
<authors>Matt Ellis</authors>
88
<owners>Matt Ellis</owners>
99
<description>A unit test provider for xUnit.net. Discovers and runs xUnit.net 1.x and 2.x tests. Includes annotations to aid ReSharper inspections and Live Templates to speed up inserting test methods and asserts.</description>
1010
<summary>A unit test provider for xUnit.net</summary>
1111
<releaseNotes>
12-
&#8226; Fixed issue with discovering theories at runtime
12+
&#8226; Now works properly with code coverage, continuous testing + dotMemoryUnit, by disabling all concurrency under these conditions
1313

1414
From previous releases:
15+
&#8226; Fixed issue with discovering theories at runtime
1516
&#8226; Support for ReSharper 10 (#82)
1617
&#8226; Inherited tests not showing in results (#79)
1718
&#8226; Theories can now be excluded by category (#89)

resharper/src/provider/UnitTestElements/XunitTestClassElement.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,38 @@ where gc.State.IsValid()
7777
var knownChildren = new HashSet<string>(knownMethods);
7878
knownChildren.AddRange(knownTheories);
7979

80+
var disableAllConcurrency = ShouldDisableAllConcurrency(run);
81+
8082
var projectId = Id.Project.GetPersistentID();
8183
return new List<UnitTestTask>
8284
{
83-
new UnitTestTask(null, new XunitBootstrapTask(projectId)),
85+
new UnitTestTask(null, new XunitBootstrapTask(projectId, disableAllConcurrency)),
8486
new UnitTestTask(null, new XunitTestAssemblyTask(projectId, AssemblyLocation)),
8587
new UnitTestTask(this, new XunitTestClassTask(projectId, TypeName.FullName, explicitElements.Contains(this), knownChildren))
8688
};
8789
}
8890

91+
private bool ShouldDisableAllConcurrency(IUnitTestRun run)
92+
{
93+
// Code coverage (and therefore continuous testing) and dotMemoryUnit cannot handle
94+
// tests running concurrently (code coverage and memory usage need to be tied back
95+
// to a specific test), so we need to disable concurrency in these environments. For
96+
// xunit, this means disabling parallelisation and async reporting of test messages.
97+
// This is likely to be set automatically in the test runner process by the ReSharper
98+
// test hosts - TaskExecutorConfiguration.DisallowTestConcurrency or some such.
99+
// See https://youtrack.jetbrains.com/issue/DCVR-7804
100+
switch (run.Launch.HostProvider.ID)
101+
{
102+
// TODO: It would be nice to use the actual constants, but they're not referenced
103+
// case ContinuousTestingHostProvider.ContinuousTestingHostProviderId:
104+
case "ContinuousTestingHostProviderId":
105+
case "Cover":
106+
case "dotMemoryUnit":
107+
return true;
108+
}
109+
return false;
110+
}
111+
89112
public override string Kind
90113
{
91114
get { return "xUnit.net Test Class"; }

resharper/src/runner/Executor.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ public void RunTests(IList<ITestCase> testCases)
2727

2828
var options = TestFrameworkOptions.ForExecution(environment.TestAssemblyConfiguration);
2929

30+
// If the test hosts have requested all concurrency to be disabled, make sure we
31+
// also make reporting of test messages synchronous. E.g. continuous testing needs
32+
// to know when a tests starts/finishes. If we report that asynchronously, it can't
33+
// accurately track what code is affected by which tests
34+
if (environment.DisableAllConcurrency)
35+
options.SetSynchronousMessageReporting(true);
36+
3037
Logger.LogVerbose("Starting execution");
3138
LogExecutionOptions(options);
3239

resharper/src/runner/Tasks/AttributeNames.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public static class AttributeNames
99
public const string Explicitly = "Explicitly";
1010
public const string Dynamic = "Dynamic";
1111
public const string ProjectId = "ProjectId";
12+
public const string DisableAllConcurrency = "DisableAllConcurrency";
1213

1314
// Note the case. TestRemoteChannelMessageListener strips this attribute
1415
// when running tests. Makes for cleaner gold output

resharper/src/runner/Tasks/XunitBootstrapTask.cs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,54 @@
11
using System;
2+
using System.Globalization;
23
using System.Xml;
34
using JetBrains.ReSharper.TaskRunnerFramework;
45

56
namespace XunitContrib.Runner.ReSharper.RemoteRunner.Tasks
67
{
7-
// This class's only purpose is to be the first class in a task sequence.
8-
// The first task is always serialised and sent from the external process
9-
// to the main ReSharper process. If we use XunitTestAssemblyTask as the
10-
// first class, it includes the path to the assembly under test, which
11-
// means failing tests if the assembly is in a different location to what's
12-
// in the gold files. So, this class is used first, and when it gets
13-
// serialised, there is nothing changeable in it (the project id is constant
14-
// for tests) and as long as we never report the assembly task (there's no
15-
// need, it's not a meaningful task - not associated with an element), then
16-
// our tests will pass
8+
// This class serves two purposes. Firstly, it notifies the runner of some
9+
// config that's only available from the provider (i.e. if the host is running,
10+
// debugging, covering or continuous testing). Secondly, it means that the
11+
// first task in the sequence isn't XunitTestAssemblyTask, which contains
12+
// the path of the assembly under test. This greatly helps the tests for
13+
// the runner itself, as the first task is output to the gold file, and
14+
// showing the path to the assembly under test will break things if we
15+
// run the tests in a different location. We can serialise this task,
16+
// as the project ID is constant in test runs, and there is nothing
17+
// else changeable in it. As long as we never report the assembly
18+
// task (there's no need, it's not a meaningful task - not associated
19+
// with an element), then our tests will pass.
1720
[Serializable]
1821
public class XunitBootstrapTask : RemoteTask, IEquatable<XunitBootstrapTask>
1922
{
20-
public XunitBootstrapTask(string projectId)
23+
24+
public XunitBootstrapTask(string projectId, bool disableAllConcurrency)
2125
: base(XunitTaskRunner.RunnerId)
2226
{
27+
DisableAllConcurrency = disableAllConcurrency;
2328
ProjectId = projectId;
2429
}
2530

2631
public XunitBootstrapTask(XmlElement element)
2732
: base(element)
2833
{
2934
ProjectId = GetXmlAttribute(element, AttributeNames.ProjectId);
35+
36+
bool disableAllConcurrency;
37+
if (!bool.TryParse(GetXmlAttribute(element, AttributeNames.DisableAllConcurrency), out disableAllConcurrency))
38+
disableAllConcurrency = false;
39+
DisableAllConcurrency = disableAllConcurrency;
3040
}
3141

3242
public string ProjectId { get; set; }
43+
public bool DisableAllConcurrency { get; set; }
44+
3345
public override bool IsMeaningfulTask { get { return false; }}
3446

3547
public override void SaveXml(XmlElement element)
3648
{
3749
base.SaveXml(element);
3850
SetXmlAttribute(element, AttributeNames.ProjectId, ProjectId);
51+
SetXmlAttribute(element, AttributeNames.DisableAllConcurrency, DisableAllConcurrency.ToString(CultureInfo.InvariantCulture));
3952
}
4053

4154
public override bool Equals(object obj)

resharper/src/runner/TestEnvironment.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace XunitContrib.Runner.ReSharper.RemoteRunner
99
{
1010
public class TestEnvironment
1111
{
12-
public TestEnvironment(XunitTestAssemblyTask assemblyTask)
12+
public TestEnvironment(XunitTestAssemblyTask assemblyTask, bool disableAllConcurrency)
1313
{
1414
// Use the assembly in the folder that the user has specified, or, if not, use the assembly location
1515
var assemblyFolder = GetAssemblyFolder(ResharperConfiguration, assemblyTask);
@@ -24,6 +24,9 @@ public TestEnvironment(XunitTestAssemblyTask assemblyTask)
2424
// clean up at the end of the run
2525
ShadowCopyPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
2626

27+
// The hosts require disabling concurrency (e.g. code coverage, continuous testing, dotMemoryUnit)
28+
DisableAllConcurrency = disableAllConcurrency;
29+
2730
TestAssemblyConfiguration = GetXunitConfiguration(AssemblyPath, ConfigPath);
2831
MergeConfiguration();
2932
DiagnosticMessages = new DiagnosticMessages(TestAssemblyConfiguration.DiagnosticMessagesOrDefault);
@@ -32,6 +35,7 @@ public TestEnvironment(XunitTestAssemblyTask assemblyTask)
3235
public string AssemblyPath { get; private set; }
3336
public string ConfigPath { get; private set; }
3437
public string ShadowCopyPath { get; private set; }
38+
public bool DisableAllConcurrency { get; private set; }
3539
public DiagnosticMessages DiagnosticMessages { get; private set; }
3640
public TestAssemblyConfiguration TestAssemblyConfiguration { get; private set; }
3741

@@ -90,6 +94,14 @@ private void MergeConfiguration()
9094
// I can tell if the xunit config was set, but not if the ReSharper one isn't. Which takes precedence?
9195
// (ReSharper's for now) Add diagnostic warning that the xunit
9296
TestAssemblyConfiguration.ShadowCopy = ResharperConfiguration.ShadowCopy;
97+
98+
// Disable parallelisation if the test host requests it (e.g. for code coverage, continuous testing
99+
// or dotMemoryUnit). Note that we'll also need to disable async test message reporting
100+
if (DisableAllConcurrency)
101+
{
102+
TestAssemblyConfiguration.ParallelizeAssembly = false;
103+
TestAssemblyConfiguration.ParallelizeTestCollections = false;
104+
}
93105
}
94106
}
95107
}

resharper/src/runner/TestRunner.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ public TestRunner(IRemoteTaskServer server)
1919

2020
public RunContext RunContext { get; private set; }
2121

22-
public void Run(XunitTestAssemblyTask assemblyTask)
22+
public void Run(XunitTestAssemblyTask assemblyTask, bool disableAllConcurrency)
2323
{
24-
var environment = new TestEnvironment(assemblyTask);
24+
var environment = new TestEnvironment(assemblyTask, disableAllConcurrency);
2525
try
2626
{
2727
if (environment.TestAssemblyConfiguration.ShadowCopyOrDefault)

resharper/src/runner/XunitTaskRunner.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@ public XunitTaskRunner(IRemoteTaskServer server)
1717

1818
public override void ExecuteRecursive(TaskExecutionNode node)
1919
{
20-
// node is XunitBootstapTask, which we don't care about (see comment on XunitBootstrapTask)
20+
var bootstrapTask = (XunitBootstrapTask) node.RemoteTask;
21+
2122
var assemblyTaskNode = node.Children[0];
2223
var assemblyTask = (XunitTestAssemblyTask) assemblyTaskNode.RemoteTask;
2324

2425
PopulateRunContext(testRunner.RunContext, assemblyTaskNode);
2526

26-
testRunner.Run(assemblyTask);
27+
testRunner.Run(assemblyTask, bootstrapTask.DisableAllConcurrency);
2728
}
2829

2930
public override void Abort()

0 commit comments

Comments
 (0)