|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.DotNet.Cli.Commands.New.PostActions;
using Microsoft.TemplateEngine.Abstractions;
using Microsoft.TemplateEngine.Mocks;
using Microsoft.TemplateEngine.TestHelper;
namespace Microsoft.DotNet.Cli.New.Tests
{
public class DotnetSlnPostActionTests : IClassFixture<EnvironmentSettingsHelper>
{
private readonly IEngineEnvironmentSettings _engineEnvironmentSettings;
public DotnetSlnPostActionTests(EnvironmentSettingsHelper environmentSettingsHelper)
{
_engineEnvironmentSettings = environmentSettingsHelper.CreateEnvironment(hostIdentifier: GetType().Name, virtualize: true);
}
[Fact(DisplayName = nameof(AddProjectToSolutionPostActionFindSolutionFileAtOutputPath))]
public void AddProjectToSolutionPostActionFindSolutionFileAtOutputPath()
{
string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
string solutionFileFullPath = Path.Combine(targetBasePath, "MySln.sln");
_engineEnvironmentSettings.Host.FileSystem.WriteAllText(solutionFileFullPath, string.Empty);
IReadOnlyList<string> solutionFiles = DotnetSlnPostActionProcessor.FindSolutionFilesAtOrAbovePath(_engineEnvironmentSettings.Host.FileSystem, targetBasePath);
Assert.Single(solutionFiles);
Assert.Equal(solutionFileFullPath, solutionFiles[0]);
}
[PlatformSpecificFact(TestPlatforms.Any & ~TestPlatforms.Linux)] // https://github.com/dotnet/sdk/issues/49923
public void AddProjectToSolutionPostActionFindSlnxFileAtOutputPath()
{
string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
_engineEnvironmentSettings.Host.VirtualizeDirectory(targetBasePath);
EnsureParentDirectoriesExist(targetBasePath);
string solutionFileFullPath = Path.Combine(targetBasePath, "MySln.slnx");
_engineEnvironmentSettings.Host.FileSystem.WriteAllText(solutionFileFullPath, string.Empty);
IReadOnlyList<string> solutionFiles = DotnetSlnPostActionProcessor.FindSolutionFilesAtOrAbovePath(_engineEnvironmentSettings.Host.FileSystem, targetBasePath);
Assert.Single(solutionFiles);
Assert.Equal(solutionFileFullPath, solutionFiles[0]);
}
[Fact(DisplayName = nameof(AddProjectToSolutionPostActionPrefersSlnOverSlnx))]
public void AddProjectToSolutionPostActionPrefersSlnOverSlnx()
{
string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
_engineEnvironmentSettings.Host.VirtualizeDirectory(targetBasePath);
EnsureParentDirectoriesExist(targetBasePath);
string slnFileFullPath = Path.Combine(targetBasePath, "MySln.sln");
string slnxFileFullPath = Path.Combine(targetBasePath, "MySln.slnx");
_engineEnvironmentSettings.Host.FileSystem.WriteAllText(slnFileFullPath, string.Empty);
_engineEnvironmentSettings.Host.FileSystem.WriteAllText(slnxFileFullPath, string.Empty);
IReadOnlyList<string> solutionFiles = DotnetSlnPostActionProcessor.FindSolutionFilesAtOrAbovePath(_engineEnvironmentSettings.Host.FileSystem, targetBasePath);
Assert.Single(solutionFiles);
Assert.Equal(slnFileFullPath, solutionFiles[0]);
}
[Fact(DisplayName = nameof(AddProjectToSolutionPostActionFindsOneProjectToAdd))]
public void AddProjectToSolutionPostActionFindsOneProjectToAdd()
{
string outputBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
IPostAction postAction = new MockPostAction(default, default, default, default, default!)
{
ActionId = DotnetSlnPostActionProcessor.ActionProcessorId,
Args = new Dictionary<string, string>()
{
{ "primaryOutputIndexes", "0" }
}
};
ICreationResult creationResult = new MockCreationResult(primaryOutputs: new[] { new MockCreationPath(Path.GetFullPath("outputProj1.csproj")) });
Assert.True(DotnetSlnPostActionProcessor.TryGetProjectFilesToAdd(postAction, creationResult, outputBasePath, out IReadOnlyList<string>? foundProjectFiles));
Assert.Equal(1, foundProjectFiles?.Count);
Assert.Equal(creationResult.PrimaryOutputs[0].Path, foundProjectFiles?[0]);
}
[Fact(DisplayName = nameof(AddProjectToSolutionPostActionFindsMultipleProjectsToAdd))]
public void AddProjectToSolutionPostActionFindsMultipleProjectsToAdd()
{
string outputBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
IPostAction postAction = new MockPostAction(default, default, default, default, default!)
{
ActionId = DotnetSlnPostActionProcessor.ActionProcessorId,
Args = new Dictionary<string, string>()
{
{ "primaryOutputIndexes", "0; 2" }
}
};
ICreationResult creationResult = new MockCreationResult(
primaryOutputs: new[]
{
new MockCreationPath(Path.GetFullPath("outputProj1.csproj")),
new MockCreationPath(Path.GetFullPath("dontFindMe.csproj")),
new MockCreationPath(Path.GetFullPath("outputProj2.csproj"))
});
Assert.True(DotnetSlnPostActionProcessor.TryGetProjectFilesToAdd(postAction, creationResult, outputBasePath, out IReadOnlyList<string>? foundProjectFiles));
Assert.NotNull(foundProjectFiles);
Assert.Equal(2, foundProjectFiles.Count);
Assert.Contains(creationResult.PrimaryOutputs[0].Path, foundProjectFiles.ToList());
Assert.Contains(creationResult.PrimaryOutputs[2].Path, foundProjectFiles.ToList());
Assert.DoesNotContain(creationResult.PrimaryOutputs[1].Path, foundProjectFiles.ToList());
}
[Fact(DisplayName = nameof(AddProjectToSolutionPostActionDoesntFindProjectOutOfRange))]
public void AddProjectToSolutionPostActionDoesntFindProjectOutOfRange()
{
IPostAction postAction = new MockPostAction(default, default, default, default, default!)
{
ActionId = DotnetSlnPostActionProcessor.ActionProcessorId,
Args = new Dictionary<string, string>()
{
{ "primaryOutputIndexes", "1" }
}
};
ICreationResult creationResult = new MockCreationResult(primaryOutputs: new[] { new MockCreationPath("outputProj1.csproj") });
Assert.False(DotnetSlnPostActionProcessor.TryGetProjectFilesToAdd(postAction, creationResult, string.Empty, out IReadOnlyList<string>? foundProjectFiles));
Assert.Empty(foundProjectFiles);
}
[Fact(DisplayName = nameof(AddProjectToSolutionPostActionFindsMultipleProjectsToAddWithOutputBasePath))]
public void AddProjectToSolutionPostActionFindsMultipleProjectsToAddWithOutputBasePath()
{
string outputBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
IPostAction postAction = new MockPostAction(default, default, default, default, default!)
{
ActionId = DotnetSlnPostActionProcessor.ActionProcessorId,
Args = new Dictionary<string, string>()
{
{ "primaryOutputIndexes", "0; 2" }
}
};
ICreationResult creationResult = new MockCreationResult(
primaryOutputs: new[]
{
new MockCreationPath("outputProj1.csproj"),
new MockCreationPath("dontFindMe.csproj"),
new MockCreationPath("outputProj2.csproj")
});
string outputFileFullPath0 = Path.Combine(outputBasePath, creationResult.PrimaryOutputs[0].Path);
string dontFindMeFullPath1 = Path.Combine(outputBasePath, creationResult.PrimaryOutputs[1].Path);
string outputFileFullPath2 = Path.Combine(outputBasePath, creationResult.PrimaryOutputs[2].Path);
Assert.True(DotnetSlnPostActionProcessor.TryGetProjectFilesToAdd(postAction, creationResult, outputBasePath, out IReadOnlyList<string>? foundProjectFiles));
Assert.NotNull(foundProjectFiles);
Assert.Equal(2, foundProjectFiles.Count);
Assert.Contains(outputFileFullPath0, foundProjectFiles.ToList());
Assert.Contains(outputFileFullPath2, foundProjectFiles.ToList());
Assert.DoesNotContain(dontFindMeFullPath1, foundProjectFiles.ToList());
}
[Fact(DisplayName = nameof(AddProjectToSolutionPostActionWithoutPrimaryOutputIndexesWithOutputBasePath))]
public void AddProjectToSolutionPostActionWithoutPrimaryOutputIndexesWithOutputBasePath()
{
string outputBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
IPostAction postAction = new MockPostAction(default, default, default, default, default!)
{
ActionId = DotnetSlnPostActionProcessor.ActionProcessorId,
Args = new Dictionary<string, string>()
};
ICreationResult creationResult = new MockCreationResult(
primaryOutputs: new[]
{
new MockCreationPath("outputProj1.csproj"),
new MockCreationPath("outputProj2.csproj"),
});
string outputFileFullPath0 = Path.Combine(outputBasePath, creationResult.PrimaryOutputs[0].Path);
string outputFileFullPath1 = Path.Combine(outputBasePath, creationResult.PrimaryOutputs[1].Path);
Assert.True(DotnetSlnPostActionProcessor.TryGetProjectFilesToAdd(postAction, creationResult, outputBasePath, out IReadOnlyList<string>? foundProjectFiles));
Assert.NotNull(foundProjectFiles);
Assert.Equal(2, foundProjectFiles.Count);
Assert.Contains(outputFileFullPath0, foundProjectFiles.ToList());
Assert.Contains(outputFileFullPath1, foundProjectFiles.ToList());
}
[Fact(DisplayName = nameof(AddProjectToSolutionCanTargetASingleProjectWithAJsonArray))]
public void AddProjectToSolutionCanTargetASingleProjectWithAJsonArray()
{
var callback = new MockAddProjectToSolutionCallback();
var actionProcessor = new DotnetSlnPostActionProcessor(callback.AddProjectToSolution);
string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
string slnFileFullPath = Path.Combine(targetBasePath, "MyApp.sln");
string projFileFullPath = Path.Combine(targetBasePath, "MyApp.csproj");
_engineEnvironmentSettings.Host.FileSystem.WriteAllText(slnFileFullPath, "");
var args = new Dictionary<string, string>() { { "projectFiles", "[\"MyApp.csproj\"]" } };
var postAction = new MockPostAction(default, default, default, default, default!) { ActionId = DotnetSlnPostActionProcessor.ActionProcessorId, Args = args };
MockCreationEffects creationEffects = new MockCreationEffects()
.WithFileChange(new MockFileChange("./MyApp.csproj", "./MyApp.csproj", ChangeKind.Create));
actionProcessor.Process(
_engineEnvironmentSettings,
postAction,
creationEffects,
new MockCreationResult(),
targetBasePath);
Assert.Equal(new[] { projFileFullPath }, callback.Projects);
Assert.Equal(slnFileFullPath, callback.Solution);
}
[Fact(DisplayName = nameof(AddProjectToSolutionCanTargetASingleProjectWithTheProjectName))]
public void AddProjectToSolutionCanTargetASingleProjectWithTheProjectName()
{
var callback = new MockAddProjectToSolutionCallback();
var actionProcessor = new DotnetSlnPostActionProcessor(callback.AddProjectToSolution);
string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
string slnFileFullPath = Path.Combine(targetBasePath, "MyApp.sln");
string projFileFullPath = Path.Combine(targetBasePath, "MyApp.csproj");
_engineEnvironmentSettings.Host.FileSystem.WriteAllText(slnFileFullPath, "");
var args = new Dictionary<string, string>() { { "projectFiles", "MyApp.csproj" } };
var postAction = new MockPostAction(default, default, default, default, default!) { ActionId = DotnetSlnPostActionProcessor.ActionProcessorId, Args = args };
MockCreationEffects creationEffects = new MockCreationEffects()
.WithFileChange(new MockFileChange("./MyApp.csproj", "./MyApp.csproj", ChangeKind.Create));
actionProcessor.Process(
_engineEnvironmentSettings,
postAction,
creationEffects,
new MockCreationResult(),
targetBasePath);
Assert.Equal(new[] { projFileFullPath }, callback.Projects);
Assert.Equal(slnFileFullPath, callback.Solution);
}
[Fact(DisplayName = nameof(AddProjectToSolutionCanPlaceProjectInSolutionRoot))]
public void AddProjectToSolutionCanPlaceProjectInSolutionRoot()
{
var callback = new MockAddProjectToSolutionCallback();
var actionProcessor = new DotnetSlnPostActionProcessor(callback.AddProjectToSolution);
string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
string slnFileFullPath = Path.Combine(targetBasePath, "MyApp.sln");
string projFileFullPath = Path.Combine(targetBasePath, "MyApp.csproj");
_engineEnvironmentSettings.Host.FileSystem.WriteAllText(slnFileFullPath, "");
var args = new Dictionary<string, string>() {
{ "projectFiles", "MyApp.csproj" },
{ "inRoot", "true" }
};
var postAction = new MockPostAction(default, default, default, default, default!) { ActionId = DotnetSlnPostActionProcessor.ActionProcessorId, Args = args };
MockCreationEffects creationEffects = new MockCreationEffects()
.WithFileChange(new MockFileChange("./MyApp.csproj", "./MyApp.csproj", ChangeKind.Create));
actionProcessor.Process(
_engineEnvironmentSettings,
postAction,
creationEffects,
new MockCreationResult(),
targetBasePath);
Assert.True(callback.InRoot);
Assert.Null(callback.TargetFolder);
}
[Fact]
public void AddProjectToSolutionCanPlaceProjectInSolutionFolder()
{
var callback = new MockAddProjectToSolutionCallback();
var actionProcessor = new DotnetSlnPostActionProcessor(callback.AddProjectToSolution);
string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
string slnFileFullPath = Path.Combine(targetBasePath, "MyApp.sln");
string projFileFullPath = Path.Combine(targetBasePath, "MyApp.csproj");
_engineEnvironmentSettings.Host.FileSystem.WriteAllText(slnFileFullPath, "");
var args = new Dictionary<string, string>() {
{ "projectFiles", "MyApp.csproj" },
{ "solutionFolder", "src" }
};
var postAction = new MockPostAction(default, default, default, default, default!) { ActionId = DotnetSlnPostActionProcessor.ActionProcessorId, Args = args };
MockCreationEffects creationEffects = new MockCreationEffects()
.WithFileChange(new MockFileChange("./MyApp.csproj", "./MyApp.csproj", ChangeKind.Create));
actionProcessor.Process(
_engineEnvironmentSettings,
postAction,
creationEffects,
new MockCreationResult(),
targetBasePath);
Assert.Null(callback.InRoot);
Assert.Equal("src", callback.TargetFolder);
}
[Fact]
public void AddProjectToSolutionFailsWhenSolutionFolderAndInRootSpecified()
{
var callback = new MockAddProjectToSolutionCallback();
var actionProcessor = new DotnetSlnPostActionProcessor(callback.AddProjectToSolution);
string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
string slnFileFullPath = Path.Combine(targetBasePath, "MyApp.sln");
string projFileFullPath = Path.Combine(targetBasePath, "MyApp.csproj");
_engineEnvironmentSettings.Host.FileSystem.WriteAllText(slnFileFullPath, "");
var args = new Dictionary<string, string>() {
{ "projectFiles", "MyApp.csproj" },
{ "inRoot", "true" },
{ "solutionFolder", "src" }
};
var postAction = new MockPostAction(default, default, default, default, default!) { ActionId = DotnetSlnPostActionProcessor.ActionProcessorId, Args = args };
MockCreationEffects creationEffects = new MockCreationEffects()
.WithFileChange(new MockFileChange("./MyApp.csproj", "./MyApp.csproj", ChangeKind.Create));
bool result = actionProcessor.Process(
_engineEnvironmentSettings,
postAction,
creationEffects,
new MockCreationResult(),
targetBasePath);
Assert.False(result);
}
private void EnsureParentDirectoriesExist(string targetBasePath)
{
// Ensure parent directories exist to avoid DirectoryNotFoundException during traversal
string? currentPath = targetBasePath;
while (!string.IsNullOrEmpty(currentPath) && currentPath != Path.GetPathRoot(currentPath))
{
if (!_engineEnvironmentSettings.Host.FileSystem.DirectoryExists(currentPath))
{
_engineEnvironmentSettings.Host.FileSystem.CreateDirectory(currentPath);
}
currentPath = Path.GetDirectoryName(currentPath);
}
}
private class MockAddProjectToSolutionCallback
{
public string? Solution { get; private set; }
public IReadOnlyList<string?>? Projects { get; private set; }
public string? TargetFolder { get; private set; }
public bool? InRoot { get; private set; }
public bool AddProjectToSolution(string solution, IReadOnlyList<string?> projects, string? targetFolder, bool? inRoot)
{
Solution = solution;
Projects = projects;
InRoot = inRoot;
TargetFolder = targetFolder;
return true;
}
}
}
}
|