diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e9ea482 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,42 @@ +# EditorConfig helps developers define and +# maintain consistent coding styles between +# different editors and IDEs + +# http://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = crlf +# Make selection with keyboard easier in all files +insert_final_newline = true + +[*.proj] +indent_size = 2 + +[*.csproj] +indent_size = 2 + +[*.vcxproj] +indent_size = 2 + +[*.xproj] +indent_size = 2 + +[*.json] +indent_size = 2 + +[*.config] +indent_size = 2 + +[*.nuspec] +indent_size = 2 + +[*.xml] +indent_size = 2 + +[*.cs] +csharp_new_line_before_open_brace = all diff --git a/CHANGES.txt b/CHANGES.txt index 0cb20ec..88edec6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,12 @@ +VS Project Loader Extension 3.7 - August 6, 2017 + + Added support for new file format for .NET Core csproj files. Added .editorconfig for + consistency with main NUnit project. + + Issues Resolved + + * 11 Invalid Project Format Exception if Project is new project file format + VS Project Loader Extension 3.6 - August 1, 2017 Fixes several packaging errors and adds a new chocolatey package. Runners and engines diff --git a/src/extension/VSProject.cs b/src/extension/VSProject.cs index 3c236a7..2393e5c 100644 --- a/src/extension/VSProject.cs +++ b/src/extension/VSProject.cs @@ -187,17 +187,23 @@ private void Load() ThrowInvalidFileType( ProjectPath ); StreamReader rdr = new StreamReader(ProjectPath, System.Text.Encoding.UTF8); - + try { _doc = new XmlDocument(); - _doc.Load( rdr ); + _doc.Load(rdr); - string extension = Path.GetExtension( ProjectPath ); + string extension = Path.GetExtension(ProjectPath); - switch ( extension ) + switch (extension) { case ".csproj": + // We try legacy project first, then new format for .NET Core projects + if (!TryLoadLegacyProject()) + if (!TryLoadDotNetCoreProject()) + LoadMSBuildProject(); + break; + case ".vbproj": case ".vjsproj": case ".fsproj": @@ -228,6 +234,74 @@ private void Load() } } + /// + /// Load a project in the new project format for .NET Core 1.0/1.1. Note that this method + /// is only called for file extensions .csproj. + /// + /// True if the project was successfully loaded, false otherwise. + private bool TryLoadDotNetCoreProject() { + XmlNode root = _doc.SelectSingleNode("Project"); + + if (root != null && SafeAttributeValue(root, "Sdk") != null) + { + string targetFramework = _doc.SelectSingleNode("Project/PropertyGroup/TargetFramework").InnerText; + + XmlNode assemblyNameNode = _doc.SelectSingleNode("Project/PropertyGroup/AssemblyName"); + // Even console apps are dll's even if has value 'EXE' + string assemblyName = assemblyNameNode == null ? $"{Name}.dll" : $"{assemblyNameNode.InnerText}.dll"; + + XmlNodeList nodes = _doc.SelectNodes("/Project/PropertyGroup"); + + string commonOutputPath = null; + + foreach (XmlElement configNode in nodes) + { + string configName = GetConfigNameFromCondition(configNode); + + XmlElement outputPathElement = (XmlElement)configNode.SelectSingleNode("OutputPath"); + string outputPath = null; + if (outputPathElement != null) + outputPath = outputPathElement.InnerText; + + if (configName == null) + { + commonOutputPath = outputPath; + continue; + } + + if (outputPath == null) + outputPath = commonOutputPath; + + if (outputPath != null) + _configs.Add(configName, new ProjectConfig(this, configName, outputPath, assemblyName)); + } + + // By convention there is a Debug and a Release configuration unless others are explicitly + // mentioned in the project file. If we have less than 2 then at least one of those is missing. + // We cannot tell however if the existing configuration is meant to replace Debug or Release. + // Therefore we just add what is missing. The one that has been replaced will not be used. + if (_configs.Count < 2) + { + if (!_configs.ContainsKey("Debug")) + { + string configName = "Debug"; + string outputPath = $@"bin\{configName}\{targetFramework}"; + _configs.Add(configName, new ProjectConfig(this, configName, outputPath, assemblyName)); + } + if (!_configs.ContainsKey("Release")) + { + string configName = "Release"; + string outputPath = $@"bin\{configName}\{targetFramework}"; + _configs.Add(configName, new ProjectConfig(this, configName, outputPath, assemblyName)); + } + } + + return true; + } + + return false; + } + /// /// Load a project in the legacy VS2003 format. Note that this method is not /// called for C++ projects using the same format, because the details differ. diff --git a/src/tests/VisualStudioProjectLoaderTests.cs b/src/tests/VisualStudioProjectLoaderTests.cs index aba199a..091c8fa 100644 --- a/src/tests/VisualStudioProjectLoaderTests.cs +++ b/src/tests/VisualStudioProjectLoaderTests.cs @@ -23,6 +23,7 @@ using System; using System.IO; +using System.Linq; using NUnit.Engine.Extensibility; using NUnit.Engine.Tests.resources; using NUnit.Framework; @@ -119,6 +120,7 @@ public void CannotLoadWebProject() [TestCase("legacy-cpp-sample.vcproj", new string[] { "Debug|Win32", "Release|Win32" }, "cpp-sample")] [TestCase("legacy-cpp-library-with-macros.vcproj", new string[] { "Debug|Win32", "Release|Win32" }, "legacy-cpp-library-with-macros")] [TestCase("legacy-cpp-makefile-project.vcproj", new string[] { "Debug|Win32", "Release|Win32" }, "MakeFileProject")] + [TestCase("netcoreapp1.1-minimal.csproj", new string[] { "Debug", "Release" }, "netcoreapp1.1-minimal")] public void CanLoadVsProject(string resourceName, string[] configs, string assemblyName) { Assert.That(_loader.CanLoadFrom(resourceName)); @@ -142,6 +144,40 @@ public void CanLoadVsProject(string resourceName, string[] configs, string assem } } + [TestCase("netcoreapp1.1-minimal.csproj", "Debug", @"bin/Debug/netcoreapp1.1")] + [TestCase("netcoreapp1.1-minimal.csproj", "Release", @"bin/Release/netcoreapp1.1")] + [TestCase("netcoreapp1.1-with-output-path.csproj", "Debug", @"bin/Debug/netcoreapp1.1")] + [TestCase("netcoreapp1.1-with-output-path.csproj", "Release", @"bin/Release/special")] + public void PicksUpCorrectOutputPath(string resourceName, string configuration, string expectedOutputPath) + { + using (TestResource file = new TestResource(resourceName)) + { + IProject project = _loader.LoadFrom(file.Path); + + var package = project.GetTestPackage(configuration); + // adjust for difference between Linux/Win: + var basePath = package.Settings["BasePath"].ToString().Replace('\\', '/'); + Assert.That(basePath.EndsWith(expectedOutputPath)); + } + } + + [TestCase("netcoreapp1.1-minimal.csproj", "netcoreapp1.1-minimal")] + [TestCase("netcoreapp1.1-with-assembly-name.csproj", "the-assembly-name")] + public void PicksUpCorrectAssemplyName(string resouresName, string expectedAssemblyName) + { + using (TestResource file = new TestResource(resouresName)) + { + IProject project = _loader.LoadFrom(file.Path); + + foreach(var config in project.ConfigNames) + { + TestPackage package = project.GetTestPackage(config); + + Assert.That(Path.GetFileNameWithoutExtension(package.SubPackages[0].FullName) == expectedAssemblyName); + } + } + } + [Test] public void FromVSSolution2003() { diff --git a/src/tests/resources/netcoreapp1.1-minimal.csproj b/src/tests/resources/netcoreapp1.1-minimal.csproj new file mode 100644 index 0000000..abb9969 --- /dev/null +++ b/src/tests/resources/netcoreapp1.1-minimal.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp1.1 + + + diff --git a/src/tests/resources/netcoreapp1.1-with-assembly-name.csproj b/src/tests/resources/netcoreapp1.1-with-assembly-name.csproj new file mode 100644 index 0000000..90024a0 --- /dev/null +++ b/src/tests/resources/netcoreapp1.1-with-assembly-name.csproj @@ -0,0 +1,9 @@ + + + + Exe + netcoreapp1.1 + the-assembly-name + + + \ No newline at end of file diff --git a/src/tests/resources/netcoreapp1.1-with-output-path.csproj b/src/tests/resources/netcoreapp1.1-with-output-path.csproj new file mode 100644 index 0000000..d02a2a7 --- /dev/null +++ b/src/tests/resources/netcoreapp1.1-with-output-path.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp1.1 + + + + bin\Release\special + + + \ No newline at end of file diff --git a/src/tests/vs-project-loader.tests.csproj b/src/tests/vs-project-loader.tests.csproj index 425eb83..4d5b25a 100644 --- a/src/tests/vs-project-loader.tests.csproj +++ b/src/tests/vs-project-loader.tests.csproj @@ -97,6 +97,15 @@ + + + + + + + + +