From 806d5a2589bcb6993a3c44650df0d30a613ce0e1 Mon Sep 17 00:00:00 2001 From: Justin Goshi Date: Fri, 16 Dec 2016 06:41:47 -1000 Subject: [PATCH] Implement dotnet list projects (#5027) * Implement dotnet list projects * Address PR comments * Fix build breaks --- .../SlnFileWithNoProjectReferences.sln | 18 ++ .../CommandLine/CommandLineApplication.cs | 8 + .../CommandLine/CommandParsingException.cs | 7 +- src/dotnet/CommonLocalizableStrings.cs | 6 +- src/dotnet/DotNetTopLevelCommandBase.cs | 6 +- .../commands/dotnet-add/LocalizableStrings.cs | 3 + .../dotnet-add-p2p/LocalizableStrings.cs | 3 + .../commands/dotnet-list/IListSubCommand.cs | 13 ++ .../dotnet-list/ListSubCommandBase.cs | 71 +++++++ .../dotnet-list/LocalizableStrings.cs | 5 +- src/dotnet/commands/dotnet-list/Program.cs | 2 + .../dotnet-list-p2ps/LocalizableStrings.cs | 3 + .../dotnet-list/dotnet-list-p2ps/Program.cs | 90 ++++----- .../dotnet-list-proj/LocalizableStrings.cs | 12 ++ .../dotnet-list/dotnet-list-proj/Program.cs | 46 +++++ .../dotnet-remove/LocalizableStrings.cs | 3 + .../dotnet-remove-p2p/LocalizableStrings.cs | 3 + .../GivenDotnetAddProj.cs | 9 + .../GivenDotnetListProj.cs | 182 ++++++++++++++++++ test/dotnet-list-proj.Tests/MSBuild.exe | 1 + .../dotnet-list-proj.Tests/MSBuild.exe.config | 1 + .../dotnet-list-proj.Tests.csproj | 44 +++++ 22 files changed, 480 insertions(+), 56 deletions(-) create mode 100644 TestAssets/TestProjects/SlnFileWithNoProjectReferences/SlnFileWithNoProjectReferences.sln create mode 100644 src/dotnet/commands/dotnet-list/IListSubCommand.cs create mode 100644 src/dotnet/commands/dotnet-list/ListSubCommandBase.cs create mode 100644 src/dotnet/commands/dotnet-list/dotnet-list-proj/LocalizableStrings.cs create mode 100644 src/dotnet/commands/dotnet-list/dotnet-list-proj/Program.cs create mode 100644 test/dotnet-list-proj.Tests/GivenDotnetListProj.cs create mode 100644 test/dotnet-list-proj.Tests/MSBuild.exe create mode 100644 test/dotnet-list-proj.Tests/MSBuild.exe.config create mode 100644 test/dotnet-list-proj.Tests/dotnet-list-proj.Tests.csproj diff --git a/TestAssets/TestProjects/SlnFileWithNoProjectReferences/SlnFileWithNoProjectReferences.sln b/TestAssets/TestProjects/SlnFileWithNoProjectReferences/SlnFileWithNoProjectReferences.sln new file mode 100644 index 000000000..5b61df887 --- /dev/null +++ b/TestAssets/TestProjects/SlnFileWithNoProjectReferences/SlnFileWithNoProjectReferences.sln @@ -0,0 +1,18 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26006.2 +MinimumVisualStudioVersion = 10.0.40219.1 +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/dotnet/CommandLine/CommandLineApplication.cs b/src/dotnet/CommandLine/CommandLineApplication.cs index 2eaa2369f..4c7a52a47 100644 --- a/src/dotnet/CommandLine/CommandLineApplication.cs +++ b/src/dotnet/CommandLine/CommandLineApplication.cs @@ -175,6 +175,14 @@ namespace Microsoft.DotNet.Cli.CommandLine } } + if (Commands.Count > 0 && command == this) + { + throw new CommandParsingException( + command, + "Required command missing", + isRequireSubCommandMissing: true); + } + return command.Invoke(); } diff --git a/src/dotnet/CommandLine/CommandParsingException.cs b/src/dotnet/CommandLine/CommandParsingException.cs index 1b7ed2171..e495534a3 100644 --- a/src/dotnet/CommandLine/CommandParsingException.cs +++ b/src/dotnet/CommandLine/CommandParsingException.cs @@ -7,12 +7,17 @@ namespace Microsoft.DotNet.Cli.CommandLine { internal class CommandParsingException : Exception { - public CommandParsingException(CommandLineApplication command, string message) + public CommandParsingException( + CommandLineApplication command, + string message, + bool isRequireSubCommandMissing = false) : base(message) { Command = command; + IsRequireSubCommandMissing = isRequireSubCommandMissing; } public CommandLineApplication Command { get; } + public bool IsRequireSubCommandMissing { get; } } } diff --git a/src/dotnet/CommonLocalizableStrings.cs b/src/dotnet/CommonLocalizableStrings.cs index b979ad7a5..f64bf0fb6 100644 --- a/src/dotnet/CommonLocalizableStrings.cs +++ b/src/dotnet/CommonLocalizableStrings.cs @@ -1,4 +1,7 @@ -namespace Microsoft.DotNet.Tools +// 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. + +namespace Microsoft.DotNet.Tools { internal class CommonLocalizableStrings { @@ -88,6 +91,7 @@ public const string OptionIsInvalid = "Option {0} is invalid."; public const string ArgumentIsInvalid = "Argument {0} is invalid."; public const string RequiredArgumentNotPassed = "Required argument {0} was not provided."; + public const string RequiredCommandNotPassed = "Required command was not provided."; // dotnet /// Project diff --git a/src/dotnet/DotNetTopLevelCommandBase.cs b/src/dotnet/DotNetTopLevelCommandBase.cs index 45a6a401f..e4e3b91dd 100644 --- a/src/dotnet/DotNetTopLevelCommandBase.cs +++ b/src/dotnet/DotNetTopLevelCommandBase.cs @@ -49,7 +49,11 @@ namespace Microsoft.DotNet.Cli } catch (CommandParsingException e) { - Reporter.Error.WriteLine(e.Message.Red()); + string errorMessage = e.IsRequireSubCommandMissing + ? CommonLocalizableStrings.RequiredCommandNotPassed + : e.Message; + + Reporter.Error.WriteLine(errorMessage.Red()); return 1; } } diff --git a/src/dotnet/commands/dotnet-add/LocalizableStrings.cs b/src/dotnet/commands/dotnet-add/LocalizableStrings.cs index b3354c357..2e6868f4b 100644 --- a/src/dotnet/commands/dotnet-add/LocalizableStrings.cs +++ b/src/dotnet/commands/dotnet-add/LocalizableStrings.cs @@ -1,3 +1,6 @@ +// 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. + namespace Microsoft.DotNet.Tools.Add { internal class LocalizableStrings diff --git a/src/dotnet/commands/dotnet-add/dotnet-add-p2p/LocalizableStrings.cs b/src/dotnet/commands/dotnet-add/dotnet-add-p2p/LocalizableStrings.cs index 2978267b0..8d7f7987f 100644 --- a/src/dotnet/commands/dotnet-add/dotnet-add-p2p/LocalizableStrings.cs +++ b/src/dotnet/commands/dotnet-add/dotnet-add-p2p/LocalizableStrings.cs @@ -1,3 +1,6 @@ +// 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. + namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference { internal class LocalizableStrings diff --git a/src/dotnet/commands/dotnet-list/IListSubCommand.cs b/src/dotnet/commands/dotnet-list/IListSubCommand.cs new file mode 100644 index 000000000..d0f674759 --- /dev/null +++ b/src/dotnet/commands/dotnet-list/IListSubCommand.cs @@ -0,0 +1,13 @@ +// 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; + +namespace Microsoft.DotNet.Tools.List +{ + public interface IListSubCommand + { + string LocalizedErrorMessageNoItemsFound { get; } + IList Items { get; } + } +} diff --git a/src/dotnet/commands/dotnet-list/ListSubCommandBase.cs b/src/dotnet/commands/dotnet-list/ListSubCommandBase.cs new file mode 100644 index 000000000..5f773a68a --- /dev/null +++ b/src/dotnet/commands/dotnet-list/ListSubCommandBase.cs @@ -0,0 +1,71 @@ +// 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.CommandLine; +using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.Tools.Common; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Microsoft.DotNet.Tools.List +{ + public abstract class ListSubCommandBase + { + protected abstract string CommandName { get; } + protected abstract string LocalizedDisplayName { get; } + protected abstract string LocalizedDescription { get; } + protected abstract IListSubCommand CreateIListSubCommand(string fileOrDirectory); + + internal CommandLineApplication Create(CommandLineApplication parentApp) + { + CommandLineApplication app = parentApp.Command(CommandName, throwOnUnexpectedArg: false); + app.FullName = LocalizedDisplayName; + app.Description = LocalizedDescription; + + app.HelpOption("-h|--help"); + + app.OnExecute(() => { + try + { + if (!parentApp.Arguments.Any()) + { + throw new GracefulException( + CommonLocalizableStrings.RequiredArgumentNotPassed, + Constants.ProjectOrSolutionArgumentName); + } + + var projectOrDirectory = parentApp.Arguments.First().Value; + if (string.IsNullOrEmpty(projectOrDirectory)) + { + projectOrDirectory = PathUtility.EnsureTrailingSlash(Directory.GetCurrentDirectory()); + } + + var listCommand = CreateIListSubCommand(projectOrDirectory); + if (listCommand.Items.Count == 0) + { + Reporter.Output.WriteLine(listCommand.LocalizedErrorMessageNoItemsFound); + return 0; + } + + Reporter.Output.WriteLine($"{CommonLocalizableStrings.ProjectReferenceOneOrMore}"); + Reporter.Output.WriteLine(new string('-', CommonLocalizableStrings.ProjectReferenceOneOrMore.Length)); + foreach (var item in listCommand.Items) + { + Reporter.Output.WriteLine(item); + } + + return 0; + } + catch (GracefulException e) + { + Reporter.Error.WriteLine(e.Message.Red()); + app.ShowHelp(); + return 1; + } + }); + + return app; + } + } +} diff --git a/src/dotnet/commands/dotnet-list/LocalizableStrings.cs b/src/dotnet/commands/dotnet-list/LocalizableStrings.cs index 1d556737b..66425450d 100644 --- a/src/dotnet/commands/dotnet-list/LocalizableStrings.cs +++ b/src/dotnet/commands/dotnet-list/LocalizableStrings.cs @@ -1,4 +1,7 @@ -namespace Microsoft.DotNet.Tools.List +// 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. + +namespace Microsoft.DotNet.Tools.List { internal class LocalizableStrings { diff --git a/src/dotnet/commands/dotnet-list/Program.cs b/src/dotnet/commands/dotnet-list/Program.cs index db015aae2..365708664 100644 --- a/src/dotnet/commands/dotnet-list/Program.cs +++ b/src/dotnet/commands/dotnet-list/Program.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Tools.List.ProjectToProjectReferences; +using Microsoft.DotNet.Tools.List.ProjectsInSolution; namespace Microsoft.DotNet.Tools.List { @@ -16,6 +17,7 @@ namespace Microsoft.DotNet.Tools.List internal override List> SubCommands => new List> { + ListProjectsInSolutionCommand.CreateApplication, ListProjectToProjectReferencesCommand.CreateApplication, }; diff --git a/src/dotnet/commands/dotnet-list/dotnet-list-p2ps/LocalizableStrings.cs b/src/dotnet/commands/dotnet-list/dotnet-list-p2ps/LocalizableStrings.cs index 07c83eb72..6c32c56c4 100644 --- a/src/dotnet/commands/dotnet-list/dotnet-list-p2ps/LocalizableStrings.cs +++ b/src/dotnet/commands/dotnet-list/dotnet-list-p2ps/LocalizableStrings.cs @@ -1,3 +1,6 @@ +// 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. + namespace Microsoft.DotNet.Tools.List.ProjectToProjectReferences { internal class LocalizableStrings diff --git a/src/dotnet/commands/dotnet-list/dotnet-list-p2ps/Program.cs b/src/dotnet/commands/dotnet-list/dotnet-list-p2ps/Program.cs index cc439e68a..2cedca511 100644 --- a/src/dotnet/commands/dotnet-list/dotnet-list-p2ps/Program.cs +++ b/src/dotnet/commands/dotnet-list/dotnet-list-p2ps/Program.cs @@ -3,65 +3,51 @@ using Microsoft.Build.Evaluation; using Microsoft.DotNet.Cli.CommandLine; -using Microsoft.DotNet.Cli.Utils; -using Microsoft.DotNet.Tools.Common; using System.Collections.Generic; -using System.IO; -using System.Linq; +using Microsoft.DotNet.Tools.List; namespace Microsoft.DotNet.Tools.List.ProjectToProjectReferences { - public class ListProjectToProjectReferencesCommand + public class ListProjectToProjectReferences : IListSubCommand { + private string _fileOrDirectory = null; + private IList _items = new List(); + + public ListProjectToProjectReferences(string fileOrDirectory) + { + _fileOrDirectory = fileOrDirectory; + var msbuildProj = MsbuildProject.FromFileOrDirectory(new ProjectCollection(), fileOrDirectory); + + var p2ps = msbuildProj.GetProjectToProjectReferences(); + foreach (var p2p in p2ps) + { + _items.Add(p2p.Include); + } + } + + public string LocalizedErrorMessageNoItemsFound => string.Format( + LocalizableStrings.NoReferencesFound, + CommonLocalizableStrings.P2P, + _fileOrDirectory); + + public IList Items => _items; + } + + public class ListProjectToProjectReferencesCommand : ListSubCommandBase + { + protected override string CommandName => "p2ps"; + protected override string LocalizedDisplayName => LocalizableStrings.AppFullName; + protected override string LocalizedDescription => LocalizableStrings.AppDescription; + + protected override IListSubCommand CreateIListSubCommand(string fileOrDirectory) + { + return new ListProjectToProjectReferences(fileOrDirectory); + } + internal static CommandLineApplication CreateApplication(CommandLineApplication parentApp) { - CommandLineApplication app = parentApp.Command("p2ps", throwOnUnexpectedArg: false); - app.FullName = LocalizableStrings.AppFullName; - app.Description = LocalizableStrings.AppDescription; - - app.HelpOption("-h|--help"); - - app.OnExecute(() => { - try - { - if (!parentApp.Arguments.Any()) - { - throw new GracefulException(CommonLocalizableStrings.RequiredArgumentNotPassed, Constants.ProjectOrSolutionArgumentName); - } - - var projectOrDirectory = parentApp.Arguments.First().Value; - if (string.IsNullOrEmpty(projectOrDirectory)) - { - projectOrDirectory = PathUtility.EnsureTrailingSlash(Directory.GetCurrentDirectory()); - } - - var msbuildProj = MsbuildProject.FromFileOrDirectory(new ProjectCollection(), projectOrDirectory); - - var p2ps = msbuildProj.GetProjectToProjectReferences(); - if (p2ps.Count() == 0) - { - Reporter.Output.WriteLine(string.Format(LocalizableStrings.NoReferencesFound, CommonLocalizableStrings.P2P, projectOrDirectory)); - return 0; - } - - Reporter.Output.WriteLine($"{CommonLocalizableStrings.ProjectReferenceOneOrMore}"); - Reporter.Output.WriteLine(new string('-', CommonLocalizableStrings.ProjectReferenceOneOrMore.Length)); - foreach (var p2p in p2ps) - { - Reporter.Output.WriteLine(p2p.Include); - } - - return 0; - } - catch (GracefulException e) - { - Reporter.Error.WriteLine(e.Message.Red()); - app.ShowHelp(); - return 1; - } - }); - - return app; + var command = new ListProjectToProjectReferencesCommand(); + return command.Create(parentApp); } } } diff --git a/src/dotnet/commands/dotnet-list/dotnet-list-proj/LocalizableStrings.cs b/src/dotnet/commands/dotnet-list/dotnet-list-proj/LocalizableStrings.cs new file mode 100644 index 000000000..e18cea956 --- /dev/null +++ b/src/dotnet/commands/dotnet-list/dotnet-list-proj/LocalizableStrings.cs @@ -0,0 +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. + +namespace Microsoft.DotNet.Tools.List.ProjectsInSolution +{ + internal class LocalizableStrings + { + public const string AppFullName = ".NET Projects in Solution viewer"; + + public const string AppDescription = "Command to list projects in a solution"; + } +} diff --git a/src/dotnet/commands/dotnet-list/dotnet-list-proj/Program.cs b/src/dotnet/commands/dotnet-list/dotnet-list-proj/Program.cs new file mode 100644 index 000000000..8fec98706 --- /dev/null +++ b/src/dotnet/commands/dotnet-list/dotnet-list-proj/Program.cs @@ -0,0 +1,46 @@ +// 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.CommandLine; +using Microsoft.DotNet.Cli.Sln.Internal; +using Microsoft.DotNet.Tools.Common; +using System.Collections.Generic; +using Microsoft.DotNet.Tools.List; + +namespace Microsoft.DotNet.Tools.List.ProjectsInSolution +{ + public class ListProjectsInSolution : IListSubCommand + { + private IList _items = new List(); + + public ListProjectsInSolution(string fileOrDirectory) + { + SlnFile slnFile = SlnFileFactory.CreateFromFileOrDirectory(fileOrDirectory); + foreach (var slnProject in slnFile.Projects) + { + _items.Add(slnProject.FilePath); + } + } + + public string LocalizedErrorMessageNoItemsFound => CommonLocalizableStrings.NoProjectsFound; + public IList Items => _items; + } + + public class ListProjectsInSolutionCommand : ListSubCommandBase + { + protected override string CommandName => "projects"; + protected override string LocalizedDisplayName => LocalizableStrings.AppFullName; + protected override string LocalizedDescription => LocalizableStrings.AppDescription; + + protected override IListSubCommand CreateIListSubCommand(string fileOrDirectory) + { + return new ListProjectsInSolution(fileOrDirectory); + } + + internal static CommandLineApplication CreateApplication(CommandLineApplication parentApp) + { + var command = new ListProjectsInSolutionCommand(); + return command.Create(parentApp); + } + } +} diff --git a/src/dotnet/commands/dotnet-remove/LocalizableStrings.cs b/src/dotnet/commands/dotnet-remove/LocalizableStrings.cs index a9730e3bd..424151bb0 100644 --- a/src/dotnet/commands/dotnet-remove/LocalizableStrings.cs +++ b/src/dotnet/commands/dotnet-remove/LocalizableStrings.cs @@ -1,3 +1,6 @@ +// 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. + namespace Microsoft.DotNet.Tools.Remove { internal class LocalizableStrings diff --git a/src/dotnet/commands/dotnet-remove/dotnet-remove-p2p/LocalizableStrings.cs b/src/dotnet/commands/dotnet-remove/dotnet-remove-p2p/LocalizableStrings.cs index 3cedc8539..e9f47f4d6 100644 --- a/src/dotnet/commands/dotnet-remove/dotnet-remove-p2p/LocalizableStrings.cs +++ b/src/dotnet/commands/dotnet-remove/dotnet-remove-p2p/LocalizableStrings.cs @@ -1,3 +1,6 @@ +// 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. + namespace Microsoft.DotNet.Tools.Remove.ProjectToProjectReference { internal class LocalizableStrings diff --git a/test/dotnet-add-proj.Tests/GivenDotnetAddProj.cs b/test/dotnet-add-proj.Tests/GivenDotnetAddProj.cs index 9e61470b6..7ae7d30b5 100644 --- a/test/dotnet-add-proj.Tests/GivenDotnetAddProj.cs +++ b/test/dotnet-add-proj.Tests/GivenDotnetAddProj.cs @@ -25,6 +25,15 @@ namespace Microsoft.DotNet.Cli.Add.Proj.Tests cmd.StdOut.Should().Contain("Usage"); } + [Fact] + public void WhenTooManyArgumentsArePassedItPrintsError() + { + var cmd = new DotnetCommand() + .ExecuteWithCapturedOutput("add one.sln two.sln three.sln project"); + cmd.Should().Fail(); + cmd.StdErr.Should().Contain("Unrecognized command or argument"); + } + [Theory] [InlineData("idontexist.sln")] [InlineData("ihave?invalidcharacters")] diff --git a/test/dotnet-list-proj.Tests/GivenDotnetListProj.cs b/test/dotnet-list-proj.Tests/GivenDotnetListProj.cs new file mode 100644 index 000000000..9c9049ed0 --- /dev/null +++ b/test/dotnet-list-proj.Tests/GivenDotnetListProj.cs @@ -0,0 +1,182 @@ +// 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 FluentAssertions; +using Microsoft.DotNet.Cli.Sln.Internal; +using Microsoft.DotNet.Tools.Test.Utilities; +using System; +using System.IO; +using System.Linq; +using Xunit; + +namespace Microsoft.DotNet.Cli.List.Proj.Tests +{ + public class GivenDotnetListProj : TestBase + { + private const string HelpText = @".NET Projects in Solution viewer + +Usage: dotnet list projects [options] + +Arguments: + The project or solution to operation on. If a file is not specified, the current directory is searched. + +Options: + -h|--help Show help information"; + + [Theory] + [InlineData("--help")] + [InlineData("-h")] + public void WhenHelpOptionIsPassedItPrintsUsage(string helpArg) + { + var cmd = new DotnetCommand() + .ExecuteWithCapturedOutput($"list projects {helpArg}"); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain(HelpText); + } + + [Theory] + [InlineData("")] + [InlineData("unknownCommandName")] + public void WhenNoCommandIsPassedItPrintsError(string commandName) + { + var cmd = new DotnetCommand() + .ExecuteWithCapturedOutput($"list {commandName}"); + cmd.Should().Fail(); + cmd.StdErr.Should().Contain("Required command was not provided."); + } + + [Fact] + public void WhenTooManyArgumentsArePassedItPrintsError() + { + var cmd = new DotnetCommand() + .ExecuteWithCapturedOutput("list one.sln two.sln three.sln projects"); + cmd.Should().Fail(); + cmd.StdErr.Should().Contain("Unrecognized command or argument 'two.sln'"); + } + + [Theory] + [InlineData("idontexist.sln")] + [InlineData("ihave?invalidcharacters.sln")] + [InlineData("ihaveinv@lidcharacters.sln")] + [InlineData("ihaveinvalid/characters")] + [InlineData("ihaveinvalidchar\\acters")] + public void WhenNonExistingSolutionIsPassedItPrintsErrorAndUsage(string solutionName) + { + var cmd = new DotnetCommand() + .ExecuteWithCapturedOutput($"list {solutionName} projects"); + cmd.Should().Fail(); + cmd.StdErr.Should().Contain($"Could not find solution or directory `{solutionName}`"); + cmd.StdOut.Should().Contain(HelpText); + } + + [Fact] + public void WhenInvalidSolutionIsPassedItPrintsErrorAndUsage() + { + var projectDirectory = TestAssets + .Get("InvalidSolution") + .CreateInstance() + .WithSourceFiles() + .Root + .FullName; + + var cmd = new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput("list InvalidSolution.sln projects"); + cmd.Should().Fail(); + cmd.StdErr.Should().Contain("Invalid solution `InvalidSolution.sln`"); + cmd.StdOut.Should().Contain(HelpText); + } + + [Fact] + public void WhenInvalidSolutionIsFoundItPrintsErrorAndUsage() + { + var projectDirectory = TestAssets + .Get("InvalidSolution") + .CreateInstance() + .WithSourceFiles() + .Root + .FullName; + + var solutionFullPath = Path.Combine(projectDirectory, "InvalidSolution.sln"); + var cmd = new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput("list projects"); + cmd.Should().Fail(); + cmd.StdErr.Should().Contain($"Invalid solution `{solutionFullPath}`"); + cmd.StdOut.Should().Contain(HelpText); + } + + [Fact] + public void WhenNoSolutionExistsInTheDirectoryItPrintsErrorAndUsage() + { + var projectDirectory = TestAssets + .Get("TestAppWithSlnAndCsprojFiles") + .CreateInstance() + .WithSourceFiles() + .Root + .FullName; + + var solutionDir = Path.Combine(projectDirectory, "App"); + var cmd = new DotnetCommand() + .WithWorkingDirectory(solutionDir) + .ExecuteWithCapturedOutput("list projects"); + cmd.Should().Fail(); + cmd.StdErr.Should().Contain($"Specified solution file {solutionDir + Path.DirectorySeparatorChar} does not exist, or there is no solution file in the directory"); + cmd.StdOut.Should().Contain(HelpText); + } + + [Fact] + public void WhenMoreThanOneSolutionExistsInTheDirectoryItPrintsErrorAndUsage() + { + var projectDirectory = TestAssets + .Get("TestAppWithMultipleSlnFiles") + .CreateInstance() + .WithSourceFiles() + .Root + .FullName; + + var cmd = new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput("list projects"); + cmd.Should().Fail(); + cmd.StdErr.Should().Contain($"Found more than one solution file in {projectDirectory + Path.DirectorySeparatorChar}. Please specify which one to use."); + cmd.StdOut.Should().Contain(HelpText); + } + + [Fact] + public void WhenNoProjectReferencesArePresentInTheSolutionItPrintsANoProjectMessage() + { + var projectDirectory = TestAssets + .Get("SlnFileWithNoProjectReferences") + .CreateInstance() + .WithSourceFiles() + .Root + .FullName; + + var cmd = new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput("list projects"); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("No projects found in the solution."); + } + + [Fact] + public void WhenProjectReferencesArePresentInTheSolutionItListsThem() + { + var projectDirectory = TestAssets + .Get("TestAppWithSlnAndExistingCsprojReferences") + .CreateInstance() + .WithSourceFiles() + .Root + .FullName; + + var cmd = new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput("list projects"); + cmd.Should().Pass(); + cmd.StdOut.Should().Contain("Project reference(s)"); + cmd.StdOut.Should().Contain(@"App\App.csproj"); + cmd.StdOut.Should().Contain(@"Lib\Lib.csproj"); + } + } +} diff --git a/test/dotnet-list-proj.Tests/MSBuild.exe b/test/dotnet-list-proj.Tests/MSBuild.exe new file mode 100644 index 000000000..9bda258ef --- /dev/null +++ b/test/dotnet-list-proj.Tests/MSBuild.exe @@ -0,0 +1 @@ +https://github.com/Microsoft/msbuild/issues/927 diff --git a/test/dotnet-list-proj.Tests/MSBuild.exe.config b/test/dotnet-list-proj.Tests/MSBuild.exe.config new file mode 100644 index 000000000..9bda258ef --- /dev/null +++ b/test/dotnet-list-proj.Tests/MSBuild.exe.config @@ -0,0 +1 @@ +https://github.com/Microsoft/msbuild/issues/927 diff --git a/test/dotnet-list-proj.Tests/dotnet-list-proj.Tests.csproj b/test/dotnet-list-proj.Tests/dotnet-list-proj.Tests.csproj new file mode 100644 index 000000000..3041dd11e --- /dev/null +++ b/test/dotnet-list-proj.Tests/dotnet-list-proj.Tests.csproj @@ -0,0 +1,44 @@ + + + + + + netcoreapp1.0 + true + dotnet-list-proj.Tests + $(PackageTargetFallback);dotnet5.4;portable-net451+win8 + + + + + + + + + + + + + + + + + $(CLI_NETSDK_Version) + All + + + 15.0.0-preview-20161024-02 + + + 2.2.0-beta4-build1194 + + + 1.0.1 + + + 2.2.0-beta4-build3444 + + + + +