diff --git a/src/Cake.Cli.Tests/VerifyConfig.cs b/src/Cake.Cli.Tests/VerifyConfig.cs
index a28a9cb467..9946ee2346 100644
--- a/src/Cake.Cli.Tests/VerifyConfig.cs
+++ b/src/Cake.Cli.Tests/VerifyConfig.cs
@@ -14,7 +14,10 @@ public static class VerifyConfig
[ModuleInitializer]
public static void Init()
{
- DerivePathInfo(Expectations.Initialize);
- VerifyDiffPlex.Initialize(OutputType.Compact);
+ if (!VerifyDiffPlex.Initialized)
+ {
+ VerifyDiffPlex.Initialize(OutputType.Compact);
+ DerivePathInfo(Expectations.Initialize);
+ }
}
}
\ No newline at end of file
diff --git a/src/Cake.Common.Tests/Cake.Common.Tests.csproj b/src/Cake.Common.Tests/Cake.Common.Tests.csproj
index 495523babc..84c82340e6 100644
--- a/src/Cake.Common.Tests/Cake.Common.Tests.csproj
+++ b/src/Cake.Common.Tests/Cake.Common.Tests.csproj
@@ -3,6 +3,7 @@
Cake.Common.Tests
true
true
+ enable
@@ -17,6 +18,9 @@
+
+
+
all
@@ -48,4 +52,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSln.Should_Properly_Parse_Folders.verified.txt b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSln.Should_Properly_Parse_Folders.verified.txt
new file mode 100644
index 0000000000..06946dbfe5
--- /dev/null
+++ b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSln.Should_Properly_Parse_Folders.verified.txt
@@ -0,0 +1,72 @@
+{
+ Version: Format Version 12.00,
+ VisualStudioVersion: 14.0.25123.0,
+ MinimumVisualStudioVersion: 10.0.40219.1,
+ Projects: [
+ {
+ Items: [
+ {
+ Id: Guid_1,
+ Name: dummy,
+ Path: /Working/src/dummy/dummy.csproj,
+ Type: Guid_2
+ }
+ ],
+ Id: Guid_3,
+ Name: src,
+ Path: /Working/src,
+ Type: Guid_4,
+ Parent: null
+ },
+ {
+ Items: [
+ {
+ Id: Guid_5,
+ Name: dummy.Tests,
+ Path: /Working/test/dummy.Tests/dummy.Tests.csproj,
+ Type: Guid_2
+ }
+ ],
+ Id: Guid_6,
+ Name: test,
+ Path: /Working/test,
+ Type: Guid_4,
+ Parent: null
+ },
+ {
+ Id: Guid_1,
+ Name: dummy,
+ Path: /Working/src/dummy/dummy.csproj,
+ Type: Guid_2,
+ Parent: {
+ Items: [],
+ Id: Guid_3,
+ Name: src,
+ Path: /Working/src,
+ Type: Guid_4,
+ Parent: null
+ }
+ },
+ {
+ Id: Guid_5,
+ Name: dummy.Tests,
+ Path: /Working/test/dummy.Tests/dummy.Tests.csproj,
+ Type: Guid_2,
+ Parent: {
+ Items: [],
+ Id: Guid_6,
+ Name: test,
+ Path: /Working/test,
+ Type: Guid_4,
+ Parent: null
+ }
+ },
+ {
+ Id: Guid_7,
+ Name: executable,
+ Path: /Working/executable/executable.csproj,
+ Type: Guid_2,
+ Parent: null
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSln.Should_Properly_Parse_Projects.verified.txt b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSln.Should_Properly_Parse_Projects.verified.txt
new file mode 100644
index 0000000000..06946dbfe5
--- /dev/null
+++ b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSln.Should_Properly_Parse_Projects.verified.txt
@@ -0,0 +1,72 @@
+{
+ Version: Format Version 12.00,
+ VisualStudioVersion: 14.0.25123.0,
+ MinimumVisualStudioVersion: 10.0.40219.1,
+ Projects: [
+ {
+ Items: [
+ {
+ Id: Guid_1,
+ Name: dummy,
+ Path: /Working/src/dummy/dummy.csproj,
+ Type: Guid_2
+ }
+ ],
+ Id: Guid_3,
+ Name: src,
+ Path: /Working/src,
+ Type: Guid_4,
+ Parent: null
+ },
+ {
+ Items: [
+ {
+ Id: Guid_5,
+ Name: dummy.Tests,
+ Path: /Working/test/dummy.Tests/dummy.Tests.csproj,
+ Type: Guid_2
+ }
+ ],
+ Id: Guid_6,
+ Name: test,
+ Path: /Working/test,
+ Type: Guid_4,
+ Parent: null
+ },
+ {
+ Id: Guid_1,
+ Name: dummy,
+ Path: /Working/src/dummy/dummy.csproj,
+ Type: Guid_2,
+ Parent: {
+ Items: [],
+ Id: Guid_3,
+ Name: src,
+ Path: /Working/src,
+ Type: Guid_4,
+ Parent: null
+ }
+ },
+ {
+ Id: Guid_5,
+ Name: dummy.Tests,
+ Path: /Working/test/dummy.Tests/dummy.Tests.csproj,
+ Type: Guid_2,
+ Parent: {
+ Items: [],
+ Id: Guid_6,
+ Name: test,
+ Path: /Working/test,
+ Type: Guid_4,
+ Parent: null
+ }
+ },
+ {
+ Id: Guid_7,
+ Name: executable,
+ Path: /Working/executable/executable.csproj,
+ Type: Guid_2,
+ Parent: null
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSln.Should_Properly_Parse_Projects_With_Absolute_Path.verified.txt b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSln.Should_Properly_Parse_Projects_With_Absolute_Path.verified.txt
new file mode 100644
index 0000000000..68e19a45fc
--- /dev/null
+++ b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSln.Should_Properly_Parse_Projects_With_Absolute_Path.verified.txt
@@ -0,0 +1,14 @@
+{
+ Version: Format Version 12.00,
+ VisualStudioVersion: 16.0.31702.278,
+ MinimumVisualStudioVersion: 10.0.40219.1,
+ Projects: [
+ {
+ Id: Guid_1,
+ Name: dummy,
+ Path: C:/project/dummy/src/dummy/dummy.csproj,
+ Type: Guid_2,
+ Parent: null
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSln.Should_Properly_Parse_Projects_With_Empty_Lines.verified.txt b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSln.Should_Properly_Parse_Projects_With_Empty_Lines.verified.txt
new file mode 100644
index 0000000000..06946dbfe5
--- /dev/null
+++ b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSln.Should_Properly_Parse_Projects_With_Empty_Lines.verified.txt
@@ -0,0 +1,72 @@
+{
+ Version: Format Version 12.00,
+ VisualStudioVersion: 14.0.25123.0,
+ MinimumVisualStudioVersion: 10.0.40219.1,
+ Projects: [
+ {
+ Items: [
+ {
+ Id: Guid_1,
+ Name: dummy,
+ Path: /Working/src/dummy/dummy.csproj,
+ Type: Guid_2
+ }
+ ],
+ Id: Guid_3,
+ Name: src,
+ Path: /Working/src,
+ Type: Guid_4,
+ Parent: null
+ },
+ {
+ Items: [
+ {
+ Id: Guid_5,
+ Name: dummy.Tests,
+ Path: /Working/test/dummy.Tests/dummy.Tests.csproj,
+ Type: Guid_2
+ }
+ ],
+ Id: Guid_6,
+ Name: test,
+ Path: /Working/test,
+ Type: Guid_4,
+ Parent: null
+ },
+ {
+ Id: Guid_1,
+ Name: dummy,
+ Path: /Working/src/dummy/dummy.csproj,
+ Type: Guid_2,
+ Parent: {
+ Items: [],
+ Id: Guid_3,
+ Name: src,
+ Path: /Working/src,
+ Type: Guid_4,
+ Parent: null
+ }
+ },
+ {
+ Id: Guid_5,
+ Name: dummy.Tests,
+ Path: /Working/test/dummy.Tests/dummy.Tests.csproj,
+ Type: Guid_2,
+ Parent: {
+ Items: [],
+ Id: Guid_6,
+ Name: test,
+ Path: /Working/test,
+ Type: Guid_4,
+ Parent: null
+ }
+ },
+ {
+ Id: Guid_7,
+ Name: executable,
+ Path: /Working/executable/executable.csproj,
+ Type: Guid_2,
+ Parent: null
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSln.Should_Properly_Parse_Relation_Between_Project_And_Folder.verified.txt b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSln.Should_Properly_Parse_Relation_Between_Project_And_Folder.verified.txt
new file mode 100644
index 0000000000..06946dbfe5
--- /dev/null
+++ b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSln.Should_Properly_Parse_Relation_Between_Project_And_Folder.verified.txt
@@ -0,0 +1,72 @@
+{
+ Version: Format Version 12.00,
+ VisualStudioVersion: 14.0.25123.0,
+ MinimumVisualStudioVersion: 10.0.40219.1,
+ Projects: [
+ {
+ Items: [
+ {
+ Id: Guid_1,
+ Name: dummy,
+ Path: /Working/src/dummy/dummy.csproj,
+ Type: Guid_2
+ }
+ ],
+ Id: Guid_3,
+ Name: src,
+ Path: /Working/src,
+ Type: Guid_4,
+ Parent: null
+ },
+ {
+ Items: [
+ {
+ Id: Guid_5,
+ Name: dummy.Tests,
+ Path: /Working/test/dummy.Tests/dummy.Tests.csproj,
+ Type: Guid_2
+ }
+ ],
+ Id: Guid_6,
+ Name: test,
+ Path: /Working/test,
+ Type: Guid_4,
+ Parent: null
+ },
+ {
+ Id: Guid_1,
+ Name: dummy,
+ Path: /Working/src/dummy/dummy.csproj,
+ Type: Guid_2,
+ Parent: {
+ Items: [],
+ Id: Guid_3,
+ Name: src,
+ Path: /Working/src,
+ Type: Guid_4,
+ Parent: null
+ }
+ },
+ {
+ Id: Guid_5,
+ Name: dummy.Tests,
+ Path: /Working/test/dummy.Tests/dummy.Tests.csproj,
+ Type: Guid_2,
+ Parent: {
+ Items: [],
+ Id: Guid_6,
+ Name: test,
+ Path: /Working/test,
+ Type: Guid_4,
+ Parent: null
+ }
+ },
+ {
+ Id: Guid_7,
+ Name: executable,
+ Path: /Working/executable/executable.csproj,
+ Type: Guid_2,
+ Parent: null
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Folders.verified.txt b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Folders.verified.txt
new file mode 100644
index 0000000000..53421f1f50
--- /dev/null
+++ b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Folders.verified.txt
@@ -0,0 +1,72 @@
+{
+ Version: ,
+ VisualStudioVersion: ,
+ MinimumVisualStudioVersion: ,
+ Projects: [
+ {
+ Items: [
+ {
+ Id: ,
+ Name: dummy,
+ Path: /Working/src/dummy/dummy.csproj,
+ Type: Guid_1
+ }
+ ],
+ Id: ,
+ Name: src,
+ Path: /Working/src,
+ Type: Guid_2,
+ Parent: null
+ },
+ {
+ Id: ,
+ Name: dummy,
+ Path: /Working/src/dummy/dummy.csproj,
+ Type: Guid_1,
+ Parent: {
+ Items: [],
+ Id: ,
+ Name: src,
+ Path: /Working/src,
+ Type: Guid_2,
+ Parent: null
+ }
+ },
+ {
+ Items: [
+ {
+ Id: ,
+ Name: dummy.Tests,
+ Path: /Working/test/dummy.Tests/dummy.Tests.csproj,
+ Type: Guid_1
+ }
+ ],
+ Id: ,
+ Name: test,
+ Path: /Working/test,
+ Type: Guid_2,
+ Parent: null
+ },
+ {
+ Id: ,
+ Name: dummy.Tests,
+ Path: /Working/test/dummy.Tests/dummy.Tests.csproj,
+ Type: Guid_1,
+ Parent: {
+ Items: [],
+ Id: ,
+ Name: test,
+ Path: /Working/test,
+ Type: Guid_2,
+ Parent: null
+ }
+ },
+ {
+ Id: ,
+ Name: executable,
+ Path: /Working/executable/executable.csproj,
+ Type: Guid_1,
+ Parent: null
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Projects.verified.txt b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Projects.verified.txt
new file mode 100644
index 0000000000..53421f1f50
--- /dev/null
+++ b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Projects.verified.txt
@@ -0,0 +1,72 @@
+{
+ Version: ,
+ VisualStudioVersion: ,
+ MinimumVisualStudioVersion: ,
+ Projects: [
+ {
+ Items: [
+ {
+ Id: ,
+ Name: dummy,
+ Path: /Working/src/dummy/dummy.csproj,
+ Type: Guid_1
+ }
+ ],
+ Id: ,
+ Name: src,
+ Path: /Working/src,
+ Type: Guid_2,
+ Parent: null
+ },
+ {
+ Id: ,
+ Name: dummy,
+ Path: /Working/src/dummy/dummy.csproj,
+ Type: Guid_1,
+ Parent: {
+ Items: [],
+ Id: ,
+ Name: src,
+ Path: /Working/src,
+ Type: Guid_2,
+ Parent: null
+ }
+ },
+ {
+ Items: [
+ {
+ Id: ,
+ Name: dummy.Tests,
+ Path: /Working/test/dummy.Tests/dummy.Tests.csproj,
+ Type: Guid_1
+ }
+ ],
+ Id: ,
+ Name: test,
+ Path: /Working/test,
+ Type: Guid_2,
+ Parent: null
+ },
+ {
+ Id: ,
+ Name: dummy.Tests,
+ Path: /Working/test/dummy.Tests/dummy.Tests.csproj,
+ Type: Guid_1,
+ Parent: {
+ Items: [],
+ Id: ,
+ Name: test,
+ Path: /Working/test,
+ Type: Guid_2,
+ Parent: null
+ }
+ },
+ {
+ Id: ,
+ Name: executable,
+ Path: /Working/executable/executable.csproj,
+ Type: Guid_1,
+ Parent: null
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Projects_With_Absolute_Path.verified.txt b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Projects_With_Absolute_Path.verified.txt
new file mode 100644
index 0000000000..1c24bdfc2f
--- /dev/null
+++ b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Projects_With_Absolute_Path.verified.txt
@@ -0,0 +1,14 @@
+{
+ Version: ,
+ VisualStudioVersion: ,
+ MinimumVisualStudioVersion: ,
+ Projects: [
+ {
+ Id: ,
+ Name: dummy,
+ Path: C:/project/dummy/src/dummy/dummy.csproj,
+ Type: Guid_1,
+ Parent: null
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Projects_With_Different_Type_Id.verified.txt b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Projects_With_Different_Type_Id.verified.txt
new file mode 100644
index 0000000000..d5050fbcdd
--- /dev/null
+++ b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Projects_With_Different_Type_Id.verified.txt
@@ -0,0 +1,14 @@
+{
+ Version: ,
+ VisualStudioVersion: ,
+ MinimumVisualStudioVersion: ,
+ Projects: [
+ {
+ Id: ,
+ Name: WebApplication,
+ Path: /Working/WebApplication/WebApplication.csproj,
+ Type: Guid_1,
+ Parent: null
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Projects_With_Empty_Lines.verified.txt b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Projects_With_Empty_Lines.verified.txt
new file mode 100644
index 0000000000..53421f1f50
--- /dev/null
+++ b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Projects_With_Empty_Lines.verified.txt
@@ -0,0 +1,72 @@
+{
+ Version: ,
+ VisualStudioVersion: ,
+ MinimumVisualStudioVersion: ,
+ Projects: [
+ {
+ Items: [
+ {
+ Id: ,
+ Name: dummy,
+ Path: /Working/src/dummy/dummy.csproj,
+ Type: Guid_1
+ }
+ ],
+ Id: ,
+ Name: src,
+ Path: /Working/src,
+ Type: Guid_2,
+ Parent: null
+ },
+ {
+ Id: ,
+ Name: dummy,
+ Path: /Working/src/dummy/dummy.csproj,
+ Type: Guid_1,
+ Parent: {
+ Items: [],
+ Id: ,
+ Name: src,
+ Path: /Working/src,
+ Type: Guid_2,
+ Parent: null
+ }
+ },
+ {
+ Items: [
+ {
+ Id: ,
+ Name: dummy.Tests,
+ Path: /Working/test/dummy.Tests/dummy.Tests.csproj,
+ Type: Guid_1
+ }
+ ],
+ Id: ,
+ Name: test,
+ Path: /Working/test,
+ Type: Guid_2,
+ Parent: null
+ },
+ {
+ Id: ,
+ Name: dummy.Tests,
+ Path: /Working/test/dummy.Tests/dummy.Tests.csproj,
+ Type: Guid_1,
+ Parent: {
+ Items: [],
+ Id: ,
+ Name: test,
+ Path: /Working/test,
+ Type: Guid_2,
+ Parent: null
+ }
+ },
+ {
+ Id: ,
+ Name: executable,
+ Path: /Working/executable/executable.csproj,
+ Type: Guid_1,
+ Parent: null
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Relation_Between_Project_And_Folder.verified.txt b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Relation_Between_Project_And_Folder.verified.txt
new file mode 100644
index 0000000000..53421f1f50
--- /dev/null
+++ b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Relation_Between_Project_And_Folder.verified.txt
@@ -0,0 +1,72 @@
+{
+ Version: ,
+ VisualStudioVersion: ,
+ MinimumVisualStudioVersion: ,
+ Projects: [
+ {
+ Items: [
+ {
+ Id: ,
+ Name: dummy,
+ Path: /Working/src/dummy/dummy.csproj,
+ Type: Guid_1
+ }
+ ],
+ Id: ,
+ Name: src,
+ Path: /Working/src,
+ Type: Guid_2,
+ Parent: null
+ },
+ {
+ Id: ,
+ Name: dummy,
+ Path: /Working/src/dummy/dummy.csproj,
+ Type: Guid_1,
+ Parent: {
+ Items: [],
+ Id: ,
+ Name: src,
+ Path: /Working/src,
+ Type: Guid_2,
+ Parent: null
+ }
+ },
+ {
+ Items: [
+ {
+ Id: ,
+ Name: dummy.Tests,
+ Path: /Working/test/dummy.Tests/dummy.Tests.csproj,
+ Type: Guid_1
+ }
+ ],
+ Id: ,
+ Name: test,
+ Path: /Working/test,
+ Type: Guid_2,
+ Parent: null
+ },
+ {
+ Id: ,
+ Name: dummy.Tests,
+ Path: /Working/test/dummy.Tests/dummy.Tests.csproj,
+ Type: Guid_1,
+ Parent: {
+ Items: [],
+ Id: ,
+ Name: test,
+ Path: /Working/test,
+ Type: Guid_2,
+ Parent: null
+ }
+ },
+ {
+ Id: ,
+ Name: executable,
+ Path: /Working/executable/executable.csproj,
+ Type: Guid_1,
+ Parent: null
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Solution_With_Nested_Solution_Folders.verified.txt b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Solution_With_Nested_Solution_Folders.verified.txt
new file mode 100644
index 0000000000..bdd23783d5
--- /dev/null
+++ b/src/Cake.Common.Tests/Expectations/SolutionParserTests.TheParseMethodForSlnx.Should_Properly_Parse_Solution_With_Nested_Solution_Folders.verified.txt
@@ -0,0 +1,44 @@
+{
+ Version: ,
+ VisualStudioVersion: ,
+ MinimumVisualStudioVersion: ,
+ Projects: [
+ {
+ Items: [],
+ Id: ,
+ Name: SolutionFolder1,
+ Path: /Working/SolutionFolder1,
+ Type: Guid_1,
+ Parent: null
+ },
+ {
+ Items: [
+ {
+ Id: ,
+ Name: ClassLibraryNestedSolutionFolder,
+ Path: /Working/ClassLibraryNestedSolutionFolder/ClassLibraryNestedSolutionFolder.csproj,
+ Type: Guid_2
+ }
+ ],
+ Id: ,
+ Name: NestedSolutionFolder,
+ Path: /Working/SolutionFolder1/NestedSolutionFolder,
+ Type: Guid_1,
+ Parent: null
+ },
+ {
+ Id: ,
+ Name: ClassLibraryNestedSolutionFolder,
+ Path: /Working/ClassLibraryNestedSolutionFolder/ClassLibraryNestedSolutionFolder.csproj,
+ Type: Guid_2,
+ Parent: {
+ Items: [],
+ Id: ,
+ Name: NestedSolutionFolder,
+ Path: /Working/SolutionFolder1/NestedSolutionFolder,
+ Type: Guid_1,
+ Parent: null
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/Cake.Common.Tests/Fixtures/Solution/SolutionParserFixture.cs b/src/Cake.Common.Tests/Fixtures/Solution/SolutionParserFixture.cs
index cc8a0b265f..b42294d100 100644
--- a/src/Cake.Common.Tests/Fixtures/Solution/SolutionParserFixture.cs
+++ b/src/Cake.Common.Tests/Fixtures/Solution/SolutionParserFixture.cs
@@ -26,5 +26,11 @@ public FilePath WithSolutionFile(string slnContent)
var file = FileSystem.CreateFile("/Working/dummySolution.sln").SetContent(slnContent);
return file.Path;
}
+
+ public FilePath WithXmlSolutionFile(string slnxContent)
+ {
+ var file = FileSystem.CreateFile("/Working/dummySolution.slnx").SetContent(slnxContent);
+ return file.Path;
+ }
}
}
\ No newline at end of file
diff --git a/src/Cake.Common.Tests/Properties/Resources.Designer.cs b/src/Cake.Common.Tests/Properties/Resources.Designer.cs
index c82f75cde0..6c66f8bfc9 100644
--- a/src/Cake.Common.Tests/Properties/Resources.Designer.cs
+++ b/src/Cake.Common.Tests/Properties/Resources.Designer.cs
@@ -1,7 +1,6 @@
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -216,14 +215,14 @@ public static string DupFinderReportWithDuplicates {
///
/// Looks up a localized string similar to //------------------------------------------------------------------------------
- ///// <auto-generated>
- ///// This code was generated by a tool.
- ///// Runtime Version:4.0.30319.42000
- /////
- ///// Changes to this file may cause incorrect behavior and will be lost if
- ///// the code is regenerated.
- ///// </auto-generated>
- /////------------------------------------------------------------------------------
+ /// // <auto-generated>
+ /// // This code was generated by a tool.
+ /// // Runtime Version:4.0.30319.42000
+ /// //
+ /// // Changes to this file may cause incorrect behavior and will be lost if
+ /// // the code is regenerated.
+ /// // </auto-generated>
+ /// //------------------------------------------------------------------------------
///
///using System;
///using System.Reflection;
@@ -283,8 +282,8 @@ public static string InspectCodeReportWithViolations {
/// Looks up a localized string similar to using System.Reflection;
///using System.Runtime.CompilerServices;
///
- ///// Information about this assembly is defined by the following attributes.
- ///// Change them to the values specific to your project.
+ /// // Information about this assembly is defined by the following attributes.
+ /// // Change them to the values specific to your project.
///
///[assembly: AssemblyTitle ("MonoDevelopAssemblyTitle")]
///[assembly: AssemblyDescription ("MonoDevelopAssemblyDescription")]
@@ -328,7 +327,7 @@ public static string Nuspec_ContentFiles {
/// <owners>Owner #1,Owner #2</owners>
/// <licenseUrl>https://license.com</licenseUrl>
/// <projectUrl>https://project.com</projectUrl>
- /// <iconUr [rest of string was truncated]";.
+ /// <icon>i [rest of string was truncated]";.
///
public static string Nuspec_FrameworkAssemblies {
get {
@@ -347,7 +346,7 @@ public static string Nuspec_FrameworkAssemblies {
/// <owners>Owner #1,Owner #2</owners>
/// <licenseUrl>https://license.com</licenseUrl>
/// <projectUrl>https://project.com</projectUrl>
- /// <iconUr [rest of string was truncated]";.
+ /// <icon>i [rest of string was truncated]";.
///
public static string Nuspec_License {
get {
@@ -366,7 +365,7 @@ public static string Nuspec_License {
/// <owners>Owner #1,Owner #2</owners>
/// <licenseUrl>https://license.com</licenseUrl>
/// <projectUrl>https://project.com</projectUrl>
- /// <iconUr [rest of string was truncated]";.
+ /// <icon>i [rest of string was truncated]";.
///
public static string Nuspec_Metadata {
get {
@@ -405,7 +404,7 @@ public static string Nuspec_Metadata_PackWithTargetFrameworkDependencies {
/// <owners>Owner #1,Owner #2</owners>
/// <licenseUrl>https://license.com</licenseUrl>
/// <projectUrl>https://project.com</projectUrl>
- /// <iconUr [rest of string was truncated]";.
+ /// <icon>i [rest of string was truncated]";.
///
public static string Nuspec_Metadata_WithDependencies {
get {
@@ -424,10 +423,10 @@ public static string Nuspec_Metadata_WithDependencies {
/// <owners>Owner #1,Owner #2</owners>
/// <licenseUrl>https://license.com</licenseUrl>
/// <projectUrl>https://project.com</projectUrl>
+ /// <icon>images\icon.png</icon>
/// <iconUrl>https://icon.com</iconUrl>
/// <requireLicenseAcceptance>true</requireLicenseAcceptance>
- /// <developmentDependency>true</developmentDependency>
- /// <descriptio [rest of string was truncated]";.
+ /// <developmentDependency>true</devel [rest of string was truncated]";.
///
public static string Nuspec_Metadata_WithoutNamespaces {
get {
@@ -446,10 +445,10 @@ public static string Nuspec_Metadata_WithoutNamespaces {
/// <owners>Owner #1,Owner #2</owners>
/// <licenseUrl>https://license.com</licenseUrl>
/// <projectUrl>https://project.com</projectUrl>
+ /// <icon>images\icon.png</icon>
/// <iconUrl>https://icon.com</iconUrl>
/// <requireLicenseAcceptance>true</requireLicenseAcceptance>
- /// <developmentDependency>true</developmentDependency>
- /// <descriptio [rest of string was truncated]";.
+ /// <developmentDependency>true</devel [rest of string was truncated]";.
///
public static string Nuspec_Metadata_WithoutNamespaces_WithDependencies {
get {
@@ -468,10 +467,10 @@ public static string Nuspec_Metadata_WithoutNamespaces_WithDependencies {
/// <owners>Owner #1,Owner #2</owners>
/// <licenseUrl>https://license.com</licenseUrl>
/// <projectUrl>https://project.com</projectUrl>
+ /// <icon>images\icon.png</icon>
/// <iconUrl>https://icon.com</iconUrl>
/// <requireLicenseAcceptance>true</requireLicenseAcceptance>
- /// <developmentDependency>true</developmentDependency>
- /// <descriptio [rest of string was truncated]";.
+ /// <developmentDependency>true</devel [rest of string was truncated]";.
///
public static string Nuspec_Metadata_WithoutNamespaces_WithTargetFramworkDependencies {
get {
@@ -490,7 +489,7 @@ public static string Nuspec_Metadata_WithoutNamespaces_WithTargetFramworkDepende
/// <owners>Owner #1,Owner #2</owners>
/// <licenseUrl>https://license.com</licenseUrl>
/// <projectUrl>https://project.com</projectUrl>
- /// <iconUr [rest of string was truncated]";.
+ /// <icon>i [rest of string was truncated]";.
///
public static string Nuspec_Metadata_WithTargetFrameworkDependencies {
get {
@@ -562,7 +561,7 @@ public static string Nuspec_NoMetadataValues_WithoutNamespaces {
/// <owners>Owner #1,Owner #2</owners>
/// <licenseUrl>https://license.com</licenseUrl>
/// <projectUrl>https://project.com</projectUrl>
- /// <iconUr [rest of string was truncated]";.
+ /// <icon>i [rest of string was truncated]";.
///
public static string Nuspec_PackageTypes {
get {
@@ -598,7 +597,7 @@ public static string Nuspec_ProjectFile {
/// <owners>Owner #1,Owner #2</owners>
/// <licenseUrl>https://license.com</licenseUrl>
/// <projectUrl>https://project.com</projectUrl>
- /// <iconUr [rest of string was truncated]";.
+ /// <icon>i [rest of string was truncated]";.
///
public static string Nuspec_References {
get {
@@ -617,7 +616,7 @@ public static string Nuspec_References {
/// <owners>Owner #1,Owner #2</owners>
/// <licenseUrl>https://license.com</licenseUrl>
/// <projectUrl>https://project.com</projectUrl>
- /// <iconUr [rest of string was truncated]";.
+ /// <icon>i [rest of string was truncated]";.
///
public static string Nuspec_References_WithTargetFramework {
get {
@@ -636,7 +635,7 @@ public static string Nuspec_References_WithTargetFramework {
/// <owners>Owner #1,Owner #2</owners>
/// <licenseUrl>https://license.com</licenseUrl>
/// <projectUrl>https://project.com</projectUrl>
- /// <iconUr [rest of string was truncated]";.
+ /// <icon>i [rest of string was truncated]";.
///
public static string Nuspec_Repository {
get {
@@ -660,7 +659,7 @@ public static string Solution_WithProjectsAndFolders {
return ResourceManager.GetString("Solution_WithProjectsAndFolders", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Microsoft Visual Studio Solution File, Format Version 12.00
///# Visual Studio 14
@@ -698,6 +697,94 @@ public static string Solution_WithProjectUsingAbsolutePath {
}
}
+ ///
+ /// Looks up a localized string similar to <Solution>
+ /// <Folder Name="/SolutionFolder1/">
+ /// </Folder>
+ /// <Folder Name="/SolutionFolder1/NestedSolutionFolder/">
+ /// <Project Path="ClassLibraryNestedSolutionFolder/ClassLibraryNestedSolutionFolder.csproj" />
+ /// </Folder>
+ ///</Solution>
+ ///.
+ ///
+ public static string SolutionXml_WithNestedSolutionFolders {
+ get {
+ return ResourceManager.GetString("SolutionXml_WithNestedSolutionFolders", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to <Solution>
+ /// <Folder Name="/src/">
+ /// <Project Path="src/dummy/dummy.csproj" />
+ /// </Folder>
+ /// <Folder Name="/test/">
+ /// <Project Path="test/dummy.Tests/dummy.Tests.csproj" />
+ /// </Folder>
+ /// <Project Path="executable/executable.csproj" />
+ ///</Solution>
+ ///.
+ ///
+ public static string SolutionXml_WithProjectsAndFolders {
+ get {
+ return ResourceManager.GetString("SolutionXml_WithProjectsAndFolders", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to <Solution>
+ /// <Folder Name="/src/">
+ /// <Project Path="src/dummy/dummy.csproj" />
+ /// </Folder>
+ ///
+ ///
+ ///
+ /// <Folder Name="/test/">
+ /// <Project Path="test/dummy.Tests/dummy.Tests.csproj" />
+ /// </Folder>
+ ///
+ ///
+ ///
+ /// <Project Path="executable/executable.csproj" />
+ ///
+ ///
+ ///
+ ///
+ ///</Solution>
+ ///.
+ ///
+ public static string SolutionXml_WithProjectsAndFoldersAndAdditionalLines {
+ get {
+ return ResourceManager.GetString("SolutionXml_WithProjectsAndFoldersAndAdditionalLines", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to <Solution>
+ /// <Project Path="C:/project/dummy/src/dummy/dummy.csproj" />
+ ///</Solution>
+ ///.
+ ///
+ public static string SolutionXml_WithProjectUsingAbsolutePath {
+ get {
+ return ResourceManager.GetString("SolutionXml_WithProjectUsingAbsolutePath", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to <Solution>
+ /// <Project Path="WebApplication/WebApplication.csproj" Type="e6fdf86b-f3d1-11d4-8576-0002a516ece8">
+ /// <Platform Project="AnyCPU" />
+ /// </Project>
+ ///</Solution>
+ ///.
+ ///
+ public static string SolutionXml_WithProjectWithDifferentTypeId {
+ get {
+ return ResourceManager.GetString("SolutionXml_WithProjectWithDifferentTypeId", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" standalone="no"?>
///<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
@@ -750,8 +837,8 @@ public static string TeamCity_Runner_Properties_Xml {
/// Looks up a localized string similar to using System.Reflection;
///using System.Runtime.CompilerServices;
///
- ///// Information about this assembly is defined by the following attributes.
- ///// Change them to the values specific to your project.
+ /// // Information about this assembly is defined by the following attributes.
+ /// // Change them to the values specific to your project.
///
///[assembly: AssemblyTitle("VisualStudioAssemblyTitle")]
///[assembly: AssemblyDescription("VisualStudioAssemblyDescription")]
diff --git a/src/Cake.Common.Tests/Properties/Resources.resx b/src/Cake.Common.Tests/Properties/Resources.resx
index 69d086648b..730da8f4a0 100644
--- a/src/Cake.Common.Tests/Properties/Resources.resx
+++ b/src/Cake.Common.Tests/Properties/Resources.resx
@@ -1501,4 +1501,62 @@ EndGlobal
</xsl:template>
</xsl:stylesheet>
+
+ <Solution>
+ <Folder Name="/src/">
+ <Project Path="src/dummy/dummy.csproj" />
+ </Folder>
+ <Folder Name="/test/">
+ <Project Path="test/dummy.Tests/dummy.Tests.csproj" />
+ </Folder>
+ <Project Path="executable/executable.csproj" />
+</Solution>
+
+
+
+ <Solution>
+ <Folder Name="/src/">
+ <Project Path="src/dummy/dummy.csproj" />
+ </Folder>
+
+
+
+ <Folder Name="/test/">
+ <Project Path="test/dummy.Tests/dummy.Tests.csproj" />
+ </Folder>
+
+
+
+ <Project Path="executable/executable.csproj" />
+
+
+
+
+</Solution>
+
+
+
+ <Solution>
+ <Project Path="C:/project/dummy/src/dummy/dummy.csproj" />
+</Solution>
+
+
+
+ <Solution>
+ <Project Path="WebApplication/WebApplication.csproj" Type="e6fdf86b-f3d1-11d4-8576-0002a516ece8">
+ <Platform Project="AnyCPU" />
+ </Project>
+</Solution>
+
+
+
+ <Solution>
+ <Folder Name="/SolutionFolder1/">
+ </Folder>
+ <Folder Name="/SolutionFolder1/NestedSolutionFolder/">
+ <Project Path="ClassLibraryNestedSolutionFolder/ClassLibraryNestedSolutionFolder.csproj" />
+ </Folder>
+</Solution>
+
+
\ No newline at end of file
diff --git a/src/Cake.Common.Tests/Unit/Solution/SolutionParserTests.cs b/src/Cake.Common.Tests/Unit/Solution/SolutionParserTests.cs
index ab18f8bc71..0197b469f2 100644
--- a/src/Cake.Common.Tests/Unit/Solution/SolutionParserTests.cs
+++ b/src/Cake.Common.Tests/Unit/Solution/SolutionParserTests.cs
@@ -2,11 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System.Linq;
using Cake.Common.Solution;
using Cake.Common.Tests.Fixtures.Solution;
using Cake.Common.Tests.Properties;
-using Xunit;
namespace Cake.Common.Tests.Unit.Solution
{
@@ -37,7 +35,8 @@ public void Should_Throw_If_Environment_Is_Null()
AssertEx.IsArgumentNullException(result, "environment");
}
}
- public sealed class TheParseMethod
+
+ public sealed class TheParseMethodForSln
{
[Fact]
public void Should_Throw_If_SolutionPath_Is_Null()
@@ -54,7 +53,7 @@ public void Should_Throw_If_SolutionPath_Is_Null()
}
[Fact]
- public void Should_Properly_Parse_Projects()
+ public async Task Should_Properly_Parse_Projects()
{
// Given
var fixture = new SolutionParserFixture();
@@ -65,15 +64,11 @@ public void Should_Properly_Parse_Projects()
var result = solutionParser.Parse(slnFilePath);
// Then
- Assert.NotNull(result);
- Assert.NotNull(result.Projects);
- Assert.Equal(5, result.Projects.Count);
- var onlyProjects = result.Projects.Where(x => !(x is SolutionFolder)).ToList();
- Assert.Equal(3, onlyProjects.Count);
+ await Verify(result);
}
[Fact]
- public void Should_Properly_Parse_Folders()
+ public async Task Should_Properly_Parse_Folders()
{
// Given
var fixture = new SolutionParserFixture();
@@ -84,14 +79,11 @@ public void Should_Properly_Parse_Folders()
var result = solutionParser.Parse(slnFilePath);
// Then
- Assert.NotNull(result);
- Assert.NotNull(result.Projects);
- var folders = result.Projects.OfType().ToList();
- Assert.Equal(2, folders.Count);
+ await Verify(result);
}
[Fact]
- public void Should_Properly_Parse_Relation_Between_Project_And_Folder()
+ public async Task Should_Properly_Parse_Relation_Between_Project_And_Folder()
{
// Given
var fixture = new SolutionParserFixture();
@@ -102,18 +94,11 @@ public void Should_Properly_Parse_Relation_Between_Project_And_Folder()
var result = solutionParser.Parse(slnFilePath);
// Then
- Assert.NotNull(result);
- Assert.NotNull(result.Projects);
- var folders = result.Projects.OfType().ToList();
- var srcFolder = folders.First(x => x.Name == "src");
- Assert.Single(srcFolder.Items);
- var dummyProject = result.Projects.First(x => x.Name == "dummy");
- Assert.Contains(dummyProject, srcFolder.Items);
- Assert.Equal(srcFolder, dummyProject.Parent);
+ await Verify(result);
}
[Fact]
- public void Should_Properly_Parse_Projects_With_Empty_Lines()
+ public async Task Should_Properly_Parse_Projects_With_Empty_Lines()
{
// Given
var fixture = new SolutionParserFixture();
@@ -124,15 +109,11 @@ public void Should_Properly_Parse_Projects_With_Empty_Lines()
var result = solutionParser.Parse(slnFilePath);
// Then
- Assert.NotNull(result);
- Assert.NotNull(result.Projects);
- Assert.Equal(5, result.Projects.Count);
- var onlyProjects = result.Projects.Where(x => !(x is SolutionFolder)).ToList();
- Assert.Equal(3, onlyProjects.Count);
+ await Verify(result);
}
[Fact]
- public void Should_Properly_Parse_Projects_With_Absolute_Path()
+ public async Task Should_Properly_Parse_Projects_With_Absolute_Path()
{
// Given
var fixture = new SolutionParserFixture();
@@ -143,11 +124,115 @@ public void Should_Properly_Parse_Projects_With_Absolute_Path()
var result = solutionParser.Parse(slnFilePath);
// Then
- Assert.NotNull(result);
- Assert.NotNull(result.Projects);
- Assert.Single(result.Projects);
- var onlyProjects = result.Projects.Where(x => !(x is SolutionFolder)).ToList();
- Assert.Single(onlyProjects);
+ await Verify(result);
+ }
+ }
+
+ public sealed class TheParseMethodForSlnx
+ {
+ [Fact]
+ public async Task Should_Properly_Parse_Projects()
+ {
+ // Given
+ var fixture = new SolutionParserFixture();
+ var slnFilePath = fixture.WithXmlSolutionFile(Resources.SolutionXml_WithProjectsAndFolders);
+ var solutionParser = new SolutionParser(fixture.FileSystem, fixture.Environment);
+
+ // When
+ var result = solutionParser.Parse(slnFilePath);
+
+ // Then
+ await Verify(result);
+ }
+
+ [Fact]
+ public async Task Should_Properly_Parse_Folders()
+ {
+ // Given
+ var fixture = new SolutionParserFixture();
+ var slnFilePath = fixture.WithXmlSolutionFile(Resources.SolutionXml_WithProjectsAndFolders);
+ var solutionParser = new SolutionParser(fixture.FileSystem, fixture.Environment);
+
+ // When
+ var result = solutionParser.Parse(slnFilePath);
+
+ // Then
+ await Verify(result);
+ }
+
+ [Fact]
+ public async Task Should_Properly_Parse_Relation_Between_Project_And_Folder()
+ {
+ // Given
+ var fixture = new SolutionParserFixture();
+ var slnFilePath = fixture.WithXmlSolutionFile(Resources.SolutionXml_WithProjectsAndFolders);
+ var solutionParser = new SolutionParser(fixture.FileSystem, fixture.Environment);
+
+ // When
+ var result = solutionParser.Parse(slnFilePath);
+
+ // Then
+ await Verify(result);
+ }
+
+ [Fact]
+ public async Task Should_Properly_Parse_Projects_With_Empty_Lines()
+ {
+ // Given
+ var fixture = new SolutionParserFixture();
+ var slnFilePath = fixture.WithXmlSolutionFile(Resources.SolutionXml_WithProjectsAndFoldersAndAdditionalLines);
+ var solutionParser = new SolutionParser(fixture.FileSystem, fixture.Environment);
+
+ // When
+ var result = solutionParser.Parse(slnFilePath);
+
+ // Then
+ await Verify(result);
+ }
+
+ [Fact]
+ public async Task Should_Properly_Parse_Projects_With_Absolute_Path()
+ {
+ // Given
+ var fixture = new SolutionParserFixture();
+ var slnFilePath = fixture.WithXmlSolutionFile(Resources.SolutionXml_WithProjectUsingAbsolutePath);
+ var solutionParser = new SolutionParser(fixture.FileSystem, fixture.Environment);
+
+ // When
+ var result = solutionParser.Parse(slnFilePath);
+
+ // Then
+ await Verify(result);
+ }
+
+ [Fact]
+ public async Task Should_Properly_Parse_Projects_With_Different_Type_Id()
+ {
+ // Given
+ var fixture = new SolutionParserFixture();
+ var slnFilePath = fixture.WithXmlSolutionFile(Resources.SolutionXml_WithProjectWithDifferentTypeId);
+ var solutionParser = new SolutionParser(fixture.FileSystem, fixture.Environment);
+
+ // When
+ var result = solutionParser.Parse(slnFilePath);
+
+ // Then
+ await Verify(result);
+ }
+
+ [Fact]
+ public async Task Should_Properly_Parse_Solution_With_Nested_Solution_Folders()
+ {
+ // Given
+ var fixture = new SolutionParserFixture();
+ var slnFilePath = fixture.WithXmlSolutionFile(Resources.SolutionXml_WithNestedSolutionFolders);
+ var solutionParser = new SolutionParser(fixture.FileSystem, fixture.Environment);
+
+ // When
+ var result = solutionParser.Parse(slnFilePath);
+
+ // Then
+ await Verify(result);
}
}
}
diff --git a/src/Cake.Common.Tests/VerifyConfig.cs b/src/Cake.Common.Tests/VerifyConfig.cs
new file mode 100644
index 0000000000..a67b3c9ea6
--- /dev/null
+++ b/src/Cake.Common.Tests/VerifyConfig.cs
@@ -0,0 +1,25 @@
+using System.Runtime.CompilerServices;
+using Argon;
+using VerifyTests.DiffPlex;
+
+namespace Cake.Common.Tests;
+
+public static class VerifyConfig
+{
+ [ModuleInitializer]
+ public static void Init()
+ {
+ EmptyFiles.FileExtensions.AddTextExtension("cake");
+
+ if (!VerifyDiffPlex.Initialized)
+ {
+ VerifyDiffPlex.Initialize(OutputType.Compact);
+ DerivePathInfo(Expectations.Initialize);
+ }
+
+ VerifierSettings.DontScrubDateTimes();
+ VerifierSettings.DontIgnoreEmptyCollections();
+ VerifierSettings.AddExtraSettings(settings => settings.DefaultValueHandling = DefaultValueHandling.Include);
+ VerifierSettings.IgnoreStackTrace();
+ }
+}
diff --git a/src/Cake.Common/Solution/SolutionParser.cs b/src/Cake.Common/Solution/SolutionParser.cs
index e2f2dcf2c4..4606273e4e 100644
--- a/src/Cake.Common/Solution/SolutionParser.cs
+++ b/src/Cake.Common/Solution/SolutionParser.cs
@@ -7,6 +7,7 @@
using System.Globalization;
using System.Linq;
using System.Text;
+using System.Xml;
using Cake.Core;
using Cake.Core.IO;
@@ -46,7 +47,7 @@ public SolutionParserResult Parse(FilePath solutionPath)
solutionPath = solutionPath.MakeAbsolute(_environment);
}
- // Get the release notes file.
+ // Get solution file.
var file = _fileSystem.GetFile(solutionPath);
if (!file.Exists)
{
@@ -55,6 +56,18 @@ public SolutionParserResult Parse(FilePath solutionPath)
throw new CakeException(message);
}
+ var fileExtension = file.Path.GetExtension().ToLowerInvariant();
+
+ return fileExtension switch
+ {
+ ".sln" => ParseSlnSolution(file),
+ ".slnx" => ParseSlnxSolution(file),
+ _ => throw new CakeException($"Unknown file extension {fileExtension} for solution file '{solutionPath.FullPath}'."),
+ };
+ }
+
+ private static SolutionParserResult ParseSlnSolution(IFile file)
+ {
string
version = null,
visualStudioVersion = null,
@@ -182,5 +195,114 @@ private static void ParseNestedProjectLine(List projects, strin
parent.Items.Add(child);
child.Parent = parent;
}
+
+ private static SolutionParserResult ParseSlnxSolution(IFile file)
+ {
+ var xmlReaderSettings = new XmlReaderSettings
+ {
+ DtdProcessing = DtdProcessing.Prohibit, // Prevent XXE
+ XmlResolver = null, // Disable external entity resolution
+ };
+ using var xmlContentStream = file.OpenRead();
+ var xmlDocument = new XmlDocument();
+
+ using var xmlReader = XmlReader.Create(xmlContentStream, xmlReaderSettings);
+ xmlDocument.Load(xmlReader);
+
+ var root = xmlDocument.DocumentElement;
+ if (root == null)
+ {
+ throw new CakeException($"Solution file '{file.Path.FullPath}' does not contain a root element.");
+ }
+
+ var projects = new List();
+ foreach (var xmlElement in root.ChildNodes.OfType())
+ {
+ var projectsFromElement = ParseSlnxElement(xmlElement, file);
+ projects.AddRange(projectsFromElement);
+ }
+
+ // the new SLNX has no information about the file format version, VS version and minimal VS version, so it's omitted.
+ return new SolutionParserResult(string.Empty, string.Empty, string.Empty, projects.AsReadOnly());
+ }
+
+ private static List ParseSlnxElement(XmlElement xmlElement, IFile solutionFile)
+ {
+ return xmlElement.Name switch
+ {
+ "Folder" => ParseSlnxFolder(xmlElement, solutionFile),
+ "Project" => ParseSlnxProject(xmlElement, solutionFile),
+ _ =>[],
+ };
+ }
+
+ private static List ParseSlnxFolder(XmlElement xmlElement, IFile solutionFile)
+ {
+ var folderName = xmlElement.GetAttribute("Name");
+ if (string.IsNullOrWhiteSpace(folderName))
+ {
+ throw new CakeException($"Could not find solution folder name attribute in solution file '{solutionFile.Path.FullPath}'.");
+ }
+
+ // the name of the folder is also its path. And it always has a starting and trailing "/", which break the check if its relative, so they are removed.
+ var folderPath = new FilePath(folderName.Trim('/'));
+ var folderFullPath = folderPath.IsRelative ?
+ solutionFile.Path.GetDirectory().CombineWithFilePath(folderPath) :
+ folderPath;
+
+ var actualFolderName = folderName.Split('/', StringSplitOptions.RemoveEmptyEntries).Last();
+
+ // elements in the SLNX file don't have any IDs anymore, so it's omitted.
+ var solutionFolder = new SolutionFolder(string.Empty, actualFolderName, folderFullPath);
+ var projects = new List
+ {
+ solutionFolder,
+ };
+
+ foreach (var childElement in xmlElement.ChildNodes.OfType())
+ {
+ var childProjects = ParseSlnxElement(childElement, solutionFile);
+ foreach (var childProject in childProjects)
+ {
+ childProject.Parent = solutionFolder;
+ solutionFolder.Items.Add(childProject);
+ projects.Add(childProject);
+ }
+ }
+
+ return projects;
+ }
+
+ private static List ParseSlnxProject(XmlElement xmlElement, IFile solutionFile)
+ {
+ const string defaultProjectTypeId = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}";
+
+ var projectPath = xmlElement.GetAttribute("Path");
+ if (string.IsNullOrWhiteSpace(projectPath))
+ {
+ throw new CakeException($"Could not find solution project path attribute in solution file '{solutionFile.Path.FullPath}'.");
+ }
+
+ var projectTypeId = xmlElement.GetAttribute("Type");
+ projectTypeId = string.IsNullOrWhiteSpace(projectTypeId)
+ ? defaultProjectTypeId
+ // the new project type id notation does not quite fit the known format, so it is adjusted.
+ : $"{{{projectTypeId.ToUpper()}}}";
+
+ var projectFilePath = new FilePath(projectPath);
+ var projectFileFullPath = projectFilePath.IsRelative ?
+ solutionFile.Path.GetDirectory().CombineWithFilePath(projectFilePath) :
+ projectFilePath;
+
+ // the project name is part of its path.
+ var actualProjectName = projectFilePath.GetFilenameWithoutExtension().FullPath;
+
+ // elements in the SLNX file don't have any IDs anymore, so it's omitted.
+ var solutionProject = new SolutionProject(string.Empty, actualProjectName, projectFileFullPath, projectTypeId);
+ return
+ [
+ solutionProject,
+ ];
+ }
}
}
\ No newline at end of file
diff --git a/src/Cake.Core.Tests/VerifyConfig.cs b/src/Cake.Core.Tests/VerifyConfig.cs
index 7118e6a57f..79981e8aaf 100644
--- a/src/Cake.Core.Tests/VerifyConfig.cs
+++ b/src/Cake.Core.Tests/VerifyConfig.cs
@@ -12,8 +12,12 @@ public static class VerifyConfig
public static void Init()
{
EmptyFiles.FileExtensions.AddTextExtension(Extensions.Cake);
- DerivePathInfo(Expectations.Initialize);
- VerifyDiffPlex.Initialize(OutputType.Compact);
+
+ if (!VerifyDiffPlex.Initialized)
+ {
+ VerifyDiffPlex.Initialize(OutputType.Compact);
+ DerivePathInfo(Expectations.Initialize);
+ }
}
public static class Extensions
diff --git a/src/Cake.Testing.Tests/VerifyConfig.cs b/src/Cake.Testing.Tests/VerifyConfig.cs
index dc5f79e454..1c74af1d37 100644
--- a/src/Cake.Testing.Tests/VerifyConfig.cs
+++ b/src/Cake.Testing.Tests/VerifyConfig.cs
@@ -9,9 +9,14 @@ public static class VerifyConfig
[ModuleInitializer]
public static void Init()
{
- DerivePathInfo(Expectations.Initialize);
EmptyFiles.FileExtensions.AddTextExtension("cake");
- VerifyDiffPlex.Initialize(OutputType.Compact);
+
+ if (!VerifyDiffPlex.Initialized)
+ {
+ VerifyDiffPlex.Initialize(OutputType.Compact);
+ DerivePathInfo(Expectations.Initialize);
+ }
+
VerifierSettings.DontScrubDateTimes();
VerifierSettings.IgnoreMember(x => x.Content);
VerifierSettings.IgnoreMember("LastWriteTime");
diff --git a/tests/integration/Cake.Common/Solution/SolutionAliases.cake b/tests/integration/Cake.Common/Solution/SolutionAliases.cake
index e4621a3cd6..a28b693e10 100644
--- a/tests/integration/Cake.Common/Solution/SolutionAliases.cake
+++ b/tests/integration/Cake.Common/Solution/SolutionAliases.cake
@@ -13,7 +13,7 @@ Task("Cake.Common.Solution.SolutionAliases.ParseSolution")
version: "Format Version 12.00",
visualStudioVersion: "14.0.25420.1",
minimumVisualStudioVersion: "10.0.40219.1",
- projects: new System.Collections.ObjectModel.ReadOnlyCollection(new [] {
+ projects: new System.Collections.ObjectModel.ReadOnlyCollection(new[] {
new SolutionProject(
id: "{4147F50A-8743-4393-A414-EFC707D4BA2C}",
name: "Project",
@@ -38,6 +38,78 @@ Task("Cake.Common.Solution.SolutionAliases.ParseSolution")
Assert.Equal(expect, result, SolutionParserResultEqualityComparer.Comparer);
});
+Task("Cake.Common.Solution.SolutionAliases.ParseXmlSolution")
+ .Does(() =>
+{
+ // Given
+ var path = Paths.Resources.Combine("./Cake.Common/Solution");
+ var file = path.CombineWithFilePath("./XmlSolution.slnx");
+ var expect = new SolutionParserResult(
+ version: string.Empty,
+ visualStudioVersion: string.Empty,
+ minimumVisualStudioVersion: string.Empty,
+ projects: new System.Collections.ObjectModel.ReadOnlyCollection(new[] {
+ new SolutionProject(
+ id: string.Empty,
+ name: "Project",
+ path: path.CombineWithFilePath("Project/Project.csproj"),
+ type: "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"
+ ),
+ new SolutionProject(
+ id: string.Empty,
+ name: "ReferenceProject",
+ path: path.CombineWithFilePath("ReferenceProject/ReferenceProject.csproj"),
+ type: "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"
+ )
+ })
+ );
+
+ // When
+ var result = ParseSolution(file);
+
+ // Then
+ Assert.NotNull(result);
+ Assert.Equal(expect.Projects, result.Projects, SolutionProjectEqualityComparer.Comparer);
+ Assert.Equal(expect, result, SolutionParserResultEqualityComparer.Comparer);
+});
+
+Task("Cake.Common.Solution.SolutionAliases.ParseXmlSolutionWithProjectWithTypeId")
+ .Does(() =>
+{
+ // Given
+ var path = Paths.Resources.Combine("./Cake.Common/Solution");
+ var file = path.CombineWithFilePath("./XmlSolutionWithProjectWithTypeId.slnx");
+ var expect = new SolutionParserResult(
+ version: string.Empty,
+ visualStudioVersion: string.Empty,
+ minimumVisualStudioVersion: string.Empty,
+ projects: new System.Collections.ObjectModel.ReadOnlyCollection(new[] {
+ new SolutionProject(
+ id: string.Empty,
+ name: "Project",
+ path: path.CombineWithFilePath("Project/Project.csproj"),
+ type: "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"
+ ),
+ new SolutionProject(
+ id: string.Empty,
+ name: "ReferenceProject",
+ path: path.CombineWithFilePath("ReferenceProject/ReferenceProject.csproj"),
+ type: "{E6FDF86B-F3D1-11D4-8576-0002A516ECE8}"
+ )
+ })
+ );
+
+ // When
+ var result = ParseSolution(file);
+
+ // Then
+ Assert.NotNull(result);
+ Assert.Equal(expect.Projects, result.Projects, SolutionProjectEqualityComparer.Comparer);
+ Assert.Equal(expect, result, SolutionParserResultEqualityComparer.Comparer);
+});
+
Task("Cake.Common.Solution.SolutionAliases")
- .IsDependentOn("Cake.Common.Solution.SolutionAliases.ParseSolution");
\ No newline at end of file
+ .IsDependentOn("Cake.Common.Solution.SolutionAliases.ParseSolution")
+ .IsDependentOn("Cake.Common.Solution.SolutionAliases.ParseXmlSolution")
+ .IsDependentOn("Cake.Common.Solution.SolutionAliases.ParseXmlSolutionWithProjectWithTypeId");
\ No newline at end of file
diff --git a/tests/integration/resources/Cake.Common/Solution/XmlSolution.slnx b/tests/integration/resources/Cake.Common/Solution/XmlSolution.slnx
new file mode 100644
index 0000000000..431e54bb9e
--- /dev/null
+++ b/tests/integration/resources/Cake.Common/Solution/XmlSolution.slnx
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/tests/integration/resources/Cake.Common/Solution/XmlSolutionWithProjectWithTypeId.slnx b/tests/integration/resources/Cake.Common/Solution/XmlSolutionWithProjectWithTypeId.slnx
new file mode 100644
index 0000000000..cced27e425
--- /dev/null
+++ b/tests/integration/resources/Cake.Common/Solution/XmlSolutionWithProjectWithTypeId.slnx
@@ -0,0 +1,4 @@
+
+
+
+