From 4a9985be03d8769f519d4c1ce6845a6a72379278 Mon Sep 17 00:00:00 2001 From: Richard Stanton Date: Thu, 19 Dec 2024 21:03:10 -0800 Subject: [PATCH] Solution folders with duplicate ids should not error when reading sln files to preserve legacy behavior. --- .../SlnV12/SlnFileV12Serializer.Reader.cs | 8 +++++ .../Serialization/InvalidSolutions.cs | 31 +++++++++++++++++++ .../Invalid/DuplicateFolderId.sln.txt | 10 ++++++ .../Invalid/DuplicateFolderId.slnx.xml | 6 ++++ 4 files changed, 55 insertions(+) create mode 100644 test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateFolderId.sln.txt create mode 100644 test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateFolderId.slnx.xml diff --git a/src/Microsoft.VisualStudio.SolutionPersistence/Serializer/SlnV12/SlnFileV12Serializer.Reader.cs b/src/Microsoft.VisualStudio.SolutionPersistence/Serializer/SlnV12/SlnFileV12Serializer.Reader.cs index e1df8ac1..40ec8ede 100644 --- a/src/Microsoft.VisualStudio.SolutionPersistence/Serializer/SlnV12/SlnFileV12Serializer.Reader.cs +++ b/src/Microsoft.VisualStudio.SolutionPersistence/Serializer/SlnV12/SlnFileV12Serializer.Reader.cs @@ -532,6 +532,14 @@ private SolutionItemModel ReadProjectInfo(SolutionModel solution, ref StringToke #pragma warning disable CS0618 // Type or member is obsolete (Temporaily create a potentially invalid solution folder until nested projects is interpreted.) SolutionFolderModel folder = solution.CreateSlnFolder(name: displayName.ToString()); #pragma warning restore CS0618 // Type or member is obsolete + + // Solution folders with duplicate ids should not error when reading sln files to preserve legacy behavior. + if (solution.FindItemById(projectId) is not null) + { + projectId = Guid.NewGuid(); + this.tarnished = true; + } + folder.Id = projectId; return folder; } diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/InvalidSolutions.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/InvalidSolutions.cs index e962799c..b5fc7a13 100644 --- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/InvalidSolutions.cs +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/InvalidSolutions.cs @@ -208,4 +208,35 @@ public async Task InvalidSlnxVersionAsync() Assert.Equal(1, ex.Line); Assert.Equal(2, ex.Column); } + + /// + /// The legacy sln solution parser would ignore duplicate folder guids. + /// Ensure this behavior is maintained. + /// + [Fact] + public async Task DuplicateFolderIdSlnAsync() + { + ResourceStream duplicateId = SlnAssets.LoadResource("Invalid/DuplicateFolderId.sln"); + SolutionModel solution = await SolutionSerializers.SlnFileV12.OpenAsync(duplicateId.Stream, CancellationToken.None); + + Assert.Equal(4, solution.SolutionFolders.Count); + + Assert.NotNull(solution.SerializerExtension); + Assert.True(solution.SerializerExtension.Tarnished); + } + + /// + /// Ensure the slnx parser does fail on duplicate folder guids. + /// + [Fact] + public async Task DuplicateFolderIdSlnxAsync() + { + ResourceStream duplicateId = SlnAssets.LoadResource("Invalid/DuplicateFolderId.slnx"); + SolutionException ex = await Assert.ThrowsAsync( + async () => _ = await SolutionSerializers.SlnXml.OpenAsync(duplicateId.Stream, CancellationToken.None)); + + Assert.StartsWith(string.Format(Errors.DuplicateItemRef_Args2, "11111111-1111-1111-1111-111111111111", nameof(SolutionFolderModel)), ex.Message); + Assert.Equal(3, ex.Line); + Assert.Equal(4, ex.Column); + } } diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateFolderId.sln.txt b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateFolderId.sln.txt new file mode 100644 index 00000000..9c5a5cbe --- /dev/null +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateFolderId.sln.txt @@ -0,0 +1,10 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Apple", "Apple", "{11111111-1111-1111-1111-111111111111}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mango", "Mango", "{11111111-1111-1111-1111-111111111111}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Pear", "Pear", "{11111111-1111-1111-1111-111111111111}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Orange", "Orange", "{11111111-1111-1111-1111-111111111111}" +EndProject diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateFolderId.slnx.xml b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateFolderId.slnx.xml new file mode 100644 index 00000000..bdbd9979 --- /dev/null +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateFolderId.slnx.xml @@ -0,0 +1,6 @@ + + + + + +