diff --git a/Chutzpah/BatchCompiler/BatchCompilerService.cs b/Chutzpah/BatchCompiler/BatchCompilerService.cs index 77a6fdc1..c507849a 100644 --- a/Chutzpah/BatchCompiler/BatchCompilerService.cs +++ b/Chutzpah/BatchCompiler/BatchCompilerService.cs @@ -23,7 +23,7 @@ public BatchCompilerService(IProcessHelper processHelper, IFileSystemWrapper fil this.sourceMapDiscoverer = sourceMapDiscoverer; } - public void Compile(IEnumerable testContexts) + public void Compile(IEnumerable testContexts, ITestMethodRunnerCallback callback = null) { // Group the test contexts by test settings to run batch aware settings like compile // For each test settings file that defines a compile step we will run it and update @@ -101,7 +101,8 @@ where testSettings.Compile.Extensions.Any(x => file.Path.EndsWith(x, StringCompa if (!testSettings.Compile.IgnoreMissingFiles.GetValueOrDefault()) { // Throw and fail here since if we cant find the file we cannot be sure anything will run - throw new FileNotFoundException(error, outputPath); + var exception = new FileNotFoundException(error, outputPath); + callback.ExceptionThrown(exception, outputPath); } } } diff --git a/Chutzpah/BatchCompiler/IBatchCompilerService.cs b/Chutzpah/BatchCompiler/IBatchCompilerService.cs index 28246972..bcf465e5 100644 --- a/Chutzpah/BatchCompiler/IBatchCompilerService.cs +++ b/Chutzpah/BatchCompiler/IBatchCompilerService.cs @@ -6,6 +6,6 @@ namespace Chutzpah.BatchProcessor { public interface IBatchCompilerService { - void Compile(IEnumerable testContexts); + void Compile(IEnumerable testContexts, ITestMethodRunnerCallback callback = null); } } diff --git a/Chutzpah/ChutzpahTestSettingsService.cs b/Chutzpah/ChutzpahTestSettingsService.cs index d1cfb0fe..bab76799 100644 --- a/Chutzpah/ChutzpahTestSettingsService.cs +++ b/Chutzpah/ChutzpahTestSettingsService.cs @@ -75,7 +75,13 @@ public ChutzpahTestSettingsFile FindSettingsFileFromDirectory(string directory, ChutzpahTestSettingsFile settings; if (!ChutzpahSettingsFileCache.TryGetValue(directory, out settings)) { - return ProcessSettingsFile(directory, environments, new List()).InheritFromDefault(); + ChutzpahSettingsFileEnvironment environment = null; + if (environments != null) + { + environment = environments.GetSettingsFileEnvironment(directory); + } + + return ProcessSettingsFile(directory, environment).InheritFromDefault(); } else { @@ -83,14 +89,14 @@ public ChutzpahTestSettingsFile FindSettingsFileFromDirectory(string directory, } } - private ChutzpahTestSettingsFile ProcessSettingsFile(string directory, ChutzpahSettingsFileEnvironments environments, IList environmentChain) + private ChutzpahTestSettingsFile ProcessSettingsFile(string directory, ChutzpahSettingsFileEnvironment environment, bool forceFresh = false) { if (string.IsNullOrEmpty(directory)) return ChutzpahTestSettingsFile.Default; directory = directory.TrimEnd('/', '\\'); ChutzpahTestSettingsFile settings; - if (!ChutzpahSettingsFileCache.TryGetValue(directory, out settings)) + if (!ChutzpahSettingsFileCache.TryGetValue(directory, out settings) || forceFresh) { var testSettingsFilePath = fileProbe.FindTestSettingsFile(directory); if (string.IsNullOrEmpty(testSettingsFilePath)) @@ -98,7 +104,7 @@ private ChutzpahTestSettingsFile ProcessSettingsFile(string directory, ChutzpahS ChutzpahTracer.TraceInformation("Chutzpah.json file not found given starting directory {0}", directory); settings = ChutzpahTestSettingsFile.Default; } - else if (!ChutzpahSettingsFileCache.TryGetValue(Path.GetDirectoryName(testSettingsFilePath), out settings)) + else if (!ChutzpahSettingsFileCache.TryGetValue(Path.GetDirectoryName(testSettingsFilePath), out settings) || forceFresh) { ChutzpahTracer.TraceInformation("Chutzpah.json file found at {0} given starting directory {1}", testSettingsFilePath, directory); settings = serializer.DeserializeFromFile(testSettingsFilePath); @@ -111,25 +117,9 @@ private ChutzpahTestSettingsFile ProcessSettingsFile(string directory, ChutzpahS { settings.IsDefaultSettings = false; } - - ChutzpahSettingsFileEnvironment environment = null; - if (environments != null) - { - environment = environments.GetSettingsFileEnvironment(directory); - - if (environment != null) - { - // Add this environment to track the list - // of environments we have as we walk up the chutzpah.json chain. - // We enumerate in order from child to parent letting the more specific environment win - environmentChain.Add(environment); - } - } - - settings.SettingsFileDirectory = Path.GetDirectoryName(testSettingsFilePath); - var chutzpahVariables = BuildChutzpahReplacementVariables(testSettingsFilePath, environmentChain, settings); + var chutzpahVariables = BuildChutzpahReplacementVariables(testSettingsFilePath, environment, settings); ResolveTestHarnessDirectory(settings, chutzpahVariables); @@ -141,18 +131,22 @@ private ChutzpahTestSettingsFile ProcessSettingsFile(string directory, ChutzpahS ProcessTraceFilePath(settings, chutzpahVariables); - ProcessInheritance(environments, environmentChain, settings, chutzpahVariables); + ProcessInheritance(environment, settings, chutzpahVariables); - // Add a mapping in the cache for the directory that contains the test settings file - ChutzpahSettingsFileCache.TryAdd(settings.SettingsFileDirectory, settings); + if (!forceFresh) + { + // Add a mapping in the cache for the directory that contains the test settings file + ChutzpahSettingsFileCache.TryAdd(settings.SettingsFileDirectory, settings); + } } - // Add mapping in the cache for the original directory tried to skip needing to traverse the tree again - ChutzpahSettingsFileCache.TryAdd(directory, settings); + if (!forceFresh) + { + // Add mapping in the cache for the original directory tried to skip needing to traverse the tree again + ChutzpahSettingsFileCache.TryAdd(directory, settings); + } } - - return settings; } @@ -165,7 +159,7 @@ private void ProcessTraceFilePath(ChutzpahTestSettingsFile settings, IDictionary } } - private void ProcessInheritance(ChutzpahSettingsFileEnvironments environments, IList environmentChain, ChutzpahTestSettingsFile settings, IDictionary chutzpahVariables) + private void ProcessInheritance(ChutzpahSettingsFileEnvironment environment, ChutzpahTestSettingsFile settings, IDictionary chutzpahVariables) { if (settings.InheritFromParent || !string.IsNullOrEmpty(settings.InheritFromPath)) { @@ -190,7 +184,11 @@ private void ProcessInheritance(ChutzpahSettingsFileEnvironments environments, I settings.InheritFromPath = settingsToInherit; } - var parentSettingsFile = ProcessSettingsFile(settings.InheritFromPath, environments, environmentChain); + // If we have any environment properties do not use cached + // parents and re-evaluate using current environment + var forceFresh = environment != null && environment.Properties.Any(); + + var parentSettingsFile = ProcessSettingsFile(settings.InheritFromPath, environment, forceFresh); if (!parentSettingsFile.IsDefaultSettings) { @@ -454,7 +452,7 @@ private string ExpandChutzpahVariables(IDictionary chutzpahCompi return chutzpahCompileVariables.Aggregate(str, (current, pair) => current.Replace(pair.Key, pair.Value)); } - private IDictionary BuildChutzpahReplacementVariables(string settingsFilePath, IList environmentChain, ChutzpahTestSettingsFile settings) + private IDictionary BuildChutzpahReplacementVariables(string settingsFilePath, ChutzpahSettingsFileEnvironment environment, ChutzpahTestSettingsFile settings) { IDictionary chutzpahVariables = new Dictionary(); @@ -472,20 +470,16 @@ private IDictionary BuildChutzpahReplacementVariables(string set // This is not needed but it is a nice alias AddChutzpahVariable(chutzpahVariables, "cmdexe", Environment.ExpandEnvironmentVariables("%comspec%")); - - // See if we have a settingsfileenvironment set and if so add its properties as chutzpah settings file variables - foreach (var environment in environmentChain) + if (environment != null) { + // See if we have a settingsfileenvironment set and if so add its properties as chutzpah settings file variables var props = environment.Properties; foreach (var prop in props) { - if (!string.IsNullOrEmpty(prop.Value)) - { - AddChutzpahVariable(chutzpahVariables, prop.Name, prop.Value); - } + AddChutzpahVariable(chutzpahVariables, prop.Name, prop.Value); } } - + return chutzpahVariables; } diff --git a/Chutzpah/ITestRunner.cs b/Chutzpah/ITestRunner.cs index 59b6db64..88e2052e 100644 --- a/Chutzpah/ITestRunner.cs +++ b/Chutzpah/ITestRunner.cs @@ -31,6 +31,7 @@ public interface ITestRunner IEnumerable DiscoverTests(string testPath); IEnumerable DiscoverTests(IEnumerable testPaths); + IEnumerable DiscoverTests(IEnumerable testPaths, TestOptions options, ITestMethodRunnerCallback callback); bool IsTestFile(string testFile, ChutzpahSettingsFileEnvironments envionrments); IEnumerable DiscoverTests(IEnumerable testPaths, TestOptions options); void CleanTestContext(TestContext context); diff --git a/Chutzpah/TestRunner.cs b/Chutzpah/TestRunner.cs index 4ad718eb..28dd7455 100644 --- a/Chutzpah/TestRunner.cs +++ b/Chutzpah/TestRunner.cs @@ -113,6 +113,12 @@ public IEnumerable DiscoverTests(IEnumerable testPaths, TestOp return summary.Tests; } + public IEnumerable DiscoverTests(IEnumerable testPaths, TestOptions options, ITestMethodRunnerCallback callback) + { + var summary = ProcessTestPaths(testPaths, options, TestExecutionMode.Discovery, callback); + return summary.Tests; + } + public TestCaseSummary RunTests(string testPath, ITestMethodRunnerCallback callback = null) { @@ -298,7 +304,7 @@ private bool PerformBatchCompile(ITestMethodRunnerCallback callback, IEnumerable { try { - batchCompilerService.Compile(testContexts); + batchCompilerService.Compile(testContexts, callback); } catch (FileNotFoundException e) { diff --git a/Facts/Library/BatchCompilerServiceFacts.cs b/Facts/Library/BatchCompilerServiceFacts.cs index ff865454..481047aa 100644 --- a/Facts/Library/BatchCompilerServiceFacts.cs +++ b/Facts/Library/BatchCompilerServiceFacts.cs @@ -102,10 +102,11 @@ public void Will_throw_if_file_is_missing() .Returns(false) .Returns(true); service.Mock().Setup(x => x.FileExists(It.Is(f => f.EndsWith(".ts")))).Returns(true); + var callback = service.Mock(); - var ex = Record.Exception( () => service.ClassUnderTest.Compile(new[] { context })); + service.ClassUnderTest.Compile(new[] { context }, callback.Object); - Assert.IsType(ex); + callback.Verify(x => x.ExceptionThrown(It.IsAny(), It.IsAny())); service.Mock().Verify(x => x.RunBatchCompileProcess(It.IsAny()), Times.Never()); } diff --git a/VS2012.TestAdapter/ChutzpahTestDiscoverer.cs b/VS2012.TestAdapter/ChutzpahTestDiscoverer.cs index af0abd98..ff4e89f9 100644 --- a/VS2012.TestAdapter/ChutzpahTestDiscoverer.cs +++ b/VS2012.TestAdapter/ChutzpahTestDiscoverer.cs @@ -8,6 +8,7 @@ using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; +using Chutzpah.Callbacks; namespace Chutzpah.VS2012.TestAdapter { @@ -44,21 +45,12 @@ public void DiscoverTests(IEnumerable sources, IDiscoveryContext discove ChutzpahSettingsFileEnvironments = new ChutzpahSettingsFileEnvironments(settings.ChutzpahSettingsFileEnvironments) }; - IList errors; - var testCases = testRunner.DiscoverTests(sources, testOptions, out errors); ChutzpahTracer.TraceInformation("Sending discovered tests to test case discovery sink"); - foreach (var testCase in testCases) - { - var vsTestCase = testCase.ToVsTestCase(); - discoverySink.SendTestCase(vsTestCase); - } + var callback = new ParallelRunnerCallbackAdapter(new DiscoveryCallback(logger, discoverySink)); + var testCases = testRunner.DiscoverTests(sources, testOptions, callback); - foreach (var error in errors) - { - logger.SendMessage(TestMessageLevel.Error, RunnerCallback.FormatFileErrorMessage(error)); - } ChutzpahTracer.TraceInformation("End Test Adapter Discover Tests"); diff --git a/VS2012.TestAdapter/ChutzpahTestExecutor.cs b/VS2012.TestAdapter/ChutzpahTestExecutor.cs index dbe4e559..f0381a7f 100644 --- a/VS2012.TestAdapter/ChutzpahTestExecutor.cs +++ b/VS2012.TestAdapter/ChutzpahTestExecutor.cs @@ -1,15 +1,10 @@ using System.Collections.Generic; -using System.Diagnostics; -using System.IO; using System.Linq; using Chutzpah.Callbacks; -using Chutzpah.Coverage; using Chutzpah.Models; using Chutzpah.VS.Common; -using Chutzpah.Wrappers; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; -using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; namespace Chutzpah.VS2012.TestAdapter { diff --git a/VS2012.TestAdapter/DiscoveryCallback.cs b/VS2012.TestAdapter/DiscoveryCallback.cs new file mode 100644 index 00000000..59bb77c2 --- /dev/null +++ b/VS2012.TestAdapter/DiscoveryCallback.cs @@ -0,0 +1,36 @@ +using System; +using Chutzpah.Models; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; + +namespace Chutzpah.VS2012.TestAdapter +{ + public class DiscoveryCallback : RunnerCallback + { + private readonly IMessageLogger logger; + private readonly ITestCaseDiscoverySink discoverySink; + + public DiscoveryCallback(IMessageLogger logger, ITestCaseDiscoverySink discoverySink) + { + this.logger = logger; + this.discoverySink = discoverySink; + } + + public override void FileError(TestError error) + { + logger.SendMessage(TestMessageLevel.Error, GetFileErrorMessage(error)); + } + + public override void ExceptionThrown(Exception exception, string fileName) + { + logger.SendMessage(TestMessageLevel.Error, GetExceptionThrownMessage(exception, fileName)); + } + + public override void TestFinished(TestCase test) + { + var testCase = test.ToVsTestCase(); + discoverySink.SendTestCase(testCase); + } + + } +} \ No newline at end of file diff --git a/VS2012.TestAdapter/VS2012.TestAdapter.csproj b/VS2012.TestAdapter/VS2012.TestAdapter.csproj index 7c24a173..2fb8ff98 100644 --- a/VS2012.TestAdapter/VS2012.TestAdapter.csproj +++ b/VS2012.TestAdapter/VS2012.TestAdapter.csproj @@ -59,6 +59,7 @@ + diff --git a/VS2012/Adapter/ChutzpahTestContainerDiscoverer.cs b/VS2012/Adapter/ChutzpahTestContainerDiscoverer.cs index a2a35d99..8ee01183 100644 --- a/VS2012/Adapter/ChutzpahTestContainerDiscoverer.cs +++ b/VS2012/Adapter/ChutzpahTestContainerDiscoverer.cs @@ -424,14 +424,19 @@ private void UpdateChutzpahEnvironmentForProject(string projectPath) var dirPath = Path.GetDirectoryName(projectPath); foreach (var prop in ChutzpahMsBuildProps.GetProps()) { - chutzpahEnvProps.Add(new ChutzpahSettingsFileEnvironmentProperty(prop, buildProject.GetPropertyValue(prop))); + var value = buildProject.GetPropertyValue(prop); + if (!string.IsNullOrEmpty(value)) + { + chutzpahEnvProps.Add(new ChutzpahSettingsFileEnvironmentProperty(prop, value)); + } } lock (sync) { var envProps = settingsMapper.Settings.ChutzpahSettingsFileEnvironments .FirstOrDefault(x => x.Path.TrimEnd('/', '\\').Equals(dirPath, StringComparison.OrdinalIgnoreCase)); - if (envProps == null) + + if (envProps == null && chutzpahEnvProps.Any()) { envProps = new ChutzpahSettingsFileEnvironment(dirPath); @@ -439,7 +444,10 @@ private void UpdateChutzpahEnvironmentForProject(string projectPath) } - envProps.Properties = chutzpahEnvProps; + if (envProps != null) + { + envProps.Properties = chutzpahEnvProps; + } } } } diff --git a/VisualStudioContextMenu/VisualStudioContextMenuPackage.cs b/VisualStudioContextMenu/VisualStudioContextMenuPackage.cs index 743beafb..b7894b6a 100644 --- a/VisualStudioContextMenu/VisualStudioContextMenuPackage.cs +++ b/VisualStudioContextMenu/VisualStudioContextMenuPackage.cs @@ -150,10 +150,17 @@ private void InitializeSettingsFileEnvironments() var environment = new ChutzpahSettingsFileEnvironment(dirPath); foreach (var prop in ChutzpahMsBuildProps.GetProps()) { - environment.Properties.Add(new ChutzpahSettingsFileEnvironmentProperty(prop, buildProject.GetPropertyValue(prop))); + var value = buildProject.GetPropertyValue(prop); + if (!string.IsNullOrEmpty(value)) + { + environment.Properties.Add(new ChutzpahSettingsFileEnvironmentProperty(prop, value)); + } } - newEnvironments.AddEnvironment(environment); + if (environment.Properties.Any()) + { + newEnvironments.AddEnvironment(environment); + } } settingsEnvironments = newEnvironments; @@ -365,7 +372,7 @@ private void RunTestsInSolutionFolderNodeCallback(object sender, EventArgs e, bo private void RunTestsFromEditorCallback(object sender, EventArgs e, bool withCodeCoverage = false, bool withDebugger = false) { string filePath = CurrentDocumentPath; - RunTests(filePath, withCodeCoverage:withCodeCoverage, withDebugger:withDebugger); + RunTests(filePath, withCodeCoverage: withCodeCoverage, withDebugger: withDebugger); } private void RunTests(string filePath, bool openInBrowser = false, bool withCodeCoverage = false, bool withDebugger = false) @@ -400,17 +407,17 @@ private void RunTests(IEnumerable filePaths, bool openInBrowser = false, } var options = new TestOptions - { - MaxDegreeOfParallelism = Settings.MaxDegreeOfParallelism, - CoverageOptions = new CoverageOptions - { - Enabled = withCodeCoverage - }, - - CustomTestLauncher = withDebugger ? new VsDebuggerTestLauncher() : null, - TestLaunchMode = GetTestLaunchMode(openInBrowser, withDebugger), - ChutzpahSettingsFileEnvironments = settingsEnvironments - }; + { + MaxDegreeOfParallelism = Settings.MaxDegreeOfParallelism, + CoverageOptions = new CoverageOptions + { + Enabled = withCodeCoverage + }, + + CustomTestLauncher = withDebugger ? new VsDebuggerTestLauncher() : null, + TestLaunchMode = GetTestLaunchMode(openInBrowser, withDebugger), + ChutzpahSettingsFileEnvironments = settingsEnvironments + }; var result = testRunner.RunTests(filePaths, options, runnerCallback); if (result.CoverageObject != null)