test project - almost finished - not all review changes included

This commit is contained in:
Krzysztof Wicher 2016-11-16 15:49:25 -08:00
parent 8416085b3a
commit 5d1deeeea1
36 changed files with 1119 additions and 210 deletions

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFrameworks>net451;netcoreapp1.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
<EmbeddedResource Include="**\*.resx" />
<PackageReference Include="Microsoft.NET.Sdk">
<Version>1.0.0-alpha-20161104-2</Version>
<PrivateAssets>All</PrivateAssets>
<!-- intentonally broken -->

View file

@ -0,0 +1,3 @@
public class EmptyItemGroup
{
}

View file

@ -18,12 +18,8 @@
<Version>1.0.1</Version> <Version>1.0.1</Version>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' "> <ItemGroup>
<DefineConstants>$(DefineConstants);NETCOREAPP1_0</DefineConstants> <!-- Existing but empty item group - do not remove -->
</PropertyGroup> </ItemGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net451' ">
<DefineConstants>$(DefineConstants);NET451</DefineConstants>
</PropertyGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View file

@ -0,0 +1,4 @@
// Dummy reference - it can be used as an existing reference in any of the projects
public class Lib
{
}

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFrameworks>net451;netcoreapp1.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
<EmbeddedResource Include="**\*.resx" />
<PackageReference Include="Microsoft.NET.Sdk">
<Version>1.0.0-alpha-20161104-2</Version>
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.0.1</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -0,0 +1,8 @@
using System;
namespace SomeNS
{
public class SomeClass
{
}
}

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFrameworks>netcoreapp1.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
<EmbeddedResource Include="**\*.resx" />
<PackageReference Include="Microsoft.NET.Sdk">
<Version>1.0.0-alpha-20161104-2</Version>
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.0.1</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFrameworks>netcoreapp1.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
<EmbeddedResource Include="**\*.resx" />
<PackageReference Include="Microsoft.NET.Sdk">
<Version>1.0.0-alpha-20161104-2</Version>
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.0.1</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -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
{
}

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFrameworks>net451;netcoreapp1.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
<EmbeddedResource Include="**\*.resx" />
<PackageReference Include="Microsoft.NET.Sdk">
<Version>1.0.0-alpha-20161104-2</Version>
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.0.1</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,3 @@
public class WithExistingRefCondOnItem
{
}

View file

@ -19,18 +19,7 @@
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<!-- Spaces in the condition are intentional --> <ProjectReference Condition=" '$(TargetFramework)' == 'net451' " Include="..\Lib\Lib.csproj" />
<ProjectReference Condition=" '$(TargetFramework)' == 'netcoreapp1.0' " Include="../Lib4/Lib4.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Condition="'$(TargetFramework)' == 'net451'" Include="../Lib4/Lib4.csproj" />
</ItemGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">
<DefineConstants>$(DefineConstants);NETCOREAPP1_0</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net451' ">
<DefineConstants>$(DefineConstants);NET451</DefineConstants>
</PropertyGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View file

@ -0,0 +1,3 @@
public class WithExistingRefCondWhitespaces
{
}

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFrameworks>net451;netcoreapp1.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
<EmbeddedResource Include="**\*.resx" />
<PackageReference Include="Microsoft.NET.Sdk">
<Version>1.0.0-alpha-20161104-2</Version>
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.0.1</Version>
</PackageReference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net451'" >
<ProjectReference Include="..\Lib\Lib.csproj" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,3 @@
public class WithRefCondNonUniform
{
}

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFrameworks>net451;netcoreapp1.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
<EmbeddedResource Include="**\*.resx" />
<PackageReference Include="Microsoft.NET.Sdk">
<Version>1.0.0-alpha-20161104-2</Version>
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.0.1</Version>
</PackageReference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">
<SomeItemCausingThisGroupToNotBeUniform Include="foo" />
<ProjectReference Include="..\Lib\Lib.csproj" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,3 @@
public class WithRefNoCondNonUniform
{
}

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFrameworks>net451;netcoreapp1.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
<EmbeddedResource Include="**\*.resx" />
<PackageReference Include="Microsoft.NET.Sdk">
<Version>1.0.0-alpha-20161104-2</Version>
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.0.1</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<SomeItemCausingThisGroupToNotBeUniform Include="foo" />
<ProjectReference Include="..\Lib\Lib.csproj" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -27,6 +27,5 @@
<PropertyGroup Condition=" '$(TargetFramework)' == 'net451' "> <PropertyGroup Condition=" '$(TargetFramework)' == 'net451' ">
<DefineConstants>$(DefineConstants);NET451</DefineConstants> <DefineConstants>$(DefineConstants);NET451</DefineConstants>
</PropertyGroup> </PropertyGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View file

@ -24,6 +24,5 @@
<PropertyGroup Condition=" '$(TargetFramework)' == 'net451' "> <PropertyGroup Condition=" '$(TargetFramework)' == 'net451' ">
<DefineConstants>$(DefineConstants);NET451</DefineConstants> <DefineConstants>$(DefineConstants);NET451</DefineConstants>
</PropertyGroup> </PropertyGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View file

@ -27,6 +27,5 @@
<PropertyGroup Condition=" '$(TargetFramework)' == 'net451' "> <PropertyGroup Condition=" '$(TargetFramework)' == 'net451' ">
<DefineConstants>$(DefineConstants);NET451</DefineConstants> <DefineConstants>$(DefineConstants);NET451</DefineConstants>
</PropertyGroup> </PropertyGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View file

@ -27,14 +27,18 @@ namespace Microsoft.DotNet.ProjectJsonMigration
// Different includes // Different includes
if (item.IntersectIncludes(otherItem).Count() != item.Includes().Count()) if (item.IntersectIncludes(otherItem).Count() != item.Includes().Count())
{ {
#if !DISABLE_TRACE
MigrationTrace.Instance.WriteLine($"{nameof(MSBuildExtensions)}.{nameof(IsEquivalentTo)} includes not equivalent."); MigrationTrace.Instance.WriteLine($"{nameof(MSBuildExtensions)}.{nameof(IsEquivalentTo)} includes not equivalent.");
#endif
return false; return false;
} }
// Different Excludes // Different Excludes
if (item.IntersectExcludes(otherItem).Count() != item.Excludes().Count()) if (item.IntersectExcludes(otherItem).Count() != item.Excludes().Count())
{ {
#if !DISABLE_TRACE
MigrationTrace.Instance.WriteLine($"{nameof(MSBuildExtensions)}.{nameof(IsEquivalentTo)} excludes not equivalent."); MigrationTrace.Instance.WriteLine($"{nameof(MSBuildExtensions)}.{nameof(IsEquivalentTo)} excludes not equivalent.");
#endif
return false; return false;
} }
@ -46,7 +50,9 @@ namespace Microsoft.DotNet.ProjectJsonMigration
// Different remove // Different remove
if (item.Remove != otherItem.Remove) if (item.Remove != otherItem.Remove)
{ {
#if !DISABLE_TRACE
MigrationTrace.Instance.WriteLine($"{nameof(MSBuildExtensions)}.{nameof(IsEquivalentTo)} removes not equivalent."); MigrationTrace.Instance.WriteLine($"{nameof(MSBuildExtensions)}.{nameof(IsEquivalentTo)} removes not equivalent.");
#endif
return false; return false;
} }
@ -61,13 +67,17 @@ namespace Microsoft.DotNet.ProjectJsonMigration
var otherMetadata = itemToCompare.GetMetadataWithName(metadata.Name); var otherMetadata = itemToCompare.GetMetadataWithName(metadata.Name);
if (otherMetadata == null) if (otherMetadata == null)
{ {
#if !DISABLE_TRACE
MigrationTrace.Instance.WriteLine($"{nameof(MSBuildExtensions)}.{nameof(IsEquivalentTo)} metadata doesn't exist {{ {metadata.Name} {metadata.Value} }}"); MigrationTrace.Instance.WriteLine($"{nameof(MSBuildExtensions)}.{nameof(IsEquivalentTo)} metadata doesn't exist {{ {metadata.Name} {metadata.Value} }}");
#endif
return false; return false;
} }
if (!metadata.ValueEquals(otherMetadata)) if (!metadata.ValueEquals(otherMetadata))
{ {
#if !DISABLE_TRACE
MigrationTrace.Instance.WriteLine($"{nameof(MSBuildExtensions)}.{nameof(IsEquivalentTo)} metadata has another value {{ {metadata.Name} {metadata.Value} {otherMetadata.Value} }}"); MigrationTrace.Instance.WriteLine($"{nameof(MSBuildExtensions)}.{nameof(IsEquivalentTo)} metadata has another value {{ {metadata.Name} {metadata.Value} {otherMetadata.Value} }}");
#endif
return false; return false;
} }
} }
@ -197,7 +207,9 @@ namespace Microsoft.DotNet.ProjectJsonMigration
if (existingMetadata == default(ProjectMetadataElement)) if (existingMetadata == default(ProjectMetadataElement))
{ {
#if !DISABLE_TRACE
MigrationTrace.Instance.WriteLine($"{nameof(AddMetadata)}: Adding metadata to {item.ItemType} item: {{ {metadata.Name}, {metadata.Value} }}"); MigrationTrace.Instance.WriteLine($"{nameof(AddMetadata)}: Adding metadata to {item.ItemType} item: {{ {metadata.Name}, {metadata.Value} }}");
#endif
item.AddMetadata(metadata.Name, metadata.Value); item.AddMetadata(metadata.Name, metadata.Value);
} }
} }

View file

@ -3,12 +3,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Configurer; using Microsoft.DotNet.Configurer;
using Microsoft.DotNet.PlatformAbstractions; using Microsoft.DotNet.PlatformAbstractions;
using Microsoft.DotNet.Tools.Add;
using Microsoft.DotNet.Tools.Build; using Microsoft.DotNet.Tools.Build;
using Microsoft.DotNet.Tools.Clean; using Microsoft.DotNet.Tools.Clean;
using Microsoft.DotNet.Tools.Help; using Microsoft.DotNet.Tools.Help;
@ -24,7 +24,6 @@ using Microsoft.DotNet.Tools.Run;
using Microsoft.DotNet.Tools.Test; using Microsoft.DotNet.Tools.Test;
using Microsoft.DotNet.Tools.VSTest; using Microsoft.DotNet.Tools.VSTest;
using NuGet.Frameworks; using NuGet.Frameworks;
using Microsoft.DotNet.Tools.Add;
namespace Microsoft.DotNet.Cli namespace Microsoft.DotNet.Cli
{ {
@ -32,6 +31,7 @@ namespace Microsoft.DotNet.Cli
{ {
private static Dictionary<string, Func<string[], int>> s_builtIns = new Dictionary<string, Func<string[], int>> private static Dictionary<string, Func<string[], int>> s_builtIns = new Dictionary<string, Func<string[], int>>
{ {
["add"] = AddCommand.Run,
["build"] = BuildCommand.Run, ["build"] = BuildCommand.Run,
["clean"] = CleanCommand.Run, ["clean"] = CleanCommand.Run,
["help"] = HelpCommand.Run, ["help"] = HelpCommand.Run,
@ -46,7 +46,6 @@ namespace Microsoft.DotNet.Cli
["run"] = RunCommand.Run, ["run"] = RunCommand.Run,
["test"] = TestCommand.Run, ["test"] = TestCommand.Run,
["vstest"] = VSTestCommand.Run, ["vstest"] = VSTestCommand.Run,
["add"] = AddCommand.Run,
}; };
public static int Main(string[] args) public static int Main(string[] args)

View file

@ -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<ProjectItemElement> 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;
}
}
}

View file

@ -8,7 +8,7 @@ using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Microsoft.Build.Construction; using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectJsonMigration; using Microsoft.Build.Evaluation;
namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference
{ {
@ -22,31 +22,40 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference
{ {
Name = "dotnet add p2p", Name = "dotnet add p2p",
FullName = ".NET Add Project to Project (p2p) reference Command", 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"); app.HelpOption("-h|--help");
CommandArgument projectArgument = app.Argument("<PROJECT>", CommandArgument projectArgument = app.Argument("<PROJECT>",
"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."); " 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 <FRAMEWORK>", "Add reference only when targetting a specific framework", CommandOptionType.SingleValue); CommandOption frameworkOption = app.Option("-f|--framework <FRAMEWORK>", "Add reference only when targetting a specific framework", CommandOptionType.SingleValue);
CommandOption referenceOption = app.Option("-r|--reference <REFERENCE>", "Add project to project <REFERENCE> to <PROJECT>", CommandOptionType.MultipleValue);
app.OnExecute(() => { app.OnExecute(() => {
ProjectRootElement project = projectArgument.Value != null ? if (projectArgument.Value == null)
GetProjectFromFileOrThrow(projectArgument.Value) : {
GetProjectFromCurrentDirectoryOrThrow(); throw new GracefulException("Argument <Project> 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."); 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; return 0;
}); });
@ -57,7 +66,8 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference
} }
catch (GracefulException e) catch (GracefulException e)
{ {
Reporter.Error.WriteLine(e.Message); Reporter.Error.WriteLine(e.Message.Red());
app.ShowHelp();
return 1; return 1;
} }
} }
@ -68,7 +78,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference
{ {
try try
{ {
return ProjectRootElement.Open(filename); return ProjectRootElement.Open(filename, new ProjectCollection(), preserveFormatting: true);
} }
catch (Microsoft.Build.Exceptions.InvalidProjectFileException) catch (Microsoft.Build.Exceptions.InvalidProjectFileException)
{ {
@ -86,19 +96,34 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference
var project = TryOpenProject(filename); var project = TryOpenProject(filename);
if (project == null) if (project == null)
{ {
throw new GracefulException($"Invalid MSBuild project `{filename}`."); throw new GracefulException($"Invalid project `{filename}`.");
} }
return project; return project;
} }
public static ProjectRootElement GetProjectFromCurrentDirectoryOrThrow() // TODO: improve errors
public static ProjectRootElement GetProjectFromDirectoryOrThrow(string directory)
{ {
DirectoryInfo currDir = new DirectoryInfo(Directory.GetCurrentDirectory()); DirectoryInfo dir;
FileInfo[] files = currDir.GetFiles("*proj"); 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) 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) if (files.Length > 1)
@ -110,123 +135,40 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference
if (!projectFile.Exists) 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); var ret = TryOpenProject(projectFile.FullName);
if (ret == null) 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; return ret;
} }
public static Func<T, bool> AndPred<T>(params Func<T, bool>[] preds) public static int AddProjectToProjectReference(ProjectRootElement root, string framework, IEnumerable<string> refs)
{
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<T, bool> FrameworkPred<T>(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<ProjectItemGroupElement, bool> UniformItemElementTypePred(string projectItemElementType)
{
return (ig) => ig.Items.All((it) => it.ItemType == projectItemElementType);
}
public static Func<ProjectItemElement, bool> IncludePred(string include)
{
return (it) => it.Include == include;
}
public static ProjectItemElement[] FindExistingItemsWithCondition(ProjectRootElement root, string framework, string include)
{
return root.Items
.Where(
AndPred(
FrameworkPred<ProjectItemElement>(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<ProjectItemGroupElement>(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<string> refs)
{ {
int numberOfAddedReferences = 0;
const string ProjectItemElementType = "ProjectReference"; const string ProjectItemElementType = "ProjectReference";
ProjectItemGroupElement ig = null; ProjectItemGroupElement ig = null;
foreach (var @ref in refs) 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; continue;
} }
ig = ig ?? FindUniformOrCreateItemGroupWithCondition(root, ProjectItemElementType, framework); numberOfAddedReferences++;
ig = ig ?? root.FindUniformOrCreateItemGroupWithCondition(ProjectItemElementType, framework);
ig.AppendChild(root.CreateItemElement(ProjectItemElementType, @ref)); 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;
} }
} }
} }

View file

@ -16,15 +16,22 @@ namespace Microsoft.DotNet.Tools.Add
{ {
public class AddCommand public class AddCommand
{ {
public const string CommandName = "dotnet-add"; public const string HelpText = @".NET Add Command
public const string UsageText = @"Usage: dotnet add [command] [arguments]
Usage: dotnet add [options] [object] <command> [[--] <arg>...]]
Arguments: Arguments:
[command] The command to execute <object> The object of the operation. If a project file is not specified, it defaults to the current directory.
[arguments] Arguments to pass to the command <command> Command to be executed on <object>.
Options:
-h|--help Show help information
Args:
Any extra arguments passed to the command.
Commands: Commands:
p2p Add project to project (p2p) reference to a project"; p2p Add project to project (p2p) reference to a project";
private static Dictionary<string, Func<string[], int>> s_builtIns = new Dictionary<string, Func<string[], int>> private static Dictionary<string, Func<string[], int>> s_builtIns = new Dictionary<string, Func<string[], int>>
{ {
@ -37,33 +44,57 @@ Commands:
if (args.Length == 0 || args[0] == "--help" || args[0] == "-h") if (args.Length == 0 || args[0] == "--help" || args[0] == "-h")
{ {
Reporter.Output.WriteLine(UsageText); Reporter.Output.WriteLine(HelpText);
return 1; return 0;
} }
if (args[0].StartsWith("-")) string commandObject;
string command;
if (IsValidCommandName(args[0]))
{ {
Reporter.Error.WriteLine($"Unknown option: {args[0]}"); command = args[0];
Reporter.Output.WriteLine(UsageText); commandObject = GetCurrentDirectoryWithDirSeparator();
return 1; args = args.Skip(1).Prepend(commandObject).ToArray();
} }
else if (args.Length == 1)
string command = args[0];
Func<string[], int> builtIn;
args = args.Skip(1).ToArray();
if (s_builtIns.TryGetValue(command, out builtIn))
{ {
return builtIn(args); Reporter.Error.WriteLine("Required argument <command> was not passed.".Red());
Reporter.Output.WriteLine(HelpText);
return 1;
} }
else else
{ {
CommandResult result = Command.Create( commandObject = args[0];
$"{CommandName}-{command}", command = args[1];
args,
FrameworkConstants.CommonFrameworks.NetStandardApp15) args = args.Skip(2).Prepend(commandObject).ToArray();
.Execute();
return result.ExitCode;
} }
Func<string[], int> builtin;
if (s_builtIns.TryGetValue(command, out builtin))
{
return builtin(args);
}
Reporter.Error.WriteLine("Required argument <command> 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;
} }
} }
} }

View file

@ -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;
}
}
}

View file

@ -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<ProjectElement> 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<ProjectElement> 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();
}
}
}

View file

@ -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();
}
}
}

View file

@ -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());
}
}
}

View file

@ -0,0 +1,63 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<TargetFramework>netcoreapp1.0</TargetFramework>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<AssemblyName>dotnet-add-p2p.Tests</AssemblyName>
<PackageTargetFallback Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">$(PackageTargetFallback);dotnet5.4;portable-net451+win8</PackageTargetFallback>
<DefineConstants>$(DefineConstants);DISABLE_TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
<Compile Include="..\..\src\Microsoft.DotNet.ProjectJsonMigration\MSBuildExtensions.cs">
<Link>src\Microsoft.DotNet.ProjectJsonMigration\MSBuildExtensions.cs</Link>
</Compile>
<EmbeddedResource Include="**\*.resx" />
<EmbeddedResource Include="compiler\resources\**\*" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.DotNet.Tools.Tests.Utilities\Microsoft.DotNet.Tools.Tests.Utilities.csproj" />
<ProjectReference Include="..\..\src\Microsoft.DotNet.TestFramework\Microsoft.DotNet.TestFramework.csproj">
<FromP2P>true</FromP2P>
</ProjectReference>
<ProjectReference Include="..\..\src\Microsoft.DotNet.Cli.Utils\Microsoft.DotNet.Cli.Utils.csproj">
<FromP2P>true</FromP2P>
</ProjectReference>
<ProjectReference Include="..\..\src\Microsoft.DotNet.InternalAbstractions\Microsoft.DotNet.InternalAbstractions.csproj">
<FromP2P>true</FromP2P>
</ProjectReference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net46' ">
<Reference Include="System.Runtime">
<FromP2P>true</FromP2P>
</Reference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Sdk">
<Version>1.0.0-alpha-20161104-2</Version>
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk">
<Version>15.0.0-preview-20161024-02</Version>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio">
<Version>2.2.0-beta4-build1194</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.0.1</Version>
</PackageReference>
<PackageReference Include="System.Runtime.Serialization.Primitives">
<Version>4.1.1</Version>
</PackageReference>
<PackageReference Include="xunit">
<Version>2.2.0-beta4-build3444</Version>
</PackageReference>
<PackageReference Include="Microsoft.Build">
<Version>15.1.0-preview-000370-00</Version>
</PackageReference>
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
</PropertyGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>