diff --git a/src/NUnitTestAdapter/AdapterSettings.cs b/src/NUnitTestAdapter/AdapterSettings.cs index 71755188..099e820f 100644 --- a/src/NUnitTestAdapter/AdapterSettings.cs +++ b/src/NUnitTestAdapter/AdapterSettings.cs @@ -50,15 +50,16 @@ public interface IAdapterSettings int? RandomSeed { get; } bool RandomSeedSpecified { get; } bool InProcDataCollectorsAvailable { get; } + bool CollectDataForEachTestSeparately { get; } bool SynchronousEvents { get; } - string DomainUsage { get; } - bool DumpXmlTestDiscovery { get; } - bool DumpXmlTestResults { get; } + string DomainUsage { get; } + bool DumpXmlTestDiscovery { get; } + bool DumpXmlTestResults { get; } /// /// Syntax documentation /// - string DefaultTestNamePattern { get; } + string DefaultTestNamePattern { get; } void Load(IDiscoveryContext context); void Load(string settingsXml); @@ -143,6 +144,8 @@ public AdapterSettings(TestLogger logger) public int? RandomSeed { get; private set; } public bool RandomSeedSpecified { get; private set; } + public bool CollectDataForEachTestSeparately { get; private set; } + public bool InProcDataCollectorsAvailable { get; private set; } public bool SynchronousEvents { get; private set; } @@ -195,6 +198,7 @@ public void Load(string settingsXml) DisableAppDomain = GetInnerTextAsBool(runConfiguration, nameof(DisableAppDomain), false); DisableParallelization = GetInnerTextAsBool(runConfiguration, nameof(DisableParallelization), false); DesignMode = GetInnerTextAsBool(runConfiguration, nameof(DesignMode), false); + CollectDataForEachTestSeparately = GetInnerTextAsBool(runConfiguration, nameof(CollectDataForEachTestSeparately), false); TestProperties = new Dictionary(); foreach (XmlNode node in doc.SelectNodes("RunSettings/TestRunParameters/Parameter")) @@ -205,7 +209,7 @@ public void Load(string settingsXml) TestProperties.Add(key, value); } - // NUnit settings + // NUnit settings InternalTraceLevel = GetInnerTextWithLog(nunitNode, nameof(InternalTraceLevel), "Off", "Error", "Warning", "Info", "Verbose", "Debug"); WorkDirectory = GetInnerTextWithLog(nunitNode, nameof(WorkDirectory)); DefaultTimeout = GetInnerTextAsInt(nunitNode, nameof(DefaultTimeout), 0); @@ -220,8 +224,8 @@ public void Load(string settingsXml) RandomSeed = new Random().Next(); DefaultTestNamePattern = GetInnerTextWithLog(nunitNode, nameof(DefaultTestNamePattern)); - DumpXmlTestDiscovery = GetInnerTextAsBool(nunitNode, nameof(DumpXmlTestDiscovery),false); - DumpXmlTestResults= GetInnerTextAsBool(nunitNode, nameof(DumpXmlTestResults), false); + DumpXmlTestDiscovery = GetInnerTextAsBool(nunitNode, nameof(DumpXmlTestDiscovery), false); + DumpXmlTestResults = GetInnerTextAsBool(nunitNode, nameof(DumpXmlTestResults), false); @@ -241,22 +245,31 @@ public void Load(string settingsXml) Verbosity = 1; #endif - // If any in proc data collector will be instantiated by the TestPlatform run tests sequentially. var inProcDataCollectorNode = doc.SelectSingleNode("RunSettings/InProcDataCollectionRunSettings/InProcDataCollectors"); InProcDataCollectorsAvailable = inProcDataCollectorNode != null && inProcDataCollectorNode.SelectNodes("InProcDataCollector").Count > 0; - if (InProcDataCollectorsAvailable) + + // Older versions of VS do not pass the CollectDataForEachTestSeparately configuration together with the LiveUnitTesting collector. + // However, the adapter is expected to run in CollectDataForEachTestSeparately mode. + // As a result for backwards compatibility reasons enable CollectDataForEachTestSeparately mode whenever LiveUnitTesting collector is being used. + var hasLiveUnitTestingDataCollector = inProcDataCollectorNode?.SelectSingleNode("InProcDataCollector[@uri='InProcDataCollector://Microsoft/LiveUnitTesting/1.0']") != null; + + // TestPlatform can opt-in to run tests one at a time so that the InProcDataCollectors can collect the data for each one of them separately. + // In that case, we need to ensure that tests do not run in parallel and the test started/test ended events are sent synchronously. + if (CollectDataForEachTestSeparately || hasLiveUnitTestingDataCollector) { NumberOfTestWorkers = 0; - DomainUsage = "None"; SynchronousEvents = true; if (Verbosity >= 4) { - _logger.Info($"InProcDataCollectors are available: turning off Parallel, DomainUsage=None, SynchronousEvents=true"); + if (!InProcDataCollectorsAvailable) + { + _logger.Info("CollectDataForEachTestSeparately is set, which is used to make InProcDataCollectors collect data for each test separately. No InProcDataCollectors can be found, thus the tests will run slower unnecessarily."); + } } } // If DisableAppDomain settings is passed from the testplatform, set the DomainUsage to None. - if(DisableAppDomain) + if (DisableAppDomain) { DomainUsage = "None"; } @@ -301,13 +314,13 @@ public void RestoreRandomSeed(string dirname) private void UpdateNumberOfTestWorkers() { // Overriding the NumberOfTestWorkers if DisableParallelization is true. - if(DisableParallelization && NumberOfTestWorkers < 0) + if (DisableParallelization && NumberOfTestWorkers < 0) { NumberOfTestWorkers = 0; } - else if(DisableParallelization && NumberOfTestWorkers > 0) + else if (DisableParallelization && NumberOfTestWorkers > 0) { - if(_logger.Verbosity > 0) + if (_logger.Verbosity > 0) { _logger.Warning(string.Format("DisableParallelization:{0} & NumberOfTestWorkers:{1} are conflicting settings, hence not running in parallel", DisableParallelization, NumberOfTestWorkers)); } @@ -339,17 +352,17 @@ private string GetInnerText(XmlNode startNode, string xpath, bool log, params st "Invalid value {0} passed for element {1}.", val, xpath)); } - + } if (log) - Log(xpath,val); + Log(xpath, val); return val; } private int GetInnerTextAsInt(XmlNode startNode, string xpath, int defaultValue) { - var temp = GetInnerTextAsNullableInt(startNode, xpath,false); + var temp = GetInnerTextAsNullableInt(startNode, xpath, false); var res = defaultValue; if (temp != null) res = temp.Value; @@ -357,24 +370,24 @@ private int GetInnerTextAsInt(XmlNode startNode, string xpath, int defaultValue) return res; } - private int? GetInnerTextAsNullableInt(XmlNode startNode, string xpath,bool log=true) + private int? GetInnerTextAsNullableInt(XmlNode startNode, string xpath, bool log = true) { - string temp = GetInnerText(startNode, xpath,log); + string temp = GetInnerText(startNode, xpath, log); int? res = null; if (!string.IsNullOrEmpty(temp)) res = int.Parse(temp); if (log) - Log(xpath,res); + Log(xpath, res); return res; } private bool GetInnerTextAsBool(XmlNode startNode, string xpath, bool defaultValue) { - string temp = GetInnerText(startNode, xpath,false); + string temp = GetInnerText(startNode, xpath, false); bool res = defaultValue; if (!string.IsNullOrEmpty(temp)) res = bool.Parse(temp); - Log(xpath,res); + Log(xpath, res); return res; } diff --git a/src/NUnitTestAdapterTests/AdapterSettingsTests.cs b/src/NUnitTestAdapterTests/AdapterSettingsTests.cs index 6ba8821b..efd8aeb9 100644 --- a/src/NUnitTestAdapterTests/AdapterSettingsTests.cs +++ b/src/NUnitTestAdapterTests/AdapterSettingsTests.cs @@ -242,6 +242,27 @@ public void DefaultTestNamePattern() Assert.That(_settings.DefaultTestNamePattern,Is.EqualTo("{m}{a:1000}")); } + [Test] + public void CollectDataForEachTestSeparately() + { + _settings.Load(@" + + + true + + + + + + +"); + + Assert.Null(_settings.DomainUsage); + Assert.True(_settings.SynchronousEvents); + Assert.That(_settings.NumberOfTestWorkers, Is.Zero); + Assert.True(_settings.InProcDataCollectorsAvailable); + } + [Test] public void InProcDataCollector() { @@ -254,7 +275,25 @@ public void InProcDataCollector() "); - Assert.That(_settings.DomainUsage, Is.EqualTo("None")); + Assert.Null(_settings.DomainUsage); + Assert.False(_settings.SynchronousEvents); + Assert.That(_settings.NumberOfTestWorkers, Is.EqualTo(-1)); + Assert.True(_settings.InProcDataCollectorsAvailable); + } + + [Test] + public void LiveUnitTestingDataCollector() + { + _settings.Load(@" + + + + + + +"); + + Assert.Null(_settings.DomainUsage); Assert.True(_settings.SynchronousEvents); Assert.That(_settings.NumberOfTestWorkers, Is.Zero); Assert.True(_settings.InProcDataCollectorsAvailable);