From 19b4bad3151bbc5aeebce0d0117c1a7d908cbd65 Mon Sep 17 00:00:00 2001 From: Justin Goshi Date: Tue, 3 Jan 2017 07:18:45 -1000 Subject: [PATCH 1/3] Add/remove projects also updates build configurations (#5134) * Add/remove projects also updates build configurations * Fix the algorithm for adding/removing build configurations * Address PR comments --- .../SlnFileWithNoProjectReferences.sln | 18 -- .../TestProjects/TestAppWithEmptySln/App.sln | 5 + .../TestAppWithEmptySln/App/App.csproj | 19 ++ .../TestAppWithEmptySln/App/Program.cs | 10 + .../TestAppWithEmptySln/Lib/Lib.csproj | 15 ++ .../TestAppWithEmptySln/Lib/Library.cs | 12 ++ .../TestAppWithSlnAndCsprojToRemove/App.sln | 45 ++++ .../App/App.csproj | 15 ++ .../App/Program.cs | 9 + .../Lib/Lib.csproj | 15 ++ .../Lib/Library.cs | 12 ++ .../dotnet-add/dotnet-add-proj/Program.cs | 60 ++++++ .../dotnet-remove-proj/Program.cs | 25 +++ .../GivenDotnetAddProj.cs | 195 +++++++++++++++--- .../GivenDotnetListProj.cs | 2 +- .../GivenDotnetRemoveProj.cs | 136 +++++++++++- 16 files changed, 550 insertions(+), 43 deletions(-) delete mode 100644 TestAssets/TestProjects/SlnFileWithNoProjectReferences/SlnFileWithNoProjectReferences.sln create mode 100644 TestAssets/TestProjects/TestAppWithEmptySln/App.sln create mode 100644 TestAssets/TestProjects/TestAppWithEmptySln/App/App.csproj create mode 100644 TestAssets/TestProjects/TestAppWithEmptySln/App/Program.cs create mode 100644 TestAssets/TestProjects/TestAppWithEmptySln/Lib/Lib.csproj create mode 100644 TestAssets/TestProjects/TestAppWithEmptySln/Lib/Library.cs create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/App.sln create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/App/App.csproj create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/App/Program.cs create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/Lib/Lib.csproj create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/Lib/Library.cs diff --git a/TestAssets/TestProjects/SlnFileWithNoProjectReferences/SlnFileWithNoProjectReferences.sln b/TestAssets/TestProjects/SlnFileWithNoProjectReferences/SlnFileWithNoProjectReferences.sln deleted file mode 100644 index 5b61df887..000000000 --- a/TestAssets/TestProjects/SlnFileWithNoProjectReferences/SlnFileWithNoProjectReferences.sln +++ /dev/null @@ -1,18 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26006.2 -MinimumVisualStudioVersion = 10.0.40219.1 -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/TestAssets/TestProjects/TestAppWithEmptySln/App.sln b/TestAssets/TestProjects/TestAppWithEmptySln/App.sln new file mode 100644 index 000000000..8eca25366 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithEmptySln/App.sln @@ -0,0 +1,5 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26006.2 +MinimumVisualStudioVersion = 10.0.40219.1 diff --git a/TestAssets/TestProjects/TestAppWithEmptySln/App/App.csproj b/TestAssets/TestProjects/TestAppWithEmptySln/App/App.csproj new file mode 100644 index 000000000..423ceb827 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithEmptySln/App/App.csproj @@ -0,0 +1,19 @@ + + + Exe + netcoreapp1.0 + + + + + + + + + + + + + + + diff --git a/TestAssets/TestProjects/TestAppWithEmptySln/App/Program.cs b/TestAssets/TestProjects/TestAppWithEmptySln/App/Program.cs new file mode 100644 index 000000000..abb853a4a --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithEmptySln/App/Program.cs @@ -0,0 +1,10 @@ +using System; + +class Program +{ + static void Main(string[] args) + { + Console.WriteLine("Hello from the main app"); + Console.WriteLine(Lib.Library.GetMessage()); + } +} diff --git a/TestAssets/TestProjects/TestAppWithEmptySln/Lib/Lib.csproj b/TestAssets/TestProjects/TestAppWithEmptySln/Lib/Lib.csproj new file mode 100644 index 000000000..f47b49f65 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithEmptySln/Lib/Lib.csproj @@ -0,0 +1,15 @@ + + + + netstandard1.4 + + + + + + + + + + + diff --git a/TestAssets/TestProjects/TestAppWithEmptySln/Lib/Library.cs b/TestAssets/TestProjects/TestAppWithEmptySln/Lib/Library.cs new file mode 100644 index 000000000..205c42a01 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithEmptySln/Lib/Library.cs @@ -0,0 +1,12 @@ +using System; + +namespace Lib +{ + public class Library + { + public static string GetMessage() + { + return "Message from Lib"; + } + } +} diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/App.sln b/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/App.sln new file mode 100644 index 000000000..6af5ab783 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/App.sln @@ -0,0 +1,45 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26006.2 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App", "App\App.csproj", "{7072A694-548F-4CAE-A58F-12D257D5F486}" +EndProject +Project("{13B669BE-BB05-4DDF-9536-439F39A36129}") = "Lib", "Lib\Lib.csproj", "{21D9159F-60E6-4F65-BC6B-D01B71B15FFC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.ActiveCfg = Debug|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.Build.0 = Debug|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.ActiveCfg = Debug|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.Build.0 = Debug|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.Build.0 = Release|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.ActiveCfg = Release|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.Build.0 = Release|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.ActiveCfg = Release|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.Build.0 = Release|x86 + {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Debug|x64.ActiveCfg = Debug|x64 + {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Debug|x64.Build.0 = Debug|x64 + {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Debug|x86.ActiveCfg = Debug|x86 + {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Debug|x86.Build.0 = Debug|x86 + {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Release|Any CPU.Build.0 = Release|Any CPU + {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Release|x64.ActiveCfg = Release|x64 + {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Release|x64.Build.0 = Release|x64 + {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Release|x86.ActiveCfg = Release|x86 + {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Release|x86.Build.0 = Release|x86 + EndGlobalSection +EndGlobal diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/App/App.csproj b/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/App/App.csproj new file mode 100644 index 000000000..c1313e358 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/App/App.csproj @@ -0,0 +1,15 @@ + + + Exe + netcoreapp1.0 + + + + + + + + + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/App/Program.cs b/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/App/Program.cs new file mode 100644 index 000000000..acdf5839d --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/App/Program.cs @@ -0,0 +1,9 @@ +using System; + +class Program +{ + static void Main(string[] args) + { + Console.WriteLine("Hello from the main app"); + } +} diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/Lib/Lib.csproj b/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/Lib/Lib.csproj new file mode 100644 index 000000000..f47b49f65 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/Lib/Lib.csproj @@ -0,0 +1,15 @@ + + + + netstandard1.4 + + + + + + + + + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/Lib/Library.cs b/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/Lib/Library.cs new file mode 100644 index 000000000..205c42a01 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojToRemove/Lib/Library.cs @@ -0,0 +1,12 @@ +using System; + +namespace Lib +{ + public class Library + { + public static string GetMessage() + { + return "Message from Lib"; + } + } +} diff --git a/src/dotnet/commands/dotnet-add/dotnet-add-proj/Program.cs b/src/dotnet/commands/dotnet-add/dotnet-add-proj/Program.cs index c29c871d8..858bc2a90 100644 --- a/src/dotnet/commands/dotnet-add/dotnet-add-proj/Program.cs +++ b/src/dotnet/commands/dotnet-add/dotnet-add-proj/Program.cs @@ -8,6 +8,7 @@ using Microsoft.DotNet.Cli.Sln.Internal; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools.Common; using System; +using System.Collections.Generic; using System.IO; using System.Linq; @@ -103,10 +104,69 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToSolution FilePath = projectPathNormalized }; + AddDefaultBuildConfigurations(slnFile, slnProject); + slnFile.Projects.Add(slnProject); + Reporter.Output.WriteLine( string.Format(CommonLocalizableStrings.ProjectAddedToTheSolution, projectPath)); } } + + private void AddDefaultBuildConfigurations(SlnFile slnFile, SlnProject slnProject) + { + var defaultConfigurations = new List() + { + "Debug|Any CPU", + "Debug|x64", + "Debug|x86", + "Release|Any CPU", + "Release|x64", + "Release|x86", + }; + + // NOTE: The order you create the sections determines the order they are written to the sln + // file. In the case of an empty sln file, in order to make sure the solution configurations + // section comes first we need to add it first. This doesn't affect correctness but does + // stop VS from re-ordering things later on. Since we are keeping the SlnFile class low-level + // it shouldn't care about the VS implementation details. That's why we handle this here. + AddDefaultSolutionConfigurations(defaultConfigurations, slnFile.SolutionConfigurationsSection); + AddDefaultProjectConfigurations( + defaultConfigurations, + slnFile.ProjectConfigurationsSection.GetOrCreatePropertySet(slnProject.Id)); + } + + private void AddDefaultSolutionConfigurations( + List defaultConfigurations, + SlnPropertySet solutionConfigs) + { + foreach (var config in defaultConfigurations) + { + if (!solutionConfigs.ContainsKey(config)) + { + solutionConfigs[config] = config; + } + } + } + + private void AddDefaultProjectConfigurations( + List defaultConfigurations, + SlnPropertySet projectConfigs) + { + foreach (var config in defaultConfigurations) + { + var activeCfgKey = $"{config}.ActiveCfg"; + if (!projectConfigs.ContainsKey(activeCfgKey)) + { + projectConfigs[activeCfgKey] = config; + } + + var build0Key = $"{config}.Build.0"; + if (!projectConfigs.ContainsKey(build0Key)) + { + projectConfigs[build0Key] = config; + } + } + } } } diff --git a/src/dotnet/commands/dotnet-remove/dotnet-remove-proj/Program.cs b/src/dotnet/commands/dotnet-remove/dotnet-remove-proj/Program.cs index be386d7af..719c585a8 100644 --- a/src/dotnet/commands/dotnet-remove/dotnet-remove-proj/Program.cs +++ b/src/dotnet/commands/dotnet-remove/dotnet-remove-proj/Program.cs @@ -49,6 +49,8 @@ namespace Microsoft.DotNet.Tools.Remove.ProjectFromSolution slnChanged |= RemoveProject(slnFile, path); } + RemoveEmptyConfigurationSections(slnFile); + if (slnChanged) { slnFile.Write(); @@ -75,6 +77,11 @@ namespace Microsoft.DotNet.Tools.Remove.ProjectFromSolution { foreach (var slnProject in projectsToRemove) { + var buildConfigsToRemove = slnFile.ProjectConfigurationsSection.GetPropertySet(slnProject.Id); + if (buildConfigsToRemove != null) + { + slnFile.ProjectConfigurationsSection.Remove(buildConfigsToRemove); + } slnFile.Projects.Remove(slnProject); Reporter.Output.WriteLine( string.Format(CommonLocalizableStrings.ProjectReferenceRemoved, slnProject.FilePath)); @@ -85,5 +92,23 @@ namespace Microsoft.DotNet.Tools.Remove.ProjectFromSolution return projectRemoved; } + + private void RemoveEmptyConfigurationSections(SlnFile slnFile) + { + if (slnFile.Projects.Count == 0) + { + var solutionConfigs = slnFile.Sections.GetSection("SolutionConfigurationPlatforms"); + if (solutionConfigs != null) + { + slnFile.Sections.Remove(solutionConfigs); + } + + var projectConfigs = slnFile.Sections.GetSection("ProjectConfigurationPlatforms"); + if (projectConfigs != null) + { + slnFile.Sections.Remove(projectConfigs); + } + } + } } } diff --git a/test/dotnet-add-proj.Tests/GivenDotnetAddProj.cs b/test/dotnet-add-proj.Tests/GivenDotnetAddProj.cs index 7ea309909..1e48fc231 100644 --- a/test/dotnet-add-proj.Tests/GivenDotnetAddProj.cs +++ b/test/dotnet-add-proj.Tests/GivenDotnetAddProj.cs @@ -28,6 +28,89 @@ Additional Arguments: Projects to add to solution "; + private const string ExpectedSlnFileAfterAddingLibProj = @" +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26006.2 +MinimumVisualStudioVersion = 10.0.40219.1 +Project(""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""App"", ""App\App.csproj"", ""{7072A694-548F-4CAE-A58F-12D257D5F486}"" +EndProject +Project(""{13B669BE-BB05-4DDF-9536-439F39A36129}"") = ""Lib"", ""Lib\Lib.csproj"", ""__PROJECTGUID__"" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.ActiveCfg = Debug|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.Build.0 = Debug|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.ActiveCfg = Debug|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.Build.0 = Debug|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.Build.0 = Release|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.ActiveCfg = Release|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.Build.0 = Release|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.ActiveCfg = Release|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.Build.0 = Release|x86 + __PROJECTGUID__.Debug|Any CPU.ActiveCfg = Debug|Any CPU + __PROJECTGUID__.Debug|Any CPU.Build.0 = Debug|Any CPU + __PROJECTGUID__.Debug|x64.ActiveCfg = Debug|x64 + __PROJECTGUID__.Debug|x64.Build.0 = Debug|x64 + __PROJECTGUID__.Debug|x86.ActiveCfg = Debug|x86 + __PROJECTGUID__.Debug|x86.Build.0 = Debug|x86 + __PROJECTGUID__.Release|Any CPU.ActiveCfg = Release|Any CPU + __PROJECTGUID__.Release|Any CPU.Build.0 = Release|Any CPU + __PROJECTGUID__.Release|x64.ActiveCfg = Release|x64 + __PROJECTGUID__.Release|x64.Build.0 = Release|x64 + __PROJECTGUID__.Release|x86.ActiveCfg = Release|x86 + __PROJECTGUID__.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal +"; + + private const string ExpectedSlnFileAfterAddingLibProjToEmptySln = @" +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26006.2 +MinimumVisualStudioVersion = 10.0.40219.1 +Project(""{13B669BE-BB05-4DDF-9536-439F39A36129}"") = ""Lib"", ""Lib\Lib.csproj"", ""__PROJECTGUID__"" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + __PROJECTGUID__.Debug|Any CPU.ActiveCfg = Debug|Any CPU + __PROJECTGUID__.Debug|Any CPU.Build.0 = Debug|Any CPU + __PROJECTGUID__.Debug|x64.ActiveCfg = Debug|x64 + __PROJECTGUID__.Debug|x64.Build.0 = Debug|x64 + __PROJECTGUID__.Debug|x86.ActiveCfg = Debug|x86 + __PROJECTGUID__.Debug|x86.Build.0 = Debug|x86 + __PROJECTGUID__.Release|Any CPU.ActiveCfg = Release|Any CPU + __PROJECTGUID__.Release|Any CPU.Build.0 = Release|Any CPU + __PROJECTGUID__.Release|x64.ActiveCfg = Release|x64 + __PROJECTGUID__.Release|x64.Build.0 = Release|x64 + __PROJECTGUID__.Release|x86.ActiveCfg = Release|x86 + __PROJECTGUID__.Release|x86.Build.0 = Release|x86 + EndGlobalSection +EndGlobal +"; + [Theory] [InlineData("--help")] [InlineData("-h")] @@ -171,11 +254,51 @@ Additional Arguments: } [Theory] - [InlineData("TestAppWithSlnAndCsprojFiles", "")] - [InlineData("TestAppWithSlnAndCsprojProjectGuidFiles", "{84A45D44-B677-492D-A6DA-B3A71135AB8E}")] - public void WhenValidProjectIsPassedItGetsNormalizedAndAddedAndSlnBuilds( + [InlineData("TestAppWithSlnAndCsprojFiles", ExpectedSlnFileAfterAddingLibProj, "")] + [InlineData("TestAppWithSlnAndCsprojProjectGuidFiles", ExpectedSlnFileAfterAddingLibProj, "{84A45D44-B677-492D-A6DA-B3A71135AB8E}")] + [InlineData("TestAppWithEmptySln", ExpectedSlnFileAfterAddingLibProjToEmptySln, "")] + public void WhenValidProjectIsPassedBuildConfigsAreAdded( string testAsset, - string projectGuid) + string expectedSlnContentsTemplate, + string expectedProjectGuid) + { + var projectDirectory = TestAssets + .Get(testAsset) + .CreateInstance() + .WithSourceFiles() + .Root + .FullName; + + var projectToAdd = "Lib/Lib.csproj"; + var cmd = new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput($"add App.sln project {projectToAdd}"); + cmd.Should().Pass(); + + var slnPath = Path.Combine(projectDirectory, "App.sln"); + + if (string.IsNullOrEmpty(expectedProjectGuid)) + { + var slnFile = SlnFile.Read(slnPath); + var matchingProjects = slnFile.Projects + .Where((p) => p.Name == "Lib") + .ToList(); + + matchingProjects.Count.Should().Be(1); + var slnProject = matchingProjects[0]; + expectedProjectGuid = slnProject.Id; + } + + var expectedSlnContents = expectedSlnContentsTemplate.Replace("__PROJECTGUID__", expectedProjectGuid); + File.ReadAllText(slnPath) + .Should().BeVisuallyEquivalentTo(expectedSlnContents); + } + + [Theory] + [InlineData("TestAppWithSlnAndCsprojFiles")] + [InlineData("TestAppWithSlnAndCsprojProjectGuidFiles")] + [InlineData("TestAppWithEmptySln")] + public void WhenValidProjectIsPassedItGetsAdded(string testAsset) { var projectDirectory = TestAssets .Get(testAsset) @@ -192,29 +315,55 @@ Additional Arguments: cmd.Should().Pass(); cmd.StdOut.Should().Be($"Project `{projectPath}` added to the solution."); cmd.StdErr.Should().BeEmpty(); + } - var slnFile = SlnFile.Read(Path.Combine(projectDirectory, "App.sln")); - var matchingProjects = slnFile.Projects - .Where((p) => p.Name == "Lib") - .ToList(); + [Theory] + [InlineData("TestAppWithSlnAndCsprojFiles")] + [InlineData("TestAppWithSlnAndCsprojProjectGuidFiles")] + [InlineData("TestAppWithEmptySln")] + public void WhenValidProjectIsPassedTheSlnBuilds(string testAsset) + { + var projectDirectory = TestAssets + .Get(testAsset) + .CreateInstance() + .WithSourceFiles() + .Root + .FullName; - matchingProjects.Count.Should().Be(1); - var slnProject = matchingProjects[0]; - slnProject.FilePath.Should().Be(projectPath); - slnProject.TypeGuid.Should().Be(ProjectTypeGuids.CPSProjectTypeGuid); - if (!string.IsNullOrEmpty(projectGuid)) - { - slnProject.Id.Should().Be(projectGuid); - } - - var restoreCmd = new DotnetCommand() + var cmd = new DotnetCommand() .WithWorkingDirectory(projectDirectory) - .Execute($"restore {Path.Combine("App", "App.csproj")}"); + .ExecuteWithCapturedOutput(@"add App.sln project App/App.csproj Lib/Lib.csproj"); + cmd.Should().Pass(); - var buildCmd = new DotnetCommand() + var slnPath = Path.Combine(projectDirectory, "App.sln"); + + new DotnetCommand() .WithWorkingDirectory(projectDirectory) - .Execute("build App.sln"); - buildCmd.Should().Pass(); + .Execute($"restore App.sln") + .Should().Pass(); + + new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .Execute("build App.sln --configuration Release") + .Should().Pass(); + + var reasonString = "should be built in release mode, otherwise it means build configurations are missing from the sln file"; + + var appReleaseDirectory = Directory.EnumerateDirectories( + Path.Combine(projectDirectory, "App", "bin"), + "Release", + SearchOption.AllDirectories); + appReleaseDirectory.Count().Should().Be(1, $"App {reasonString}"); + Directory.EnumerateFiles(appReleaseDirectory.Single(), "App.dll", SearchOption.AllDirectories) + .Count().Should().Be(1, $"App {reasonString}"); + + var libReleaseDirectory = Directory.EnumerateDirectories( + Path.Combine(projectDirectory, "Lib", "bin"), + "Release", + SearchOption.AllDirectories); + libReleaseDirectory.Count().Should().Be(1, $"Lib {reasonString}"); + Directory.EnumerateFiles(libReleaseDirectory.Single(), "Lib.dll", SearchOption.AllDirectories) + .Count().Should().Be(1, $"Lib {reasonString}"); } [Fact] @@ -258,7 +407,7 @@ Additional Arguments: cmd.StdErr.Should().Be("Project `idonotexist.csproj` does not exist."); File.ReadAllText(slnFullPath) - .Should().BeEquivalentTo(contentBefore); + .Should().BeVisuallyEquivalentTo(contentBefore); } } } diff --git a/test/dotnet-list-proj.Tests/GivenDotnetListProj.cs b/test/dotnet-list-proj.Tests/GivenDotnetListProj.cs index 877450467..26e3a316b 100644 --- a/test/dotnet-list-proj.Tests/GivenDotnetListProj.cs +++ b/test/dotnet-list-proj.Tests/GivenDotnetListProj.cs @@ -147,7 +147,7 @@ Options: public void WhenNoProjectReferencesArePresentInTheSolutionItPrintsANoProjectMessage() { var projectDirectory = TestAssets - .Get("SlnFileWithNoProjectReferences") + .Get("TestAppWithEmptySln") .CreateInstance() .WithSourceFiles() .Root diff --git a/test/dotnet-remove-proj.Tests/GivenDotnetRemoveProj.cs b/test/dotnet-remove-proj.Tests/GivenDotnetRemoveProj.cs index c9da242d2..a753efc2d 100644 --- a/test/dotnet-remove-proj.Tests/GivenDotnetRemoveProj.cs +++ b/test/dotnet-remove-proj.Tests/GivenDotnetRemoveProj.cs @@ -6,6 +6,7 @@ using Microsoft.DotNet.Cli.Sln.Internal; using Microsoft.DotNet.Tools.Test.Utilities; using System; using System.IO; +using System.Linq; using Xunit; namespace Microsoft.DotNet.Cli.Remove.Project.Tests @@ -26,6 +27,48 @@ Additional Arguments: Projects to remove from a solution "; + private const string ExpectedSlnContentsAfterRemove = @" +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26006.2 +MinimumVisualStudioVersion = 10.0.40219.1 +Project(""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""App"", ""App\App.csproj"", ""{7072A694-548F-4CAE-A58F-12D257D5F486}"" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.ActiveCfg = Debug|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.Build.0 = Debug|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.ActiveCfg = Debug|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.Build.0 = Debug|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.Build.0 = Release|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.ActiveCfg = Release|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.Build.0 = Release|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.ActiveCfg = Release|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.Build.0 = Release|x86 + EndGlobalSection +EndGlobal +"; + + private const string ExpectedSlnContentsAfterRemoveAllProjects = @" +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26006.2 +MinimumVisualStudioVersion = 10.0.40219.1 +Global +EndGlobal +"; + [Theory] [InlineData("--help")] [InlineData("-h")] @@ -186,7 +229,7 @@ Additional Arguments: cmd.Should().Pass(); cmd.StdOut.Should().Be("Project reference `referenceDoesNotExistInSln.csproj` could not be found."); File.ReadAllText(solutionPath) - .Should().Be(contentBefore); + .Should().BeVisuallyEquivalentTo(contentBefore); } [Fact] @@ -273,5 +316,96 @@ Project reference `idontexisteither.csproj` could not be found."; slnFile.Projects.Count.Should().Be(1); slnFile.Projects[0].FilePath.Should().Be(Path.Combine("App", "App.csproj")); } + + [Fact] + public void WhenReferenceIsRemovedBuildConfigsAreAlsoRemoved() + { + var projectDirectory = TestAssets + .Get("TestAppWithSlnAndCsprojToRemove") + .CreateInstance() + .WithSourceFiles() + .Root + .FullName; + + var solutionPath = Path.Combine(projectDirectory, "App.sln"); + SlnFile slnFile = SlnFile.Read(solutionPath); + slnFile.Projects.Count.Should().Be(2); + + var projectToRemove = Path.Combine("Lib", "Lib.csproj"); + var cmd = new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput($"remove project {projectToRemove}"); + cmd.Should().Pass(); + + File.ReadAllText(solutionPath) + .Should().BeVisuallyEquivalentTo(ExpectedSlnContentsAfterRemove); + } + + [Fact] + public void WhenReferenceIsRemovedSlnBuilds() + { + var projectDirectory = TestAssets + .Get("TestAppWithSlnAndCsprojToRemove") + .CreateInstance() + .WithSourceFiles() + .Root + .FullName; + + var solutionPath = Path.Combine(projectDirectory, "App.sln"); + SlnFile slnFile = SlnFile.Read(solutionPath); + slnFile.Projects.Count.Should().Be(2); + + var projectToRemove = Path.Combine("Lib", "Lib.csproj"); + var cmd = new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput($"remove project {projectToRemove}"); + cmd.Should().Pass(); + + new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .Execute($"restore App.sln") + .Should().Pass(); + + new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .Execute("build App.sln --configuration Release") + .Should().Pass(); + + var reasonString = "should be built in release mode, otherwise it means build configurations are missing from the sln file"; + + var releaseDirectory = Directory.EnumerateDirectories( + Path.Combine(projectDirectory, "App", "bin"), + "Release", + SearchOption.AllDirectories); + releaseDirectory.Count().Should().Be(1, $"App {reasonString}"); + Directory.EnumerateFiles(releaseDirectory.Single(), "App.dll", SearchOption.AllDirectories) + .Count().Should().Be(1, $"App {reasonString}"); + } + + [Fact] + public void WhenFinalReferenceIsRemovedEmptySectionsAreRemoved() + { + var projectDirectory = TestAssets + .Get("TestAppWithSlnAndCsprojToRemove") + .CreateInstance() + .WithSourceFiles() + .Root + .FullName; + + var solutionPath = Path.Combine(projectDirectory, "App.sln"); + SlnFile slnFile = SlnFile.Read(solutionPath); + slnFile.Projects.Count.Should().Be(2); + + var appPath = Path.Combine("App", "App.csproj"); + var libPath = Path.Combine("Lib", "Lib.csproj"); + var projectsToRemove = $"{libPath} {appPath}"; + var cmd = new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput($"remove project {projectsToRemove}"); + cmd.Should().Pass(); + + File.ReadAllText(solutionPath) + .Should().BeVisuallyEquivalentTo(ExpectedSlnContentsAfterRemoveAllProjects); + } } } From 46e06962bab47fa7b51732593cc5cca45dc8c066 Mon Sep 17 00:00:00 2001 From: Livar Cunha Date: Tue, 3 Jan 2017 12:45:01 -0800 Subject: [PATCH 2/3] Updating the SDK version in the CLI. --- build/Microsoft.DotNet.Cli.DependencyVersions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Microsoft.DotNet.Cli.DependencyVersions.props b/build/Microsoft.DotNet.Cli.DependencyVersions.props index b16c3e15c..b5f1c45df 100644 --- a/build/Microsoft.DotNet.Cli.DependencyVersions.props +++ b/build/Microsoft.DotNet.Cli.DependencyVersions.props @@ -2,7 +2,7 @@ 15.1.0-preview-000458-02 - 1.0.0-alpha-20161221-2 + 1.0.0-alpha-20161230-1 1.0.0-alpha-20161205-1-154 15.0.0-preview-20161227-02 From 281eca2f8c401da8c1af43f066a8d219a5046b2d Mon Sep 17 00:00:00 2001 From: Justin Goshi Date: Tue, 3 Jan 2017 11:06:51 -1000 Subject: [PATCH 3/3] Disable tests that fail sporadically (#5192) --- .../PJAppWithSlnAndXprojRefs/TestApp/TestApp.sln | 8 ++++++-- .../TestApp/TestApp.sln | 8 ++++++-- test/dotnet-add-proj.Tests/GivenDotnetAddProj.cs | 9 +++++---- .../GivenThatIWantToMigrateSolutions.cs | 2 +- test/dotnet-remove-proj.Tests/GivenDotnetRemoveProj.cs | 3 ++- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndXprojRefs/TestApp/TestApp.sln b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndXprojRefs/TestApp/TestApp.sln index bc8214876..3dc27a305 100644 --- a/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndXprojRefs/TestApp/TestApp.sln +++ b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndXprojRefs/TestApp/TestApp.sln @@ -17,12 +17,16 @@ Global GlobalSection(ProjectConfigurationPlatforms) = postSolution {0138CB8F-4AA9-4029-A21E-C07C30F425BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0138CB8F-4AA9-4029-A21E-C07C30F425BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0138CB8F-4AA9-4029-A21E-C07C30F425BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0138CB8F-4AA9-4029-A21E-C07C30F425BA}.Release|Any CPU.Build.0 = Release|Any CPU {DC0B35D0-8A36-4B52-8A11-B86739F055D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DC0B35D0-8A36-4B52-8A11-B86739F055D2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0138CB8F-4AA9-4029-A21E-C07C30F425BA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0138CB8F-4AA9-4029-A21E-C07C30F425BA}.Release|Any CPU.Build.0 = Release|Any CPU {DC0B35D0-8A36-4B52-8A11-B86739F055D2}.Release|Any CPU.ActiveCfg = Release|Any CPU {DC0B35D0-8A36-4B52-8A11-B86739F055D2}.Release|Any CPU.Build.0 = Release|Any CPU + {F8F96F4A-F10C-4C54-867C-A9EFF55494C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8F96F4A-F10C-4C54-867C-A9EFF55494C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8F96F4A-F10C-4C54-867C-A9EFF55494C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8F96F4A-F10C-4C54-867C-A9EFF55494C8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndXprojRefsAndUnrelatedCsproj/TestApp/TestApp.sln b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndXprojRefsAndUnrelatedCsproj/TestApp/TestApp.sln index bc8214876..3dc27a305 100644 --- a/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndXprojRefsAndUnrelatedCsproj/TestApp/TestApp.sln +++ b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndXprojRefsAndUnrelatedCsproj/TestApp/TestApp.sln @@ -17,12 +17,16 @@ Global GlobalSection(ProjectConfigurationPlatforms) = postSolution {0138CB8F-4AA9-4029-A21E-C07C30F425BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0138CB8F-4AA9-4029-A21E-C07C30F425BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0138CB8F-4AA9-4029-A21E-C07C30F425BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0138CB8F-4AA9-4029-A21E-C07C30F425BA}.Release|Any CPU.Build.0 = Release|Any CPU {DC0B35D0-8A36-4B52-8A11-B86739F055D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DC0B35D0-8A36-4B52-8A11-B86739F055D2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0138CB8F-4AA9-4029-A21E-C07C30F425BA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0138CB8F-4AA9-4029-A21E-C07C30F425BA}.Release|Any CPU.Build.0 = Release|Any CPU {DC0B35D0-8A36-4B52-8A11-B86739F055D2}.Release|Any CPU.ActiveCfg = Release|Any CPU {DC0B35D0-8A36-4B52-8A11-B86739F055D2}.Release|Any CPU.Build.0 = Release|Any CPU + {F8F96F4A-F10C-4C54-867C-A9EFF55494C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8F96F4A-F10C-4C54-867C-A9EFF55494C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8F96F4A-F10C-4C54-867C-A9EFF55494C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8F96F4A-F10C-4C54-867C-A9EFF55494C8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/test/dotnet-add-proj.Tests/GivenDotnetAddProj.cs b/test/dotnet-add-proj.Tests/GivenDotnetAddProj.cs index 1e48fc231..ce804eaf8 100644 --- a/test/dotnet-add-proj.Tests/GivenDotnetAddProj.cs +++ b/test/dotnet-add-proj.Tests/GivenDotnetAddProj.cs @@ -317,10 +317,11 @@ EndGlobal cmd.StdErr.Should().BeEmpty(); } - [Theory] - [InlineData("TestAppWithSlnAndCsprojFiles")] - [InlineData("TestAppWithSlnAndCsprojProjectGuidFiles")] - [InlineData("TestAppWithEmptySln")] + //ISSUE: https://github.com/dotnet/sdk/issues/545 + //[Theory] + //[InlineData("TestAppWithSlnAndCsprojFiles")] + //[InlineData("TestAppWithSlnAndCsprojProjectGuidFiles")] + //[InlineData("TestAppWithEmptySln")] public void WhenValidProjectIsPassedTheSlnBuilds(string testAsset) { var projectDirectory = TestAssets diff --git a/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs b/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs index a73b1b61c..37620c979 100644 --- a/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs +++ b/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs @@ -57,7 +57,7 @@ namespace Microsoft.DotNet.Migration.Tests new DotnetCommand() .WithWorkingDirectory(projectDirectory) - .Execute($"restore \"{Path.Combine("TestApp", "TestApp.csproj")}\"") + .Execute($"restore \"{solutionRelPath}\"") .Should().Pass(); //ISSUE: https://github.com/dotnet/sdk/issues/545 diff --git a/test/dotnet-remove-proj.Tests/GivenDotnetRemoveProj.cs b/test/dotnet-remove-proj.Tests/GivenDotnetRemoveProj.cs index a753efc2d..208d5df58 100644 --- a/test/dotnet-remove-proj.Tests/GivenDotnetRemoveProj.cs +++ b/test/dotnet-remove-proj.Tests/GivenDotnetRemoveProj.cs @@ -341,7 +341,8 @@ Project reference `idontexisteither.csproj` could not be found."; .Should().BeVisuallyEquivalentTo(ExpectedSlnContentsAfterRemove); } - [Fact] + //ISSUE: https://github.com/dotnet/sdk/issues/545 + //[Fact] public void WhenReferenceIsRemovedSlnBuilds() { var projectDirectory = TestAssets