From f69eed98ebf70f9ea2cb087864c2646858329480 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Nov 2016 20:05:19 -0800 Subject: [PATCH 01/18] Adds initial code for addingn and removing a package reference --- src/dotnet/commands/dotnet-ref/Program.cs | 120 ++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 src/dotnet/commands/dotnet-ref/Program.cs diff --git a/src/dotnet/commands/dotnet-ref/Program.cs b/src/dotnet/commands/dotnet-ref/Program.cs new file mode 100644 index 000000000..edae50c38 --- /dev/null +++ b/src/dotnet/commands/dotnet-ref/Program.cs @@ -0,0 +1,120 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; +using Microsoft.DotNet.Cli.CommandLine; +using Microsoft.DotNet.Cli.Utils; +using System; +using System.IO; +using System.Linq; +using Microsoft.Build.Construction; +using Microsoft.DotNet.ProjectJsonMigration; + +namespace Microsoft.DotNet.Tools.Ref +{ + public class RefCommand + { + public static int Run(string[] args) + { + DebugHelper.HandleDebugSwitch(ref args); + + DirectoryInfo currDir = new DirectoryInfo(Directory.GetCurrentDirectory()); + FileInfo projectFile = currDir.GetFiles("*.csproj").FirstOrDefault(); + + if (!projectFile.Exists) + { + throw new Exception("Unable to find any projects in the current folder"); + } + + CommandLineApplication app = new CommandLineApplication(throwOnUnexpectedArg: false) + { + Name = "dotnet ref", + FullName = ".NET Ref Command", + Description = "Command to modify project and package references", + AllowArgumentSeparator = true, + ArgumentSeparatorHelpText = HelpMessageStrings.MSBuildAdditionalArgsHelpText + }; + app.HelpOption("-h|--help"); + + app.Command("add", c => + { + c.Description = "Add package and project references"; + c.HelpOption("-h|--help"); + + CommandArgument packageNameArg = c.Argument("", "The package name to install"); + CommandOption packageVersionOption = c.Option("-v|--version ", "The package version to install, defaults to * if omited", CommandOptionType.SingleValue); + + c.OnExecute(() => + { + + string version = "*"; + if (packageVersionOption.HasValue()) + { + version = packageVersionOption.Value(); + } + + var rootElement = ProjectRootElement.Open(projectFile.FullName); + AddOrUpdatePackageRef(packageNameArg.Value, version, rootElement); + rootElement.Save(); + + // List msbuildArgs = new List(); + // msbuildArgs.Add("/t:RefAdd"); + // msbuildArgs.Add($"/p:PackageName={packageNameArg.Value}"); + // msbuildArgs.Add($"/p:PackageVersion={version}"); + // msbuildArgs.AddRange(app.RemainingArguments); + // return new MSBuildForwardingApp(msbuildArgs).Execute(); + return 1; + }); + + }); + + app.Command("del", c => + { + c.Description = "Remove package and project references"; + c.HelpOption("-h|--help"); + + CommandArgument packageNameArg = c.Argument("", "The package name to remove"); + + c.OnExecute(() => + { + var rootElement = ProjectRootElement.Open(projectFile.FullName); + RemovePackageRef(packageNameArg.Value, rootElement); + rootElement.Save(); + + // List msbuildArgs = new List(); + // msbuildArgs.Add("/t:RefDel"); + // msbuildArgs.Add($"/p:PackageName={packageNameArg.Value}"); + // msbuildArgs.AddRange(app.RemainingArguments); + // return new MSBuildForwardingApp(msbuildArgs).Execute(); + return 1; + }); + + }); + + return app.Execute(args); + } + + private static void RemovePackageRef(string packageName, ProjectRootElement proj) + { + //find existing ones + var packageRefs = proj.Items.Where(i => i.ItemType == "PackageReference") + .Where(x => x.Include == packageName) + .ToList(); + //remove any existing ones + foreach (var packageRef in packageRefs) + { + var parent = packageRef.Parent; + packageRef.Parent.RemoveChild(packageRef); + parent.RemoveIfEmpty(); + } + } + + private static void AddOrUpdatePackageRef(string packageName, string packageVersion, ProjectRootElement proj) + { + RemovePackageRef(packageName, proj); + + //add this one + proj.AddItem("PackageReference", packageName, new Dictionary{{"Version", packageVersion}}); + } + } +} From 703863637ebe247fc907aea18d8d535bfd148f30 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 14 Nov 2016 09:36:49 +0100 Subject: [PATCH 02/18] removes old code refs to the msbuild extension usage --- src/dotnet/commands/dotnet-ref/Program.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/dotnet/commands/dotnet-ref/Program.cs b/src/dotnet/commands/dotnet-ref/Program.cs index edae50c38..9f4ca8e21 100644 --- a/src/dotnet/commands/dotnet-ref/Program.cs +++ b/src/dotnet/commands/dotnet-ref/Program.cs @@ -56,13 +56,6 @@ namespace Microsoft.DotNet.Tools.Ref var rootElement = ProjectRootElement.Open(projectFile.FullName); AddOrUpdatePackageRef(packageNameArg.Value, version, rootElement); rootElement.Save(); - - // List msbuildArgs = new List(); - // msbuildArgs.Add("/t:RefAdd"); - // msbuildArgs.Add($"/p:PackageName={packageNameArg.Value}"); - // msbuildArgs.Add($"/p:PackageVersion={version}"); - // msbuildArgs.AddRange(app.RemainingArguments); - // return new MSBuildForwardingApp(msbuildArgs).Execute(); return 1; }); @@ -80,12 +73,6 @@ namespace Microsoft.DotNet.Tools.Ref var rootElement = ProjectRootElement.Open(projectFile.FullName); RemovePackageRef(packageNameArg.Value, rootElement); rootElement.Save(); - - // List msbuildArgs = new List(); - // msbuildArgs.Add("/t:RefDel"); - // msbuildArgs.Add($"/p:PackageName={packageNameArg.Value}"); - // msbuildArgs.AddRange(app.RemainingArguments); - // return new MSBuildForwardingApp(msbuildArgs).Execute(); return 1; }); From c6bd3eeec6df8f1c225e2fb1dc9201df8945a0da Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Tue, 15 Nov 2016 11:15:29 -0800 Subject: [PATCH 03/18] rename command name to dotnet-add-p2p --- src/dotnet/commands/{dotnet-ref => dotnet-add-p2p}/Program.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/dotnet/commands/{dotnet-ref => dotnet-add-p2p}/Program.cs (100%) diff --git a/src/dotnet/commands/dotnet-ref/Program.cs b/src/dotnet/commands/dotnet-add-p2p/Program.cs similarity index 100% rename from src/dotnet/commands/dotnet-ref/Program.cs rename to src/dotnet/commands/dotnet-add-p2p/Program.cs From e4ccc0ff9bfb39d8d6a687f96e545351c2a2f047 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Tue, 15 Nov 2016 15:42:15 -0800 Subject: [PATCH 04/18] Add dotnet-add command --- src/dotnet/Program.cs | 2 + src/dotnet/commands/dotnet-add/Program.cs | 68 +++++++++++++++++++ .../commands/dotnet-help/HelpCommand.cs | 1 + 3 files changed, 71 insertions(+) create mode 100644 src/dotnet/commands/dotnet-add/Program.cs diff --git a/src/dotnet/Program.cs b/src/dotnet/Program.cs index 429f4567a..5265a75e2 100644 --- a/src/dotnet/Program.cs +++ b/src/dotnet/Program.cs @@ -24,6 +24,7 @@ using Microsoft.DotNet.Tools.Run; using Microsoft.DotNet.Tools.Test; using Microsoft.DotNet.Tools.VSTest; using NuGet.Frameworks; +using Microsoft.DotNet.Tools.Add; namespace Microsoft.DotNet.Cli { @@ -45,6 +46,7 @@ namespace Microsoft.DotNet.Cli ["run"] = RunCommand.Run, ["test"] = TestCommand.Run, ["vstest"] = VSTestCommand.Run, + ["add"] = AddCommand.Run, }; public static int Main(string[] args) diff --git a/src/dotnet/commands/dotnet-add/Program.cs b/src/dotnet/commands/dotnet-add/Program.cs new file mode 100644 index 000000000..5c9a5b359 --- /dev/null +++ b/src/dotnet/commands/dotnet-add/Program.cs @@ -0,0 +1,68 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; +using Microsoft.DotNet.Cli.CommandLine; +using Microsoft.DotNet.Cli.Utils; +using System; +using System.IO; +using System.Linq; +using Microsoft.Build.Construction; +using Microsoft.DotNet.ProjectJsonMigration; +using NuGet.Frameworks; + +namespace Microsoft.DotNet.Tools.Add +{ + public class AddCommand + { + public const string CommandName = "dotnet-add"; + public const string UsageText = @"Usage: dotnet add [command] [arguments] + +Arguments: + [command] The command to execute + [arguments] Arguments to pass to the command + +Commands: + p2p Add project to project (p2p) reference to a project"; + + private static Dictionary> s_builtIns = new Dictionary> + { + ["p2p"] = null, + }; + + public static int Run(string[] args) + { + DebugHelper.HandleDebugSwitch(ref args); + + if (args.Length == 0 || args[0] == "--help" || args[0] == "-h") + { + Reporter.Output.WriteLine(UsageText); + return 1; + } + + if (args[0].StartsWith("-")) + { + Reporter.Error.WriteLine($"Unknown option: {args[0]}"); + Reporter.Output.WriteLine(UsageText); + return 1; + } + + string command = args[0]; + Func builtIn; + args = args.Skip(1).ToArray(); + if (s_builtIns.TryGetValue(command, out builtIn)) + { + return builtIn(args); + } + else + { + CommandResult result = Command.Create( + $"{CommandName}-{command}", + args, + FrameworkConstants.CommonFrameworks.NetStandardApp15) + .Execute(); + return result.ExitCode; + } + } + } +} diff --git a/src/dotnet/commands/dotnet-help/HelpCommand.cs b/src/dotnet/commands/dotnet-help/HelpCommand.cs index 5abe53b92..e520ce827 100644 --- a/src/dotnet/commands/dotnet-help/HelpCommand.cs +++ b/src/dotnet/commands/dotnet-help/HelpCommand.cs @@ -36,6 +36,7 @@ Commands: migrate Migrates a project.json based project to a msbuild based project Advanced Commands: + add Group of commands - run `dotnet add --help` for more information nuget Provides additional NuGet commands msbuild msbuilds a project and all of its dependencies vstest Runs tests from the specified files"; From ca33d9853175a2d54e267b572421da7745d9a24e Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Tue, 15 Nov 2016 16:41:29 -0800 Subject: [PATCH 05/18] add dotnet add p2p command --- src/dotnet/commands/dotnet-add-p2p/Program.cs | 208 +++++++++++++++++- src/dotnet/commands/dotnet-add/Program.cs | 3 +- src/dotnet/dotnet.csproj | 11 +- 3 files changed, 210 insertions(+), 12 deletions(-) diff --git a/src/dotnet/commands/dotnet-add-p2p/Program.cs b/src/dotnet/commands/dotnet-add-p2p/Program.cs index 9f4ca8e21..01b9de081 100644 --- a/src/dotnet/commands/dotnet-add-p2p/Program.cs +++ b/src/dotnet/commands/dotnet-add-p2p/Program.cs @@ -10,14 +10,218 @@ using System.Linq; using Microsoft.Build.Construction; using Microsoft.DotNet.ProjectJsonMigration; -namespace Microsoft.DotNet.Tools.Ref +namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference { - public class RefCommand + public class AddProjectToProjectReferenceCommand { public static int Run(string[] args) { DebugHelper.HandleDebugSwitch(ref args); + CommandLineApplication app = new CommandLineApplication(throwOnUnexpectedArg: false) + { + Name = "dotnet add p2p", + FullName = ".NET Add Project to Project (p2p) reference Command", + Description = "Command to add project to project (p2p) reference" + }; + app.HelpOption("-h|--help"); + + CommandArgument projectArgument = app.Argument("", + "The MSBuild project file to modify. If a project file is not specified," + + " it searches the current working directory for an MSBuild file that has a file extension that ends in `proj` and uses that file."); + + CommandOption frameworkOption = app.Option("-f|--framework ", "Add reference only when targetting a specific framework", CommandOptionType.SingleValue); + CommandOption referenceOption = app.Option("-r|--reference ", "Add project to project to ", CommandOptionType.MultipleValue); + + + app.OnExecute(() => { + ProjectRootElement project = projectArgument.Value != null ? + GetProjectFromFileOrThrow(projectArgument.Value) : + GetProjectFromCurrentDirectoryOrThrow(); + + if (referenceOption.Values.Count == 0) + { + throw new GracefulException("You must specify at least one reference to add."); + } + + AddProjectToProjectReference(project, frameworkOption.Value(), referenceOption.Values); + + //project.Save(); + + return 0; + }); + + try + { + return app.Execute(args); + } + catch (GracefulException e) + { + Reporter.Error.WriteLine(e.Message); + return 1; + } + } + + // There is ProjectRootElement.TryOpen but it does not work as expected + // I.e. it returns null for some valid projects + public static ProjectRootElement TryOpenProject(string filename) + { + try + { + return ProjectRootElement.Open(filename); + } + catch (Microsoft.Build.Exceptions.InvalidProjectFileException) + { + return null; + } + } + + public static ProjectRootElement GetProjectFromFileOrThrow(string filename) + { + if (!File.Exists(filename)) + { + throw new GracefulException($"Provided project `{filename}` does not exist."); + } + + var project = TryOpenProject(filename); + if (project == null) + { + throw new GracefulException($"Invalid MSBuild project `{filename}`."); + } + + return project; + } + + public static ProjectRootElement GetProjectFromCurrentDirectoryOrThrow() + { + DirectoryInfo currDir = new DirectoryInfo(Directory.GetCurrentDirectory()); + FileInfo[] files = currDir.GetFiles("*proj"); + if (files.Length == 0) + { + throw new GracefulException("Could not find any MSBuild project in the current directory."); + } + + if (files.Length > 1) + { + throw new GracefulException("Found more than one MSBuild project in the current directory. Please specify which one to use."); + } + + FileInfo projectFile = files.First(); + + if (!projectFile.Exists) + { + throw new GracefulException("Could not find any project in the current directory."); + } + + var ret = TryOpenProject(projectFile.FullName); + if (ret == null) + { + throw new GracefulException($"Found an MSBuild project `{projectFile.FullName}` in the current directory but it is invalid."); + } + + return ret; + } + + public static Func AndPred(params Func[] preds) + { + return (el) => preds.All((pred) => pred == null || pred(el)); + } + + public static string GetFrameworkConditionString(string framework) + { + return $" '$(TargetFramework)' == '{framework}' "; + } + + public static Func FrameworkPred(string framework) where T : ProjectElement + { + if (string.IsNullOrEmpty(framework)) + { + return (ig) => { + var condChain = ig.ConditionChain(); + return condChain.Count == 0; + }; + } + + string conditionStr = GetFrameworkConditionString(framework).Trim(); + return (ig) => { + var condChain = ig.ConditionChain(); + return condChain.Count == 1 && condChain.First().Trim() == conditionStr; + }; + } + + public static Func UniformItemElementTypePred(string projectItemElementType) + { + return (ig) => ig.Items.All((it) => it.ItemType == projectItemElementType); + } + + public static Func IncludePred(string include) + { + return (it) => it.Include == include; + } + + public static ProjectItemElement[] FindExistingItemsWithCondition(ProjectRootElement root, string framework, string include) + { + return root.Items + .Where( + AndPred( + FrameworkPred(framework), + IncludePred(include))) + .ToArray(); + } + + public static ProjectItemGroupElement FindExistingUniformItemGroupWithCondition(ProjectRootElement root, string projectItemElementType, string framework) + { + return root.ItemGroupsReversed + .FirstOrDefault( + AndPred( + // When adding more predicates which operate on ItemGroup.Condition + // some slightly more advanced logic need to be used: + // i.e. ConditionPred(FrameworkConditionPred(framework), RuntimeConditionPred(runtime)) + // FrameworkConditionPred and RuntimeConditionPred would need to operate on a single condition + // and ConditionPred would need to check if whole Condition Chain is satisfied + FrameworkPred(framework), + UniformItemElementTypePred(projectItemElementType))); + } + + public static ProjectItemGroupElement FindUniformOrCreateItemGroupWithCondition(ProjectRootElement root, string projectItemElementType, string framework) + { + var lastMatchingItemGroup = FindExistingUniformItemGroupWithCondition(root, projectItemElementType, framework); + + if (lastMatchingItemGroup != null) + { + return lastMatchingItemGroup; + } + + ProjectItemGroupElement ret = root.CreateItemGroupElement(); + ret.Condition = GetFrameworkConditionString(framework); + root.AppendChild(ret); + return ret; + } + + public static void AddProjectToProjectReference(ProjectRootElement root, string framework, IEnumerable refs) + { + const string ProjectItemElementType = "ProjectReference"; + + ProjectItemGroupElement ig = null; + foreach (var @ref in refs) + { + if (FindExistingItemsWithCondition(root, framework, @ref).Length == 0) + { + Reporter.Output.WriteLine($"Item {ProjectItemElementType} including `{@ref}` is already present."); + continue; + } + + ig = ig ?? FindUniformOrCreateItemGroupWithCondition(root, ProjectItemElementType, framework); + ig.AppendChild(root.CreateItemElement(ProjectItemElementType, @ref)); + + Reporter.Output.WriteLine($"Item {ProjectItemElementType} including `{@ref}` added to project."); + } + } + + public static int Run2(string[] args) + { + DebugHelper.HandleDebugSwitch(ref args); + DirectoryInfo currDir = new DirectoryInfo(Directory.GetCurrentDirectory()); FileInfo projectFile = currDir.GetFiles("*.csproj").FirstOrDefault(); diff --git a/src/dotnet/commands/dotnet-add/Program.cs b/src/dotnet/commands/dotnet-add/Program.cs index 5c9a5b359..faf32a201 100644 --- a/src/dotnet/commands/dotnet-add/Program.cs +++ b/src/dotnet/commands/dotnet-add/Program.cs @@ -10,6 +10,7 @@ using System.Linq; using Microsoft.Build.Construction; using Microsoft.DotNet.ProjectJsonMigration; using NuGet.Frameworks; +using Microsoft.DotNet.Tools.Add.ProjectToProjectReference; namespace Microsoft.DotNet.Tools.Add { @@ -27,7 +28,7 @@ Commands: private static Dictionary> s_builtIns = new Dictionary> { - ["p2p"] = null, + ["p2p"] = AddProjectToProjectReferenceCommand.Run, }; public static int Run(string[] args) diff --git a/src/dotnet/dotnet.csproj b/src/dotnet/dotnet.csproj index 7cfa0e25c..c5af004c3 100755 --- a/src/dotnet/dotnet.csproj +++ b/src/dotnet/dotnet.csproj @@ -1,6 +1,5 @@  - 1.0.0-preview4 netcoreapp1.0 @@ -9,15 +8,13 @@ ../../tools/Key.snk true true - $(PackageTargetFallback);dotnet5.4 + $(PackageTargetFallback);dotnet5.4 - - @@ -25,7 +22,6 @@ - 1.0.0-alpha-20161104-2 @@ -78,14 +74,11 @@ 1.0.1-beta-000933 - $(DefineConstants);NETCOREAPP1_0 - $(DefineConstants);RELEASE - - + \ No newline at end of file From 7edcf556d7bb973599101d3897e246118232c39b Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Wed, 16 Nov 2016 13:33:36 -0800 Subject: [PATCH 06/18] remove currently unused code --- src/dotnet/commands/dotnet-add-p2p/Program.cs | 90 ------------------- 1 file changed, 90 deletions(-) diff --git a/src/dotnet/commands/dotnet-add-p2p/Program.cs b/src/dotnet/commands/dotnet-add-p2p/Program.cs index 01b9de081..605dd863c 100644 --- a/src/dotnet/commands/dotnet-add-p2p/Program.cs +++ b/src/dotnet/commands/dotnet-add-p2p/Program.cs @@ -217,95 +217,5 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference Reporter.Output.WriteLine($"Item {ProjectItemElementType} including `{@ref}` added to project."); } } - - public static int Run2(string[] args) - { - DebugHelper.HandleDebugSwitch(ref args); - - DirectoryInfo currDir = new DirectoryInfo(Directory.GetCurrentDirectory()); - FileInfo projectFile = currDir.GetFiles("*.csproj").FirstOrDefault(); - - if (!projectFile.Exists) - { - throw new Exception("Unable to find any projects in the current folder"); - } - - CommandLineApplication app = new CommandLineApplication(throwOnUnexpectedArg: false) - { - Name = "dotnet ref", - FullName = ".NET Ref Command", - Description = "Command to modify project and package references", - AllowArgumentSeparator = true, - ArgumentSeparatorHelpText = HelpMessageStrings.MSBuildAdditionalArgsHelpText - }; - app.HelpOption("-h|--help"); - - app.Command("add", c => - { - c.Description = "Add package and project references"; - c.HelpOption("-h|--help"); - - CommandArgument packageNameArg = c.Argument("", "The package name to install"); - CommandOption packageVersionOption = c.Option("-v|--version ", "The package version to install, defaults to * if omited", CommandOptionType.SingleValue); - - c.OnExecute(() => - { - - string version = "*"; - if (packageVersionOption.HasValue()) - { - version = packageVersionOption.Value(); - } - - var rootElement = ProjectRootElement.Open(projectFile.FullName); - AddOrUpdatePackageRef(packageNameArg.Value, version, rootElement); - rootElement.Save(); - return 1; - }); - - }); - - app.Command("del", c => - { - c.Description = "Remove package and project references"; - c.HelpOption("-h|--help"); - - CommandArgument packageNameArg = c.Argument("", "The package name to remove"); - - c.OnExecute(() => - { - var rootElement = ProjectRootElement.Open(projectFile.FullName); - RemovePackageRef(packageNameArg.Value, rootElement); - rootElement.Save(); - return 1; - }); - - }); - - return app.Execute(args); - } - - private static void RemovePackageRef(string packageName, ProjectRootElement proj) - { - //find existing ones - var packageRefs = proj.Items.Where(i => i.ItemType == "PackageReference") - .Where(x => x.Include == packageName) - .ToList(); - //remove any existing ones - foreach (var packageRef in packageRefs) - { - var parent = packageRef.Parent; - packageRef.Parent.RemoveChild(packageRef); - parent.RemoveIfEmpty(); - } - } - - private static void AddOrUpdatePackageRef(string packageName, string packageVersion, ProjectRootElement proj) - { - RemovePackageRef(packageName, proj); - - //add this one - proj.AddItem("PackageReference", packageName, new Dictionary{{"Version", packageVersion}}); - } } } From 8416085b3a6f6c91bc74c712f65b1c96f3cdeecb Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Wed, 16 Nov 2016 14:35:48 -0800 Subject: [PATCH 07/18] Add test assets for testing and tiny bugfix --- .../DotnetAddP2PProjects/App/App.csproj | 32 +++++++++++++++++ .../DotnetAddP2PProjects/App/Program.cs | 21 +++++++++++ .../DotnetAddP2PProjects/Lib1/Lib1.cs | 24 +++++++++++++ .../DotnetAddP2PProjects/Lib1/Lib1.csproj | 29 +++++++++++++++ .../DotnetAddP2PProjects/Lib2/Lib2.cs | 24 +++++++++++++ .../DotnetAddP2PProjects/Lib2/Lib2.csproj | 36 +++++++++++++++++++ .../DotnetAddP2PProjects/Lib3/Lib3.cs | 21 +++++++++++ .../DotnetAddP2PProjects/Lib3/Lib3.csproj | 29 +++++++++++++++ .../DotnetAddP2PProjects/Lib4/Lib4.cs | 21 +++++++++++ .../DotnetAddP2PProjects/Lib4/Lib4.csproj | 32 +++++++++++++++++ src/dotnet/commands/dotnet-add-p2p/Program.cs | 17 +++++++-- 11 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/App/App.csproj create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/App/Program.cs create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib1/Lib1.cs create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib1/Lib1.csproj create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib2/Lib2.cs create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib2/Lib2.csproj create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib3/Lib3.cs create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib3/Lib3.csproj create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib4/Lib4.cs create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib4/Lib4.csproj diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/App/App.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/App/App.csproj new file mode 100644 index 000000000..2df913261 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/App/App.csproj @@ -0,0 +1,32 @@ + + + + + Exe + net451;netcoreapp1.0 + + + + + + 1.0.0-alpha-20161104-2 + All + + + + + 1.0.1 + + + + + + + $(DefineConstants);NETCOREAPP1_0 + + + $(DefineConstants);NET451 + + + + \ No newline at end of file diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/App/Program.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/App/Program.cs new file mode 100644 index 000000000..8d6e7d1db --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/App/Program.cs @@ -0,0 +1,21 @@ +using NSLib1; +using NSLib3; + +namespace ConsoleApplication +{ + public class Program + { + public static void Main(string[] args) + { +#if NETCOREAPP1_0 + Lib1.HelloNCA(); + Lib3.HelloNCA(); +#endif + +#if NET451 + Lib1.HelloNet451(); + Lib3.HelloNet451(); +#endif + } + } +} diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib1/Lib1.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib1/Lib1.cs new file mode 100644 index 000000000..7250d586f --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib1/Lib1.cs @@ -0,0 +1,24 @@ +using System; +using NSLib2; + +namespace NSLib1 +{ + public class Lib1 + { +#if NETCOREAPP1_0 + public static void HelloNCA() + { + Console.WriteLine("Hello World from Lib1! (netcoreapp)"); + Lib2.HelloNCA(); + } +#endif + +#if NET451 + public static void HelloNet451() + { + Console.WriteLine("Hello World from Lib1! (net45)"); + Lib2.HelloNet451(); + } +#endif + } +} diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib1/Lib1.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib1/Lib1.csproj new file mode 100644 index 000000000..3466eb925 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib1/Lib1.csproj @@ -0,0 +1,29 @@ + + + + + Library + net451;netcoreapp1.0 + + + + + + 1.0.0-alpha-20161104-2 + All + + + + + 1.0.1 + + + + $(DefineConstants);NETCOREAPP1_0 + + + $(DefineConstants);NET451 + + + + \ No newline at end of file diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib2/Lib2.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib2/Lib2.cs new file mode 100644 index 000000000..9452473dc --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib2/Lib2.cs @@ -0,0 +1,24 @@ +using System; +using NSLib4; + +namespace NSLib2 +{ + public class Lib2 + { +#if NETCOREAPP1_0 + public static void HelloNCA() + { + Console.WriteLine("Hello World from Lib2! (netcoreapp)"); + Lib4.HelloNCA(); + } +#endif + +#if NET451 + public static void HelloNet451() + { + Console.WriteLine("Hello World from Lib2! (net45)"); + Lib4.HelloNet451(); + } +#endif + } +} diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib2/Lib2.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib2/Lib2.csproj new file mode 100644 index 000000000..e4d8997f8 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib2/Lib2.csproj @@ -0,0 +1,36 @@ + + + + + Library + net451;netcoreapp1.0 + + + + + + 1.0.0-alpha-20161104-2 + All + + + + + 1.0.1 + + + + + + + + + + + $(DefineConstants);NETCOREAPP1_0 + + + $(DefineConstants);NET451 + + + + \ No newline at end of file diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib3/Lib3.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib3/Lib3.cs new file mode 100644 index 000000000..b86a60de4 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib3/Lib3.cs @@ -0,0 +1,21 @@ +using System; + +namespace NSLib3 +{ + public class Lib3 + { +#if NETCOREAPP1_0 + public static void HelloNCA() + { + Console.WriteLine("Hello World from Lib3! (netcoreapp)"); + } +#endif + +#if NET451 + public static void HelloNet451() + { + Console.WriteLine("Hello World from Lib3! (net45)"); + } +#endif + } +} diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib3/Lib3.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib3/Lib3.csproj new file mode 100644 index 000000000..3466eb925 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib3/Lib3.csproj @@ -0,0 +1,29 @@ + + + + + Library + net451;netcoreapp1.0 + + + + + + 1.0.0-alpha-20161104-2 + All + + + + + 1.0.1 + + + + $(DefineConstants);NETCOREAPP1_0 + + + $(DefineConstants);NET451 + + + + \ No newline at end of file diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib4/Lib4.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib4/Lib4.cs new file mode 100644 index 000000000..d993cc499 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib4/Lib4.cs @@ -0,0 +1,21 @@ +using System; + +namespace NSLib4 +{ + public class Lib4 + { +#if NETCOREAPP1_0 + public static void HelloNCA() + { + Console.WriteLine("Hello World from Lib4! (netcoreapp)"); + } +#endif + +#if NET451 + public static void HelloNet451() + { + Console.WriteLine("Hello World from Lib4! (net45)"); + } +#endif + } +} diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib4/Lib4.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib4/Lib4.csproj new file mode 100644 index 000000000..76b8b884d --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib4/Lib4.csproj @@ -0,0 +1,32 @@ + + + + + Library + net451;netcoreapp1.0 + + + + + + 1.0.0-alpha-20161104-2 + All + + + + + 1.0.1 + + + + + + + $(DefineConstants);NETCOREAPP1_0 + + + $(DefineConstants);NET451 + + + + \ No newline at end of file diff --git a/src/dotnet/commands/dotnet-add-p2p/Program.cs b/src/dotnet/commands/dotnet-add-p2p/Program.cs index 605dd863c..611e5ee7d 100644 --- a/src/dotnet/commands/dotnet-add-p2p/Program.cs +++ b/src/dotnet/commands/dotnet-add-p2p/Program.cs @@ -129,12 +129,18 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference public static string GetFrameworkConditionString(string framework) { + if (string.IsNullOrEmpty(framework)) + { + return null; + } + return $" '$(TargetFramework)' == '{framework}' "; } public static Func FrameworkPred(string framework) where T : ProjectElement { - if (string.IsNullOrEmpty(framework)) + string conditionStr = GetFrameworkConditionString(framework); + if (conditionStr == null) { return (ig) => { var condChain = ig.ConditionChain(); @@ -142,7 +148,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference }; } - string conditionStr = GetFrameworkConditionString(framework).Trim(); + conditionStr = conditionStr.Trim(); return (ig) => { var condChain = ig.ConditionChain(); return condChain.Count == 1 && condChain.First().Trim() == conditionStr; @@ -193,7 +199,12 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference } ProjectItemGroupElement ret = root.CreateItemGroupElement(); - ret.Condition = GetFrameworkConditionString(framework); + string condStr = GetFrameworkConditionString(framework); + if (condStr != null) + { + ret.Condition = condStr; + } + root.AppendChild(ret); return ret; } From 5d1deeeea158139fd5262eb9c67a0c9fe38d764e Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Wed, 16 Nov 2016 15:49:25 -0800 Subject: [PATCH 08/18] test project - almost finished - not all review changes included --- .../DotnetAddP2PProjects/Broken/Broken.csproj | 14 + .../EmptyItemGroup/EmptyItemGroup.cs | 3 + .../EmptyItemGroup.csproj} | 12 +- .../DotnetAddP2PProjects/Lib/Lib.cs | 4 + .../DotnetAddP2PProjects/Lib/Lib.csproj | 22 + .../DotnetAddP2PProjects/Lib2/Lib2.cs | 24 - .../DotnetAddP2PProjects/Lib3/Lib3.cs | 21 - .../MoreThanOne/SomeSourceFile.cs | 8 + .../DotnetAddP2PProjects/MoreThanOne/a.csproj | 20 + .../DotnetAddP2PProjects/MoreThanOne/b.csproj | 20 + .../DotnetAddP2PProjects/ValidRef/ValidRef.cs | 5 + .../ValidRef/ValidRef.csproj | 22 + .../WithExistingRefCondOnItem.cs | 3 + .../WithExistingRefCondOnItem.csproj} | 15 +- .../WithExistingRefCondWhitespaces.cs | 3 + .../WithExistingRefCondWhitespaces.csproj | 25 + .../WithRefCondNonUniform.cs | 3 + .../WithRefCondNonUniform.csproj | 26 + .../WithRefNoCondNonUniform.cs | 3 + .../WithRefNoCondNonUniform.csproj | 26 + .../{App => _remove_me_/AppE2E}/App.csproj | 3 +- .../{App => _remove_me_/AppE2E}/Program.cs | 0 .../{ => _remove_me_}/Lib1/Lib1.cs | 0 .../{ => _remove_me_}/Lib1/Lib1.csproj | 3 +- .../{ => _remove_me_}/Lib4/Lib4.cs | 0 .../{ => _remove_me_}/Lib4/Lib4.csproj | 3 +- .../MSBuildExtensions.cs | 12 + src/dotnet/Program.cs | 5 +- .../MsbuildProjectExtensions.cs | 84 ++++ src/dotnet/commands/dotnet-add-p2p/Program.cs | 166 ++---- src/dotnet/commands/dotnet-add/Program.cs | 77 ++- .../Commands/AddP2PCommand.cs | 30 ++ test/dotnet-add-p2p.Tests/Extensions.cs | 90 ++++ .../dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs | 473 ++++++++++++++++++ test/dotnet-add-p2p.Tests/ProjDir.cs | 41 ++ .../dotnet-add-p2p.Tests.csproj | 63 +++ 36 files changed, 1119 insertions(+), 210 deletions(-) create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Broken/Broken.csproj create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/EmptyItemGroup/EmptyItemGroup.cs rename TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/{Lib3/Lib3.csproj => EmptyItemGroup/EmptyItemGroup.csproj} (73%) create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib/Lib.cs create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib/Lib.csproj delete mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib2/Lib2.cs delete mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib3/Lib3.cs create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/SomeSourceFile.cs create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/a.csproj create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/b.csproj create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/ValidRef/ValidRef.cs create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/ValidRef/ValidRef.csproj create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondOnItem/WithExistingRefCondOnItem.cs rename TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/{Lib2/Lib2.csproj => WithExistingRefCondOnItem/WithExistingRefCondOnItem.csproj} (59%) create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondWhitespaces/WithExistingRefCondWhitespaces.cs create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondWhitespaces/WithExistingRefCondWhitespaces.csproj create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefCondNonUniform/WithRefCondNonUniform.cs create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefCondNonUniform/WithRefCondNonUniform.csproj create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefNoCondNonUniform/WithRefNoCondNonUniform.cs create mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefNoCondNonUniform/WithRefNoCondNonUniform.csproj rename TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/{App => _remove_me_/AppE2E}/App.csproj (99%) rename TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/{App => _remove_me_/AppE2E}/Program.cs (100%) rename TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/{ => _remove_me_}/Lib1/Lib1.cs (100%) rename TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/{ => _remove_me_}/Lib1/Lib1.csproj (99%) rename TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/{ => _remove_me_}/Lib4/Lib4.cs (100%) rename TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/{ => _remove_me_}/Lib4/Lib4.csproj (99%) create mode 100644 src/dotnet/commands/dotnet-add-p2p/MsbuildProjectExtensions.cs create mode 100644 test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/AddP2PCommand.cs create mode 100644 test/dotnet-add-p2p.Tests/Extensions.cs create mode 100644 test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs create mode 100644 test/dotnet-add-p2p.Tests/ProjDir.cs create mode 100644 test/dotnet-add-p2p.Tests/dotnet-add-p2p.Tests.csproj diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Broken/Broken.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Broken/Broken.csproj new file mode 100644 index 000000000..3203e50a0 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Broken/Broken.csproj @@ -0,0 +1,14 @@ + + + + + Library + net451;netcoreapp1.0 + + + + + + 1.0.0-alpha-20161104-2 + All + diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/EmptyItemGroup/EmptyItemGroup.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/EmptyItemGroup/EmptyItemGroup.cs new file mode 100644 index 000000000..4c665f47a --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/EmptyItemGroup/EmptyItemGroup.cs @@ -0,0 +1,3 @@ +public class EmptyItemGroup +{ +} \ No newline at end of file diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib3/Lib3.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/EmptyItemGroup/EmptyItemGroup.csproj similarity index 73% rename from TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib3/Lib3.csproj rename to TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/EmptyItemGroup/EmptyItemGroup.csproj index 3466eb925..3db580ead 100644 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib3/Lib3.csproj +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/EmptyItemGroup/EmptyItemGroup.csproj @@ -18,12 +18,8 @@ 1.0.1 - - $(DefineConstants);NETCOREAPP1_0 - - - $(DefineConstants);NET451 - - + + + - \ No newline at end of file + diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib/Lib.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib/Lib.cs new file mode 100644 index 000000000..e4708deab --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib/Lib.cs @@ -0,0 +1,4 @@ +// Dummy reference - it can be used as an existing reference in any of the projects +public class Lib +{ +} \ No newline at end of file diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib/Lib.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib/Lib.csproj new file mode 100644 index 000000000..b61db6f8d --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib/Lib.csproj @@ -0,0 +1,22 @@ + + + + + Library + net451;netcoreapp1.0 + + + + + + 1.0.0-alpha-20161104-2 + All + + + + + 1.0.1 + + + + diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib2/Lib2.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib2/Lib2.cs deleted file mode 100644 index 9452473dc..000000000 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib2/Lib2.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using NSLib4; - -namespace NSLib2 -{ - public class Lib2 - { -#if NETCOREAPP1_0 - public static void HelloNCA() - { - Console.WriteLine("Hello World from Lib2! (netcoreapp)"); - Lib4.HelloNCA(); - } -#endif - -#if NET451 - public static void HelloNet451() - { - Console.WriteLine("Hello World from Lib2! (net45)"); - Lib4.HelloNet451(); - } -#endif - } -} diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib3/Lib3.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib3/Lib3.cs deleted file mode 100644 index b86a60de4..000000000 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib3/Lib3.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; - -namespace NSLib3 -{ - public class Lib3 - { -#if NETCOREAPP1_0 - public static void HelloNCA() - { - Console.WriteLine("Hello World from Lib3! (netcoreapp)"); - } -#endif - -#if NET451 - public static void HelloNet451() - { - Console.WriteLine("Hello World from Lib3! (net45)"); - } -#endif - } -} diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/SomeSourceFile.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/SomeSourceFile.cs new file mode 100644 index 000000000..3e8c40581 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/SomeSourceFile.cs @@ -0,0 +1,8 @@ +using System; + +namespace SomeNS +{ + public class SomeClass + { + } +} diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/a.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/a.csproj new file mode 100644 index 000000000..7a03821ea --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/a.csproj @@ -0,0 +1,20 @@ + + + + + Library + netcoreapp1.0 + + + + + + 1.0.0-alpha-20161104-2 + All + + + 1.0.1 + + + + diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/b.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/b.csproj new file mode 100644 index 000000000..7a03821ea --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/b.csproj @@ -0,0 +1,20 @@ + + + + + Library + netcoreapp1.0 + + + + + + 1.0.0-alpha-20161104-2 + All + + + 1.0.1 + + + + diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/ValidRef/ValidRef.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/ValidRef/ValidRef.cs new file mode 100644 index 000000000..f250057d8 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/ValidRef/ValidRef.cs @@ -0,0 +1,5 @@ +// Dummy reference - it should not be referenced by any other project directly +// it should be used as a reference passed to the dotnet add p2p +public class ValidRef +{ +} \ No newline at end of file diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/ValidRef/ValidRef.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/ValidRef/ValidRef.csproj new file mode 100644 index 000000000..b61db6f8d --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/ValidRef/ValidRef.csproj @@ -0,0 +1,22 @@ + + + + + Library + net451;netcoreapp1.0 + + + + + + 1.0.0-alpha-20161104-2 + All + + + + + 1.0.1 + + + + diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondOnItem/WithExistingRefCondOnItem.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondOnItem/WithExistingRefCondOnItem.cs new file mode 100644 index 000000000..15d976dc2 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondOnItem/WithExistingRefCondOnItem.cs @@ -0,0 +1,3 @@ +public class WithExistingRefCondOnItem +{ +} \ No newline at end of file diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib2/Lib2.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondOnItem/WithExistingRefCondOnItem.csproj similarity index 59% rename from TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib2/Lib2.csproj rename to TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondOnItem/WithExistingRefCondOnItem.csproj index e4d8997f8..a85516e34 100644 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib2/Lib2.csproj +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondOnItem/WithExistingRefCondOnItem.csproj @@ -19,18 +19,7 @@ - - + - - - - - $(DefineConstants);NETCOREAPP1_0 - - - $(DefineConstants);NET451 - - - \ No newline at end of file + diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondWhitespaces/WithExistingRefCondWhitespaces.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondWhitespaces/WithExistingRefCondWhitespaces.cs new file mode 100644 index 000000000..4de50b9d8 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondWhitespaces/WithExistingRefCondWhitespaces.cs @@ -0,0 +1,3 @@ +public class WithExistingRefCondWhitespaces +{ +} \ No newline at end of file diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondWhitespaces/WithExistingRefCondWhitespaces.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondWhitespaces/WithExistingRefCondWhitespaces.csproj new file mode 100644 index 000000000..79aca8ec9 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondWhitespaces/WithExistingRefCondWhitespaces.csproj @@ -0,0 +1,25 @@ + + + + + Library + net451;netcoreapp1.0 + + + + + + 1.0.0-alpha-20161104-2 + All + + + + + 1.0.1 + + + + + + + diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefCondNonUniform/WithRefCondNonUniform.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefCondNonUniform/WithRefCondNonUniform.cs new file mode 100644 index 000000000..d827b8018 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefCondNonUniform/WithRefCondNonUniform.cs @@ -0,0 +1,3 @@ +public class WithRefCondNonUniform +{ +} \ No newline at end of file diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefCondNonUniform/WithRefCondNonUniform.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefCondNonUniform/WithRefCondNonUniform.csproj new file mode 100644 index 000000000..0f5e7d1b7 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefCondNonUniform/WithRefCondNonUniform.csproj @@ -0,0 +1,26 @@ + + + + + Library + net451;netcoreapp1.0 + + + + + + 1.0.0-alpha-20161104-2 + All + + + + + 1.0.1 + + + + + + + + diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefNoCondNonUniform/WithRefNoCondNonUniform.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefNoCondNonUniform/WithRefNoCondNonUniform.cs new file mode 100644 index 000000000..c71d17552 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefNoCondNonUniform/WithRefNoCondNonUniform.cs @@ -0,0 +1,3 @@ +public class WithRefNoCondNonUniform +{ +} \ No newline at end of file diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefNoCondNonUniform/WithRefNoCondNonUniform.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefNoCondNonUniform/WithRefNoCondNonUniform.csproj new file mode 100644 index 000000000..a4f9328b0 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefNoCondNonUniform/WithRefNoCondNonUniform.csproj @@ -0,0 +1,26 @@ + + + + + Library + net451;netcoreapp1.0 + + + + + + 1.0.0-alpha-20161104-2 + All + + + + + 1.0.1 + + + + + + + + diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/App/App.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/AppE2E/App.csproj similarity index 99% rename from TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/App/App.csproj rename to TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/AppE2E/App.csproj index 2df913261..a476f96b3 100644 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/App/App.csproj +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/AppE2E/App.csproj @@ -27,6 +27,5 @@ $(DefineConstants);NET451 - - \ No newline at end of file + diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/App/Program.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/AppE2E/Program.cs similarity index 100% rename from TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/App/Program.cs rename to TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/AppE2E/Program.cs diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib1/Lib1.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib1/Lib1.cs similarity index 100% rename from TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib1/Lib1.cs rename to TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib1/Lib1.cs diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib1/Lib1.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib1/Lib1.csproj similarity index 99% rename from TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib1/Lib1.csproj rename to TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib1/Lib1.csproj index 3466eb925..a5ea660c6 100644 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib1/Lib1.csproj +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib1/Lib1.csproj @@ -24,6 +24,5 @@ $(DefineConstants);NET451 - - \ No newline at end of file + diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib4/Lib4.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib4/Lib4.cs similarity index 100% rename from TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib4/Lib4.cs rename to TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib4/Lib4.cs diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib4/Lib4.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib4/Lib4.csproj similarity index 99% rename from TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib4/Lib4.csproj rename to TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib4/Lib4.csproj index 76b8b884d..55cecb538 100644 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib4/Lib4.csproj +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib4/Lib4.csproj @@ -27,6 +27,5 @@ $(DefineConstants);NET451 - - \ No newline at end of file + diff --git a/src/Microsoft.DotNet.ProjectJsonMigration/MSBuildExtensions.cs b/src/Microsoft.DotNet.ProjectJsonMigration/MSBuildExtensions.cs index 2d0d9bbfd..1cd3440a5 100644 --- a/src/Microsoft.DotNet.ProjectJsonMigration/MSBuildExtensions.cs +++ b/src/Microsoft.DotNet.ProjectJsonMigration/MSBuildExtensions.cs @@ -27,14 +27,18 @@ namespace Microsoft.DotNet.ProjectJsonMigration // Different includes if (item.IntersectIncludes(otherItem).Count() != item.Includes().Count()) { +#if !DISABLE_TRACE MigrationTrace.Instance.WriteLine($"{nameof(MSBuildExtensions)}.{nameof(IsEquivalentTo)} includes not equivalent."); +#endif return false; } // Different Excludes if (item.IntersectExcludes(otherItem).Count() != item.Excludes().Count()) { +#if !DISABLE_TRACE MigrationTrace.Instance.WriteLine($"{nameof(MSBuildExtensions)}.{nameof(IsEquivalentTo)} excludes not equivalent."); +#endif return false; } @@ -46,7 +50,9 @@ namespace Microsoft.DotNet.ProjectJsonMigration // Different remove if (item.Remove != otherItem.Remove) { +#if !DISABLE_TRACE MigrationTrace.Instance.WriteLine($"{nameof(MSBuildExtensions)}.{nameof(IsEquivalentTo)} removes not equivalent."); +#endif return false; } @@ -61,13 +67,17 @@ namespace Microsoft.DotNet.ProjectJsonMigration var otherMetadata = itemToCompare.GetMetadataWithName(metadata.Name); if (otherMetadata == null) { +#if !DISABLE_TRACE MigrationTrace.Instance.WriteLine($"{nameof(MSBuildExtensions)}.{nameof(IsEquivalentTo)} metadata doesn't exist {{ {metadata.Name} {metadata.Value} }}"); +#endif return false; } if (!metadata.ValueEquals(otherMetadata)) { +#if !DISABLE_TRACE MigrationTrace.Instance.WriteLine($"{nameof(MSBuildExtensions)}.{nameof(IsEquivalentTo)} metadata has another value {{ {metadata.Name} {metadata.Value} {otherMetadata.Value} }}"); +#endif return false; } } @@ -197,7 +207,9 @@ namespace Microsoft.DotNet.ProjectJsonMigration if (existingMetadata == default(ProjectMetadataElement)) { +#if !DISABLE_TRACE MigrationTrace.Instance.WriteLine($"{nameof(AddMetadata)}: Adding metadata to {item.ItemType} item: {{ {metadata.Name}, {metadata.Value} }}"); +#endif item.AddMetadata(metadata.Name, metadata.Value); } } diff --git a/src/dotnet/Program.cs b/src/dotnet/Program.cs index 5265a75e2..46241701f 100644 --- a/src/dotnet/Program.cs +++ b/src/dotnet/Program.cs @@ -3,12 +3,12 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Text; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Configurer; using Microsoft.DotNet.PlatformAbstractions; +using Microsoft.DotNet.Tools.Add; using Microsoft.DotNet.Tools.Build; using Microsoft.DotNet.Tools.Clean; using Microsoft.DotNet.Tools.Help; @@ -24,7 +24,6 @@ using Microsoft.DotNet.Tools.Run; using Microsoft.DotNet.Tools.Test; using Microsoft.DotNet.Tools.VSTest; using NuGet.Frameworks; -using Microsoft.DotNet.Tools.Add; namespace Microsoft.DotNet.Cli { @@ -32,6 +31,7 @@ namespace Microsoft.DotNet.Cli { private static Dictionary> s_builtIns = new Dictionary> { + ["add"] = AddCommand.Run, ["build"] = BuildCommand.Run, ["clean"] = CleanCommand.Run, ["help"] = HelpCommand.Run, @@ -46,7 +46,6 @@ namespace Microsoft.DotNet.Cli ["run"] = RunCommand.Run, ["test"] = TestCommand.Run, ["vstest"] = VSTestCommand.Run, - ["add"] = AddCommand.Run, }; public static int Main(string[] args) diff --git a/src/dotnet/commands/dotnet-add-p2p/MsbuildProjectExtensions.cs b/src/dotnet/commands/dotnet-add-p2p/MsbuildProjectExtensions.cs new file mode 100644 index 000000000..1e3bd9488 --- /dev/null +++ b/src/dotnet/commands/dotnet-add-p2p/MsbuildProjectExtensions.cs @@ -0,0 +1,84 @@ +using Microsoft.Build.Construction; +using Microsoft.DotNet.ProjectJsonMigration; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference +{ + internal static class MsbuildProjectExtensions + { + public static bool IsConditionalOnFramework(this ProjectElement el, string framework) + { + string conditionStr; + if (!TryGetFrameworkConditionString(framework, out conditionStr)) + { + return el.ConditionChain().Count == 0; + } + + var condChain = el.ConditionChain(); + return condChain.Count == 1 && condChain.First().Trim() == conditionStr; + } + + public static ProjectItemGroupElement LastItemGroup(this ProjectRootElement root) + { + return root.ItemGroupsReversed.FirstOrDefault(); + } + + public static ProjectItemGroupElement FindUniformOrCreateItemGroupWithCondition(this ProjectRootElement root, string projectItemElementType, string framework) + { + var lastMatchingItemGroup = FindExistingUniformItemGroupWithCondition(root, projectItemElementType, framework); + + if (lastMatchingItemGroup != null) + { + return lastMatchingItemGroup; + } + + ProjectItemGroupElement ret = root.CreateItemGroupElement(); + string condStr; + if (TryGetFrameworkConditionString(framework, out condStr)) + { + ret.Condition = condStr; + } + + root.InsertAfterChild(ret, root.LastItemGroup()); + return ret; + } + + public static ProjectItemGroupElement FindExistingUniformItemGroupWithCondition(this ProjectRootElement root, string projectItemElementType, string framework) + { + return root.ItemGroupsReversed.FirstOrDefault((ig) => ig.IsConditionalOnFramework(framework) && ig.IsUniformItemElementType(projectItemElementType)); + } + + public static bool IsUniformItemElementType(this ProjectItemGroupElement group, string projectItemElementType) + { + return group.Items.All((it) => it.ItemType == projectItemElementType); + } + + public static IEnumerable FindExistingItemsWithCondition(this ProjectRootElement root, string framework, string include) + { + return root.Items.Where((el) => el.IsConditionalOnFramework(framework) && el.HasInclude(include)); + } + + public static bool HasExistingItemWithCondition(this ProjectRootElement root, string framework, string include) + { + return root.FindExistingItemsWithCondition(framework, include).Count() != 0; + } + + public static bool HasInclude(this ProjectItemElement el, string include) + { + return el.Includes().Contains(include); + } + + private static bool TryGetFrameworkConditionString(string framework, out string condition) + { + if (string.IsNullOrEmpty(framework)) + { + condition = null; + return false; + } + + condition = $"'$(TargetFramework)' == '{framework}'"; + return true; + } + } +} diff --git a/src/dotnet/commands/dotnet-add-p2p/Program.cs b/src/dotnet/commands/dotnet-add-p2p/Program.cs index 611e5ee7d..42dd41997 100644 --- a/src/dotnet/commands/dotnet-add-p2p/Program.cs +++ b/src/dotnet/commands/dotnet-add-p2p/Program.cs @@ -8,7 +8,7 @@ using System; using System.IO; using System.Linq; using Microsoft.Build.Construction; -using Microsoft.DotNet.ProjectJsonMigration; +using Microsoft.Build.Evaluation; namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference { @@ -22,31 +22,40 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference { Name = "dotnet add p2p", FullName = ".NET Add Project to Project (p2p) reference Command", - Description = "Command to add project to project (p2p) reference" + Description = "Command to add project to project (p2p) reference", + AllowArgumentSeparator = true, + ArgumentSeparatorHelpText = "Project to project references to add" }; + app.HelpOption("-h|--help"); CommandArgument projectArgument = app.Argument("", - "The MSBuild project file to modify. If a project file is not specified," + + "The project file to modify. If a project file is not specified," + " it searches the current working directory for an MSBuild file that has a file extension that ends in `proj` and uses that file."); CommandOption frameworkOption = app.Option("-f|--framework ", "Add reference only when targetting a specific framework", CommandOptionType.SingleValue); - CommandOption referenceOption = app.Option("-r|--reference ", "Add project to project to ", CommandOptionType.MultipleValue); - app.OnExecute(() => { - ProjectRootElement project = projectArgument.Value != null ? - GetProjectFromFileOrThrow(projectArgument.Value) : - GetProjectFromCurrentDirectoryOrThrow(); + if (projectArgument.Value == null) + { + throw new GracefulException("Argument is required."); + } - if (referenceOption.Values.Count == 0) + ProjectRootElement project = File.Exists(projectArgument.Value) ? + GetProjectFromFileOrThrow(projectArgument.Value) : + GetProjectFromDirectoryOrThrow(projectArgument.Value); + + if (app.RemainingArguments.Count == 0) { throw new GracefulException("You must specify at least one reference to add."); } - AddProjectToProjectReference(project, frameworkOption.Value(), referenceOption.Values); + int numberOfAddedReferences = AddProjectToProjectReference(project, frameworkOption.Value(), app.RemainingArguments); - //project.Save(); + if (numberOfAddedReferences != 0) + { + project.Save(); + } return 0; }); @@ -57,7 +66,8 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference } catch (GracefulException e) { - Reporter.Error.WriteLine(e.Message); + Reporter.Error.WriteLine(e.Message.Red()); + app.ShowHelp(); return 1; } } @@ -68,7 +78,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference { try { - return ProjectRootElement.Open(filename); + return ProjectRootElement.Open(filename, new ProjectCollection(), preserveFormatting: true); } catch (Microsoft.Build.Exceptions.InvalidProjectFileException) { @@ -86,19 +96,34 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference var project = TryOpenProject(filename); if (project == null) { - throw new GracefulException($"Invalid MSBuild project `{filename}`."); + throw new GracefulException($"Invalid project `{filename}`."); } return project; } - public static ProjectRootElement GetProjectFromCurrentDirectoryOrThrow() + // TODO: improve errors + public static ProjectRootElement GetProjectFromDirectoryOrThrow(string directory) { - DirectoryInfo currDir = new DirectoryInfo(Directory.GetCurrentDirectory()); - FileInfo[] files = currDir.GetFiles("*proj"); + DirectoryInfo dir; + try + { + dir = new DirectoryInfo(directory); + } + catch (ArgumentException) + { + throw new GracefulException($"Could not find project or directory `{directory}`."); + } + + if (!dir.Exists) + { + throw new GracefulException($"Could not find project or directory `{directory}`."); + } + + FileInfo[] files = dir.GetFiles("*proj"); if (files.Length == 0) { - throw new GracefulException("Could not find any MSBuild project in the current directory."); + throw new GracefulException($"Could not find any project in `{directory}`."); } if (files.Length > 1) @@ -110,123 +135,40 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference if (!projectFile.Exists) { - throw new GracefulException("Could not find any project in the current directory."); + throw new GracefulException($"Could not find any project in `{directory}`."); } var ret = TryOpenProject(projectFile.FullName); if (ret == null) { - throw new GracefulException($"Found an MSBuild project `{projectFile.FullName}` in the current directory but it is invalid."); + throw new GracefulException($"Found a project `{projectFile.FullName}` but it is invalid."); } return ret; } - public static Func AndPred(params Func[] preds) - { - return (el) => preds.All((pred) => pred == null || pred(el)); - } - - public static string GetFrameworkConditionString(string framework) - { - if (string.IsNullOrEmpty(framework)) - { - return null; - } - - return $" '$(TargetFramework)' == '{framework}' "; - } - - public static Func FrameworkPred(string framework) where T : ProjectElement - { - string conditionStr = GetFrameworkConditionString(framework); - if (conditionStr == null) - { - return (ig) => { - var condChain = ig.ConditionChain(); - return condChain.Count == 0; - }; - } - - conditionStr = conditionStr.Trim(); - return (ig) => { - var condChain = ig.ConditionChain(); - return condChain.Count == 1 && condChain.First().Trim() == conditionStr; - }; - } - - public static Func UniformItemElementTypePred(string projectItemElementType) - { - return (ig) => ig.Items.All((it) => it.ItemType == projectItemElementType); - } - - public static Func IncludePred(string include) - { - return (it) => it.Include == include; - } - - public static ProjectItemElement[] FindExistingItemsWithCondition(ProjectRootElement root, string framework, string include) - { - return root.Items - .Where( - AndPred( - FrameworkPred(framework), - IncludePred(include))) - .ToArray(); - } - - public static ProjectItemGroupElement FindExistingUniformItemGroupWithCondition(ProjectRootElement root, string projectItemElementType, string framework) - { - return root.ItemGroupsReversed - .FirstOrDefault( - AndPred( - // When adding more predicates which operate on ItemGroup.Condition - // some slightly more advanced logic need to be used: - // i.e. ConditionPred(FrameworkConditionPred(framework), RuntimeConditionPred(runtime)) - // FrameworkConditionPred and RuntimeConditionPred would need to operate on a single condition - // and ConditionPred would need to check if whole Condition Chain is satisfied - FrameworkPred(framework), - UniformItemElementTypePred(projectItemElementType))); - } - - public static ProjectItemGroupElement FindUniformOrCreateItemGroupWithCondition(ProjectRootElement root, string projectItemElementType, string framework) - { - var lastMatchingItemGroup = FindExistingUniformItemGroupWithCondition(root, projectItemElementType, framework); - - if (lastMatchingItemGroup != null) - { - return lastMatchingItemGroup; - } - - ProjectItemGroupElement ret = root.CreateItemGroupElement(); - string condStr = GetFrameworkConditionString(framework); - if (condStr != null) - { - ret.Condition = condStr; - } - - root.AppendChild(ret); - return ret; - } - - public static void AddProjectToProjectReference(ProjectRootElement root, string framework, IEnumerable refs) + public static int AddProjectToProjectReference(ProjectRootElement root, string framework, IEnumerable refs) { + int numberOfAddedReferences = 0; const string ProjectItemElementType = "ProjectReference"; ProjectItemGroupElement ig = null; foreach (var @ref in refs) { - if (FindExistingItemsWithCondition(root, framework, @ref).Length == 0) + if (root.HasExistingItemWithCondition(framework, @ref)) { - Reporter.Output.WriteLine($"Item {ProjectItemElementType} including `{@ref}` is already present."); + Reporter.Output.WriteLine($"Project already has a reference to `{@ref}`."); continue; } - ig = ig ?? FindUniformOrCreateItemGroupWithCondition(root, ProjectItemElementType, framework); + numberOfAddedReferences++; + ig = ig ?? root.FindUniformOrCreateItemGroupWithCondition(ProjectItemElementType, framework); ig.AppendChild(root.CreateItemElement(ProjectItemElementType, @ref)); - Reporter.Output.WriteLine($"Item {ProjectItemElementType} including `{@ref}` added to project."); + Reporter.Output.WriteLine($"Reference `{@ref}` added to the project."); } + + return numberOfAddedReferences; } } } diff --git a/src/dotnet/commands/dotnet-add/Program.cs b/src/dotnet/commands/dotnet-add/Program.cs index faf32a201..1e3b2b29a 100644 --- a/src/dotnet/commands/dotnet-add/Program.cs +++ b/src/dotnet/commands/dotnet-add/Program.cs @@ -16,15 +16,22 @@ namespace Microsoft.DotNet.Tools.Add { public class AddCommand { - public const string CommandName = "dotnet-add"; - public const string UsageText = @"Usage: dotnet add [command] [arguments] + public const string HelpText = @".NET Add Command + +Usage: dotnet add [options] [object] [[--] ...]] Arguments: - [command] The command to execute - [arguments] Arguments to pass to the command + The object of the operation. If a project file is not specified, it defaults to the current directory. + Command to be executed on . + +Options: + -h|--help Show help information + +Args: + Any extra arguments passed to the command. Commands: - p2p Add project to project (p2p) reference to a project"; + p2p Add project to project (p2p) reference to a project"; private static Dictionary> s_builtIns = new Dictionary> { @@ -37,33 +44,57 @@ Commands: if (args.Length == 0 || args[0] == "--help" || args[0] == "-h") { - Reporter.Output.WriteLine(UsageText); - return 1; + Reporter.Output.WriteLine(HelpText); + return 0; } - if (args[0].StartsWith("-")) + string commandObject; + string command; + if (IsValidCommandName(args[0])) { - Reporter.Error.WriteLine($"Unknown option: {args[0]}"); - Reporter.Output.WriteLine(UsageText); - return 1; + command = args[0]; + commandObject = GetCurrentDirectoryWithDirSeparator(); + args = args.Skip(1).Prepend(commandObject).ToArray(); } - - string command = args[0]; - Func builtIn; - args = args.Skip(1).ToArray(); - if (s_builtIns.TryGetValue(command, out builtIn)) + else if (args.Length == 1) { - return builtIn(args); + Reporter.Error.WriteLine("Required argument was not passed.".Red()); + Reporter.Output.WriteLine(HelpText); + return 1; } else { - CommandResult result = Command.Create( - $"{CommandName}-{command}", - args, - FrameworkConstants.CommonFrameworks.NetStandardApp15) - .Execute(); - return result.ExitCode; + commandObject = args[0]; + command = args[1]; + + args = args.Skip(2).Prepend(commandObject).ToArray(); } + + Func builtin; + if (s_builtIns.TryGetValue(command, out builtin)) + { + return builtin(args); + } + + Reporter.Error.WriteLine("Required argument is invalid.".Red()); + Reporter.Output.WriteLine(HelpText); + return 1; + } + + private static bool IsValidCommandName(string s) + { + return s_builtIns.ContainsKey(s); + } + + private static string GetCurrentDirectoryWithDirSeparator() + { + string ret = Directory.GetCurrentDirectory(); + if (ret[ret.Length - 1] != Path.DirectorySeparatorChar) + { + ret += Path.DirectorySeparatorChar; + } + + return ret; } } } diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/AddP2PCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/AddP2PCommand.cs new file mode 100644 index 000000000..9a7f2b517 --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/AddP2PCommand.cs @@ -0,0 +1,30 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public sealed class AddP2PCommand : TestCommand + { + private string _projectName = null; + + public AddP2PCommand() + : base("dotnet") + { + + } + + public override CommandResult Execute(string args = "") + { + args = $"add {_projectName} p2p {args}"; + return base.ExecuteWithCapturedOutput(args); + } + + public AddP2PCommand WithProject(string projectName) + { + _projectName = projectName; + return this; + } + } +} diff --git a/test/dotnet-add-p2p.Tests/Extensions.cs b/test/dotnet-add-p2p.Tests/Extensions.cs new file mode 100644 index 000000000..cbea6cca6 --- /dev/null +++ b/test/dotnet-add-p2p.Tests/Extensions.cs @@ -0,0 +1,90 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Build.Construction; +using System; +using System.Linq; +using Microsoft.DotNet.ProjectJsonMigration; +using System.Collections.Generic; + +namespace Microsoft.DotNet.Cli.Add.P2P.Tests +{ + internal static class Extensions + { + //public static int CountOccurrances(this string s, string pattern) + //{ + // int ret = 0; + // for (int i = s.IndexOf(pattern); i != -1; i = s.IndexOf(pattern, i + 1)) + // { + // ret++; + // } + + // return ret; + //} + + //public static int NumberOfLinesWith(this string s, params string[] patterns) + //{ + // int ret = 0; + // string[] lines = s.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + // foreach (var line in lines) + // { + // bool shouldCount = true; + + // foreach (var p in patterns) + // { + // if (!line.Contains(p)) + // { + // shouldCount = false; + // break; + // } + // } + + // if (shouldCount) + // { + // ret++; + // } + // } + + // return ret; + //} + + public static int NumberOfItemGroupsWithConditionContaining(this ProjectRootElement root, string patternInCondition) + { + return root.ItemGroups.Count((ig) => ig.Condition.Contains(patternInCondition)); + } + + public static int NumberOfItemGroupsWithoutCondition(this ProjectRootElement root) + { + return root.ItemGroups.Count((ig) => string.IsNullOrEmpty(ig.Condition)); + } + + public static IEnumerable ItemsWithIncludeAndConditionContaining(this ProjectRootElement root, string itemType, string includePattern, string patternInCondition) + { + return root.Items.Where((it) => + { + if (it.ItemType != itemType || !it.Include.Contains(includePattern)) + { + return false; + } + + var condChain = it.ConditionChain(); + return condChain.Count == 1 && condChain.First().Contains(patternInCondition); + }); + } + + public static int NumberOfProjectReferencesWithIncludeAndConditionContaining(this ProjectRootElement root, string includePattern, string patternInCondition) + { + return root.ItemsWithIncludeAndConditionContaining("ProjectReference", includePattern, patternInCondition).Count(); + } + + public static IEnumerable ItemsWithIncludeContaining(this ProjectRootElement root, string itemType, string includePattern) + { + return root.Items.Where((it) => it.ItemType == itemType && it.Include.Contains(includePattern)); + } + + public static int NumberOfProjectReferencesWithIncludeContaining(this ProjectRootElement root, string includePattern) + { + return root.ItemsWithIncludeContaining("ProjectReference", includePattern).Count(); + } + } +} \ No newline at end of file diff --git a/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs b/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs new file mode 100644 index 000000000..27a01aede --- /dev/null +++ b/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs @@ -0,0 +1,473 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; +using Microsoft.DotNet.Tools.Test.Utilities; +using Xunit; +using FluentAssertions; + +namespace Microsoft.DotNet.Cli.Add.P2P.Tests +{ + public class GivenDotnetAddP2P : TestBase + { + const string FrameworkNet451Arg = "-f net451"; + const string ConditionFrameworkNet451 = "== 'net451'"; + const string FrameworkNetCoreApp10Arg = "-f netcoreapp1.0"; + const string ConditionFrameworkNetCoreApp10 = "== 'netcoreapp1.0'"; + private static readonly string ValidRef = "ValidRef"; + private static readonly string ValidRefCsproj = $"{ValidRef}.csproj"; + private static readonly string ValidRefPath = Path.Combine("..", ValidRef, ValidRefCsproj); + + private static readonly string LibRef = "Lib"; + private static readonly string LibRefCsproj = $"{LibRef}.csproj"; + private static readonly string LibRefPath = Path.Combine("..", LibRef, LibRefCsproj); + + private static readonly string AppPath = Path.Combine("App", "App.csproj"); + private static readonly string Lib1Path = Path.Combine("Lib1", "Lib1.csproj"); + private static readonly string Lib2Path = Path.Combine("Lib2", "Lib2.csproj"); + private static readonly string Lib3Path = Path.Combine("Lib3", "Lib3.csproj"); + private static readonly string Lib4Path = Path.Combine("Lib4", "Lib4.csproj"); + + private string Setup([System.Runtime.CompilerServices.CallerMemberName] string callingMethod = nameof(Setup), string identifier = "") + { + const string TestGroup = "NonRestoredTestProjects"; + const string ProjectName = "DotnetAddP2PProjects"; + return GetTestGroupTestAssetsManager(TestGroup).CreateTestInstance(ProjectName, callingMethod: callingMethod, identifier: identifier).Path; + } + + private ProjDir NewDir([System.Runtime.CompilerServices.CallerMemberName] string callingMethod = nameof(NewDir), string identifier = "") + { + return new ProjDir(TestAssetsManager, callingMethod, identifier: identifier); + } + + private ProjDir NewLib([System.Runtime.CompilerServices.CallerMemberName] string callingMethod = nameof(NewDir), string identifier = "") + { + var dir = new ProjDir(TestAssetsManager, callingMethod, identifier: identifier); + + try + { + new NewCommand() + .WithWorkingDirectory(dir.Path) + .ExecuteWithCapturedOutput("-t Lib") + .Should().Pass(); + } + catch (System.ComponentModel.Win32Exception e) + { + throw new Exception($"DIDIDIDIDOIDIR: {dir.Path}\n{e}"); + } + + return dir; + } + + [Theory] + [InlineData("--help")] + [InlineData("-h")] + public void WhenHelpOptionIsPassedItPrintsUsage(string helpArg) + { + var cmd = new AddP2PCommand().Execute(helpArg); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("Usage"); + } + + [Theory] + [InlineData("idontexist.csproj")] + [InlineData("ihave?inv@lid/char\\acters")] + public void WhenNonExistingProjectIsPassedItPrintsErrorAndUsage(string projName) + { + string testRoot = NewDir().Path; + var cmd = new AddP2PCommand() + .WithWorkingDirectory(testRoot) + .WithProject(projName) + .Execute($"\"{ValidRefPath}\""); + cmd.ExitCode.Should().NotBe(0); + cmd.StdErr.Should().Contain("Could not find"); + cmd.StdOut.Should().Contain("Usage"); + } + + [Fact] + + public void WhenBrokenProjectIsPassedItPrintsErrorAndUsage() + { + string projName = "Broken/Broken.csproj"; + string testRoot = Setup(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(testRoot) + .WithProject(projName) + .Execute($"\"{ValidRefPath}\""); + cmd.ExitCode.Should().NotBe(0); + cmd.StdErr.Should().Contain("Invalid project"); + cmd.StdOut.Should().Contain("Usage"); + } + + [Fact] + public void WhenMoreThanOneProjectExistsInTheDirectoryItPrintsErrorAndUsage() + { + string testRoot = Setup(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(Path.Combine(testRoot, "MoreThanOne")) + .Execute($"\"{ValidRefPath}\""); + cmd.ExitCode.Should().NotBe(0); + cmd.StdErr.Should().Contain("more than one"); + cmd.StdOut.Should().Contain("Usage"); + } + + [Fact] + public void WhenNoProjectsExistsInTheDirectoryItPrintsErrorAndUsage() + { + string testRoot = Setup(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(testRoot) + .Execute($"\"{ValidRefPath}\""); + cmd.ExitCode.Should().NotBe(0); + cmd.StdErr.Should().Contain("not find any"); + cmd.StdOut.Should().Contain("Usage"); + } + + [Fact] + public void ItAddsRefWithoutCondAndPrintsStatus() + { + var lib = NewLib(); + + int noCondBefore = lib.CsProj().NumberOfItemGroupsWithoutCondition(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"\"{ValidRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("added to the project"); + cmd.StdErr.Should().BeEmpty(); + var csproj = lib.CsProj(); + csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1); + csproj.NumberOfProjectReferencesWithIncludeContaining(ValidRefCsproj).Should().Be(1); + } + + [Fact] + public void ItAddsRefWithCondAndPrintsStatus() + { + var lib = NewLib(); + + int condBefore = lib.CsProj().NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"{FrameworkNet451Arg} \"{ValidRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("added to the project"); + cmd.StdErr.Should().BeEmpty(); + var csproj = lib.CsProj(); + csproj.NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451).Should().Be(condBefore + 1); + csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(ValidRefCsproj, ConditionFrameworkNet451).Should().Be(1); + } + + [Fact] + public void WhenRefWithoutCondIsPresentItAddsDifferentRefWithoutCond() + { + var lib = NewLib(); + + new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"\"{LibRefPath}\"") + .Should().Pass(); + + int noCondBefore = lib.CsProj().NumberOfItemGroupsWithoutCondition(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"\"{ValidRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("added to the project"); + var csproj = lib.CsProj(); + csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore); + csproj.NumberOfProjectReferencesWithIncludeContaining(ValidRefCsproj).Should().Be(1); + } + + [Fact] + public void WhenRefWithCondIsPresentItAddsDifferentRefWithCond() + { + var lib = NewLib(); + + new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"{FrameworkNet451Arg} \"{LibRefPath}\"") + .Should().Pass(); + + int condBefore = lib.CsProj().NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"{FrameworkNet451Arg} \"{ValidRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("added to the project"); + var csproj = lib.CsProj(); + csproj.NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451).Should().Be(condBefore); + csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(ValidRefCsproj, ConditionFrameworkNet451).Should().Be(1); + } + + [Fact] + public void WhenRefWithCondIsPresentItAddsRefWithDifferentCond() + { + var lib = NewLib(); + + new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"{FrameworkNetCoreApp10Arg} \"{ValidRefPath}\"") + .Should().Pass(); + + int condBefore = lib.CsProj().NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"{FrameworkNet451Arg} \"{ValidRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("added to the project"); + var csproj = lib.CsProj(); + csproj.NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451).Should().Be(condBefore + 1); + csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(ValidRefCsproj, ConditionFrameworkNet451).Should().Be(1); + } + + [Fact] + public void WhenRefWithConditionIsPresentItAddsDifferentRefWithoutCond() + { + var lib = NewLib(); + + new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"{FrameworkNet451Arg} \"{LibRefPath}\"") + .Should().Pass(); + + int noCondBefore = lib.CsProj().NumberOfItemGroupsWithoutCondition(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"\"{ValidRefPath}\""); + cmd.Should().Pass(); + + var csproj = lib.CsProj(); + csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1); + csproj.NumberOfProjectReferencesWithIncludeContaining(ValidRefCsproj).Should().Be(1); + } + + [Fact] + public void WhenRefWithNoCondAlreadyExistsItDoesntDuplicate() + { + var lib = NewLib(); + + new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"\"{ValidRefPath}\"") + .Should().Pass(); + + int noCondBefore = lib.CsProj().NumberOfItemGroupsWithoutCondition(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"\"{ValidRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("already has a reference"); + + var csproj = lib.CsProj(); + csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore); + csproj.NumberOfProjectReferencesWithIncludeContaining(ValidRefCsproj).Should().Be(1); + } + + [Fact] + public void WhenRefWithCondOnItemAlreadyExistsItDoesntDuplicate() + { + string testRoot = Setup(); + + string projDir = Path.Combine(testRoot, "WithExistingRefCondOnItem"); + string projName = Path.Combine(projDir, "WithExistingRefCondOnItem.csproj"); + string contentBefore = File.ReadAllText(projName); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(projDir) + .WithProject(projName) + .Execute($"{FrameworkNet451Arg} \"{LibRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("already has a reference"); + File.ReadAllText(projName).Should().BeEquivalentTo(contentBefore); + } + + [Fact] + public void WhenRefWithCondOnItemGroupAlreadyExistsItDoesntDuplicate() + { + var lib = NewLib(); + + new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"{FrameworkNet451Arg} \"{ValidRefPath}\"") + .Should().Pass(); + + var csprojContentBefore = lib.CsProjContent(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"{FrameworkNet451Arg} \"{ValidRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("already has a reference"); + lib.CsProjContent().Should().BeEquivalentTo(csprojContentBefore); + } + + [Fact] + public void WhenRefWithCondWithWhitespaceOnItemGroupExistsItDoesntDuplicate() + { + string testRoot = Setup(); + + string projDir = Path.Combine(testRoot, "WithExistingRefCondWhitespaces"); + string projName = Path.Combine(projDir, "WithExistingRefCondWhitespaces.csproj"); + string contentBefore = File.ReadAllText(projName); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(projDir) + .WithProject(projName) + .Execute($"{FrameworkNet451Arg} \"{LibRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("already has a reference"); + File.ReadAllText(projName).Should().BeEquivalentTo(contentBefore); + } + + [Fact] + public void WhenRefWithoutCondAlreadyExistsInNonUniformItemGroupItDoesntDuplicate() + { + string testRoot = Setup(); + + string projDir = Path.Combine(testRoot, "WithRefNoCondNonUniform"); + string projName = Path.Combine(projDir, "WithRefNoCondNonUniform.csproj"); + string contentBefore = File.ReadAllText(projName); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(projDir) + .WithProject(projName) + .Execute($"\"{LibRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("already has a reference"); + File.ReadAllText(projName).Should().BeEquivalentTo(contentBefore); + } + + [Fact] + public void WhenRefWithoutCondAlreadyExistsInNonUniformItemGroupItAddsDifferentRefInDifferentGroup() + { + string testRoot = Setup(); + + var proj = new ProjDir(Path.Combine(testRoot, "WithRefNoCondNonUniform")); + + int noCondBefore = proj.CsProj().NumberOfItemGroupsWithoutCondition(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(proj.Path) + .WithProject(proj.CsProjPath) + .Execute($"\"{ValidRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("added to the project"); + var csproj = proj.CsProj(); + csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1); + csproj.NumberOfProjectReferencesWithIncludeContaining(ValidRefPath).Should().Be(1); + } + + [Fact] + public void WhenRefWithCondAlreadyExistsInNonUniformItemGroupItDoesntDuplicate() + { + string testRoot = Setup(); + + string projDir = Path.Combine(testRoot, "WithRefCondNonUniform"); + string projName = Path.Combine(projDir, "WithRefCondNonUniform.csproj"); + string contentBefore = File.ReadAllText(projName); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(projDir) + .WithProject(projName) + .Execute($"{FrameworkNet451Arg} \"{LibRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("already has a reference"); + File.ReadAllText(projName).Should().BeEquivalentTo(contentBefore); + } + + [Fact] + public void WhenRefWithCondAlreadyExistsInNonUniformItemGroupItAddsDifferentRefInDifferentGroup() + { + string testRoot = Setup(); + + var proj = new ProjDir(Path.Combine(testRoot, "WithRefCondNonUniform")); + + int condBefore = proj.CsProj().NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(proj.Path) + .WithProject(proj.CsProjPath) + .Execute($"{FrameworkNet451Arg} \"{ValidRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("added to the project"); + var csproj = proj.CsProj(); + csproj.NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451).Should().Be(condBefore + 1); + csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(ValidRefPath, ConditionFrameworkNet451).Should().Be(1); + } + + [Fact] + public void WhenEmptyItemGroupPresentItAddsRefInIt() + { + string testRoot = Setup(); + + var proj = new ProjDir(Path.Combine(testRoot, "EmptyItemGroup")); + + int noCondBefore = proj.CsProj().NumberOfItemGroupsWithoutCondition(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(proj.Path) + .WithProject(proj.CsProjPath) + .Execute($"\"{ValidRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("added to the project"); + var csproj = proj.CsProj(); + csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore); + csproj.NumberOfProjectReferencesWithIncludeContaining(ValidRefPath).Should().Be(1); + } + + [Fact] + public void ItAddsMultipleRefsNoCondToTheSameItemGroup() + { + var lib = NewLib(); + + int noCondBefore = lib.CsProj().NumberOfItemGroupsWithoutCondition(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"\"{LibRefPath}\" \"{ValidRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("added to the project").And.NotContain("already has a reference"); + var csproj = lib.CsProj(); + csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1); + csproj.NumberOfProjectReferencesWithIncludeContaining(ValidRefCsproj).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeContaining(LibRefPath).Should().Be(1); + } + + [Fact] + public void ItAddsMultipleRefsWithCondToTheSameItemGroup() + { + var lib = NewLib(); + + int noCondBefore = lib.CsProj().NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"{FrameworkNet451Arg} \"{LibRefPath}\" \"{ValidRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("added to the project").And.NotContain("already has a reference"); + var csproj = lib.CsProj(); + csproj.NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451).Should().Be(noCondBefore + 1); + csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(ValidRefCsproj, ConditionFrameworkNet451).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(LibRefPath, ConditionFrameworkNet451).Should().Be(1); + } + + [Fact(Skip = "Not finished")] + public void WhenProjectNameIsNotPassedItFindsItAndAddsReference() + { + throw new NotImplementedException(); + } + + [Fact(Skip = "Not finished")] + public void ItAddsRefBeforeImports() + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/test/dotnet-add-p2p.Tests/ProjDir.cs b/test/dotnet-add-p2p.Tests/ProjDir.cs new file mode 100644 index 000000000..2b568ae78 --- /dev/null +++ b/test/dotnet-add-p2p.Tests/ProjDir.cs @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.IO; +using Microsoft.DotNet.TestFramework; +using Microsoft.Build.Construction; +using Microsoft.Build.Evaluation; + +namespace Microsoft.DotNet.Cli.Add.P2P.Tests +{ + internal class ProjDir + { + public ProjDir(TestAssetsManager tam, [System.Runtime.CompilerServices.CallerMemberName] string callingMethod = nameof(ProjDir), string identifier = "") + { + Path = tam.CreateTestDirectory(callingMethod: callingMethod, identifier: identifier).Path; + Name = new DirectoryInfo(Path).Name; + } + + public ProjDir(string path) + { + Path = path; + Name = new DirectoryInfo(Path).Name; + } + + public string Path { get; private set; } + public string Name { get; private set; } + public string CsProjName => $"{Name}.csproj"; + public string CsProjPath => System.IO.Path.Combine(Path, CsProjName); + + public string CsProjContent() + { + return File.ReadAllText(CsProjPath); + } + + public ProjectRootElement CsProj() + { + // Passing new collection to prevent using cached version + return ProjectRootElement.Open(CsProjPath, new ProjectCollection()); + } + } +} \ No newline at end of file diff --git a/test/dotnet-add-p2p.Tests/dotnet-add-p2p.Tests.csproj b/test/dotnet-add-p2p.Tests/dotnet-add-p2p.Tests.csproj new file mode 100644 index 000000000..338f7cb62 --- /dev/null +++ b/test/dotnet-add-p2p.Tests/dotnet-add-p2p.Tests.csproj @@ -0,0 +1,63 @@ + + + + netcoreapp1.0 + true + dotnet-add-p2p.Tests + $(PackageTargetFallback);dotnet5.4;portable-net451+win8 + $(DefineConstants);DISABLE_TRACE + + + + + src\Microsoft.DotNet.ProjectJsonMigration\MSBuildExtensions.cs + + + + + + + + true + + + true + + + true + + + + + true + + + + + 1.0.0-alpha-20161104-2 + All + + + 15.0.0-preview-20161024-02 + + + 2.2.0-beta4-build1194 + + + 1.0.1 + + + 4.1.1 + + + 2.2.0-beta4-build3444 + + + 15.1.0-preview-000370-00 + + + + $(DefineConstants);RELEASE + + + From 8e5ffaf176b79c7bb2e9106b1087992d943e52d8 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Mon, 21 Nov 2016 11:38:25 -0800 Subject: [PATCH 09/18] fix some of the stuff in the feedback --- .../DotnetAddP2PProjects/Broken/Broken.csproj | 3 +- .../EmptyItemGroup/EmptyItemGroup.csproj | 5 +- .../DotnetAddP2PProjects/Lib/Lib.csproj | 5 +- .../MoreThanOne/SomeSourceFile.cs | 7 +- .../DotnetAddP2PProjects/MoreThanOne/a.csproj | 5 +- .../DotnetAddP2PProjects/MoreThanOne/b.csproj | 5 +- .../ValidRef/ValidRef.csproj | 5 +- .../WithExistingRefCondOnItem.csproj | 5 +- .../WithExistingRefCondWhitespaces.csproj | 5 +- .../WithRefCondNonUniform.csproj | 5 +- .../WithRefNoCondNonUniform.csproj | 5 +- .../_remove_me_/AppE2E/App.csproj | 31 ------- .../_remove_me_/AppE2E/Program.cs | 21 ----- .../_remove_me_/Lib1/Lib1.cs | 24 ----- .../_remove_me_/Lib1/Lib1.csproj | 28 ------ .../_remove_me_/Lib4/Lib4.cs | 21 ----- .../_remove_me_/Lib4/Lib4.csproj | 31 ------- src/dotnet/commands/dotnet-add-p2p/Program.cs | 15 ++-- .../dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs | 90 ++++++++++++++++++- 19 files changed, 126 insertions(+), 190 deletions(-) delete mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/AppE2E/App.csproj delete mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/AppE2E/Program.cs delete mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib1/Lib1.cs delete mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib1/Lib1.csproj delete mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib4/Lib4.cs delete mode 100644 TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib4/Lib4.csproj diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Broken/Broken.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Broken/Broken.csproj index 3203e50a0..1d2021f11 100644 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Broken/Broken.csproj +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Broken/Broken.csproj @@ -1,5 +1,4 @@ - - + Library diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/EmptyItemGroup/EmptyItemGroup.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/EmptyItemGroup/EmptyItemGroup.csproj index 3db580ead..7ff8da173 100644 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/EmptyItemGroup/EmptyItemGroup.csproj +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/EmptyItemGroup/EmptyItemGroup.csproj @@ -1,5 +1,4 @@ - - + Library @@ -8,6 +7,8 @@ + + 1.0.0-alpha-20161104-2 All diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib/Lib.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib/Lib.csproj index b61db6f8d..375b9dc16 100644 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib/Lib.csproj +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/Lib/Lib.csproj @@ -1,5 +1,4 @@ - - + Library @@ -8,6 +7,8 @@ + + 1.0.0-alpha-20161104-2 All diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/SomeSourceFile.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/SomeSourceFile.cs index 3e8c40581..d99a03229 100644 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/SomeSourceFile.cs +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/SomeSourceFile.cs @@ -1,8 +1,3 @@ -using System; - -namespace SomeNS +public class SomeClass { - public class SomeClass - { - } } diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/a.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/a.csproj index 7a03821ea..58b10291d 100644 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/a.csproj +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/a.csproj @@ -1,5 +1,4 @@ - - + Library @@ -8,6 +7,8 @@ + + 1.0.0-alpha-20161104-2 All diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/b.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/b.csproj index 7a03821ea..58b10291d 100644 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/b.csproj +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/MoreThanOne/b.csproj @@ -1,5 +1,4 @@ - - + Library @@ -8,6 +7,8 @@ + + 1.0.0-alpha-20161104-2 All diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/ValidRef/ValidRef.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/ValidRef/ValidRef.csproj index b61db6f8d..375b9dc16 100644 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/ValidRef/ValidRef.csproj +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/ValidRef/ValidRef.csproj @@ -1,5 +1,4 @@ - - + Library @@ -8,6 +7,8 @@ + + 1.0.0-alpha-20161104-2 All diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondOnItem/WithExistingRefCondOnItem.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondOnItem/WithExistingRefCondOnItem.csproj index a85516e34..b80bede54 100644 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondOnItem/WithExistingRefCondOnItem.csproj +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondOnItem/WithExistingRefCondOnItem.csproj @@ -1,5 +1,4 @@ - - + Library @@ -8,6 +7,8 @@ + + 1.0.0-alpha-20161104-2 All diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondWhitespaces/WithExistingRefCondWhitespaces.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondWhitespaces/WithExistingRefCondWhitespaces.csproj index 79aca8ec9..a6b86bd03 100644 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondWhitespaces/WithExistingRefCondWhitespaces.csproj +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithExistingRefCondWhitespaces/WithExistingRefCondWhitespaces.csproj @@ -1,5 +1,4 @@ - - + Library @@ -8,6 +7,8 @@ + + 1.0.0-alpha-20161104-2 All diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefCondNonUniform/WithRefCondNonUniform.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefCondNonUniform/WithRefCondNonUniform.csproj index 0f5e7d1b7..d8b39ba5b 100644 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefCondNonUniform/WithRefCondNonUniform.csproj +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefCondNonUniform/WithRefCondNonUniform.csproj @@ -1,5 +1,4 @@ - - + Library @@ -8,6 +7,8 @@ + + 1.0.0-alpha-20161104-2 All diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefNoCondNonUniform/WithRefNoCondNonUniform.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefNoCondNonUniform/WithRefNoCondNonUniform.csproj index a4f9328b0..ced0f1d8d 100644 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefNoCondNonUniform/WithRefNoCondNonUniform.csproj +++ b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/WithRefNoCondNonUniform/WithRefNoCondNonUniform.csproj @@ -1,5 +1,4 @@ - - + Library @@ -8,6 +7,8 @@ + + 1.0.0-alpha-20161104-2 All diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/AppE2E/App.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/AppE2E/App.csproj deleted file mode 100644 index a476f96b3..000000000 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/AppE2E/App.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - - Exe - net451;netcoreapp1.0 - - - - - - 1.0.0-alpha-20161104-2 - All - - - - - 1.0.1 - - - - - - - $(DefineConstants);NETCOREAPP1_0 - - - $(DefineConstants);NET451 - - - diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/AppE2E/Program.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/AppE2E/Program.cs deleted file mode 100644 index 8d6e7d1db..000000000 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/AppE2E/Program.cs +++ /dev/null @@ -1,21 +0,0 @@ -using NSLib1; -using NSLib3; - -namespace ConsoleApplication -{ - public class Program - { - public static void Main(string[] args) - { -#if NETCOREAPP1_0 - Lib1.HelloNCA(); - Lib3.HelloNCA(); -#endif - -#if NET451 - Lib1.HelloNet451(); - Lib3.HelloNet451(); -#endif - } - } -} diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib1/Lib1.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib1/Lib1.cs deleted file mode 100644 index 7250d586f..000000000 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib1/Lib1.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using NSLib2; - -namespace NSLib1 -{ - public class Lib1 - { -#if NETCOREAPP1_0 - public static void HelloNCA() - { - Console.WriteLine("Hello World from Lib1! (netcoreapp)"); - Lib2.HelloNCA(); - } -#endif - -#if NET451 - public static void HelloNet451() - { - Console.WriteLine("Hello World from Lib1! (net45)"); - Lib2.HelloNet451(); - } -#endif - } -} diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib1/Lib1.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib1/Lib1.csproj deleted file mode 100644 index a5ea660c6..000000000 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib1/Lib1.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - - Library - net451;netcoreapp1.0 - - - - - - 1.0.0-alpha-20161104-2 - All - - - - - 1.0.1 - - - - $(DefineConstants);NETCOREAPP1_0 - - - $(DefineConstants);NET451 - - - diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib4/Lib4.cs b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib4/Lib4.cs deleted file mode 100644 index d993cc499..000000000 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib4/Lib4.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; - -namespace NSLib4 -{ - public class Lib4 - { -#if NETCOREAPP1_0 - public static void HelloNCA() - { - Console.WriteLine("Hello World from Lib4! (netcoreapp)"); - } -#endif - -#if NET451 - public static void HelloNet451() - { - Console.WriteLine("Hello World from Lib4! (net45)"); - } -#endif - } -} diff --git a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib4/Lib4.csproj b/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib4/Lib4.csproj deleted file mode 100644 index 55cecb538..000000000 --- a/TestAssets/NonRestoredTestProjects/DotnetAddP2PProjects/_remove_me_/Lib4/Lib4.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - - Library - net451;netcoreapp1.0 - - - - - - 1.0.0-alpha-20161104-2 - All - - - - - 1.0.1 - - - - - - - $(DefineConstants);NETCOREAPP1_0 - - - $(DefineConstants);NET451 - - - diff --git a/src/dotnet/commands/dotnet-add-p2p/Program.cs b/src/dotnet/commands/dotnet-add-p2p/Program.cs index 42dd41997..4abf50e36 100644 --- a/src/dotnet/commands/dotnet-add-p2p/Program.cs +++ b/src/dotnet/commands/dotnet-add-p2p/Program.cs @@ -50,7 +50,10 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference throw new GracefulException("You must specify at least one reference to add."); } - int numberOfAddedReferences = AddProjectToProjectReference(project, frameworkOption.Value(), app.RemainingArguments); + int numberOfAddedReferences = AddProjectToProjectReference( + project, + frameworkOption.Value(), + app.RemainingArguments); if (numberOfAddedReferences != 0) { @@ -74,7 +77,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference // There is ProjectRootElement.TryOpen but it does not work as expected // I.e. it returns null for some valid projects - public static ProjectRootElement TryOpenProject(string filename) + internal static ProjectRootElement TryOpenProject(string filename) { try { @@ -86,7 +89,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference } } - public static ProjectRootElement GetProjectFromFileOrThrow(string filename) + internal static ProjectRootElement GetProjectFromFileOrThrow(string filename) { if (!File.Exists(filename)) { @@ -103,7 +106,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference } // TODO: improve errors - public static ProjectRootElement GetProjectFromDirectoryOrThrow(string directory) + internal static ProjectRootElement GetProjectFromDirectoryOrThrow(string directory) { DirectoryInfo dir; try @@ -128,7 +131,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference if (files.Length > 1) { - throw new GracefulException("Found more than one MSBuild project in the current directory. Please specify which one to use."); + throw new GracefulException("Found more than one project in the current directory. Please specify which one to use."); } FileInfo projectFile = files.First(); @@ -147,7 +150,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference return ret; } - public static int AddProjectToProjectReference(ProjectRootElement root, string framework, IEnumerable refs) + internal static int AddProjectToProjectReference(ProjectRootElement root, string framework, IEnumerable refs) { int numberOfAddedReferences = 0; const string ProjectItemElementType = "ProjectReference"; diff --git a/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs b/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs index 27a01aede..7d05abb56 100644 --- a/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs +++ b/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs @@ -6,6 +6,7 @@ using System.IO; using Microsoft.DotNet.Tools.Test.Utilities; using Xunit; using FluentAssertions; +using Microsoft.Build.Construction; namespace Microsoft.DotNet.Cli.Add.P2P.Tests { @@ -458,14 +459,99 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(LibRefPath, ConditionFrameworkNet451).Should().Be(1); } - [Fact(Skip = "Not finished")] + [Fact] public void WhenProjectNameIsNotPassedItFindsItAndAddsReference() + { + var lib = NewLib(); + + int noCondBefore = lib.CsProj().NumberOfItemGroupsWithoutCondition(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .Execute($"\"{ValidRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("added to the project"); + cmd.StdErr.Should().BeEmpty(); + var csproj = lib.CsProj(); + csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1); + csproj.NumberOfProjectReferencesWithIncludeContaining(ValidRefCsproj).Should().Be(1); + } + + [Fact] + public void ItAddsRefBetweenImports() + { + var lib = NewLib(); + + var cmd = new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"\"{ValidRefPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("added to the project"); + cmd.StdErr.Should().BeEmpty(); + + int state = 0; + foreach (var el in lib.CsProj().AllChildren) + { + var import = el as ProjectImportElement; + var projRef = el as ProjectItemElement; + switch (state) + { + case 0: + if (import != null && import.Project.EndsWith(".props")) + { + state++; + } + break; + case 1: + if (projRef != null && projRef.ItemType == "ProjectReference" && projRef.Include.Contains(ValidRefCsproj)) + { + state++; + } + break; + case 2: + if (import != null && import.Project.EndsWith(".targets")) + { + state++; + } + break; + } + } + + state.Should().Be(3); + } + + [Fact(Skip = "Not finished")] + public void WhenPassedReferenceDoesNotExistItShowsAnError() { throw new NotImplementedException(); } [Fact(Skip = "Not finished")] - public void ItAddsRefBeforeImports() + public void WhenPassedMultipleRefsAndOneOfthemDoesNotExistItCancelsWholeOperation() + { + throw new NotImplementedException(); + } + + [Fact(Skip = "Not finished")] + public void WhenPassedReferenceDoesNotExistAndForceSwitchIsPassedItAddsIt() + { + throw new NotImplementedException(); + } + + [Fact(Skip = "Not finished")] + public void WhenPassedReferenceIsUsingSlashesItNormalizesItToBackslashes() + { + throw new NotImplementedException(); + } + + [Fact(Skip = "Not finished")] + public void WhenPassedRefIsUsingBackslashesItDoesntNormalizeIt() + { + throw new NotImplementedException(); + } + + [Fact(Skip = "Not finished")] + public void WhenReferenceIsRelativeAndProjectIsNotInCurrentDirectoryReferencePathIsFixed() { throw new NotImplementedException(); } From 19dde128baf4fe8e17673054bb53515bc0e52ab4 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Tue, 22 Nov 2016 10:45:45 -0800 Subject: [PATCH 10/18] detect non-existing references and tiny refactor of tests --- src/dotnet/commands/dotnet-add-p2p/Program.cs | 39 ++- .../dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs | 302 ++++++++++-------- test/dotnet-add-p2p.Tests/TestSetup.cs | 36 +++ 3 files changed, 231 insertions(+), 146 deletions(-) create mode 100644 test/dotnet-add-p2p.Tests/TestSetup.cs diff --git a/src/dotnet/commands/dotnet-add-p2p/Program.cs b/src/dotnet/commands/dotnet-add-p2p/Program.cs index 4abf50e36..d08770d7e 100644 --- a/src/dotnet/commands/dotnet-add-p2p/Program.cs +++ b/src/dotnet/commands/dotnet-add-p2p/Program.cs @@ -29,11 +29,21 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference app.HelpOption("-h|--help"); - CommandArgument projectArgument = app.Argument("", + CommandArgument projectArgument = app.Argument( + "", "The project file to modify. If a project file is not specified," + - " it searches the current working directory for an MSBuild file that has a file extension that ends in `proj` and uses that file."); + " it searches the current working directory for an MSBuild file that has" + + " a file extension that ends in `proj` and uses that file."); - CommandOption frameworkOption = app.Option("-f|--framework ", "Add reference only when targetting a specific framework", CommandOptionType.SingleValue); + CommandOption frameworkOption = app.Option( + "-f|--framework ", + "Add reference only when targetting a specific framework", + CommandOptionType.SingleValue); + + CommandOption forceOption = app.Option( + "--force", + "Add reference even if it does not exist", + CommandOptionType.NoValue); app.OnExecute(() => { if (projectArgument.Value == null) @@ -50,10 +60,31 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference throw new GracefulException("You must specify at least one reference to add."); } + List references = app.RemainingArguments; + if (!forceOption.HasValue()) + { + var notExisting = new List(); + foreach (var r in references) + { + if (!File.Exists(r)) + { + notExisting.Add(r); + } + } + + if (notExisting.Count > 0) + { + throw new GracefulException( + string.Join( + Environment.NewLine, + notExisting.Select((ne) => $"Reference `{ne}` does not exist."))); + } + } + int numberOfAddedReferences = AddProjectToProjectReference( project, frameworkOption.Value(), - app.RemainingArguments); + references); if (numberOfAddedReferences != 0) { diff --git a/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs b/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs index 7d05abb56..e3155b059 100644 --- a/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs +++ b/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs @@ -16,25 +16,13 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests const string ConditionFrameworkNet451 = "== 'net451'"; const string FrameworkNetCoreApp10Arg = "-f netcoreapp1.0"; const string ConditionFrameworkNetCoreApp10 = "== 'netcoreapp1.0'"; - private static readonly string ValidRef = "ValidRef"; - private static readonly string ValidRefCsproj = $"{ValidRef}.csproj"; - private static readonly string ValidRefPath = Path.Combine("..", ValidRef, ValidRefCsproj); - private static readonly string LibRef = "Lib"; - private static readonly string LibRefCsproj = $"{LibRef}.csproj"; - private static readonly string LibRefPath = Path.Combine("..", LibRef, LibRefCsproj); - - private static readonly string AppPath = Path.Combine("App", "App.csproj"); - private static readonly string Lib1Path = Path.Combine("Lib1", "Lib1.csproj"); - private static readonly string Lib2Path = Path.Combine("Lib2", "Lib2.csproj"); - private static readonly string Lib3Path = Path.Combine("Lib3", "Lib3.csproj"); - private static readonly string Lib4Path = Path.Combine("Lib4", "Lib4.csproj"); - - private string Setup([System.Runtime.CompilerServices.CallerMemberName] string callingMethod = nameof(Setup), string identifier = "") + private TestSetup Setup([System.Runtime.CompilerServices.CallerMemberName] string callingMethod = nameof(Setup), string identifier = "") { - const string TestGroup = "NonRestoredTestProjects"; - const string ProjectName = "DotnetAddP2PProjects"; - return GetTestGroupTestAssetsManager(TestGroup).CreateTestInstance(ProjectName, callingMethod: callingMethod, identifier: identifier).Path; + return new TestSetup( + GetTestGroupTestAssetsManager(TestSetup.TestGroup) + .CreateTestInstance(TestSetup.ProjectName, callingMethod: callingMethod, identifier: identifier) + .Path); } private ProjDir NewDir([System.Runtime.CompilerServices.CallerMemberName] string callingMethod = nameof(NewDir), string identifier = "") @@ -55,7 +43,7 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests } catch (System.ComponentModel.Win32Exception e) { - throw new Exception($"DIDIDIDIDOIDIR: {dir.Path}\n{e}"); + throw new Exception($"Intermittent error in `dotnet new` occurred when running it in dir `{dir.Path}`\nException:\n{e}"); } return dir; @@ -77,10 +65,12 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests public void WhenNonExistingProjectIsPassedItPrintsErrorAndUsage(string projName) { string testRoot = NewDir().Path; + var setup = Setup(); + var cmd = new AddP2PCommand() - .WithWorkingDirectory(testRoot) + .WithWorkingDirectory(setup.TestRoot) .WithProject(projName) - .Execute($"\"{ValidRefPath}\""); + .Execute($"\"{setup.ValidRefCsprojPath}\""); cmd.ExitCode.Should().NotBe(0); cmd.StdErr.Should().Contain("Could not find"); cmd.StdOut.Should().Contain("Usage"); @@ -91,11 +81,12 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests public void WhenBrokenProjectIsPassedItPrintsErrorAndUsage() { string projName = "Broken/Broken.csproj"; - string testRoot = Setup(); + var setup = Setup(); + var cmd = new AddP2PCommand() - .WithWorkingDirectory(testRoot) + .WithWorkingDirectory(setup.TestRoot) .WithProject(projName) - .Execute($"\"{ValidRefPath}\""); + .Execute($"\"{setup.ValidRefCsprojPath}\""); cmd.ExitCode.Should().NotBe(0); cmd.StdErr.Should().Contain("Invalid project"); cmd.StdOut.Should().Contain("Usage"); @@ -104,10 +95,11 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests [Fact] public void WhenMoreThanOneProjectExistsInTheDirectoryItPrintsErrorAndUsage() { - string testRoot = Setup(); + var setup = Setup(); + var cmd = new AddP2PCommand() - .WithWorkingDirectory(Path.Combine(testRoot, "MoreThanOne")) - .Execute($"\"{ValidRefPath}\""); + .WithWorkingDirectory(Path.Combine(setup.TestRoot, "MoreThanOne")) + .Execute($"\"{setup.ValidRefCsprojRelPath}\""); cmd.ExitCode.Should().NotBe(0); cmd.StdErr.Should().Contain("more than one"); cmd.StdOut.Should().Contain("Usage"); @@ -116,10 +108,11 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests [Fact] public void WhenNoProjectsExistsInTheDirectoryItPrintsErrorAndUsage() { - string testRoot = Setup(); + var setup = Setup(); + var cmd = new AddP2PCommand() - .WithWorkingDirectory(testRoot) - .Execute($"\"{ValidRefPath}\""); + .WithWorkingDirectory(setup.TestRoot) + .Execute($"\"{setup.ValidRefCsprojPath}\""); cmd.ExitCode.Should().NotBe(0); cmd.StdErr.Should().Contain("not find any"); cmd.StdOut.Should().Contain("Usage"); @@ -129,187 +122,194 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests public void ItAddsRefWithoutCondAndPrintsStatus() { var lib = NewLib(); + var setup = Setup(); int noCondBefore = lib.CsProj().NumberOfItemGroupsWithoutCondition(); var cmd = new AddP2PCommand() - .WithWorkingDirectory(lib.Path) - .WithProject(lib.CsProjName) - .Execute($"\"{ValidRefPath}\""); + .WithWorkingDirectory(setup.TestRoot) + .WithProject(lib.CsProjPath) + .Execute($"\"{setup.ValidRefCsprojPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("added to the project"); cmd.StdErr.Should().BeEmpty(); var csproj = lib.CsProj(); csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1); - csproj.NumberOfProjectReferencesWithIncludeContaining(ValidRefCsproj).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojName).Should().Be(1); } [Fact] public void ItAddsRefWithCondAndPrintsStatus() { var lib = NewLib(); + var setup = Setup(); int condBefore = lib.CsProj().NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451); var cmd = new AddP2PCommand() - .WithWorkingDirectory(lib.Path) - .WithProject(lib.CsProjName) - .Execute($"{FrameworkNet451Arg} \"{ValidRefPath}\""); + .WithWorkingDirectory(setup.TestRoot) + .WithProject(lib.CsProjPath) + .Execute($"{FrameworkNet451Arg} \"{setup.ValidRefCsprojPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("added to the project"); cmd.StdErr.Should().BeEmpty(); var csproj = lib.CsProj(); csproj.NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451).Should().Be(condBefore + 1); - csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(ValidRefCsproj, ConditionFrameworkNet451).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(setup.ValidRefCsprojName, ConditionFrameworkNet451).Should().Be(1); } [Fact] public void WhenRefWithoutCondIsPresentItAddsDifferentRefWithoutCond() { var lib = NewLib(); + var setup = Setup(); new AddP2PCommand() - .WithWorkingDirectory(lib.Path) - .WithProject(lib.CsProjName) - .Execute($"\"{LibRefPath}\"") + .WithWorkingDirectory(setup.TestRoot) + .WithProject(lib.CsProjPath) + .Execute($"\"{setup.LibCsprojPath}\"") .Should().Pass(); int noCondBefore = lib.CsProj().NumberOfItemGroupsWithoutCondition(); var cmd = new AddP2PCommand() .WithWorkingDirectory(lib.Path) .WithProject(lib.CsProjName) - .Execute($"\"{ValidRefPath}\""); + .Execute($"\"{setup.ValidRefCsprojPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("added to the project"); var csproj = lib.CsProj(); csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore); - csproj.NumberOfProjectReferencesWithIncludeContaining(ValidRefCsproj).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojName).Should().Be(1); } [Fact] public void WhenRefWithCondIsPresentItAddsDifferentRefWithCond() { var lib = NewLib(); + var setup = Setup(); new AddP2PCommand() - .WithWorkingDirectory(lib.Path) - .WithProject(lib.CsProjName) - .Execute($"{FrameworkNet451Arg} \"{LibRefPath}\"") + .WithWorkingDirectory(setup.TestRoot) + .WithProject(lib.CsProjPath) + .Execute($"{FrameworkNet451Arg} \"{setup.LibCsprojPath}\"") .Should().Pass(); int condBefore = lib.CsProj().NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451); var cmd = new AddP2PCommand() - .WithWorkingDirectory(lib.Path) - .WithProject(lib.CsProjName) - .Execute($"{FrameworkNet451Arg} \"{ValidRefPath}\""); + .WithWorkingDirectory(setup.TestRoot) + .WithProject(lib.CsProjPath) + .Execute($"{FrameworkNet451Arg} \"{setup.ValidRefCsprojPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("added to the project"); var csproj = lib.CsProj(); csproj.NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451).Should().Be(condBefore); - csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(ValidRefCsproj, ConditionFrameworkNet451).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(setup.ValidRefCsprojName, ConditionFrameworkNet451).Should().Be(1); } [Fact] public void WhenRefWithCondIsPresentItAddsRefWithDifferentCond() { var lib = NewLib(); + var setup = Setup(); new AddP2PCommand() - .WithWorkingDirectory(lib.Path) - .WithProject(lib.CsProjName) - .Execute($"{FrameworkNetCoreApp10Arg} \"{ValidRefPath}\"") + .WithWorkingDirectory(setup.TestRoot) + .WithProject(lib.CsProjPath) + .Execute($"{FrameworkNetCoreApp10Arg} \"{setup.ValidRefCsprojPath}\"") .Should().Pass(); int condBefore = lib.CsProj().NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451); var cmd = new AddP2PCommand() - .WithWorkingDirectory(lib.Path) - .WithProject(lib.CsProjName) - .Execute($"{FrameworkNet451Arg} \"{ValidRefPath}\""); + .WithWorkingDirectory(setup.TestRoot) + .WithProject(lib.CsProjPath) + .Execute($"{FrameworkNet451Arg} \"{setup.ValidRefCsprojPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("added to the project"); var csproj = lib.CsProj(); csproj.NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451).Should().Be(condBefore + 1); - csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(ValidRefCsproj, ConditionFrameworkNet451).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(setup.ValidRefCsprojName, ConditionFrameworkNet451).Should().Be(1); } [Fact] public void WhenRefWithConditionIsPresentItAddsDifferentRefWithoutCond() { var lib = NewLib(); + var setup = Setup(); new AddP2PCommand() - .WithWorkingDirectory(lib.Path) - .WithProject(lib.CsProjName) - .Execute($"{FrameworkNet451Arg} \"{LibRefPath}\"") + .WithWorkingDirectory(setup.TestRoot) + .WithProject(lib.CsProjPath) + .Execute($"{FrameworkNet451Arg} \"{setup.LibCsprojPath}\"") .Should().Pass(); int noCondBefore = lib.CsProj().NumberOfItemGroupsWithoutCondition(); var cmd = new AddP2PCommand() - .WithWorkingDirectory(lib.Path) - .WithProject(lib.CsProjName) - .Execute($"\"{ValidRefPath}\""); + .WithWorkingDirectory(setup.TestRoot) + .WithProject(lib.CsProjPath) + .Execute($"\"{setup.ValidRefCsprojPath}\""); cmd.Should().Pass(); var csproj = lib.CsProj(); csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1); - csproj.NumberOfProjectReferencesWithIncludeContaining(ValidRefCsproj).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojName).Should().Be(1); } [Fact] public void WhenRefWithNoCondAlreadyExistsItDoesntDuplicate() { var lib = NewLib(); + var setup = Setup(); new AddP2PCommand() - .WithWorkingDirectory(lib.Path) - .WithProject(lib.CsProjName) - .Execute($"\"{ValidRefPath}\"") + .WithWorkingDirectory(setup.TestRoot) + .WithProject(lib.CsProjPath) + .Execute($"\"{setup.ValidRefCsprojPath}\"") .Should().Pass(); int noCondBefore = lib.CsProj().NumberOfItemGroupsWithoutCondition(); var cmd = new AddP2PCommand() .WithWorkingDirectory(lib.Path) .WithProject(lib.CsProjName) - .Execute($"\"{ValidRefPath}\""); + .Execute($"\"{setup.ValidRefCsprojPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("already has a reference"); var csproj = lib.CsProj(); csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore); - csproj.NumberOfProjectReferencesWithIncludeContaining(ValidRefCsproj).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojName).Should().Be(1); } [Fact] public void WhenRefWithCondOnItemAlreadyExistsItDoesntDuplicate() { - string testRoot = Setup(); + var setup = Setup(); + var proj = new ProjDir(Path.Combine(setup.TestRoot, "WithExistingRefCondOnItem")); - string projDir = Path.Combine(testRoot, "WithExistingRefCondOnItem"); - string projName = Path.Combine(projDir, "WithExistingRefCondOnItem.csproj"); - string contentBefore = File.ReadAllText(projName); + string contentBefore = proj.CsProjContent(); var cmd = new AddP2PCommand() - .WithWorkingDirectory(projDir) - .WithProject(projName) - .Execute($"{FrameworkNet451Arg} \"{LibRefPath}\""); + .WithWorkingDirectory(proj.Path) + .WithProject(proj.CsProjPath) + .Execute($"{FrameworkNet451Arg} \"{setup.LibCsprojRelPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("already has a reference"); - File.ReadAllText(projName).Should().BeEquivalentTo(contentBefore); + proj.CsProjContent().Should().BeEquivalentTo(contentBefore); } [Fact] public void WhenRefWithCondOnItemGroupAlreadyExistsItDoesntDuplicate() { var lib = NewLib(); + var setup = Setup(); new AddP2PCommand() - .WithWorkingDirectory(lib.Path) - .WithProject(lib.CsProjName) - .Execute($"{FrameworkNet451Arg} \"{ValidRefPath}\"") + .WithWorkingDirectory(setup.TestRoot) + .WithProject(lib.CsProjPath) + .Execute($"{FrameworkNet451Arg} \"{setup.ValidRefCsprojPath}\"") .Should().Pass(); var csprojContentBefore = lib.CsProjContent(); var cmd = new AddP2PCommand() - .WithWorkingDirectory(lib.Path) - .WithProject(lib.CsProjName) - .Execute($"{FrameworkNet451Arg} \"{ValidRefPath}\""); + .WithWorkingDirectory(setup.TestRoot) + .WithProject(lib.CsProjPath) + .Execute($"{FrameworkNet451Arg} \"{setup.ValidRefCsprojPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("already has a reference"); lib.CsProjContent().Should().BeEquivalentTo(csprojContentBefore); @@ -318,173 +318,171 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests [Fact] public void WhenRefWithCondWithWhitespaceOnItemGroupExistsItDoesntDuplicate() { - string testRoot = Setup(); + var setup = Setup(); + var proj = new ProjDir(Path.Combine(setup.TestRoot, "WithExistingRefCondWhitespaces")); - string projDir = Path.Combine(testRoot, "WithExistingRefCondWhitespaces"); - string projName = Path.Combine(projDir, "WithExistingRefCondWhitespaces.csproj"); - string contentBefore = File.ReadAllText(projName); + string contentBefore = proj.CsProjContent(); var cmd = new AddP2PCommand() - .WithWorkingDirectory(projDir) - .WithProject(projName) - .Execute($"{FrameworkNet451Arg} \"{LibRefPath}\""); + .WithWorkingDirectory(proj.Path) + .WithProject(proj.CsProjName) + .Execute($"{FrameworkNet451Arg} \"{setup.LibCsprojRelPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("already has a reference"); - File.ReadAllText(projName).Should().BeEquivalentTo(contentBefore); + proj.CsProjContent().Should().BeEquivalentTo(contentBefore); } [Fact] public void WhenRefWithoutCondAlreadyExistsInNonUniformItemGroupItDoesntDuplicate() { - string testRoot = Setup(); + var setup = Setup(); + var proj = new ProjDir(Path.Combine(setup.TestRoot, "WithRefNoCondNonUniform")); - string projDir = Path.Combine(testRoot, "WithRefNoCondNonUniform"); - string projName = Path.Combine(projDir, "WithRefNoCondNonUniform.csproj"); - string contentBefore = File.ReadAllText(projName); + string contentBefore = proj.CsProjContent(); var cmd = new AddP2PCommand() - .WithWorkingDirectory(projDir) - .WithProject(projName) - .Execute($"\"{LibRefPath}\""); + .WithWorkingDirectory(proj.Path) + .WithProject(proj.CsProjName) + .Execute($"\"{setup.LibCsprojRelPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("already has a reference"); - File.ReadAllText(projName).Should().BeEquivalentTo(contentBefore); + proj.CsProjContent().Should().BeEquivalentTo(contentBefore); } [Fact] public void WhenRefWithoutCondAlreadyExistsInNonUniformItemGroupItAddsDifferentRefInDifferentGroup() { - string testRoot = Setup(); - - var proj = new ProjDir(Path.Combine(testRoot, "WithRefNoCondNonUniform")); + var setup = Setup(); + var proj = new ProjDir(Path.Combine(setup.TestRoot, "WithRefNoCondNonUniform")); int noCondBefore = proj.CsProj().NumberOfItemGroupsWithoutCondition(); var cmd = new AddP2PCommand() - .WithWorkingDirectory(proj.Path) + .WithWorkingDirectory(setup.TestRoot) .WithProject(proj.CsProjPath) - .Execute($"\"{ValidRefPath}\""); + .Execute($"\"{setup.ValidRefCsprojPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("added to the project"); var csproj = proj.CsProj(); csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1); - csproj.NumberOfProjectReferencesWithIncludeContaining(ValidRefPath).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojName).Should().Be(1); } [Fact] public void WhenRefWithCondAlreadyExistsInNonUniformItemGroupItDoesntDuplicate() { - string testRoot = Setup(); + var setup = Setup(); + var proj = new ProjDir(Path.Combine(setup.TestRoot, "WithRefCondNonUniform")); - string projDir = Path.Combine(testRoot, "WithRefCondNonUniform"); - string projName = Path.Combine(projDir, "WithRefCondNonUniform.csproj"); - string contentBefore = File.ReadAllText(projName); + string contentBefore = proj.CsProjContent(); var cmd = new AddP2PCommand() - .WithWorkingDirectory(projDir) - .WithProject(projName) - .Execute($"{FrameworkNet451Arg} \"{LibRefPath}\""); + .WithWorkingDirectory(proj.Path) + .WithProject(proj.CsProjName) + .Execute($"{FrameworkNet451Arg} \"{setup.LibCsprojRelPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("already has a reference"); - File.ReadAllText(projName).Should().BeEquivalentTo(contentBefore); + proj.CsProjContent().Should().BeEquivalentTo(contentBefore); } [Fact] public void WhenRefWithCondAlreadyExistsInNonUniformItemGroupItAddsDifferentRefInDifferentGroup() { - string testRoot = Setup(); - - var proj = new ProjDir(Path.Combine(testRoot, "WithRefCondNonUniform")); + var setup = Setup(); + var proj = new ProjDir(Path.Combine(setup.TestRoot, "WithRefCondNonUniform")); int condBefore = proj.CsProj().NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451); var cmd = new AddP2PCommand() - .WithWorkingDirectory(proj.Path) + .WithWorkingDirectory(setup.TestRoot) .WithProject(proj.CsProjPath) - .Execute($"{FrameworkNet451Arg} \"{ValidRefPath}\""); + .Execute($"{FrameworkNet451Arg} \"{setup.ValidRefCsprojPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("added to the project"); var csproj = proj.CsProj(); csproj.NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451).Should().Be(condBefore + 1); - csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(ValidRefPath, ConditionFrameworkNet451).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(setup.ValidRefCsprojName, ConditionFrameworkNet451).Should().Be(1); } [Fact] public void WhenEmptyItemGroupPresentItAddsRefInIt() { - string testRoot = Setup(); - - var proj = new ProjDir(Path.Combine(testRoot, "EmptyItemGroup")); + var setup = Setup(); + var proj = new ProjDir(Path.Combine(setup.TestRoot, "EmptyItemGroup")); int noCondBefore = proj.CsProj().NumberOfItemGroupsWithoutCondition(); var cmd = new AddP2PCommand() - .WithWorkingDirectory(proj.Path) + .WithWorkingDirectory(setup.TestRoot) .WithProject(proj.CsProjPath) - .Execute($"\"{ValidRefPath}\""); + .Execute($"\"{setup.ValidRefCsprojPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("added to the project"); var csproj = proj.CsProj(); csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore); - csproj.NumberOfProjectReferencesWithIncludeContaining(ValidRefPath).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojName).Should().Be(1); } [Fact] public void ItAddsMultipleRefsNoCondToTheSameItemGroup() { var lib = NewLib(); + var setup = Setup(); int noCondBefore = lib.CsProj().NumberOfItemGroupsWithoutCondition(); var cmd = new AddP2PCommand() - .WithWorkingDirectory(lib.Path) - .WithProject(lib.CsProjName) - .Execute($"\"{LibRefPath}\" \"{ValidRefPath}\""); + .WithWorkingDirectory(setup.TestRoot) + .WithProject(lib.CsProjPath) + .Execute($"\"{setup.LibCsprojPath}\" \"{setup.ValidRefCsprojPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("added to the project").And.NotContain("already has a reference"); var csproj = lib.CsProj(); csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1); - csproj.NumberOfProjectReferencesWithIncludeContaining(ValidRefCsproj).Should().Be(1); - csproj.NumberOfProjectReferencesWithIncludeContaining(LibRefPath).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojName).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeContaining(setup.LibCsprojName).Should().Be(1); } [Fact] public void ItAddsMultipleRefsWithCondToTheSameItemGroup() { var lib = NewLib(); + var setup = Setup(); int noCondBefore = lib.CsProj().NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451); var cmd = new AddP2PCommand() - .WithWorkingDirectory(lib.Path) - .WithProject(lib.CsProjName) - .Execute($"{FrameworkNet451Arg} \"{LibRefPath}\" \"{ValidRefPath}\""); + .WithWorkingDirectory(setup.TestRoot) + .WithProject(lib.CsProjPath) + .Execute($"{FrameworkNet451Arg} \"{setup.LibCsprojPath}\" \"{setup.ValidRefCsprojPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("added to the project").And.NotContain("already has a reference"); var csproj = lib.CsProj(); csproj.NumberOfItemGroupsWithConditionContaining(ConditionFrameworkNet451).Should().Be(noCondBefore + 1); - csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(ValidRefCsproj, ConditionFrameworkNet451).Should().Be(1); - csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(LibRefPath, ConditionFrameworkNet451).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(setup.ValidRefCsprojName, ConditionFrameworkNet451).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeAndConditionContaining(setup.LibCsprojName, ConditionFrameworkNet451).Should().Be(1); } [Fact] public void WhenProjectNameIsNotPassedItFindsItAndAddsReference() { var lib = NewLib(); + var setup = Setup(); int noCondBefore = lib.CsProj().NumberOfItemGroupsWithoutCondition(); var cmd = new AddP2PCommand() .WithWorkingDirectory(lib.Path) - .Execute($"\"{ValidRefPath}\""); + .Execute($"\"{setup.ValidRefCsprojPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("added to the project"); cmd.StdErr.Should().BeEmpty(); var csproj = lib.CsProj(); csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1); - csproj.NumberOfProjectReferencesWithIncludeContaining(ValidRefCsproj).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojName).Should().Be(1); } [Fact] public void ItAddsRefBetweenImports() { var lib = NewLib(); + var setup = Setup(); var cmd = new AddP2PCommand() .WithWorkingDirectory(lib.Path) .WithProject(lib.CsProjName) - .Execute($"\"{ValidRefPath}\""); + .Execute($"\"{setup.ValidRefCsprojPath}\""); cmd.Should().Pass(); cmd.StdOut.Should().Contain("added to the project"); cmd.StdErr.Should().BeEmpty(); @@ -503,7 +501,7 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests } break; case 1: - if (projRef != null && projRef.ItemType == "ProjectReference" && projRef.Include.Contains(ValidRefCsproj)) + if (projRef != null && projRef.ItemType == "ProjectReference" && projRef.Include.Contains(setup.ValidRefCsprojName)) { state++; } @@ -520,16 +518,36 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests state.Should().Be(3); } - [Fact(Skip = "Not finished")] + [Fact] public void WhenPassedReferenceDoesNotExistItShowsAnError() { - throw new NotImplementedException(); + var lib = NewLib(); + + var contentBefore = lib.CsProjContent(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute("\"IDoNotExist.csproj\""); + cmd.Should().Fail(); + cmd.StdErr.Should().Contain("does not exist"); + lib.CsProjContent().Should().BeEquivalentTo(contentBefore); } - [Fact(Skip = "Not finished")] + [Fact] public void WhenPassedMultipleRefsAndOneOfthemDoesNotExistItCancelsWholeOperation() { - throw new NotImplementedException(); + var lib = NewLib(); + var setup = Setup(); + + var contentBefore = lib.CsProjContent(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(setup.TestRoot) + .WithProject(lib.CsProjPath) + .Execute($"\"{setup.ValidRefCsprojPath}\" \"IDoNotExist.csproj\""); + cmd.Should().Fail(); + cmd.StdErr.Should().Contain("does not exist"); + cmd.StdErr.Should().NotMatchRegex("(.*does not exist.*){2,}"); + lib.CsProjContent().Should().BeEquivalentTo(contentBefore); } [Fact(Skip = "Not finished")] diff --git a/test/dotnet-add-p2p.Tests/TestSetup.cs b/test/dotnet-add-p2p.Tests/TestSetup.cs new file mode 100644 index 000000000..32703702d --- /dev/null +++ b/test/dotnet-add-p2p.Tests/TestSetup.cs @@ -0,0 +1,36 @@ +using Microsoft.DotNet.TestFramework; +using Microsoft.DotNet.Tools.Test.Utilities; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Microsoft.DotNet.Cli.Add.P2P.Tests +{ + internal class TestSetup + { + public const string TestGroup = "NonRestoredTestProjects"; + public const string ProjectName = "DotnetAddP2PProjects"; + + public string TestRoot { get; private set; } + + + private const string ValidRef = "ValidRef"; + public string ValidRefCsprojName => $"{ValidRef}.csproj"; + public string ValidRefCsprojPath => Path.Combine(TestRoot, ValidRef, ValidRefCsprojName); + public string ValidRefCsprojRelPath => Path.Combine("..", ValidRef, ValidRefCsprojName); + + + private const string Lib = "Lib"; + public string LibCsprojName => $"{Lib}.csproj"; + public string LibCsprojPath => Path.Combine(TestRoot, Lib, LibCsprojName); + public string LibCsprojRelPath => Path.Combine("..", Lib, LibCsprojName); + + + + public TestSetup(string testRoot) + { + TestRoot = testRoot; + } + } +} From 58fe57e315ac9ea0ae18dbda84eb47588b4ffdc9 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Tue, 22 Nov 2016 13:58:16 -0800 Subject: [PATCH 11/18] Lock on spec - all tests passing on Windows --- src/dotnet/commands/dotnet-add-p2p/Program.cs | 73 +++++++++++------ test/dotnet-add-p2p.Tests/Extensions.cs | 37 --------- .../dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs | 79 +++++++++++++++---- test/dotnet-add-p2p.Tests/TestSetup.cs | 10 +-- 4 files changed, 120 insertions(+), 79 deletions(-) diff --git a/src/dotnet/commands/dotnet-add-p2p/Program.cs b/src/dotnet/commands/dotnet-add-p2p/Program.cs index d08770d7e..ebd8d7d0d 100644 --- a/src/dotnet/commands/dotnet-add-p2p/Program.cs +++ b/src/dotnet/commands/dotnet-add-p2p/Program.cs @@ -9,6 +9,7 @@ using System.IO; using System.Linq; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; +using Microsoft.DotNet.Tools.Common; namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference { @@ -42,18 +43,29 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference CommandOption forceOption = app.Option( "--force", - "Add reference even if it does not exist", + "Add reference even if it does not exist, do not convert paths to relative", CommandOptionType.NoValue); app.OnExecute(() => { - if (projectArgument.Value == null) + if (string.IsNullOrEmpty(projectArgument.Value)) { throw new GracefulException("Argument is required."); } - ProjectRootElement project = File.Exists(projectArgument.Value) ? - GetProjectFromFileOrThrow(projectArgument.Value) : - GetProjectFromDirectoryOrThrow(projectArgument.Value); + ProjectRootElement project; + string projectDir; + if (File.Exists(projectArgument.Value)) + { + project = GetProjectFromFileOrThrow(projectArgument.Value); + projectDir = new FileInfo(projectArgument.Value).DirectoryName; + } + else + { + project = GetProjectFromDirectoryOrThrow(projectArgument.Value); + projectDir = projectArgument.Value; + } + + projectDir = PathUtility.EnsureTrailingSlash(projectDir); if (app.RemainingArguments.Count == 0) { @@ -63,22 +75,8 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference List references = app.RemainingArguments; if (!forceOption.HasValue()) { - var notExisting = new List(); - foreach (var r in references) - { - if (!File.Exists(r)) - { - notExisting.Add(r); - } - } - - if (notExisting.Count > 0) - { - throw new GracefulException( - string.Join( - Environment.NewLine, - notExisting.Select((ne) => $"Reference `{ne}` does not exist."))); - } + EnsureAllReferencesExist(references); + ConvertPathsToRelative(projectDir, ref references); } int numberOfAddedReferences = AddProjectToProjectReference( @@ -106,6 +104,32 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference } } + internal static void EnsureAllReferencesExist(List references) + { + var notExisting = new List(); + foreach (var r in references) + { + if (!File.Exists(r)) + { + notExisting.Add(r); + } + } + + if (notExisting.Count > 0) + { + throw new GracefulException( + string.Join( + Environment.NewLine, + notExisting.Select((ne) => $"Reference `{ne}` does not exist."))); + } + } + + internal static void ConvertPathsToRelative(string root, ref List references) + { + root = PathUtility.EnsureTrailingSlash(Path.GetFullPath(root)); + references = references.Select((r) => PathUtility.GetRelativePath(root, Path.GetFullPath(r))).ToList(); + } + // There is ProjectRootElement.TryOpen but it does not work as expected // I.e. it returns null for some valid projects internal static ProjectRootElement TryOpenProject(string filename) @@ -181,13 +205,18 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference return ret; } + private static string NormalizeSlashesForMsbuild(string path) + { + return path.Replace('/', '\\'); + } + internal static int AddProjectToProjectReference(ProjectRootElement root, string framework, IEnumerable refs) { int numberOfAddedReferences = 0; const string ProjectItemElementType = "ProjectReference"; ProjectItemGroupElement ig = null; - foreach (var @ref in refs) + foreach (var @ref in refs.Select((r) => NormalizeSlashesForMsbuild(r))) { if (root.HasExistingItemWithCondition(framework, @ref)) { diff --git a/test/dotnet-add-p2p.Tests/Extensions.cs b/test/dotnet-add-p2p.Tests/Extensions.cs index cbea6cca6..31070e853 100644 --- a/test/dotnet-add-p2p.Tests/Extensions.cs +++ b/test/dotnet-add-p2p.Tests/Extensions.cs @@ -11,43 +11,6 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests { internal static class Extensions { - //public static int CountOccurrances(this string s, string pattern) - //{ - // int ret = 0; - // for (int i = s.IndexOf(pattern); i != -1; i = s.IndexOf(pattern, i + 1)) - // { - // ret++; - // } - - // return ret; - //} - - //public static int NumberOfLinesWith(this string s, params string[] patterns) - //{ - // int ret = 0; - // string[] lines = s.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); - // foreach (var line in lines) - // { - // bool shouldCount = true; - - // foreach (var p in patterns) - // { - // if (!line.Contains(p)) - // { - // shouldCount = false; - // break; - // } - // } - - // if (shouldCount) - // { - // ret++; - // } - // } - - // return ret; - //} - public static int NumberOfItemGroupsWithConditionContaining(this ProjectRootElement root, string patternInCondition) { return root.ItemGroups.Count((ig) => ig.Condition.Contains(patternInCondition)); diff --git a/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs b/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs index e3155b059..e9cb2dc2c 100644 --- a/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs +++ b/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs @@ -64,7 +64,6 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests [InlineData("ihave?inv@lid/char\\acters")] public void WhenNonExistingProjectIsPassedItPrintsErrorAndUsage(string projName) { - string testRoot = NewDir().Path; var setup = Setup(); var cmd = new AddP2PCommand() @@ -99,7 +98,7 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests var cmd = new AddP2PCommand() .WithWorkingDirectory(Path.Combine(setup.TestRoot, "MoreThanOne")) - .Execute($"\"{setup.ValidRefCsprojRelPath}\""); + .Execute($"\"{setup.ValidRefCsprojRelToOtherProjPath}\""); cmd.ExitCode.Should().NotBe(0); cmd.StdErr.Should().Contain("more than one"); cmd.StdOut.Should().Contain("Usage"); @@ -550,28 +549,80 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests lib.CsProjContent().Should().BeEquivalentTo(contentBefore); } - [Fact(Skip = "Not finished")] + [Fact] public void WhenPassedReferenceDoesNotExistAndForceSwitchIsPassedItAddsIt() { - throw new NotImplementedException(); + var lib = NewLib(); + const string nonExisting = "IDoNotExist.csproj"; + + int noCondBefore = lib.CsProj().NumberOfItemGroupsWithoutCondition(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"--force \"{nonExisting}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("added to the project"); + cmd.StdErr.Should().BeEmpty(); + var csproj = lib.CsProj(); + csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1); + csproj.NumberOfProjectReferencesWithIncludeContaining(nonExisting).Should().Be(1); } - [Fact(Skip = "Not finished")] + [Fact] public void WhenPassedReferenceIsUsingSlashesItNormalizesItToBackslashes() { - throw new NotImplementedException(); + var lib = NewLib(); + var setup = Setup(); + + int noCondBefore = lib.CsProj().NumberOfItemGroupsWithoutCondition(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(lib.Path) + .WithProject(lib.CsProjName) + .Execute($"--force \"{setup.ValidRefCsprojPath.Replace('\\', '/')}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("added to the project"); + cmd.StdErr.Should().BeEmpty(); + var csproj = lib.CsProj(); + csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1); + csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojPath.Replace('/', '\\')).Should().Be(1); } - [Fact(Skip = "Not finished")] - public void WhenPassedRefIsUsingBackslashesItDoesntNormalizeIt() - { - throw new NotImplementedException(); - } - - [Fact(Skip = "Not finished")] + [Fact] public void WhenReferenceIsRelativeAndProjectIsNotInCurrentDirectoryReferencePathIsFixed() { - throw new NotImplementedException(); + var setup = Setup(); + var proj = new ProjDir(setup.LibDir); + + int noCondBefore = proj.CsProj().NumberOfItemGroupsWithoutCondition(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(setup.TestRoot) + .WithProject(setup.LibCsprojPath) + .Execute($"\"{setup.ValidRefCsprojRelPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("added to the project"); + cmd.StdErr.Should().BeEmpty(); + var csproj = proj.CsProj(); + csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1); + csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojRelToOtherProjPath).Should().Be(1); + } + + [Fact] + public void WhenReferenceIsRelativeAndProjectIsNotInCurrentDirectoryAndForceSwitchIsPassedItDoesNotChangeIt() + { + var setup = Setup(); + var proj = new ProjDir(setup.LibDir); + + int noCondBefore = proj.CsProj().NumberOfItemGroupsWithoutCondition(); + var cmd = new AddP2PCommand() + .WithWorkingDirectory(setup.TestRoot) + .WithProject(setup.LibCsprojPath) + .Execute($"--force \"{setup.ValidRefCsprojRelPath}\""); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("added to the project"); + cmd.StdErr.Should().BeEmpty(); + var csproj = proj.CsProj(); + csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1); + csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojRelPath).Should().Be(1); } } } \ No newline at end of file diff --git a/test/dotnet-add-p2p.Tests/TestSetup.cs b/test/dotnet-add-p2p.Tests/TestSetup.cs index 32703702d..be7dda378 100644 --- a/test/dotnet-add-p2p.Tests/TestSetup.cs +++ b/test/dotnet-add-p2p.Tests/TestSetup.cs @@ -14,20 +14,18 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests public string TestRoot { get; private set; } - private const string ValidRef = "ValidRef"; public string ValidRefCsprojName => $"{ValidRef}.csproj"; - public string ValidRefCsprojPath => Path.Combine(TestRoot, ValidRef, ValidRefCsprojName); - public string ValidRefCsprojRelPath => Path.Combine("..", ValidRef, ValidRefCsprojName); - + public string ValidRefCsprojRelPath => Path.Combine(ValidRef, ValidRefCsprojName); + public string ValidRefCsprojPath => Path.Combine(TestRoot, ValidRefCsprojRelPath); + public string ValidRefCsprojRelToOtherProjPath => Path.Combine("..", ValidRefCsprojRelPath); private const string Lib = "Lib"; + public string LibDir => Path.Combine(TestRoot, Lib); public string LibCsprojName => $"{Lib}.csproj"; public string LibCsprojPath => Path.Combine(TestRoot, Lib, LibCsprojName); public string LibCsprojRelPath => Path.Combine("..", Lib, LibCsprojName); - - public TestSetup(string testRoot) { TestRoot = testRoot; From b8b7d98256f54482523f929c23fc590624288a00 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Tue, 22 Nov 2016 14:41:56 -0800 Subject: [PATCH 12/18] last touches --- .../MsbuildProjectExtensions.cs | 2 +- src/dotnet/commands/dotnet-add-p2p/Program.cs | 5 ++--- src/dotnet/commands/dotnet-add/Program.cs | 10 ++++----- src/dotnet/dotnet.csproj | 9 +++++++- test/dotnet-add-p2p.Tests/Extensions.cs | 5 ++--- .../dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs | 21 ++++++++++--------- test/dotnet-add-p2p.Tests/ProjDir.cs | 7 ------- test/dotnet-add-p2p.Tests/TestSetup.cs | 8 +++---- 8 files changed, 32 insertions(+), 35 deletions(-) diff --git a/src/dotnet/commands/dotnet-add-p2p/MsbuildProjectExtensions.cs b/src/dotnet/commands/dotnet-add-p2p/MsbuildProjectExtensions.cs index 1e3bd9488..6385bd40a 100644 --- a/src/dotnet/commands/dotnet-add-p2p/MsbuildProjectExtensions.cs +++ b/src/dotnet/commands/dotnet-add-p2p/MsbuildProjectExtensions.cs @@ -46,7 +46,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference public static ProjectItemGroupElement FindExistingUniformItemGroupWithCondition(this ProjectRootElement root, string projectItemElementType, string framework) { - return root.ItemGroupsReversed.FirstOrDefault((ig) => ig.IsConditionalOnFramework(framework) && ig.IsUniformItemElementType(projectItemElementType)); + return root.ItemGroupsReversed.FirstOrDefault((itemGroup) => itemGroup.IsConditionalOnFramework(framework) && itemGroup.IsUniformItemElementType(projectItemElementType)); } public static bool IsUniformItemElementType(this ProjectItemGroupElement group, string projectItemElementType) diff --git a/src/dotnet/commands/dotnet-add-p2p/Program.cs b/src/dotnet/commands/dotnet-add-p2p/Program.cs index ebd8d7d0d..fa1fb36f2 100644 --- a/src/dotnet/commands/dotnet-add-p2p/Program.cs +++ b/src/dotnet/commands/dotnet-add-p2p/Program.cs @@ -215,7 +215,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference int numberOfAddedReferences = 0; const string ProjectItemElementType = "ProjectReference"; - ProjectItemGroupElement ig = null; + ProjectItemGroupElement itemGroup = root.FindUniformOrCreateItemGroupWithCondition(ProjectItemElementType, framework); foreach (var @ref in refs.Select((r) => NormalizeSlashesForMsbuild(r))) { if (root.HasExistingItemWithCondition(framework, @ref)) @@ -225,8 +225,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference } numberOfAddedReferences++; - ig = ig ?? root.FindUniformOrCreateItemGroupWithCondition(ProjectItemElementType, framework); - ig.AppendChild(root.CreateItemElement(ProjectItemElementType, @ref)); + itemGroup.AppendChild(root.CreateItemElement(ProjectItemElementType, @ref)); Reporter.Output.WriteLine($"Reference `{@ref}` added to the project."); } diff --git a/src/dotnet/commands/dotnet-add/Program.cs b/src/dotnet/commands/dotnet-add/Program.cs index 1e3b2b29a..2418fdb6e 100644 --- a/src/dotnet/commands/dotnet-add/Program.cs +++ b/src/dotnet/commands/dotnet-add/Program.cs @@ -18,17 +18,17 @@ namespace Microsoft.DotNet.Tools.Add { public const string HelpText = @".NET Add Command -Usage: dotnet add [options] [object] [[--] ...]] +Usage: dotnet add [options] [[--] ...]] + +Options: + -h|--help Show help information Arguments: The object of the operation. If a project file is not specified, it defaults to the current directory. Command to be executed on . -Options: - -h|--help Show help information - Args: - Any extra arguments passed to the command. + Any extra arguments passed to the command. Use `dotnet add --help` to get help about these arguments. Commands: p2p Add project to project (p2p) reference to a project"; diff --git a/src/dotnet/dotnet.csproj b/src/dotnet/dotnet.csproj index c5af004c3..3de77d4ca 100755 --- a/src/dotnet/dotnet.csproj +++ b/src/dotnet/dotnet.csproj @@ -1,5 +1,6 @@  + 1.0.0-preview4 netcoreapp1.0 @@ -10,11 +11,13 @@ true $(PackageTargetFallback);dotnet5.4 + + @@ -22,6 +25,7 @@ + 1.0.0-alpha-20161104-2 @@ -74,11 +78,14 @@ 1.0.1-beta-000933 + $(DefineConstants);NETCOREAPP1_0 + $(DefineConstants);RELEASE + - \ No newline at end of file + diff --git a/test/dotnet-add-p2p.Tests/Extensions.cs b/test/dotnet-add-p2p.Tests/Extensions.cs index 31070e853..218e1dbcc 100644 --- a/test/dotnet-add-p2p.Tests/Extensions.cs +++ b/test/dotnet-add-p2p.Tests/Extensions.cs @@ -2,9 +2,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Build.Construction; -using System; -using System.Linq; using Microsoft.DotNet.ProjectJsonMigration; +using System.Linq; using System.Collections.Generic; namespace Microsoft.DotNet.Cli.Add.P2P.Tests @@ -13,7 +12,7 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests { public static int NumberOfItemGroupsWithConditionContaining(this ProjectRootElement root, string patternInCondition) { - return root.ItemGroups.Count((ig) => ig.Condition.Contains(patternInCondition)); + return root.ItemGroups.Count((itemGroup) => itemGroup.Condition.Contains(patternInCondition)); } public static int NumberOfItemGroupsWithoutCondition(this ProjectRootElement root) diff --git a/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs b/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs index e9cb2dc2c..1d602f78c 100644 --- a/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs +++ b/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs @@ -1,12 +1,12 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; -using System.IO; -using Microsoft.DotNet.Tools.Test.Utilities; -using Xunit; using FluentAssertions; using Microsoft.Build.Construction; +using Microsoft.DotNet.Tools.Test.Utilities; +using System; +using System.IO; +using Xunit; namespace Microsoft.DotNet.Cli.Add.P2P.Tests { @@ -20,19 +20,21 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests private TestSetup Setup([System.Runtime.CompilerServices.CallerMemberName] string callingMethod = nameof(Setup), string identifier = "") { return new TestSetup( - GetTestGroupTestAssetsManager(TestSetup.TestGroup) - .CreateTestInstance(TestSetup.ProjectName, callingMethod: callingMethod, identifier: identifier) - .Path); + TestAssets.Get(TestSetup.TestGroup, TestSetup.ProjectName) + .CreateInstance() + .WithSourceFiles() + .Root + .FullName); } private ProjDir NewDir([System.Runtime.CompilerServices.CallerMemberName] string callingMethod = nameof(NewDir), string identifier = "") { - return new ProjDir(TestAssetsManager, callingMethod, identifier: identifier); + return new ProjDir(TestAssetsManager.CreateTestDirectory(callingMethod: callingMethod, identifier: identifier).Path); } private ProjDir NewLib([System.Runtime.CompilerServices.CallerMemberName] string callingMethod = nameof(NewDir), string identifier = "") { - var dir = new ProjDir(TestAssetsManager, callingMethod, identifier: identifier); + var dir = NewDir(callingMethod: callingMethod, identifier: identifier); try { @@ -76,7 +78,6 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests } [Fact] - public void WhenBrokenProjectIsPassedItPrintsErrorAndUsage() { string projName = "Broken/Broken.csproj"; diff --git a/test/dotnet-add-p2p.Tests/ProjDir.cs b/test/dotnet-add-p2p.Tests/ProjDir.cs index 2b568ae78..2f449d630 100644 --- a/test/dotnet-add-p2p.Tests/ProjDir.cs +++ b/test/dotnet-add-p2p.Tests/ProjDir.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.IO; -using Microsoft.DotNet.TestFramework; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; @@ -10,12 +9,6 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests { internal class ProjDir { - public ProjDir(TestAssetsManager tam, [System.Runtime.CompilerServices.CallerMemberName] string callingMethod = nameof(ProjDir), string identifier = "") - { - Path = tam.CreateTestDirectory(callingMethod: callingMethod, identifier: identifier).Path; - Name = new DirectoryInfo(Path).Name; - } - public ProjDir(string path) { Path = path; diff --git a/test/dotnet-add-p2p.Tests/TestSetup.cs b/test/dotnet-add-p2p.Tests/TestSetup.cs index be7dda378..7fe1e3cf8 100644 --- a/test/dotnet-add-p2p.Tests/TestSetup.cs +++ b/test/dotnet-add-p2p.Tests/TestSetup.cs @@ -1,9 +1,7 @@ -using Microsoft.DotNet.TestFramework; -using Microsoft.DotNet.Tools.Test.Utilities; -using System; -using System.Collections.Generic; +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + using System.IO; -using System.Text; namespace Microsoft.DotNet.Cli.Add.P2P.Tests { From 3fc0517cda8c9768416018e8e00d403abfc7bf58 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Tue, 22 Nov 2016 14:48:40 -0800 Subject: [PATCH 13/18] update dotnet --help text --- src/dotnet/commands/dotnet-help/HelpCommand.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/dotnet/commands/dotnet-help/HelpCommand.cs b/src/dotnet/commands/dotnet-help/HelpCommand.cs index e520ce827..5dadb6496 100644 --- a/src/dotnet/commands/dotnet-help/HelpCommand.cs +++ b/src/dotnet/commands/dotnet-help/HelpCommand.cs @@ -35,8 +35,10 @@ Commands: pack Creates a NuGet package migrate Migrates a project.json based project to a msbuild based project +Project modification commands: + add Add items to the project + Advanced Commands: - add Group of commands - run `dotnet add --help` for more information nuget Provides additional NuGet commands msbuild msbuilds a project and all of its dependencies vstest Runs tests from the specified files"; From 592af9dd616c4a0f44663622ba35bb5e3a62ad7c Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Wed, 23 Nov 2016 10:41:30 -0800 Subject: [PATCH 14/18] Extract error messages to separate class --- .../GracefulException.cs | 4 +++ src/dotnet/Strings.cs | 27 +++++++++++++++++++ src/dotnet/commands/dotnet-add-p2p/Program.cs | 27 +++++++++---------- src/dotnet/commands/dotnet-add/Program.cs | 4 +-- 4 files changed, 46 insertions(+), 16 deletions(-) create mode 100644 src/dotnet/Strings.cs diff --git a/src/Microsoft.DotNet.Cli.Utils/GracefulException.cs b/src/Microsoft.DotNet.Cli.Utils/GracefulException.cs index 4c061e4b7..817f8d03d 100644 --- a/src/Microsoft.DotNet.Cli.Utils/GracefulException.cs +++ b/src/Microsoft.DotNet.Cli.Utils/GracefulException.cs @@ -12,6 +12,10 @@ namespace Microsoft.DotNet.Cli.Utils { } + public GracefulException(string format, params string[] args) : this(string.Format(format, args)) + { + } + public GracefulException(string message, Exception innerException) : base(message, innerException) { } diff --git a/src/dotnet/Strings.cs b/src/dotnet/Strings.cs new file mode 100644 index 000000000..484da984b --- /dev/null +++ b/src/dotnet/Strings.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.DotNet.Tools +{ + internal static class Strings + { + // Arguments parsing + public const string RequiredArgumentNotPassed = "Required argument {0} was not passed."; + public const string RequiredArgumentIsInvalid = "Required argument {0} is invalid."; + + // Project + public const string CouldNotFindProjectOrDirectory = "Could not find project or directory `{0}`."; + public const string CouldNotFindAnyProjectInDirectory = "Could not find any project in `{0}`."; + public const string MoreThanOneProjectInDirectory = "Found more than one project in `{0}`. Please specify which one to use."; + public const string FoundInvalidProject = "Found a project `{0}` but it is invalid."; + public const string ProjectIsInvalid = "Invalid project `{0}`."; + public const string ProjectDoesNotExist = "Project `{0}` does not exist."; + + // Project Reference + public const string ProjectAlreadyHasAreference = "Project already has a reference to `{0}`."; + public const string ReferenceAddedToTheProject = "Reference `{0}` added to the project."; + public const string ReferenceDoesNotExist = "Reference `{0}` does not exist."; + public const string SpecifyAtLeastOneReference = "You must specify at least one reference to add."; + } +} diff --git a/src/dotnet/commands/dotnet-add-p2p/Program.cs b/src/dotnet/commands/dotnet-add-p2p/Program.cs index fa1fb36f2..cc2e50a6d 100644 --- a/src/dotnet/commands/dotnet-add-p2p/Program.cs +++ b/src/dotnet/commands/dotnet-add-p2p/Program.cs @@ -49,7 +49,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference app.OnExecute(() => { if (string.IsNullOrEmpty(projectArgument.Value)) { - throw new GracefulException("Argument is required."); + throw new GracefulException(Strings.RequiredArgumentNotPassed, ""); } ProjectRootElement project; @@ -69,7 +69,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference if (app.RemainingArguments.Count == 0) { - throw new GracefulException("You must specify at least one reference to add."); + throw new GracefulException(Strings.SpecifyAtLeastOneReference); } List references = app.RemainingArguments; @@ -120,7 +120,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference throw new GracefulException( string.Join( Environment.NewLine, - notExisting.Select((ne) => $"Reference `{ne}` does not exist."))); + notExisting.Select((r) => string.Format(Strings.ReferenceDoesNotExist, r)))); } } @@ -148,19 +148,18 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference { if (!File.Exists(filename)) { - throw new GracefulException($"Provided project `{filename}` does not exist."); + throw new GracefulException(Strings.ProjectDoesNotExist, filename); } var project = TryOpenProject(filename); if (project == null) { - throw new GracefulException($"Invalid project `{filename}`."); + throw new GracefulException(Strings.ProjectIsInvalid, filename); } return project; } - // TODO: improve errors internal static ProjectRootElement GetProjectFromDirectoryOrThrow(string directory) { DirectoryInfo dir; @@ -170,36 +169,36 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference } catch (ArgumentException) { - throw new GracefulException($"Could not find project or directory `{directory}`."); + throw new GracefulException(Strings.CouldNotFindProjectOrDirectory, directory); } if (!dir.Exists) { - throw new GracefulException($"Could not find project or directory `{directory}`."); + throw new GracefulException(Strings.CouldNotFindProjectOrDirectory, directory); } FileInfo[] files = dir.GetFiles("*proj"); if (files.Length == 0) { - throw new GracefulException($"Could not find any project in `{directory}`."); + throw new GracefulException(Strings.CouldNotFindAnyProjectInDirectory, directory); } if (files.Length > 1) { - throw new GracefulException("Found more than one project in the current directory. Please specify which one to use."); + throw new GracefulException(Strings.MoreThanOneProjectInDirectory, directory); } FileInfo projectFile = files.First(); if (!projectFile.Exists) { - throw new GracefulException($"Could not find any project in `{directory}`."); + throw new GracefulException(Strings.CouldNotFindAnyProjectInDirectory, directory); } var ret = TryOpenProject(projectFile.FullName); if (ret == null) { - throw new GracefulException($"Found a project `{projectFile.FullName}` but it is invalid."); + throw new GracefulException(Strings.FoundInvalidProject, projectFile.FullName); } return ret; @@ -220,14 +219,14 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference { if (root.HasExistingItemWithCondition(framework, @ref)) { - Reporter.Output.WriteLine($"Project already has a reference to `{@ref}`."); + Reporter.Output.WriteLine(string.Format(Strings.ProjectAlreadyHasAreference, @ref)); continue; } numberOfAddedReferences++; itemGroup.AppendChild(root.CreateItemElement(ProjectItemElementType, @ref)); - Reporter.Output.WriteLine($"Reference `{@ref}` added to the project."); + Reporter.Output.WriteLine(string.Format(Strings.ReferenceAddedToTheProject, @ref)); } return numberOfAddedReferences; diff --git a/src/dotnet/commands/dotnet-add/Program.cs b/src/dotnet/commands/dotnet-add/Program.cs index 2418fdb6e..f563c5ee4 100644 --- a/src/dotnet/commands/dotnet-add/Program.cs +++ b/src/dotnet/commands/dotnet-add/Program.cs @@ -58,7 +58,7 @@ Commands: } else if (args.Length == 1) { - Reporter.Error.WriteLine("Required argument was not passed.".Red()); + Reporter.Error.WriteLine(string.Format(Strings.RequiredArgumentNotPassed, "").Red()); Reporter.Output.WriteLine(HelpText); return 1; } @@ -76,7 +76,7 @@ Commands: return builtin(args); } - Reporter.Error.WriteLine("Required argument is invalid.".Red()); + Reporter.Error.WriteLine(string.Format(Strings.RequiredArgumentIsInvalid, "").Red()); Reporter.Output.WriteLine(HelpText); return 1; } From b22276a4a6651766873ee72e8413852c335f56d2 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Wed, 23 Nov 2016 11:03:39 -0800 Subject: [PATCH 15/18] add fake msbuild.exe to workaround the msbuild issue --- test/dotnet-add-p2p.Tests/MSBuild.exe | 1 + test/dotnet-add-p2p.Tests/MSBuild.exe.config | 1 + 2 files changed, 2 insertions(+) create mode 100644 test/dotnet-add-p2p.Tests/MSBuild.exe create mode 100644 test/dotnet-add-p2p.Tests/MSBuild.exe.config diff --git a/test/dotnet-add-p2p.Tests/MSBuild.exe b/test/dotnet-add-p2p.Tests/MSBuild.exe new file mode 100644 index 000000000..2b4d0f999 --- /dev/null +++ b/test/dotnet-add-p2p.Tests/MSBuild.exe @@ -0,0 +1 @@ +https://github.com/Microsoft/msbuild/issues/927 \ No newline at end of file diff --git a/test/dotnet-add-p2p.Tests/MSBuild.exe.config b/test/dotnet-add-p2p.Tests/MSBuild.exe.config new file mode 100644 index 000000000..2b4d0f999 --- /dev/null +++ b/test/dotnet-add-p2p.Tests/MSBuild.exe.config @@ -0,0 +1 @@ +https://github.com/Microsoft/msbuild/issues/927 \ No newline at end of file From 2bd020d015038506d0fe0fb0500a5b1c99298ac5 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Wed, 23 Nov 2016 13:46:44 -0800 Subject: [PATCH 16/18] attempt to fix broken linux build --- test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs b/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs index 1d602f78c..c8352f5c2 100644 --- a/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs +++ b/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs @@ -21,7 +21,7 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests { return new TestSetup( TestAssets.Get(TestSetup.TestGroup, TestSetup.ProjectName) - .CreateInstance() + .CreateInstance(callingMethod: callingMethod, identifier: identifier) .WithSourceFiles() .Root .FullName); From 93c30898e405b94460aa12f4e51159ee8f1c5457 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Wed, 23 Nov 2016 14:02:19 -0800 Subject: [PATCH 17/18] fix non-windows failures --- test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs b/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs index c8352f5c2..862a8483b 100644 --- a/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs +++ b/test/dotnet-add-p2p.Tests/GivenDotnetAddP2P.cs @@ -604,7 +604,7 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests cmd.StdErr.Should().BeEmpty(); var csproj = proj.CsProj(); csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1); - csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojRelToOtherProjPath).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojRelToOtherProjPath.Replace('/', '\\')).Should().Be(1); } [Fact] @@ -623,7 +623,7 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests cmd.StdErr.Should().BeEmpty(); var csproj = proj.CsProj(); csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1); - csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojRelPath).Should().Be(1); + csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojRelPath.Replace('/', '\\')).Should().Be(1); } } -} \ No newline at end of file +} From 8b565616a023cbc294c219b163a36d5a96ddaf1a Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Wed, 23 Nov 2016 15:35:01 -0800 Subject: [PATCH 18/18] Strings->LocalizableStrings, Extensions->ProjectRootElementExtensions, fix long lines, remove empty line --- .../{Strings.cs => LocalizableStrings.cs} | 2 +- src/dotnet/commands/dotnet-add-p2p/Program.cs | 26 +++++++-------- src/dotnet/commands/dotnet-add/Program.cs | 4 +-- .../Commands/AddP2PCommand.cs | 1 - ...ons.cs => ProjectRootElementExtensions.cs} | 32 +++++++++++++++---- 5 files changed, 41 insertions(+), 24 deletions(-) rename src/dotnet/{Strings.cs => LocalizableStrings.cs} (96%) rename test/dotnet-add-p2p.Tests/{Extensions.cs => ProjectRootElementExtensions.cs} (66%) diff --git a/src/dotnet/Strings.cs b/src/dotnet/LocalizableStrings.cs similarity index 96% rename from src/dotnet/Strings.cs rename to src/dotnet/LocalizableStrings.cs index 484da984b..64ec79d3f 100644 --- a/src/dotnet/Strings.cs +++ b/src/dotnet/LocalizableStrings.cs @@ -4,7 +4,7 @@ using System.Text; namespace Microsoft.DotNet.Tools { - internal static class Strings + internal static class LocalizableStrings { // Arguments parsing public const string RequiredArgumentNotPassed = "Required argument {0} was not passed."; diff --git a/src/dotnet/commands/dotnet-add-p2p/Program.cs b/src/dotnet/commands/dotnet-add-p2p/Program.cs index cc2e50a6d..6a724620d 100644 --- a/src/dotnet/commands/dotnet-add-p2p/Program.cs +++ b/src/dotnet/commands/dotnet-add-p2p/Program.cs @@ -49,7 +49,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference app.OnExecute(() => { if (string.IsNullOrEmpty(projectArgument.Value)) { - throw new GracefulException(Strings.RequiredArgumentNotPassed, ""); + throw new GracefulException(LocalizableStrings.RequiredArgumentNotPassed, ""); } ProjectRootElement project; @@ -69,7 +69,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference if (app.RemainingArguments.Count == 0) { - throw new GracefulException(Strings.SpecifyAtLeastOneReference); + throw new GracefulException(LocalizableStrings.SpecifyAtLeastOneReference); } List references = app.RemainingArguments; @@ -120,7 +120,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference throw new GracefulException( string.Join( Environment.NewLine, - notExisting.Select((r) => string.Format(Strings.ReferenceDoesNotExist, r)))); + notExisting.Select((r) => string.Format(LocalizableStrings.ReferenceDoesNotExist, r)))); } } @@ -148,13 +148,13 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference { if (!File.Exists(filename)) { - throw new GracefulException(Strings.ProjectDoesNotExist, filename); + throw new GracefulException(LocalizableStrings.ProjectDoesNotExist, filename); } var project = TryOpenProject(filename); if (project == null) { - throw new GracefulException(Strings.ProjectIsInvalid, filename); + throw new GracefulException(LocalizableStrings.ProjectIsInvalid, filename); } return project; @@ -169,36 +169,36 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference } catch (ArgumentException) { - throw new GracefulException(Strings.CouldNotFindProjectOrDirectory, directory); + throw new GracefulException(LocalizableStrings.CouldNotFindProjectOrDirectory, directory); } if (!dir.Exists) { - throw new GracefulException(Strings.CouldNotFindProjectOrDirectory, directory); + throw new GracefulException(LocalizableStrings.CouldNotFindProjectOrDirectory, directory); } FileInfo[] files = dir.GetFiles("*proj"); if (files.Length == 0) { - throw new GracefulException(Strings.CouldNotFindAnyProjectInDirectory, directory); + throw new GracefulException(LocalizableStrings.CouldNotFindAnyProjectInDirectory, directory); } if (files.Length > 1) { - throw new GracefulException(Strings.MoreThanOneProjectInDirectory, directory); + throw new GracefulException(LocalizableStrings.MoreThanOneProjectInDirectory, directory); } FileInfo projectFile = files.First(); if (!projectFile.Exists) { - throw new GracefulException(Strings.CouldNotFindAnyProjectInDirectory, directory); + throw new GracefulException(LocalizableStrings.CouldNotFindAnyProjectInDirectory, directory); } var ret = TryOpenProject(projectFile.FullName); if (ret == null) { - throw new GracefulException(Strings.FoundInvalidProject, projectFile.FullName); + throw new GracefulException(LocalizableStrings.FoundInvalidProject, projectFile.FullName); } return ret; @@ -219,14 +219,14 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference { if (root.HasExistingItemWithCondition(framework, @ref)) { - Reporter.Output.WriteLine(string.Format(Strings.ProjectAlreadyHasAreference, @ref)); + Reporter.Output.WriteLine(string.Format(LocalizableStrings.ProjectAlreadyHasAreference, @ref)); continue; } numberOfAddedReferences++; itemGroup.AppendChild(root.CreateItemElement(ProjectItemElementType, @ref)); - Reporter.Output.WriteLine(string.Format(Strings.ReferenceAddedToTheProject, @ref)); + Reporter.Output.WriteLine(string.Format(LocalizableStrings.ReferenceAddedToTheProject, @ref)); } return numberOfAddedReferences; diff --git a/src/dotnet/commands/dotnet-add/Program.cs b/src/dotnet/commands/dotnet-add/Program.cs index f563c5ee4..78cb27b6b 100644 --- a/src/dotnet/commands/dotnet-add/Program.cs +++ b/src/dotnet/commands/dotnet-add/Program.cs @@ -58,7 +58,7 @@ Commands: } else if (args.Length == 1) { - Reporter.Error.WriteLine(string.Format(Strings.RequiredArgumentNotPassed, "").Red()); + Reporter.Error.WriteLine(string.Format(LocalizableStrings.RequiredArgumentNotPassed, "").Red()); Reporter.Output.WriteLine(HelpText); return 1; } @@ -76,7 +76,7 @@ Commands: return builtin(args); } - Reporter.Error.WriteLine(string.Format(Strings.RequiredArgumentIsInvalid, "").Red()); + Reporter.Error.WriteLine(string.Format(LocalizableStrings.RequiredArgumentIsInvalid, "").Red()); Reporter.Output.WriteLine(HelpText); return 1; } diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/AddP2PCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/AddP2PCommand.cs index 9a7f2b517..f6d49a7f8 100644 --- a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/AddP2PCommand.cs +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/AddP2PCommand.cs @@ -12,7 +12,6 @@ namespace Microsoft.DotNet.Tools.Test.Utilities public AddP2PCommand() : base("dotnet") { - } public override CommandResult Execute(string args = "") diff --git a/test/dotnet-add-p2p.Tests/Extensions.cs b/test/dotnet-add-p2p.Tests/ProjectRootElementExtensions.cs similarity index 66% rename from test/dotnet-add-p2p.Tests/Extensions.cs rename to test/dotnet-add-p2p.Tests/ProjectRootElementExtensions.cs index 218e1dbcc..e53609c73 100644 --- a/test/dotnet-add-p2p.Tests/Extensions.cs +++ b/test/dotnet-add-p2p.Tests/ProjectRootElementExtensions.cs @@ -8,9 +8,11 @@ using System.Collections.Generic; namespace Microsoft.DotNet.Cli.Add.P2P.Tests { - internal static class Extensions + internal static class ProjectRootElementExtensions { - public static int NumberOfItemGroupsWithConditionContaining(this ProjectRootElement root, string patternInCondition) + public static int NumberOfItemGroupsWithConditionContaining( + this ProjectRootElement root, + string patternInCondition) { return root.ItemGroups.Count((itemGroup) => itemGroup.Condition.Contains(patternInCondition)); } @@ -20,7 +22,11 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests return root.ItemGroups.Count((ig) => string.IsNullOrEmpty(ig.Condition)); } - public static IEnumerable ItemsWithIncludeAndConditionContaining(this ProjectRootElement root, string itemType, string includePattern, string patternInCondition) + public static IEnumerable ItemsWithIncludeAndConditionContaining( + this ProjectRootElement root, + string itemType, + string includePattern, + string patternInCondition) { return root.Items.Where((it) => { @@ -34,17 +40,29 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests }); } - public static int NumberOfProjectReferencesWithIncludeAndConditionContaining(this ProjectRootElement root, string includePattern, string patternInCondition) + public static int NumberOfProjectReferencesWithIncludeAndConditionContaining( + this ProjectRootElement root, + string includePattern, + string patternInCondition) { - return root.ItemsWithIncludeAndConditionContaining("ProjectReference", includePattern, patternInCondition).Count(); + return root.ItemsWithIncludeAndConditionContaining( + "ProjectReference", + includePattern, + patternInCondition) + .Count(); } - public static IEnumerable ItemsWithIncludeContaining(this ProjectRootElement root, string itemType, string includePattern) + public static IEnumerable ItemsWithIncludeContaining( + this ProjectRootElement root, + string itemType, + string includePattern) { return root.Items.Where((it) => it.ItemType == itemType && it.Include.Contains(includePattern)); } - public static int NumberOfProjectReferencesWithIncludeContaining(this ProjectRootElement root, string includePattern) + public static int NumberOfProjectReferencesWithIncludeContaining( + this ProjectRootElement root, + string includePattern) { return root.ItemsWithIncludeContaining("ProjectReference", includePattern).Count(); }