-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve System Test performance with stubbing dotnet new (#294)
* first attempt to improve "dotnet new classlib" * improve "dotnet new sln" * stub "dotnet sln add" * try fixing linux build issue * Fix linux build * Ensure dotnet command is executed with the corresponding sdk version * code cleanup * fix Reqnroll.TestProjectGenerator.Tests * Add --no-restore for new classlib --------- Co-authored-by: obligaron <[email protected]>
- Loading branch information
1 parent
7c22e2f
commit 4eed410
Showing
17 changed files
with
285 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 87 additions & 0 deletions
87
...s/TestProjectGenerator/Reqnroll.TestProjectGenerator/Dotnet/CacheAndCopyCommandBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
using Reqnroll.TestProjectGenerator.FilesystemWriter; | ||
using System; | ||
using System.Collections.Concurrent; | ||
using System.IO; | ||
|
||
namespace Reqnroll.TestProjectGenerator.Dotnet; | ||
|
||
public class CacheAndCopyCommandBuilder : CommandBuilder | ||
{ | ||
private const string TemplateName = "TName"; | ||
private readonly NetCoreSdkInfo _sdk; | ||
private readonly CommandBuilder _baseCommandBuilder; | ||
private readonly string _targetPath; | ||
private readonly string _nameToReplace; | ||
private static readonly ConcurrentDictionary<string, object> LockObjects = new(); | ||
|
||
public CacheAndCopyCommandBuilder(IOutputWriter outputWriter, NetCoreSdkInfo sdk, CommandBuilder baseCommandBuilder, string targetPath, string nameToReplace = null) | ||
: base(outputWriter, baseCommandBuilder.ExecutablePath, baseCommandBuilder.ArgumentsFormat, baseCommandBuilder.WorkingDirectory) | ||
{ | ||
_sdk = sdk; | ||
_baseCommandBuilder = baseCommandBuilder; | ||
_targetPath = targetPath; | ||
_nameToReplace = nameToReplace; | ||
} | ||
|
||
private string CalculateCacheTargetPath(string suffix = "") | ||
{ | ||
var targetPathInfo = new DirectoryInfo(_targetPath); | ||
var directoryName = targetPathInfo.Name; | ||
string argsCleaned = ArgumentsFormat.Replace(_targetPath, "").Replace(" ", "").Replace("\"", "").Replace("/", "") + directoryName; | ||
if (_nameToReplace != null) | ||
{ | ||
argsCleaned = argsCleaned.Replace(_nameToReplace, TemplateName); | ||
directoryName = directoryName.Replace(_nameToReplace, TemplateName); | ||
} | ||
|
||
var sdkSpecifier = _sdk == null ? "" : $"_{_sdk.Version}"; | ||
return Path.Combine(Path.GetTempPath(), "RRC", $"RRC{sdkSpecifier}_{argsCleaned}{suffix}", directoryName); | ||
} | ||
|
||
public override CommandResult Execute(Func<Exception, Exception> exceptionFunction) | ||
{ | ||
var cachePath = CalculateCacheTargetPath(); | ||
|
||
CommandResult originalResult = null; | ||
if (!Directory.Exists(cachePath)) | ||
{ | ||
LockObjects.TryAdd(cachePath, new object()); | ||
|
||
lock (LockObjects[cachePath]) | ||
{ | ||
if (!Directory.Exists(cachePath)) | ||
{ | ||
var tempPath = CalculateCacheTargetPath($"-tmp{Guid.NewGuid():N}"); | ||
var arguments = ArgumentsFormat.Replace(_targetPath, tempPath); | ||
if (_nameToReplace != null) arguments = arguments.Replace(_nameToReplace, TemplateName); | ||
var commandBuilder = new CommandBuilder(_outputWriter, ExecutablePath, arguments, WorkingDirectory); | ||
|
||
originalResult = commandBuilder.Execute(exceptionFunction); | ||
try | ||
{ | ||
if (!Directory.Exists(cachePath)) | ||
Directory.Move(Path.Combine(tempPath, ".."), Path.Combine(cachePath, "..")); | ||
} | ||
catch (IOException ex) | ||
{ | ||
_outputWriter.WriteLine($"Unable to move TMP to CACHE: {ex.Message}"); | ||
} | ||
try | ||
{ | ||
if (Directory.Exists(tempPath)) | ||
Directory.Delete(Path.Combine(tempPath, ".."), true); | ||
} | ||
catch (IOException ex) | ||
{ | ||
_outputWriter.WriteLine($"Unable to delete TMP: {ex.Message}"); | ||
} | ||
} | ||
} | ||
} | ||
|
||
var copyFolderCommandBuilder = new CopyFolderCommandBuilder(_outputWriter, cachePath, _targetPath, TemplateName, _nameToReplace); | ||
var copyFolderResult = copyFolderCommandBuilder.Execute(exceptionFunction); | ||
return originalResult == null ? copyFolderResult : | ||
new CommandResult(originalResult.ExitCode, originalResult.ConsoleOutput + Environment.NewLine + copyFolderResult.ConsoleOutput); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64 changes: 64 additions & 0 deletions
64
Tests/TestProjectGenerator/Reqnroll.TestProjectGenerator/Dotnet/CopyFolderCommandBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
using System; | ||
using System.IO; | ||
|
||
namespace Reqnroll.TestProjectGenerator.Dotnet; | ||
public class CopyFolderCommandBuilder : CommandBuilder | ||
{ | ||
private readonly string _replaceFrom; | ||
private readonly string _replaceTo; | ||
|
||
public CopyFolderCommandBuilder(IOutputWriter outputWriter, string sourceFolder, string targetFolder, string replaceFrom = null, string replaceTo = null) | ||
: base(outputWriter, "[copy folder]", sourceFolder, targetFolder) | ||
{ | ||
_replaceFrom = replaceFrom; | ||
_replaceTo = replaceTo; | ||
} | ||
|
||
private string ReplaceName(string value) | ||
{ | ||
if (_replaceFrom == null || _replaceTo == null) return value; | ||
return value.Replace(_replaceFrom, _replaceTo); | ||
} | ||
|
||
private void CopyDirectoryRecursively(DirectoryInfo source, DirectoryInfo target) | ||
{ | ||
Directory.CreateDirectory(target.FullName); | ||
|
||
// Copy each file into the new directory. | ||
foreach (FileInfo fi in source.GetFiles()) | ||
{ | ||
string fiName = ReplaceName(fi.Name); | ||
_outputWriter.WriteLine(@"Copying to {0}\{1}", target.FullName, fiName); | ||
fi.CopyTo(Path.Combine(target.FullName, fiName), true); | ||
} | ||
|
||
// Copy each subdirectory using recursion. | ||
foreach (DirectoryInfo diSourceSubDir in source.GetDirectories()) | ||
{ | ||
DirectoryInfo nextTargetSubDir = | ||
target.CreateSubdirectory(diSourceSubDir.Name); | ||
CopyDirectoryRecursively(diSourceSubDir, nextTargetSubDir); | ||
} | ||
} | ||
|
||
public override CommandResult Execute(Func<Exception, Exception> exceptionFunction) | ||
{ | ||
var sourceFolder = ArgumentsFormat; | ||
var targetFolder = WorkingDirectory; | ||
|
||
try | ||
{ | ||
_outputWriter.WriteLine($"Copying '{sourceFolder}' to '{targetFolder}'..."); | ||
|
||
CopyDirectoryRecursively(new DirectoryInfo(sourceFolder), new DirectoryInfo(targetFolder)); | ||
|
||
_outputWriter.WriteLine("Copying done."); | ||
|
||
return new CommandResult(0, $"Copied '{sourceFolder}' to '{targetFolder}'."); | ||
} | ||
catch (Exception ex) | ||
{ | ||
throw exceptionFunction(ex); | ||
} | ||
} | ||
} |
4 changes: 3 additions & 1 deletion
4
Tests/TestProjectGenerator/Reqnroll.TestProjectGenerator/Dotnet/DotNet.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
...or/Reqnroll.TestProjectGenerator/Dotnet/NewCommandBuilder.StubNewProjectCommandBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using Reqnroll.TestProjectGenerator.FilesystemWriter; | ||
|
||
namespace Reqnroll.TestProjectGenerator.Dotnet; | ||
|
||
public partial class NewCommandBuilder | ||
{ | ||
public class StubNewProjectCommandBuilder(IOutputWriter outputWriter, NetCoreSdkInfo _sdk) : NewProjectCommandBuilder(outputWriter) | ||
{ | ||
public override CommandBuilder Build() | ||
{ | ||
return new CacheAndCopyCommandBuilder(_outputWriter, _sdk, base.Build(), _folder); | ||
} | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
...r/Reqnroll.TestProjectGenerator/Dotnet/NewCommandBuilder.StubNewSolutionCommandBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using Reqnroll.TestProjectGenerator.FilesystemWriter; | ||
|
||
namespace Reqnroll.TestProjectGenerator.Dotnet; | ||
|
||
public partial class NewCommandBuilder | ||
{ | ||
public class StubNewSolutionCommandBuilder(IOutputWriter outputWriter, NetCoreSdkInfo _sdk) : NewSolutionCommandBuilder(outputWriter) | ||
{ | ||
public override CommandBuilder Build() | ||
{ | ||
return new CacheAndCopyCommandBuilder(_outputWriter, _sdk, base.Build(), _rootPath, _name); | ||
} | ||
} | ||
} |
12 changes: 8 additions & 4 deletions
12
Tests/TestProjectGenerator/Reqnroll.TestProjectGenerator/Dotnet/NewCommandBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,22 @@ | ||
using Reqnroll.TestProjectGenerator.FilesystemWriter; | ||
|
||
namespace Reqnroll.TestProjectGenerator.Dotnet | ||
{ | ||
public partial class NewCommandBuilder | ||
{ | ||
private readonly IOutputWriter _outputWriter; | ||
private readonly NetCoreSdkInfo _sdk; | ||
|
||
public NewCommandBuilder(IOutputWriter outputWriter) | ||
public NewCommandBuilder(IOutputWriter outputWriter, NetCoreSdkInfo sdk) | ||
{ | ||
_outputWriter = outputWriter; | ||
_sdk = sdk; | ||
} | ||
|
||
internal static NewCommandBuilder Create(IOutputWriter outputWriter) => new NewCommandBuilder(outputWriter); | ||
internal static NewCommandBuilder Create(IOutputWriter outputWriter, NetCoreSdkInfo sdk) => new NewCommandBuilder(outputWriter, sdk); | ||
|
||
public NewSolutionCommandBuilder Solution() => new NewSolutionCommandBuilder(_outputWriter); | ||
public NewSolutionCommandBuilder Solution() => new StubNewSolutionCommandBuilder(_outputWriter, _sdk); | ||
|
||
public NewProjectCommandBuilder Project() => new NewProjectCommandBuilder(_outputWriter); | ||
public NewProjectCommandBuilder Project() => new StubNewProjectCommandBuilder(_outputWriter, _sdk); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
...estProjectGenerator/Dotnet/SolutionCommandBuilder.StubAddProjectSolutionCommandBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
using System; | ||
using System.IO; | ||
using System.Text.RegularExpressions; | ||
|
||
namespace Reqnroll.TestProjectGenerator.Dotnet; | ||
|
||
public partial class SolutionCommandBuilder | ||
{ | ||
public class StubAddProjectSolutionCommandBuilder(IOutputWriter outputWriter) : AddProjectSolutionCommandBuilder(outputWriter) | ||
{ | ||
public override CommandBuilder Build() | ||
{ | ||
return new AddProjectSolutionCommand(_outputWriter, _solutionPath, _projectPath, GetWorkingDirectory()); | ||
} | ||
|
||
class AddProjectSolutionCommand(IOutputWriter outputWriter, string _solutionPath, string _projectPath, string workingDirectory) | ||
: CommandBuilder(outputWriter, "[add project to sln]", $"{_projectPath} -> {_solutionPath}", workingDirectory) | ||
{ | ||
public override CommandResult Execute(Func<Exception, Exception> exceptionFunction) | ||
{ | ||
try | ||
{ | ||
var projectGuid = Guid.NewGuid().ToString("B").ToUpperInvariant(); | ||
var projectTypeGuid = Path.GetExtension(_projectPath).ToLowerInvariant().Equals(".vbproj") ? | ||
"{F184B08F-C81C-45F6-A57F-5ABD9991F28F}" : | ||
"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"; | ||
var projectName = Path.GetFileNameWithoutExtension(_projectPath); | ||
var projectRelativePath = _projectPath!.Substring(Path.GetDirectoryName(_solutionPath)!.Length + 1); | ||
var slnContent = File.ReadAllText(_solutionPath); | ||
var projectReference = | ||
$$""" | ||
Project("{{projectTypeGuid}}") = "{{projectName}}", "{{projectRelativePath}}", "{{projectGuid}}" | ||
EndProject | ||
"""; | ||
var projectConfPlatforms = | ||
""" | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
EndGlobalSection | ||
"""; | ||
var projectConfPlatformContent = | ||
$$""" | ||
{{projectGuid}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{{projectGuid}}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{{projectGuid}}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{{projectGuid}}.Release|Any CPU.Build.0 = Release|Any CPU | ||
"""; | ||
|
||
slnContent = Regex.Replace(slnContent, @"\r?\nGlobal\r?\n", match => Environment.NewLine + projectReference + match.Value); | ||
if (!slnContent.Contains("GlobalSection(ProjectConfigurationPlatforms)")) | ||
{ | ||
slnContent = Regex.Replace(slnContent, @"\r?\nEndGlobal", match => Environment.NewLine + projectConfPlatforms + match.Value); | ||
} | ||
|
||
slnContent = Regex.Replace(slnContent, @"GlobalSection\(ProjectConfigurationPlatforms\) = postSolution\r?\n", match => match.Value + projectConfPlatformContent + Environment.NewLine); | ||
File.WriteAllText(_solutionPath, slnContent); | ||
_outputWriter.WriteLine($"Solution file updated: {ArgumentsFormat}"); | ||
return new CommandResult(0, "Solution file updated."); | ||
} | ||
catch (Exception ex) | ||
{ | ||
throw exceptionFunction(ex); | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.