diff --git a/integrationtest/TargetProjects/NetCoreTestProject.XUnit/packages.lock.json b/integrationtest/TargetProjects/NetCoreTestProject.XUnit/packages.lock.json index a9787393ac..0006d79e9e 100644 --- a/integrationtest/TargetProjects/NetCoreTestProject.XUnit/packages.lock.json +++ b/integrationtest/TargetProjects/NetCoreTestProject.XUnit/packages.lock.json @@ -1097,7 +1097,7 @@ "targetproject": { "type": "Project", "dependencies": { - "Library": "0.15.0" + "Library": "1.0.0" } } } diff --git a/integrationtest/TargetProjects/NetFramework/FullFrameworkApp/packages.lock.json b/integrationtest/TargetProjects/NetFramework/FullFrameworkApp/packages.lock.json deleted file mode 100644 index a25e833fe0..0000000000 --- a/integrationtest/TargetProjects/NetFramework/FullFrameworkApp/packages.lock.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "version": 1, - "dependencies": { - ".NETFramework,Version=v4.6.1": { - "Newtonsoft.Json": { - "type": "Direct", - "requested": "[12.0.3, 12.0.3]", - "resolved": "12.0.3", - "contentHash": "6mgjfnRB4jKMlzHSl+VD+oUc1IebOZabkbyWj2RiTgWwYPPuaK1H97G1sHqGwPlS5npiF5Q0OrxN1wni2n5QWg==" - } - } - } -} \ No newline at end of file diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/InputFileResolverTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/InputFileResolverTests.cs index 57cfe95fec..4983a9480d 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/InputFileResolverTests.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/InputFileResolverTests.cs @@ -106,6 +106,46 @@ public void InitializeShouldFindFilesRecursively() result.ProjectContents.GetAllFiles().Count().ShouldBe(2); } + [Fact] + public void InitializeShouldUseBuildAlyzerResult() + { + var fileSystem = new MockFileSystem(new Dictionary + { + { projectUnderTestPath, new MockFileData(defaultTestProjectFileContents)}, + { Path.Combine(_filesystemRoot, "ExampleProject", "Recursive.cs"), new MockFileData(sourceFile)}, + { Path.Combine(_filesystemRoot, "ExampleProject", "Plain.cs"), new MockFileData(sourceFile)}, + { Path.Combine(_filesystemRoot, "ExampleProject", "OneFolderDeeper", "Recursive.cs"), new MockFileData(sourceFile)}, + { testProjectPath, new MockFileData(defaultTestProjectFileContents)}, + { Path.Combine(_filesystemRoot, "ExampleProject", "bin", "Debug", "netcoreapp2.1"), new MockFileData("Bytecode") }, // bin should be excluded + { Path.Combine(_filesystemRoot, "ExampleProject", "obj", "Debug", "netcoreapp2.1", "ExampleProject.AssemblyInfo.cs"), new MockFileData("Bytecode") }, // obj should be excluded + { Path.Combine(_filesystemRoot, "ExampleProject", "obj", "Release", "netcoreapp2.1"), new MockFileData("Bytecode") }, // obj should be excluded + { Path.Combine(_filesystemRoot, "ExampleProject", "node_modules", "Some package"), new MockFileData("bla") }, // node_modules should be excluded + }); + + var projectFileReaderMock = new Mock(MockBehavior.Strict); + projectFileReaderMock.Setup(x => x.AnalyzeProject(testProjectPath, null)) + .Returns(new ProjectAnalyzerResult(null, null) + { + ProjectReferences = new List() { projectUnderTestPath }, + TargetFrameworkVersionString = "netcoreapp2.1", + ProjectFilePath = testProjectPath, + References = new string[] { "" } + }); + projectFileReaderMock.Setup(x => x.AnalyzeProject(projectUnderTestPath, null)) + .Returns(new ProjectAnalyzerResult(null, null) + { + ProjectReferences = new List(), + TargetFrameworkVersionString = "netcoreapp2.1", + ProjectFilePath = projectUnderTestPath, + SourceFiles = fileSystem.AllFiles.Where(s => s.EndsWith(".cs")) + }); + var target = new InputFileResolver(fileSystem, projectFileReaderMock.Object); + + var result = target.ResolveInput(new StrykerOptions(fileSystem: fileSystem, basePath: _basePath)); + + result.ProjectContents.GetAllFiles().Count().ShouldBe(3); + } + [Fact] public void InitializeShouldFindSpecifiedTestProjectFile() { diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Options/StrykerOptionsTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Options/StrykerOptionsTests.cs index 19c5344a49..fab4b59ac3 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Options/StrykerOptionsTests.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Options/StrykerOptionsTests.cs @@ -83,14 +83,24 @@ public void FilesToExclude_should_be_converted_to_file_patterns(string fileToExc [Fact] public void ShouldValidateApiKey() { - var options = new StrykerOptions(); - - var ex = Assert.Throws(() => + const string strykerDashboardApiKey = "STRYKER_DASHBOARD_API_KEY"; + var key = Environment.GetEnvironmentVariable(strykerDashboardApiKey); + try { - new StrykerOptions(reporters: new string[] { "Dashboard" }); - }); - ex.Message.ShouldContain($"An API key is required when the {Reporter.Dashboard} reporter is turned on! You can get an API key at {options.DashboardUrl}"); - ex.Message.ShouldContain($"A project name is required when the {Reporter.Dashboard} reporter is turned on!"); + var options = new StrykerOptions(); + Environment.SetEnvironmentVariable(strykerDashboardApiKey, string.Empty); + + var ex = Assert.Throws(() => + { + new StrykerOptions(reporters: new string[] { "Dashboard" }); + }); + ex.Message.ShouldContain($"An API key is required when the {Reporter.Dashboard} reporter is turned on! You can get an API key at {options.DashboardUrl}"); + ex.Message.ShouldContain($"A project name is required when the {Reporter.Dashboard} reporter is turned on!"); + } + finally + { + Environment.SetEnvironmentVariable(strykerDashboardApiKey, key); + } } [Fact] diff --git a/src/Stryker.Core/Stryker.Core/Initialisation/InputFileResolver.cs b/src/Stryker.Core/Stryker.Core/Initialisation/InputFileResolver.cs index 23037b2f19..7a411098bc 100644 --- a/src/Stryker.Core/Stryker.Core/Initialisation/InputFileResolver.cs +++ b/src/Stryker.Core/Stryker.Core/Initialisation/InputFileResolver.cs @@ -82,9 +82,27 @@ public ProjectInfo ResolveInput(StrykerOptions options) // Analyze project under test result.ProjectUnderTestAnalyzerResult = _projectFileReader.AnalyzeProject(projectUnderTest, options.SolutionPath); + FolderComposite inputFiles; + if (result.ProjectUnderTestAnalyzerResult.SourceFiles!=null && result.ProjectUnderTestAnalyzerResult.SourceFiles.Any()) + { + inputFiles = FindProjectFilesUsingBuildAlyzer(result.ProjectUnderTestAnalyzerResult); + } + else + { + inputFiles = FindProjectFilesScanningProjectFolders(result.ProjectUnderTestAnalyzerResult); + } + result.ProjectContents = inputFiles; + + ValidateResult(result, options); + + return result; + } + + private FolderComposite FindProjectFilesScanningProjectFolders(ProjectAnalyzerResult analyzerResult) + { var inputFiles = new FolderComposite(); - var projectUnderTestDir = Path.GetDirectoryName(result.ProjectUnderTestAnalyzerResult.ProjectFilePath); - foreach (var dir in ExtractProjectFolders(result.ProjectUnderTestAnalyzerResult)) + var projectUnderTestDir = Path.GetDirectoryName(analyzerResult.ProjectFilePath); + foreach (var dir in ExtractProjectFolders(analyzerResult)) { var folder = _fileSystem.Path.Combine(Path.GetDirectoryName(projectUnderTestDir), dir); @@ -93,13 +111,107 @@ public ProjectInfo ResolveInput(StrykerOptions options) { throw new DirectoryNotFoundException($"Can't find {folder}"); } + inputFiles.Add(FindInputFiles(folder, projectUnderTestDir)); } - result.ProjectContents = inputFiles; - ValidateResult(result, options); + return inputFiles; + } - return result; + private FolderComposite FindProjectFilesUsingBuildAlyzer(ProjectAnalyzerResult analyzerResult) + { + var inputFiles = new FolderComposite(); + var projectUnderTestDir = Path.GetDirectoryName(analyzerResult.ProjectFilePath); + var projectRoot = Path.GetDirectoryName(projectUnderTestDir); + var generatedAssemblyInfo = + (_fileSystem.Path.GetFileNameWithoutExtension(analyzerResult.ProjectFilePath) + ".AssemblyInfo.cs").ToLowerInvariant(); + var rootFolderComposite = new FolderComposite() + { + Name = string.Empty, + FullPath = projectRoot, + RelativePath = string.Empty, + RelativePathToProjectFile = + Path.GetRelativePath(projectUnderTestDir, projectUnderTestDir) + }; + var cache = new Dictionary {[string.Empty] = rootFolderComposite}; + inputFiles.Add(rootFolderComposite); + foreach (var sourceFile in analyzerResult.SourceFiles) + { + if (sourceFile.EndsWith(".xaml.cs")) + { + continue; + } + + if (_fileSystem.Path.GetFileName(sourceFile).ToLowerInvariant() == generatedAssemblyInfo) + { + continue; + } + + var relativePath = Path.GetRelativePath(projectUnderTestDir, sourceFile); + var folderComposite = GetOrBuildFolderComposite(cache, Path.GetDirectoryName(relativePath), projectUnderTestDir, + projectRoot, inputFiles); + var fileName = Path.GetFileName(sourceFile); + folderComposite.Add(new FileLeaf() + { + SourceCode = _fileSystem.File.ReadAllText(sourceFile), + Name = _fileSystem.Path.GetFileName(sourceFile), + RelativePath = _fileSystem.Path.Combine(folderComposite.RelativePath, fileName), + FullPath = sourceFile, + RelativePathToProjectFile = Path.GetRelativePath(projectUnderTestDir, sourceFile) + }); + } + + return inputFiles; + } + + // get the FolderComposite object representing the the project's folder 'targetFolder'. Build the needed FolderComposite(s) for a complete path + private FolderComposite GetOrBuildFolderComposite(IDictionary cache, string targetFolder, string projectUnderTestDir, + string projectRoot, ProjectComponent inputFiles) + { + if (cache.ContainsKey(targetFolder)) + { + return cache[targetFolder]; + } + + var folder = targetFolder; + FolderComposite subDir = null; + while (!string.IsNullOrEmpty(folder)) + { + if (!cache.ContainsKey(folder)) + { + // we have not scanned this folder yet + var sub = Path.GetFileName(folder); + var fullPath = _fileSystem.Path.Combine(projectUnderTestDir, sub); + var newComposite = new FolderComposite + { + Name = sub, + FullPath = fullPath, + RelativePath = Path.GetRelativePath(projectRoot, fullPath), + RelativePathToProjectFile = + Path.GetRelativePath(projectUnderTestDir, fullPath) + }; + if (subDir != null) + { + newComposite.Add(subDir); + } + + cache.Add(folder, newComposite); + subDir = newComposite; + folder = Path.GetDirectoryName(folder); + if (string.IsNullOrEmpty(folder)) + { + // we are at root + inputFiles.Add(subDir); + } + } + else + { + cache[folder].Add(subDir); + break; + } + } + + return cache[targetFolder]; } /// diff --git a/src/Stryker.Core/Stryker.Core/Initialisation/ProjectFileReader.cs b/src/Stryker.Core/Stryker.Core/Initialisation/ProjectFileReader.cs index 89d6d94b5b..93ee3dfbb2 100644 --- a/src/Stryker.Core/Stryker.Core/Initialisation/ProjectFileReader.cs +++ b/src/Stryker.Core/Stryker.Core/Initialisation/ProjectFileReader.cs @@ -4,10 +4,8 @@ using Stryker.Core.Exceptions; using Stryker.Core.Logging; using Stryker.Core.Testing; -using Stryker.Core.ToolHelpers; using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Text; using System.Xml.Linq; diff --git a/src/Stryker.Core/Stryker.Core/Initialisation/ProjectInfo.cs b/src/Stryker.Core/Stryker.Core/Initialisation/ProjectInfo.cs index 7710c3e0f2..9ce5583b1c 100644 --- a/src/Stryker.Core/Stryker.Core/Initialisation/ProjectInfo.cs +++ b/src/Stryker.Core/Stryker.Core/Initialisation/ProjectInfo.cs @@ -69,6 +69,14 @@ public IEnumerable ProjectReferences set => _projectReferences = value; } + private IEnumerable _sourceFiles; + + public IEnumerable SourceFiles + { + get => _sourceFiles ?? _analyzerResult?.SourceFiles; + set => _sourceFiles = value; + } + private IReadOnlyDictionary _properties; public IReadOnlyDictionary Properties @@ -126,6 +134,7 @@ public Version TargetFrameworkVersion } private IList _defineConstants; + public IList DefineConstants { get => _defineConstants ?? BuildDefineConstants(); diff --git a/src/Stryker.Core/Stryker.Core/Stryker.Core.csproj b/src/Stryker.Core/Stryker.Core/Stryker.Core.csproj index acd76ce8d9..71d5aa041c 100644 --- a/src/Stryker.Core/Stryker.Core/Stryker.Core.csproj +++ b/src/Stryker.Core/Stryker.Core/Stryker.Core.csproj @@ -61,6 +61,10 @@ + + + + diff --git a/src/Stryker.DataCollector/Stryker.DataCollector/Stryker.DataCollector.csproj b/src/Stryker.DataCollector/Stryker.DataCollector/Stryker.DataCollector.csproj index 4708768121..d42d23f057 100644 --- a/src/Stryker.DataCollector/Stryker.DataCollector/Stryker.DataCollector.csproj +++ b/src/Stryker.DataCollector/Stryker.DataCollector/Stryker.DataCollector.csproj @@ -2,6 +2,9 @@ netstandard2.0 + 0.15.0.0 + 0.15.0.0 + 0.15.0