diff --git a/SolutionPersistence.sln b/SolutionPersistence.sln deleted file mode 100644 index 1c61458e..00000000 --- a/SolutionPersistence.sln +++ /dev/null @@ -1,68 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29322.22 -MinimumVisualStudioVersion = 15.0.26124.0 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1CE9670B-D5FF-46A7-9D00-24E70E6ED48B}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - Directory.Build.props = Directory.Build.props - Directory.Build.targets = Directory.Build.targets - Directory.Packages.props = Directory.Packages.props - global.json = global.json - nuget.config = nuget.config - README.md = README.md - stylecop.json = stylecop.json - version.json = version.json - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9E154A29-1796-4B85-BD81-B6A385D8FF71}" - ProjectSection(SolutionItems) = preProject - src\.editorconfig = src\.editorconfig - src\Directory.Build.props = src\Directory.Build.props - src\Directory.Build.targets = src\Directory.Build.targets - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{36CCE840-6FE5-4DB9-A8D5-8CF3CB6D342A}" - ProjectSection(SolutionItems) = preProject - test\.editorconfig = test\.editorconfig - test\Directory.Build.props = test\Directory.Build.props - test\Directory.Build.targets = test\Directory.Build.targets - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7743A30D-6296-44C3-B8FB-369D37566D94}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.VisualStudio.SolutionPersistence", "src\Microsoft.VisualStudio.SolutionPersistence\Microsoft.VisualStudio.SolutionPersistence.csproj", "{43507C4F-E739-426F-8070-5260D668C3D9}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{1EBF1959-C483-46AC-ADB6-5D5A72544EDF}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.VisualStudio.SolutionPersistence.Tests", "test\Microsoft.VisualStudio.SolutionPersistence.Tests\Microsoft.VisualStudio.SolutionPersistence.Tests.csproj", "{9D224F03-C60B-49D5-84D9-FAF6913EFB9E}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {43507C4F-E739-426F-8070-5260D668C3D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {43507C4F-E739-426F-8070-5260D668C3D9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {43507C4F-E739-426F-8070-5260D668C3D9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {43507C4F-E739-426F-8070-5260D668C3D9}.Release|Any CPU.Build.0 = Release|Any CPU - {9D224F03-C60B-49D5-84D9-FAF6913EFB9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9D224F03-C60B-49D5-84D9-FAF6913EFB9E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9D224F03-C60B-49D5-84D9-FAF6913EFB9E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9D224F03-C60B-49D5-84D9-FAF6913EFB9E}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {9E154A29-1796-4B85-BD81-B6A385D8FF71} = {1CE9670B-D5FF-46A7-9D00-24E70E6ED48B} - {36CCE840-6FE5-4DB9-A8D5-8CF3CB6D342A} = {1CE9670B-D5FF-46A7-9D00-24E70E6ED48B} - {43507C4F-E739-426F-8070-5260D668C3D9} = {7743A30D-6296-44C3-B8FB-369D37566D94} - {9D224F03-C60B-49D5-84D9-FAF6913EFB9E} = {1EBF1959-C483-46AC-ADB6-5D5A72544EDF} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {E3944F6A-384B-4B0F-B93F-3BD513DC57BD} - EndGlobalSection -EndGlobal diff --git a/global.json b/global.json index 20b589f8..6306f36b 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.102", + "version": "9.0.200", "rollForward": "patch", "allowPrerelease": false }, diff --git a/src/Microsoft.VisualStudio.SolutionPersistence/Serializer/SlnV12/SlnFileV12Serializer.Reader.cs b/src/Microsoft.VisualStudio.SolutionPersistence/Serializer/SlnV12/SlnFileV12Serializer.Reader.cs index ed558a64..be070214 100644 --- a/src/Microsoft.VisualStudio.SolutionPersistence/Serializer/SlnV12/SlnFileV12Serializer.Reader.cs +++ b/src/Microsoft.VisualStudio.SolutionPersistence/Serializer/SlnV12/SlnFileV12Serializer.Reader.cs @@ -69,6 +69,11 @@ internal ValueTask ParseAsync(ISolutionSerializer serializer, str // Some property bags need to be loaded after all projects have been resolved. List<(SolutionItemModel, SolutionPropertyBag)> delayLoadProperties = []; + // Keep track of projects with duplicate ids so that we can + // duplicate the configuration after all projects have been loaded. + // This preserves the orginial buggy behavior of the solution parser. + List<(Guid NewId, SolutionProjectModel DuplicateProject)> fixedProjectIds = []; + try { using (solutionModel.SuspendProjectValidation()) @@ -87,7 +92,7 @@ internal ValueTask ParseAsync(ISolutionSerializer serializer, str case LineType.Project: this.TarnishIf(inProject); inProject = true; - currentProject = this.ReadProjectInfo(solutionModel, ref tokenizer); + currentProject = this.ReadProjectInfo(solutionModel, ref tokenizer, fixedProjectIds); break; case LineType.EndProject: @@ -200,6 +205,17 @@ internal ValueTask ParseAsync(ISolutionSerializer serializer, str this.TarnishIf(!item.AddSlnProperties(properties)); } + foreach ((Guid newId, SolutionProjectModel duplicateProject) in fixedProjectIds) + { + // The newly generated id will not have configurations, so make a + // copy of the original project configurations. + // This preserves the orginial buggy behavior of the solution parser. + if (solutionModel.FindItemById(newId) is SolutionProjectModel project) + { + project.ProjectConfigurationRules = duplicateProject.ProjectConfigurationRules; + } + } + VisualStudioProperties vsProperties = solutionModel.VisualStudioProperties; vsProperties.OpenWith = CommentToOpenWithVS(openWithVsVersion.AsSpan()); vsProperties.MinimumVersion = minVsVersion; @@ -218,6 +234,9 @@ internal ValueTask ParseAsync(ISolutionSerializer serializer, str return new ValueTask(solutionModel); + // Adds the property bag to the project or folder. + // Handles special cases for the sln parser. + // Returns false if there was an error reading the properties and the solution file should be considered tarnished. static bool AddProjectProperties( SolutionItemModel? currentProject, SolutionPropertyBag? currentPropertyBag, @@ -498,7 +517,7 @@ private bool ReadLine(out StringTokenizer lineScanner) return checkOnly ? null : new SolutionPropertyBag(sectionName.ToString(), scope); } - private SolutionItemModel ReadProjectInfo(SolutionModel solution, ref StringTokenizer tokenizer) + private SolutionItemModel ReadProjectInfo(SolutionModel solution, ref StringTokenizer tokenizer, List<(Guid NewId, SolutionProjectModel DuplicateProject)> fixedProjectIds) { // Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App1", "App1\App1.csproj", "{B0D4AB54-EB86-4C88-A2A4-C55D0C200244}" // ^ <- this is tokenizer pos. @@ -527,6 +546,8 @@ private SolutionItemModel ReadProjectInfo(SolutionModel solution, ref StringToke this.SolutionAssert(!projectUniqueId.IsEmpty, Errors.MissingProjectId); this.TarnishIf(!Guid.TryParse(projectUniqueId.ToString(), out Guid projectId)); + SolutionItemModel? duplicateItem = solution.FindItemById(projectId); + if (projectTypeId == ProjectTypeTable.SolutionFolder) { #pragma warning disable CS0618 // Type or member is obsolete (Temporaily create a potentially invalid solution folder until nested projects is interpreted.) @@ -534,7 +555,7 @@ private SolutionItemModel ReadProjectInfo(SolutionModel solution, ref StringToke #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) + if (duplicateItem is not null) { projectId = Guid.NewGuid(); this.tarnished = true; @@ -545,9 +566,27 @@ private SolutionItemModel ReadProjectInfo(SolutionModel solution, ref StringToke } else { + string path = PathExtensions.ConvertBackslashToModel(relativePath.ToString()); + + // This should error, or remove any configuration associated with the duplicate id. + // However some old parsers would just ignore the duplicate id and continue. + if (duplicateItem is SolutionFolderModel duplicateFolder) + { + duplicateFolder.Id = Guid.NewGuid(); + this.tarnished = true; + } + else if (duplicateItem is SolutionProjectModel duplicateProject) + { + projectId = CreateNewProjectId(solution, path); + this.tarnished = true; + + // Record the new project id so it's configuration can be duplicated. + fixedProjectIds.Add((projectId, duplicateProject)); + } + #pragma warning disable CS0618 // Type or member is obsolete (Temporaily create a potentially invalid solution folder until nested projects is interpreted.) SolutionProjectModel project = solution.AddSlnProject( - filePath: PathExtensions.ConvertBackslashToModel(relativePath.ToString()), + filePath: path, projectTypeId: projectTypeId, folder: null); #pragma warning restore CS0618 // Type or member is obsolete @@ -555,6 +594,13 @@ private SolutionItemModel ReadProjectInfo(SolutionModel solution, ref StringToke project.DisplayName = displayName.ToString(); return project; } + + // If creating a new project id, go ahead and try to use the slnx default id. + static Guid CreateNewProjectId(SolutionModel solution, string path) + { + Guid id = DefaultIdGenerator.CreateIdFrom(path); + return solution.FindItemById(id) is null ? id : Guid.NewGuid(); + } } // Condition that would mark solution file as "tarnished" diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/Format.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/Format.cs index 93cbf323..911d3ff2 100644 --- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/Format.cs +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/Format.cs @@ -14,12 +14,6 @@ public class Format [Fact] public async Task ConvertASCIItoUTF8Async() { - if (IsMono) - { - // Mono is not supported. - return; - } - const string asciiFolderName = "directory123"; const string utf8FolderName = "répertoire123"; diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/IdGenerator.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/IdGenerator.cs index a382ba22..ccf0133a 100644 --- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/IdGenerator.cs +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/IdGenerator.cs @@ -77,12 +77,6 @@ public void CheckGeneratedIds() [Fact] public async Task CheckModelIds() { - if (IsMono) - { - // Mono is not supported. - return; - } - SolutionModel solution = await SolutionSerializers.SlnXml.OpenAsync(SlnAssets.XmlSlnxMany.Stream, CancellationToken.None); SolutionProjectModel? urlProject = solution.FindProject("http://localhost:8080"); diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/InvalidSolutions.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/InvalidSolutions.cs index b5fc7a13..6a36ff37 100644 --- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/InvalidSolutions.cs +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/InvalidSolutions.cs @@ -29,12 +29,6 @@ public Task InvalidXmlSlnxAsync() [Fact] public async Task InvalidSlnxAsync() { - if (IsMono) - { - // Mono is not supported. - return; - } - ResourceStream wrongRoot = SlnAssets.LoadResource("Invalid/WrongRoot.slnx"); string wrongRootFile = wrongRoot.SaveResourceToTempFile(); @@ -55,12 +49,6 @@ public async Task InvalidSlnxAsync() [Fact] public async Task InvalidSlnAsync() { - if (IsMono) - { - // Mono is not supported. - return; - } - ResourceStream invalidSln = SlnAssets.LoadResource("Invalid/Invalid.sln"); string invalidSlnFile = invalidSln.SaveResourceToTempFile(); @@ -239,4 +227,96 @@ public async Task DuplicateFolderIdSlnxAsync() Assert.Equal(3, ex.Line); Assert.Equal(4, ex.Column); } + + /// + /// Ensure the slnx parser does fail if project id collides with folder id. + /// Expected behavior is the folder id is changed to a new value. + /// + [Fact] + public async Task DuplicateItemIdSlnAsync() + { + ResourceStream duplicateId = SlnAssets.LoadResource("Invalid/DuplicateItemId.sln"); + SolutionModel solution = await SolutionSerializers.SlnFileV12.OpenAsync(duplicateId.Stream, CancellationToken.None); + Assert.NotNull(solution.SerializerExtension); + Assert.True(solution.SerializerExtension.Tarnished); + + SolutionProjectModel? projectModel = solution.FindProject("DuplicateProjectId.csproj"); + Assert.NotNull(projectModel); + Assert.Equal(projectModel.Id, new Guid("11111111-1111-1111-1111-111111111111")); + + SolutionFolderModel? folderModel = solution.FindFolder("/DuplicateFolderId/"); + Assert.NotNull(folderModel); + Assert.NotEqual(folderModel.Id, new Guid("11111111-1111-1111-1111-111111111111")); + } + + /// + /// Recreate legacy bug behavior when duplicate project id guids are found in .sln files. + /// Use corrupt configuration and change second project id to new value. + /// + [Fact] + public async Task DuplicateProjectIdSlnAsync() + { + ResourceStream duplicateId = SlnAssets.LoadResource("Invalid/DuplicateProjectId.sln"); + SolutionModel solution = await SolutionSerializers.SlnFileV12.OpenAsync(duplicateId.Stream, CancellationToken.None); + Assert.NotNull(solution.SerializerExtension); + Assert.True(solution.SerializerExtension.Tarnished); + + SolutionProjectModel? project = solution.FindProject("DuplicateProjectId.csproj"); + Assert.NotNull(project); + Assert.Equal(project.Id, new Guid("8BADBEEF-1111-2222-3333-444444444444")); + + SolutionProjectModel? project2 = solution.FindProject("DuplicateProjectIdOpposite.csproj"); + Assert.NotNull(project2); + Assert.NotEqual(project2.Id, new Guid("8BADBEEF-2222-3333-4444-555555555555")); + + (string? buildType, string? platform, bool build, bool deploy) = project.GetProjectConfiguration(BuildTypeNames.Debug, PlatformNames.AnySpaceCPU); + + (string? buildType2, string? platform2, bool build2, bool deploy2) = project2.GetProjectConfiguration(BuildTypeNames.Debug, PlatformNames.AnySpaceCPU); + + // The configuration from the "opposite" project should be used by both as it appears last in the file. + Assert.Equal(BuildTypeNames.Release, buildType); + Assert.Equal(PlatformNames.AnySpaceCPU, platform); + Assert.True(build); + Assert.False(deploy); + + // The configuration from the "opposite" project should be used by both as it appears last in the file. + Assert.Equal(BuildTypeNames.Release, buildType2); + Assert.Equal(PlatformNames.AnySpaceCPU, platform2); + Assert.True(build2); + Assert.False(deploy2); + + // Compare the new model to the 'After' sln (Change the new Guid to FFFF to match). + project2.Id = new Guid("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"); + ResourceStream duplicateIdAfter = SlnAssets.LoadResource("DuplicateProjectId-After.sln"); + AssertSolutionsAreEqual(duplicateIdAfter.ToLines(), await solution.ToLinesAsync(SolutionSerializers.SlnFileV12)); + } + + /// + /// Ensure the slnx parser does fail on duplicate project guids. + /// + [Fact] + public async Task DuplicateProjectIdSlnxAsync() + { + ResourceStream duplicateId = SlnAssets.LoadResource("Invalid/DuplicateProjectId.slnx"); + SolutionException ex = await Assert.ThrowsAsync( + async () => _ = await SolutionSerializers.SlnXml.OpenAsync(duplicateId.Stream, CancellationToken.None)); + + Assert.StartsWith(string.Format(Errors.DuplicateItemRef_Args2, "8badbeef-1111-2222-3333-444444444444", nameof(SolutionProjectModel)), ex.Message); + Assert.Equal(5, ex.Line); + Assert.Equal(6, ex.Column); + } + + /// + /// Checks that an error is thrown if the solution contains an invalid project type id. + /// + [Fact] + public async Task InvalidProjectTypeSlnAsync() + { + ResourceStream invalidProjectType = SlnAssets.LoadResource("Invalid/InvalidProjectType.sln"); + SolutionException ex = await Assert.ThrowsAsync( + async () => _ = await SolutionSerializers.SlnFileV12.OpenAsync(invalidProjectType.Stream, CancellationToken.None)); + Assert.StartsWith(Errors.InvalidProjectType, ex.Message); + Assert.Equal(5, ex.Line); + Assert.Null(ex.Column); + } } diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/LegacyValues.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/LegacyValues.cs index d1298411..87624b4b 100644 --- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/LegacyValues.cs +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/LegacyValues.cs @@ -40,12 +40,6 @@ public async Task TrimVisualStudioPropertiesAsync() [Fact] public async Task IgnoreDisplayNameSlnx() { - if (IsMono) - { - // Mono is not supported. - return; - } - await ValidateModifiedSolutionAsync(CreateModifiedModel, SlnAssets.XmlSlnxSingleNativeProject, SlnAssets.XmlSlnxSingleNativeProject); static void CreateModifiedModel(SolutionModel solution) @@ -64,12 +58,6 @@ static void CreateModifiedModel(SolutionModel solution) [Fact] public async Task IgnoreDisplayNameSln() { - if (IsMono) - { - // Mono is not supported. - return; - } - await ValidateModifiedSolutionAsync(CreateModifiedModel, SlnAssets.ClassicSlnSingleNativeProject, SlnAssets.ClassicSlnSingleNativeProject, SolutionSerializers.SlnFileV12); static void CreateModifiedModel(SolutionModel solution) diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/MakeSlnx.MakeSlnxFixture.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/MakeSlnx.MakeSlnxFixture.cs index e145774e..80c6ce15 100644 --- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/MakeSlnx.MakeSlnxFixture.cs +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/MakeSlnx.MakeSlnxFixture.cs @@ -24,7 +24,10 @@ public class MakeSlnxFixture /// public MakeSlnxFixture() { - string outputDirectory = Path.Combine(Path.GetTempPath(), OutputDirectory); + // Split output based on the .NET version (so we can run tests in parallel) + string netVersion = "net" + Environment.Version.ToString(3); + + string outputDirectory = Path.Combine(Path.GetTempPath(), OutputDirectory, netVersion); if (Directory.Exists(outputDirectory)) { Directory.Delete(outputDirectory, true); diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/MakeSlnx.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/MakeSlnx.cs index e3a3cf77..efa18630 100644 --- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/MakeSlnx.cs +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/MakeSlnx.cs @@ -32,12 +32,6 @@ public sealed partial class MakeSlnx(MakeSlnx.MakeSlnxFixture fixture) : IClassF [Trait("TestCategory", "FailsInCloudTest")] public async Task ConvertSlnToSlnxAsync(ResourceName slnFileName) { - if (IsMono) - { - // Mono is not supported. - return; - } - ResourceStream slnFile = slnFileName.Load(); int sampleFileSize = checked((int)slnFile.Stream.Length); string slnToSlnxFile = Path.ChangeExtension(Path.Join(fixture.SlnToSlnxDirectory, slnFile.Name), SolutionSerializers.SlnXml.DefaultFileExtension); @@ -65,12 +59,6 @@ public async Task ConvertSlnToSlnxAsync(ResourceName slnFileName) [Trait("TestCategory", "FailsInCloudTest")] public async Task ConvertSlnxToSlnAsync(ResourceName slnxFileName) { - if (IsMono) - { - // Mono is not supported. - return; - } - ResourceStream slnxFile = slnxFileName.Load(); string slnxToSlnFile = Path.ChangeExtension(Path.Join(fixture.SlnxToSlnDirectory, slnxFile.Name), SolutionSerializers.SlnFileV12.DefaultFileExtension); diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/ManipulateXmlKitchenSink.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/ManipulateXmlKitchenSink.cs index b3fdd143..7d63791f 100644 --- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/ManipulateXmlKitchenSink.cs +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/ManipulateXmlKitchenSink.cs @@ -20,12 +20,6 @@ public class ManipulateXmlKitchenSink [Fact] public async Task AddConfigurations() { - if (IsMono) - { - // Mono is not supported. - return; - } - await ValidateModifiedSolutionAsync( CreateModifiedModel, SlnAssets.LoadResource("SlnxWhitespace/KitchenSink.slnx"), diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/PathSlashes.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/PathSlashes.cs index 2bd2c228..3a604976 100644 --- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/PathSlashes.cs +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/PathSlashes.cs @@ -17,12 +17,6 @@ public sealed class PathSlashes [Fact] public async Task VerifyModelSlahesAsync() { - if (IsMono) - { - // Mono is not supported. - return; - } - ResourceStream slashesResource = SlnAssets.LoadResource("Invalid/PathSlashes.slnx"); SolutionModel solution = await SolutionSerializers.SlnXml.OpenAsync(slashesResource.Stream, CancellationToken.None); @@ -64,12 +58,6 @@ public async Task VerifyModelSlahesAsync() [Fact] public async Task ProjectReferencesFixedUpAsync() { - if (IsMono) - { - // Mono is not supported. - return; - } - FileContents fixedSlashesResource = SlnAssets.LoadResource("PathSlashes.slnx").ToLines(); // Save the Model back to stream. diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/Project.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/Project.cs index d63dc4ee..d8b624b2 100644 --- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/Project.cs +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/Project.cs @@ -58,12 +58,6 @@ public void AddProject() [Fact] public async Task RemoveProjectAsync() { - if (IsMono) - { - // Mono is not supported. - return; - } - SolutionModel solution = await SolutionSerializers.SlnXml.OpenAsync(SlnAssets.XmlSlnxEverything.Stream, CancellationToken.None); string toRemove = Path.Join("src", "CoreMauiApp", "CoreMauiApp.csproj"); @@ -84,12 +78,6 @@ public async Task RemoveProjectAsync() [Fact] public async Task MoveProjectAsync() { - if (IsMono) - { - // Mono is not supported. - return; - } - SolutionModel solution = await SolutionSerializers.SlnXml.OpenAsync(SlnAssets.XmlSlnxEverything.Stream, CancellationToken.None); string toMove = Path.Join("BlazorApp1", "BlazorApp1.csproj"); diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/RoundTripClassicSln.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/RoundTripClassicSln.cs index 5e6c80f4..5c495e30 100644 --- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/RoundTripClassicSln.cs +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/RoundTripClassicSln.cs @@ -56,12 +56,6 @@ public Task AllClassicSolutionAsync(ResourceName sampleFile) /// Task to track the asynchronous call status. private static async Task TestRoundTripSerializerAsync(ResourceStream slnStream) { - if (IsMono) - { - // Mono is not supported. - return; - } - FileContents originalSolution = slnStream.ToLines(); // Open the Model from stream. diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/RoundTripClassicSlnThruSlnxStream.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/RoundTripClassicSlnThruSlnxStream.cs index dc44b54f..4580cd11 100644 --- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/RoundTripClassicSlnThruSlnxStream.cs +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/RoundTripClassicSlnThruSlnxStream.cs @@ -46,12 +46,6 @@ private static async Task TestRoundTripSerializerAsync( ResourceStream slnStream, ResourceStream viaSlnxStream) { - if (IsMono) - { - // Mono is not supported. - return; - } - FileContents originalSolution = slnStream.ToLines(); // Open the Model from stream. diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/RoundTripXmlSlnx.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/RoundTripXmlSlnx.cs index 2b86b671..6a588175 100644 --- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/RoundTripXmlSlnx.cs +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/RoundTripXmlSlnx.cs @@ -60,12 +60,6 @@ public Task AllXmlSolutionAsync(ResourceName sampleFile) private static async Task TestRoundTripSerializerAsync(ResourceStream slnStream) { - if (IsMono) - { - // Mono is not supported. - return; - } - // Open the Model from stream. SolutionModel model = await SolutionSerializers.SlnXml.OpenAsync(slnStream.Stream, CancellationToken.None); AssertNotTarnished(model); diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/RoundTripXmlSlnxThruModelCopy.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/RoundTripXmlSlnxThruModelCopy.cs index 24987ff0..1f79e3e3 100644 --- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/RoundTripXmlSlnxThruModelCopy.cs +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/RoundTripXmlSlnxThruModelCopy.cs @@ -16,12 +16,6 @@ public class RoundTripXmlSlnxThruModelCopy [Fact] public Task CommentsAsync() { - if (IsMono) - { - // Mono is not supported. - return Task.CompletedTask; - } - return Assert.ThrowsAsync(() => TestRoundTripSerializerAsync(SlnAssets.XmlSlnxComments)); } @@ -57,12 +51,6 @@ public Task CommentsAsync() private static async Task TestRoundTripSerializerAsync(ResourceStream slnStream) { - if (IsMono) - { - // Mono is not supported. - return; - } - // Open the Model from stream. SolutionModel model = await SolutionSerializers.SlnXml.OpenAsync(slnStream.Stream, CancellationToken.None); AssertNotTarnished(model); diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/Samples.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/Samples.cs index 061ff31b..a8d08974 100644 --- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/Samples.cs +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Serialization/Samples.cs @@ -14,12 +14,6 @@ public class Samples [Fact] public async Task ConvertToSlnx() { - if (IsMono) - { - // Mono is not supported. - return; - } - string filePath = SlnAssets.ClassicSlnEverything.SaveResourceToTempFile(); string slnxFilePath = Path.ChangeExtension(filePath, SolutionSerializers.SlnXml.DefaultFileExtension); diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/DuplicateProjectId-After.sln.txt b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/DuplicateProjectId-After.sln.txt new file mode 100644 index 00000000..c8584a59 --- /dev/null +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/DuplicateProjectId-After.sln.txt @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.35819.311 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DuplicateProjectId", "DuplicateProjectId.csproj", "{8BADBEEF-1111-2222-3333-444444444444}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DuplicateProjectIdOpposite", "DuplicateProjectIdOpposite.csproj", "{FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8BADBEEF-1111-2222-3333-444444444444}.Debug|Any CPU.ActiveCfg = Release|Any CPU + {8BADBEEF-1111-2222-3333-444444444444}.Debug|Any CPU.Build.0 = Release|Any CPU + {8BADBEEF-1111-2222-3333-444444444444}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {8BADBEEF-1111-2222-3333-444444444444}.Release|Any CPU.Build.0 = Debug|Any CPU + {FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF}.Debug|Any CPU.ActiveCfg = Release|Any CPU + {FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF}.Debug|Any CPU.Build.0 = Release|Any CPU + {FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF}.Release|Any CPU.Build.0 = Debug|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E0F1003A-31EB-42B8-8684-F82D4D7B9108} + EndGlobalSection +EndGlobal diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateItemId.sln.txt b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateItemId.sln.txt new file mode 100644 index 00000000..a7af068b --- /dev/null +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateItemId.sln.txt @@ -0,0 +1,26 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.35819.311 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DuplicateFolderId", "DuplicateFolderId", "{11111111-1111-1111-1111-111111111111}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DuplicateProjectId", "DuplicateProjectId.csproj", "{11111111-1111-1111-1111-111111111111}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {11111111-1111-1111-1111-111111111111}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {11111111-1111-1111-1111-111111111111}.Debug|Any CPU.Build.0 = Debug|Any CPU + {11111111-1111-1111-1111-111111111111}.Release|Any CPU.ActiveCfg = Release|Any CPU + {11111111-1111-1111-1111-111111111111}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E0F1003A-31EB-42B8-8684-F82D4D7B9108} + EndGlobalSection +EndGlobal diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateProjectId.sln.txt b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateProjectId.sln.txt new file mode 100644 index 00000000..0ce6ccc5 --- /dev/null +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateProjectId.sln.txt @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.35819.311 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DuplicateProjectId", "DuplicateProjectId.csproj", "{8BADBEEF-1111-2222-3333-444444444444}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DuplicateProjectIdOpposite", "DuplicateProjectIdOpposite.csproj", "{8BADBEEF-1111-2222-3333-444444444444}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8BADBEEF-1111-2222-3333-444444444444}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8BADBEEF-1111-2222-3333-444444444444}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8BADBEEF-1111-2222-3333-444444444444}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8BADBEEF-1111-2222-3333-444444444444}.Release|Any CPU.Build.0 = Release|Any CPU + {8BADBEEF-1111-2222-3333-444444444444}.Debug|Any CPU.ActiveCfg = Release|Any CPU + {8BADBEEF-1111-2222-3333-444444444444}.Debug|Any CPU.Build.0 = Release|Any CPU + {8BADBEEF-1111-2222-3333-444444444444}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {8BADBEEF-1111-2222-3333-444444444444}.Release|Any CPU.Build.0 = Debug|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E0F1003A-31EB-42B8-8684-F82D4D7B9108} + EndGlobalSection +EndGlobal diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateProjectId.slnx.xml b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateProjectId.slnx.xml new file mode 100644 index 00000000..5f6baea4 --- /dev/null +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/DuplicateProjectId.slnx.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/InvalidProjectType.sln.txt b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/InvalidProjectType.sln.txt new file mode 100644 index 00000000..0d26d91d --- /dev/null +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/SlnAssets/Invalid/InvalidProjectType.sln.txt @@ -0,0 +1,18 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.35819.311 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("NotAGuid") = "InvalidProjectType", "InvalidProjectType.csproj", "{8A1FD751-4BAA-467E-A9FA-77239CA44A61}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {CFB61351-9264-45B3-BBE0-BAC5D49C87CE} + EndGlobalSection +EndGlobal diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Utilities/SlnAssets.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Utilities/SlnAssets.cs index 9aef8f25..093f5ad5 100644 --- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Utilities/SlnAssets.cs +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Utilities/SlnAssets.cs @@ -13,6 +13,8 @@ internal static class SlnAssets public static ResourceName[] ClassicSlnFiles => GetAllSampleFiles(".sln").ToArray(); + public static ResourceName[] XmlSlnxFiles => GetAllSampleFiles(".slnx").ToArray(); + #region Sample Classic Sln // Empty VS Solution template. ASCII encoding. @@ -97,8 +99,6 @@ internal static class SlnAssets // A solution with missing configurations. public static ResourceStream XmlSlnxMissingConfigurations => LoadResource("Configurations/MissingConfigurations.slnx"); - public static ResourceName[] XmlSlnxFiles => GetAllSampleFiles(".slnx").ToArray(); - #endregion #region Test result Slnx diff --git a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Utilities/SlnTestHelper.cs b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Utilities/SlnTestHelper.cs index ec1d182e..5497e8ae 100644 --- a/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Utilities/SlnTestHelper.cs +++ b/test/Microsoft.VisualStudio.SolutionPersistence.Tests/Utilities/SlnTestHelper.cs @@ -11,15 +11,6 @@ namespace Utilities; /// internal static class SlnTestHelper { - /// - /// We do not support mono, but xunit testing tries to run the .NET Framework tests on mono when running on Linux/Mac. - /// -#if NETFRAMEWORK - internal static bool IsMono = Environment.OSVersion.Platform != PlatformID.Win32NT; -#else - internal static bool IsMono = false; -#endif - /// /// Converts the model to it's file contents using the specified serializer. ///