Skip to content

Commit 452ec83

Browse files
committed
Added edge cases
1 parent 79f77c4 commit 452ec83

File tree

2 files changed

+63
-33
lines changed

2 files changed

+63
-33
lines changed

src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs

+36-13
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Microsoft.Extensions.EnvironmentAbstractions;
1212
using Microsoft.VisualStudio.SolutionPersistence;
1313
using Microsoft.VisualStudio.SolutionPersistence.Model;
14+
using Microsoft.VisualStudio.SolutionPersistence.Serializer.SlnV12;
1415

1516
namespace Microsoft.DotNet.Tools.Sln.Remove
1617
{
@@ -36,19 +37,26 @@ public override int Execute()
3637
throw new GracefulException(CommonLocalizableStrings.SpecifyAtLeastOneProjectToRemove);
3738
}
3839

39-
IEnumerable<string> fullProjectPaths = _projects.Select(project =>
40-
{
41-
var fullPath = Path.GetFullPath(project);
42-
return Directory.Exists(fullPath) ? MsbuildProject.GetProjectFileFromDirectory(fullPath).FullName : fullPath;
43-
});
44-
4540
try
4641
{
47-
RemoveProjectsAsync(solutionFileFullPath, fullProjectPaths, CancellationToken.None).Wait();
42+
var relativeProjectPaths = _projects.Select(p =>
43+
{
44+
var fullPath = Path.GetFullPath(p);
45+
return Path.GetRelativePath(
46+
Path.GetDirectoryName(solutionFileFullPath),
47+
Directory.Exists(fullPath)
48+
? MsbuildProject.GetProjectFileFromDirectory(fullPath).FullName
49+
: fullPath);
50+
});
51+
RemoveProjectsAsync(solutionFileFullPath, relativeProjectPaths, CancellationToken.None).Wait();
4852
return 0;
4953
}
5054
catch (Exception ex) when (ex is not GracefulException)
5155
{
56+
if (ex is SolutionException || ex.InnerException is SolutionException)
57+
{
58+
throw new GracefulException(CommonLocalizableStrings.InvalidSolutionFormatString, solutionFileFullPath, ex.Message);
59+
}
5260
throw new GracefulException(ex.Message, ex);
5361
}
5462
}
@@ -58,17 +66,32 @@ private async Task RemoveProjectsAsync(string solutionFileFullPath, IEnumerable<
5866
ISolutionSerializer serializer = SlnCommandParser.GetSolutionSerializer(solutionFileFullPath);
5967
SolutionModel solution = await serializer.OpenAsync(solutionFileFullPath, cancellationToken);
6068

69+
// set UTF8 BOM encoding for .sln
70+
if (serializer is ISolutionSerializer<SlnV12SerializerSettings> v12Serializer)
71+
{
72+
solution.SerializerExtension = v12Serializer.CreateModelExtension(new()
73+
{
74+
Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: true)
75+
});
76+
}
77+
6178
foreach (var projectPath in projectPaths)
6279
{
63-
// Open project instance to see if it is a valid project
64-
ProjectRootElement projectRootElement = ProjectRootElement.Open(projectPath);
65-
ProjectInstance projectInstance = new ProjectInstance(projectRootElement);
66-
string projectInstanceId = projectInstance.GetProjectId();
80+
var project = solution.FindProject(projectPath);
81+
if (project != null)
82+
{
83+
solution.RemoveProject(project);
6784

68-
SolutionProjectModel? projectModel = (SolutionProjectModel?) solution.FindItemById(new Guid(projectInstanceId));
69-
solution.RemoveProject(projectModel);
85+
Reporter.Output.WriteLine(CommonLocalizableStrings.ProjectRemovedFromTheSolution, projectPath);
86+
}
87+
else
88+
{
89+
Reporter.Output.WriteLine(CommonLocalizableStrings.ProjectNotFoundInTheSolution, projectPath);
90+
}
7091
}
7192

93+
// TODO: Remove empty solution folders
94+
7295
await serializer.SaveAsync(solutionFileFullPath, solution, cancellationToken);
7396
}
7497
}

test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs

+27-20
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ dotnet solution <SLN_FILE> remove [<PROJECT_PATH>...] [options]
5252
{7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.ActiveCfg = Release|x86
5353
{7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.Build.0 = Release|x86
5454
EndGlobalSection
55+
GlobalSection(SolutionProperties) = preSolution
56+
HideSolutionNode = FALSE
57+
EndGlobalSection
5558
EndGlobal
5659
";
5760

@@ -314,27 +317,31 @@ public void WhenInvalidSolutionIsPassedItPrintsErrorAndUsage(string solutionComm
314317
.WithWorkingDirectory(projectDirectory)
315318
.Execute(solutionCommand, "InvalidSolution.sln", "remove", projectToRemove);
316319
cmd.Should().Fail();
317-
cmd.StdErr.Should().Be(string.Format(CommonLocalizableStrings.InvalidSolutionFormatString, "InvalidSolution.sln", LocalizableStrings.FileHeaderMissingError));
320+
cmd.StdErr.Should().Match(string.Format(CommonLocalizableStrings.InvalidSolutionFormatString, "InvalidSolution.sln", "*"));
318321
cmd.StdOut.Should().BeVisuallyEquivalentToIfNotLocalized("");
319322
}
320323

321324
[Theory]
322-
[InlineData("sln")]
323-
[InlineData("solution")]
324-
public void WhenInvalidSolutionIsFoundRemovePrintsErrorAndUsage(string solutionCommand)
325+
[InlineData("sln", ".sln")]
326+
[InlineData("solution", ".sln")]
327+
public void WhenInvalidSolutionIsFoundRemovePrintsErrorAndUsage(string solutionCommand, string solutionExtension)
325328
{
326-
var projectDirectory = _testAssetsManager
329+
var projectDirectoryRoot = _testAssetsManager
327330
.CopyTestAsset("InvalidSolution", identifier: $"{solutionCommand}")
328331
.WithSource()
329332
.Path;
330333

334+
var projectDirectory = solutionExtension == ".sln"
335+
? Path.Join(projectDirectoryRoot, "Sln")
336+
: Path.Join(projectDirectoryRoot, "Slnx");
337+
331338
var solutionPath = Path.Combine(projectDirectory, "InvalidSolution.sln");
332339
var projectToRemove = Path.Combine("Lib", "Lib.csproj");
333340
var cmd = new DotnetCommand(Log)
334341
.WithWorkingDirectory(projectDirectory)
335342
.Execute(solutionCommand, "remove", projectToRemove);
336343
cmd.Should().Fail();
337-
cmd.StdErr.Should().Be(string.Format(CommonLocalizableStrings.InvalidSolutionFormatString, solutionPath, LocalizableStrings.FileHeaderMissingError));
344+
cmd.StdErr.Should().Match(string.Format(CommonLocalizableStrings.InvalidSolutionFormatString, solutionPath, "*"));
338345
cmd.StdOut.Should().BeVisuallyEquivalentToIfNotLocalized("");
339346
}
340347

@@ -408,7 +415,7 @@ public void WhenPassedAReferenceNotInSlnItPrintsStatus(string solutionCommand)
408415
var contentBefore = File.ReadAllText(solutionPath);
409416
var cmd = new DotnetCommand(Log)
410417
.WithWorkingDirectory(projectDirectory)
411-
.Execute(solutionCommand, "remove", "referenceDoesNotExistInSln.csproj");
418+
.Execute(solutionCommand, "App.sln", "remove", "referenceDoesNotExistInSln.csproj");
412419
cmd.Should().Pass();
413420
cmd.StdOut.Should().Be(string.Format(CommonLocalizableStrings.ProjectNotFoundInTheSolution, "referenceDoesNotExistInSln.csproj"));
414421
File.ReadAllText(solutionPath)
@@ -432,7 +439,7 @@ public void WhenPassedAReferenceItRemovesTheReferenceButNotOtherReferences(strin
432439
var projectToRemove = Path.Combine("Lib", "Lib.csproj");
433440
var cmd = new DotnetCommand(Log)
434441
.WithWorkingDirectory(projectDirectory)
435-
.Execute(solutionCommand, "remove", projectToRemove);
442+
.Execute(solutionCommand, "App.sln", "remove", projectToRemove);
436443
cmd.Should().Pass();
437444
cmd.StdOut.Should().Be(string.Format(CommonLocalizableStrings.ProjectRemovedFromTheSolution, projectToRemove));
438445

@@ -457,7 +464,7 @@ public void WhenSolutionItemsExistInFolderParentFoldersAreNotRemoved(string solu
457464
var projectToRemove = Path.Combine("ConsoleApp1", "ConsoleApp1.csproj");
458465
var cmd = new DotnetCommand(Log)
459466
.WithWorkingDirectory(projectDirectory)
460-
.Execute(solutionCommand, "remove", projectToRemove);
467+
.Execute(solutionCommand, "App.sln", "remove", projectToRemove);
461468
cmd.Should().Pass();
462469
cmd.StdOut.Should().Be(string.Format(CommonLocalizableStrings.ProjectRemovedFromTheSolution, projectToRemove));
463470

@@ -484,7 +491,7 @@ public void WhenDuplicateReferencesArePresentItRemovesThemAll(string solutionCom
484491
var projectToRemove = Path.Combine("Lib", "Lib.csproj");
485492
var cmd = new DotnetCommand(Log)
486493
.WithWorkingDirectory(projectDirectory)
487-
.Execute(solutionCommand, "remove", projectToRemove);
494+
.Execute(solutionCommand, "App.sln", "remove", projectToRemove);
488495
cmd.Should().Pass();
489496

490497
string outputText = string.Format(CommonLocalizableStrings.ProjectRemovedFromTheSolution, projectToRemove);
@@ -513,7 +520,7 @@ public void WhenPassedMultipleReferencesAndOneOfThemDoesNotExistItRemovesTheOneT
513520
var projectToRemove = Path.Combine("Lib", "Lib.csproj");
514521
var cmd = new DotnetCommand(Log)
515522
.WithWorkingDirectory(projectDirectory)
516-
.Execute(solutionCommand, "remove", "idontexist.csproj", projectToRemove, "idontexisteither.csproj");
523+
.Execute(solutionCommand, "App.sln", "remove", "idontexist.csproj", projectToRemove, "idontexisteither.csproj");
517524
cmd.Should().Pass();
518525

519526
string outputText = $@"{string.Format(CommonLocalizableStrings.ProjectNotFoundInTheSolution, "idontexist.csproj")}
@@ -544,7 +551,7 @@ public void WhenReferenceIsRemovedBuildConfigsAreAlsoRemoved(string solutionComm
544551
var projectToRemove = Path.Combine("Lib", "Lib.csproj");
545552
var cmd = new DotnetCommand(Log)
546553
.WithWorkingDirectory(projectDirectory)
547-
.Execute(solutionCommand, "remove", projectToRemove);
554+
.Execute(solutionCommand, "App.sln", "remove", projectToRemove);
548555
cmd.Should().Pass();
549556

550557
File.ReadAllText(solutionPath)
@@ -567,7 +574,7 @@ public void WhenDirectoryContainingProjectIsGivenProjectIsRemoved(string solutio
567574

568575
var cmd = new DotnetCommand(Log)
569576
.WithWorkingDirectory(projectDirectory)
570-
.Execute(solutionCommand, "remove", "Lib");
577+
.Execute(solutionCommand, "App.sln", "remove", "Lib");
571578
cmd.Should().Pass();
572579

573580
File.ReadAllText(solutionPath)
@@ -587,7 +594,7 @@ public void WhenDirectoryContainsNoProjectsItCancelsWholeOperation(string soluti
587594

588595
var cmd = new DotnetCommand(Log)
589596
.WithWorkingDirectory(projectDirectory)
590-
.Execute(solutionCommand, "remove", directoryToRemove);
597+
.Execute(solutionCommand, "App.sln", "remove", directoryToRemove);
591598
cmd.Should().Fail();
592599
cmd.StdErr.Should().Be(
593600
string.Format(
@@ -609,7 +616,7 @@ public void WhenDirectoryContainsMultipleProjectsItCancelsWholeOperation(string
609616

610617
var cmd = new DotnetCommand(Log)
611618
.WithWorkingDirectory(projectDirectory)
612-
.Execute(solutionCommand, "remove", directoryToRemove);
619+
.Execute(solutionCommand, "App.sln", "remove", directoryToRemove);
613620
cmd.Should().Fail();
614621
cmd.StdErr.Should().Be(
615622
string.Format(
@@ -635,7 +642,7 @@ public void WhenReferenceIsRemovedSlnBuilds(string solutionCommand)
635642
var projectToRemove = Path.Combine("Lib", "Lib.csproj");
636643
var cmd = new DotnetCommand(Log)
637644
.WithWorkingDirectory(projectDirectory)
638-
.Execute(solutionCommand, "remove", projectToRemove);
645+
.Execute(solutionCommand, "App.sln", "remove", projectToRemove);
639646
cmd.Should().Pass();
640647

641648
new DotnetCommand(Log)
@@ -705,7 +712,7 @@ public void WhenFinalReferenceIsRemovedEmptySectionsAreRemoved(string solutionCo
705712
var libPath = Path.Combine("Lib", "Lib.csproj");
706713
var cmd = new DotnetCommand(Log)
707714
.WithWorkingDirectory(projectDirectory)
708-
.Execute(solutionCommand, "remove", libPath, appPath);
715+
.Execute(solutionCommand, "App.sln", "remove", libPath, appPath);
709716
cmd.Should().Pass();
710717

711718
var solutionContents = File.ReadAllText(solutionPath);
@@ -728,7 +735,7 @@ public void WhenNestedProjectIsRemovedItsSolutionFoldersAreRemoved(string soluti
728735
var projectToRemove = Path.Combine("src", "NotLastProjInSrc", "NotLastProjInSrc.csproj");
729736
var cmd = new DotnetCommand(Log)
730737
.WithWorkingDirectory(projectDirectory)
731-
.Execute(solutionCommand, "remove", projectToRemove);
738+
.Execute(solutionCommand, "App.sln", "remove", projectToRemove);
732739
cmd.Should().Pass();
733740

734741
File.ReadAllText(solutionPath)
@@ -750,7 +757,7 @@ public void WhenFinalNestedProjectIsRemovedSolutionFoldersAreRemoved(string solu
750757
var projectToRemove = Path.Combine("src", "Lib", "Lib.csproj");
751758
var cmd = new DotnetCommand(Log)
752759
.WithWorkingDirectory(projectDirectory)
753-
.Execute(solutionCommand, "remove", projectToRemove);
760+
.Execute(solutionCommand, "App.sln", "remove", projectToRemove);
754761
cmd.Should().Pass();
755762

756763
File.ReadAllText(solutionPath)
@@ -772,7 +779,7 @@ public void WhenProjectIsRemovedThenDependenciesOnProjectAreAlsoRemoved(string s
772779
var projectToRemove = Path.Combine("Second", "Second.csproj");
773780
var cmd = new DotnetCommand(Log)
774781
.WithWorkingDirectory(projectDirectory)
775-
.Execute(solutionCommand, "remove", projectToRemove);
782+
.Execute(solutionCommand, "App.sln", "remove", projectToRemove);
776783
cmd.Should().Pass();
777784

778785
File.ReadAllText(solutionPath)

0 commit comments

Comments
 (0)