Skip to content

Commit cdf6f93

Browse files
authored
[dotnet test for MTP]: Copy implementation for determining project/solution to build from MSBuild (#48748)
1 parent dc0ead8 commit cdf6f93

File tree

3 files changed

+89
-45
lines changed

3 files changed

+89
-45
lines changed

src/Cli/dotnet/Commands/Test/CliConstants.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ internal static class CliConstants
1515
public const string VSTest = "VSTest";
1616
public const string MicrosoftTestingPlatform = "Microsoft.Testing.Platform";
1717

18-
public static readonly string[] ProjectExtensions = [".proj", ".csproj", ".vbproj", ".fsproj"];
1918
public static readonly string[] SolutionExtensions = [".sln", ".slnx", ".slnf"];
2019

2120
public const string ProjectExtensionPattern = "*.*proj";

src/Cli/dotnet/Commands/Test/SolutionAndProjectUtility.cs

Lines changed: 68 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -27,60 +27,91 @@ public static (bool SolutionOrProjectFileFound, string Message) TryGetProjectOrS
2727
return (false, string.Format(CliCommandStrings.CmdNonExistentDirectoryErrorDescription, directory));
2828
}
2929

30-
var solutionPaths = GetSolutionFilePaths(directory);
30+
var actualSolutionFiles = GetSolutionFilePaths(directory);
31+
var solutionFilterFiles = GetSolutionFilterFilePaths(directory);
32+
var actualProjectFiles = GetProjectFilePaths(directory);
3133

32-
// If more than a single sln file is found, an error is thrown since we can't determine which one to choose.
33-
if (solutionPaths.Length > 1)
34+
// NOTE: The logic here is duplicated from https://github.com/dotnet/msbuild/blob/b878078fbaa28491a3a7fb273474ba71675c1613/src/MSBuild/XMake.cs#L3589
35+
// If there is exactly 1 project file and exactly 1 solution file
36+
if (actualProjectFiles.Length == 1 && actualSolutionFiles.Length == 1)
3437
{
35-
return (false, string.Format(CliStrings.MoreThanOneSolutionInDirectory, directory));
36-
}
37-
38-
if (solutionPaths.Length == 1)
39-
{
40-
var projectPaths = GetProjectFilePaths(directory);
38+
// Grab the name of both project and solution without extensions
39+
string solutionName = Path.GetFileNameWithoutExtension(actualSolutionFiles[0]);
40+
string projectName = Path.GetFileNameWithoutExtension(actualProjectFiles[0]);
4141

42-
if (projectPaths.Length == 0)
42+
// Compare the names and error if they are not identical
43+
if (!string.Equals(solutionName, projectName))
4344
{
44-
projectOrSolutionFilePath = solutionPaths[0];
45-
isSolution = true;
46-
return (true, string.Empty);
45+
return (false, CliCommandStrings.CmdMultipleProjectOrSolutionFilesErrorDescription);
4746
}
4847

49-
return (false, CliCommandStrings.CmdMultipleProjectOrSolutionFilesErrorDescription);
48+
projectOrSolutionFilePath = actualSolutionFiles[0];
49+
isSolution = true;
50+
}
51+
// If there is more than one solution file in the current directory we have no idea which one to use
52+
else if (actualSolutionFiles.Length > 1)
53+
{
54+
return (false, string.Format(CliStrings.MoreThanOneSolutionInDirectory, directory));
5055
}
51-
else // If no solutions are found, look for a project file
56+
// If there is more than one project file in the current directory we may be able to figure it out
57+
else if (actualProjectFiles.Length > 1)
5258
{
53-
string[] projectPaths = GetProjectFilePaths(directory);
59+
// We have more than one project, it is ambiguous at the moment
60+
bool isAmbiguousProject = true;
5461

55-
if (projectPaths.Length == 0)
62+
// If there are exactly two projects and one of them is a .proj use that one and ignore the other
63+
if (actualProjectFiles.Length == 2)
5664
{
57-
var solutionFilterPaths = GetSolutionFilterFilePaths(directory);
65+
string firstPotentialProjectExtension = Path.GetExtension(actualProjectFiles[0]);
66+
string secondPotentialProjectExtension = Path.GetExtension(actualProjectFiles[1]);
5867

59-
if (solutionFilterPaths.Length == 0)
68+
// If the two projects have the same extension we can't decide which one to pick
69+
if (!string.Equals(firstPotentialProjectExtension, secondPotentialProjectExtension, StringComparison.OrdinalIgnoreCase))
6070
{
61-
return (false, CliCommandStrings.CmdNoProjectOrSolutionFileErrorDescription);
62-
}
63-
64-
if (solutionFilterPaths.Length == 1)
65-
{
66-
projectOrSolutionFilePath = solutionFilterPaths[0];
67-
isSolution = true;
68-
return (true, string.Empty);
69-
}
70-
else
71-
{
72-
return (false, CliCommandStrings.CmdMultipleProjectOrSolutionFilesErrorDescription);
71+
// Check to see if the first project is the proj, if it is use it
72+
if (string.Equals(firstPotentialProjectExtension, ".proj", StringComparison.OrdinalIgnoreCase))
73+
{
74+
projectOrSolutionFilePath = actualProjectFiles[0];
75+
// We have made a decision
76+
isAmbiguousProject = false;
77+
}
78+
// If the first project is not the proj check to see if the second one is the proj, if so use it
79+
else if (string.Equals(secondPotentialProjectExtension, ".proj", StringComparison.OrdinalIgnoreCase))
80+
{
81+
projectOrSolutionFilePath = actualProjectFiles[1];
82+
// We have made a decision
83+
isAmbiguousProject = false;
84+
}
7385
}
7486
}
7587

76-
if (projectPaths.Length == 1)
88+
if (isAmbiguousProject)
7789
{
78-
projectOrSolutionFilePath = projectPaths[0];
79-
return (true, string.Empty);
90+
return (false, string.Format(CliStrings.MoreThanOneProjectInDirectory, directory));
8091
}
81-
82-
return (false, string.Format(CliStrings.MoreThanOneSolutionInDirectory, directory));
8392
}
93+
// if there are no project, solution filter, or solution files in the directory, we can't build
94+
else if (actualProjectFiles.Length == 0 &&
95+
actualSolutionFiles.Length == 0 &&
96+
solutionFilterFiles.Length == 0)
97+
{
98+
return (false, CliCommandStrings.CmdNoProjectOrSolutionFileErrorDescription);
99+
}
100+
else
101+
{
102+
// We are down to only one project, solution, or solution filter.
103+
// If only 1 solution build the solution. If only 1 project build the project. Otherwise, build the solution filter.
104+
projectOrSolutionFilePath = actualSolutionFiles.Length == 1 ? actualSolutionFiles[0] : actualProjectFiles.Length == 1 ? actualProjectFiles[0] : solutionFilterFiles[0];
105+
isSolution = actualSolutionFiles.Length == 1 || (actualProjectFiles.Length != 1 && solutionFilterFiles.Length == 1);
106+
if (actualSolutionFiles.Length != 1 &&
107+
actualProjectFiles.Length != 1 &&
108+
solutionFilterFiles.Length != 1)
109+
{
110+
return (false, CliCommandStrings.CmdMultipleProjectOrSolutionFilesErrorDescription);
111+
}
112+
}
113+
114+
return (true, string.Empty);
84115
}
85116

86117
private static string[] GetSolutionFilePaths(string directory) => [
@@ -93,9 +124,7 @@ private static string[] GetSolutionFilterFilePaths(string directory)
93124
return Directory.GetFiles(directory, CliConstants.SolutionFilterExtensionPattern, SearchOption.TopDirectoryOnly);
94125
}
95126

96-
private static string[] GetProjectFilePaths(string directory) => [.. Directory.EnumerateFiles(directory, CliConstants.ProjectExtensionPattern, SearchOption.TopDirectoryOnly).Where(IsProjectFile)];
97-
98-
private static bool IsProjectFile(string filePath) => CliConstants.ProjectExtensions.Contains(Path.GetExtension(filePath), StringComparer.OrdinalIgnoreCase);
127+
private static string[] GetProjectFilePaths(string directory) => Directory.GetFiles(directory, CliConstants.ProjectExtensionPattern, SearchOption.TopDirectoryOnly);
99128

100129
private static ProjectInstance EvaluateProject(ProjectCollection collection, string projectFilePath, string? tfm)
101130
{

src/Cli/dotnet/Commands/Test/ValidationUtility.cs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,12 @@ public static bool ValidateBuildPathOptions(BuildOptions buildPathOptions, Termi
5858

5959
if (!string.IsNullOrEmpty(pathOptions.ProjectPath))
6060
{
61-
return ValidateFilePath(pathOptions.ProjectPath, CliConstants.ProjectExtensions, CliCommandStrings.CmdInvalidProjectFileExtensionErrorDescription, output);
61+
return ValidateProjectFilePath(pathOptions.ProjectPath, output);
6262
}
6363

6464
if (!string.IsNullOrEmpty(pathOptions.SolutionPath))
6565
{
66-
return ValidateFilePath(pathOptions.SolutionPath, CliConstants.SolutionExtensions, CliCommandStrings.CmdInvalidSolutionFileExtensionErrorDescription, output);
66+
return ValidateSolutionFilePath(pathOptions.SolutionPath, output);
6767
}
6868

6969
if (!string.IsNullOrEmpty(pathOptions.DirectoryPath) && !Directory.Exists(pathOptions.DirectoryPath))
@@ -75,14 +75,30 @@ public static bool ValidateBuildPathOptions(BuildOptions buildPathOptions, Termi
7575
return true;
7676
}
7777

78-
private static bool ValidateFilePath(string filePath, string[] validExtensions, string errorMessage, TerminalTestReporter output)
78+
private static bool ValidateSolutionFilePath(string filePath, TerminalTestReporter output)
7979
{
80-
if (!validExtensions.Contains(Path.GetExtension(filePath)))
80+
if (!CliConstants.SolutionExtensions.Contains(Path.GetExtension(filePath)))
8181
{
82-
output.WriteMessage(string.Format(errorMessage, filePath));
82+
output.WriteMessage(string.Format(CliCommandStrings.CmdInvalidSolutionFileExtensionErrorDescription, filePath));
8383
return false;
8484
}
8585

86+
return ValidateFilePathExists(filePath, output);
87+
}
88+
89+
private static bool ValidateProjectFilePath(string filePath, TerminalTestReporter output)
90+
{
91+
if (!Path.GetExtension(filePath).EndsWith("proj", StringComparison.OrdinalIgnoreCase))
92+
{
93+
output.WriteMessage(string.Format(CliCommandStrings.CmdInvalidProjectFileExtensionErrorDescription, filePath));
94+
return false;
95+
}
96+
97+
return ValidateFilePathExists(filePath, output);
98+
}
99+
100+
private static bool ValidateFilePathExists(string filePath, TerminalTestReporter output)
101+
{
86102
if (!File.Exists(filePath))
87103
{
88104
output.WriteMessage(string.Format(CliCommandStrings.CmdNonExistentFileErrorDescription, Path.GetFullPath(filePath)));

0 commit comments

Comments
 (0)