Skip to content

Commit

Permalink
Merge pull request #26 from ModernRonin/Refactoring
Browse files Browse the repository at this point in the history
Refactoring
  • Loading branch information
ModernRonin authored Apr 10, 2021
2 parents 1e9061f + baaa1be commit cf9fa65
Show file tree
Hide file tree
Showing 22 changed files with 388 additions and 132 deletions.
9 changes: 9 additions & 0 deletions ModernRonin.ProjectRenamer.Tests/CommonExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ public void IsDirectorySeparator_returns_true_for_linux_separator() =>
public void IsDirectorySeparator_returns_true_for_windows_separator() =>
'\\'.IsDirectorySeparator().Should().BeTrue();

[Test]
public void
On_Windows_EnsurePlatformDirectorySeparators_replaces_forward_slashes_with_backward_slashes()
{
@"alpha/bravo\charlie/delta".EnsurePlatformDirectorySeparators()
.Should()
.Be(@"alpha\bravo\charlie\delta");
}

[Test]
public void Repeat_returns_the_string_repeated_the_passed_number_of_times() =>
"alpha".Repeat(3).Should().Be("alphaalphaalpha");
Expand Down
31 changes: 31 additions & 0 deletions ModernRonin.ProjectRenamer.Tests/DotnetTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using AutofacContrib.NSubstitute;
using NSubstitute;
using NUnit.Framework;

namespace ModernRonin.ProjectRenamer.Tests
{
[TestFixture]
public class DotnetTests
{
[SetUp]
public void Setup()
{
_dependencies = new AutoSubstitute();
_underTest = _dependencies.Resolve<Dotnet>();
}

AutoSubstitute _dependencies;
Dotnet _underTest;

IExecutor Executor => _dependencies.Resolve<IExecutor>();

[Test(Description = "fix for https://github.com/ModernRonin/ProjectRenamer/issues/24")]
public void AddToSolution_replaces_forward_slashes_with_backward_slashes_in_solutionFolder()
{
_underTest.AddToSolution("c:/myproject/myproject.csproj", "Features/.shared");

Executor.Received()
.Tool("dotnet", @"sln add -s ""Features\.shared"" ""c:/myproject/myproject.csproj""");
}
}
}
35 changes: 35 additions & 0 deletions ModernRonin.ProjectRenamer.Tests/RenamerModuleTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using Autofac;
using FluentAssertions;
using NUnit.Framework;

namespace ModernRonin.ProjectRenamer.Tests
{
[TestFixture]
public class RenamerModuleTests
{
[SetUp]
public void Setup()
{
var builder = new ContainerBuilder();
builder.RegisterModule<RenamerModule>();
_container = builder.Build();
}

IContainer _container;

[Test]
public void Application_factory_can_be_resolved()
{
var factory = _container.Resolve<Func<Configuration, string, Application>>();
factory.Should().NotBeNull();
factory(new Configuration(), string.Empty).Should().NotBeNull();
}

[Test]
public void IConfigurationSetup_can_be_resolved()
{
_container.Resolve<IConfigurationSetup>().Should().NotBeNull();
}
}
}
99 changes: 35 additions & 64 deletions ModernRonin.ProjectRenamer/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,42 @@ namespace ModernRonin.ProjectRenamer
public class Application
{
readonly Configuration _configuration;
readonly IExecutor _executor;
readonly IDotnet _dotnet;
readonly IErrorHandler _errors;
readonly IFilesystem _filesystem;
readonly IGit _git;
readonly IInput _input;
readonly ILogger _logger;
readonly IRuntime _runtime;
readonly string _solutionPath;

public Application(Configuration configuration,
string solutionPath,
IExecutor executor,
IRuntime runtime,
ILogger logger,
IInput input)
IInput input,
IGit git,
IErrorHandler errors,
IDotnet dotnet,
IFilesystem filesystem)
{
_configuration = configuration;
_solutionPath = solutionPath;
_executor = executor;
_runtime = runtime;
_logger = logger;
_input = input;
_git = git;
_errors = errors;
_dotnet = dotnet;
_filesystem = filesystem;
}

public void Run()
{
EnsureGitIsClean();
_git.EnsureIsClean();

var (wasFound, oldProjectPath, solutionFolderPath) = findProject();
if (!wasFound)
_executor.Error($"{_configuration.OldProjectName} cannot be found in the solution");
if (!wasFound) _errors.Handle($"{_configuration.OldProjectName} cannot be found in the solution");

var oldDir = Path.GetDirectoryName(oldProjectPath);
var newBase = _configuration.NewProjectName.Any(CommonExtensions.IsDirectorySeparator)
Expand All @@ -46,8 +54,8 @@ public void Run()
var newDir = _configuration.NewProjectName.ToAbsolutePath(newBase);
var newFileName = Path.GetFileName(_configuration.NewProjectName);
var newProjectPath = Path.Combine(newDir, $"{newFileName}{Constants.ProjectFileExtension}");
var isPaketUsed = Directory.Exists(".paket");
var gitVersion = _executor.GitRead("--version");
var isPaketUsed = _filesystem.DoesDirectoryExist(".paket");
var gitVersion = _git.GetVersion();
if (!_configuration.DontReviewSettings)
{
var lines = new[]
Expand Down Expand Up @@ -78,32 +86,22 @@ public void Run()
addNewReferences();
addToSolution();
updatePaket();
stageAllChanges();
_git.StageAllChanges();
build();
commit();

void addNewReferences()
{
dependents.ForEach(p => addReference(p, newProjectPath));
dependencies.ForEach(d => addReference(newProjectPath, d));

void addReference(string project, string reference) =>
projectReferenceCommand("add", project, reference);
dependents.ForEach(p => _dotnet.AddReference(p, newProjectPath));
dependencies.ForEach(d => _dotnet.AddReference(newProjectPath, d));
}

void removeOldReferences()
{
dependents.ForEach(p => removeReference(p, oldProjectPath));
dependencies.ForEach(d => removeReference(oldProjectPath, d));

void removeReference(string project, string reference) =>
projectReferenceCommand("remove", project, reference);
dependents.ForEach(p => _dotnet.RemoveReference(p, oldProjectPath));
dependencies.ForEach(d => _dotnet.RemoveReference(oldProjectPath, d));
}

void projectReferenceCommand(string command, string project, string reference) =>
_executor.DotNet(
$"{command} {project.EscapeForShell()} reference {reference.EscapeForShell()}");

(string[] dependents, string[] dependencies) analyzeReferences()
{
_logger.Info(
Expand All @@ -122,10 +120,8 @@ bool hasReferenceToOldProject(string p) =>

IEnumerable<string> getReferencedProjects(string project)
{
var relativeReferences = _dotnet.GetReferencedProjects(project);
var baseDirectory = Path.GetFullPath(Path.GetDirectoryName(project));
var relativeReferences = _executor.DotNetRead($"list {project.EscapeForShell()} reference")
.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries)
.Skip(2);
return relativeReferences.Select(r => r.ToAbsolutePath(baseDirectory));
}

Expand All @@ -139,34 +135,30 @@ void commit()
var msg = wasMove
? $"Moved {oldProjectPath.ToRelativePath(CurrentDirectoryAbsolute)} to {newProjectPath.ToRelativePath(CurrentDirectoryAbsolute)}"
: $"Renamed {_configuration.OldProjectName} to {_configuration.NewProjectName}";
var arguments = $"commit -m \"{msg}\"";
_executor.Git(arguments,
() => { _logger.Error($"'git {arguments}' failed"); });
_git.Commit(msg);
}
}

void build()
{
if (_configuration.DoRunBuild)
{
_executor.DotNet("build", () =>
_dotnet.BuildSolution(() =>
{
if (_input.AskUser(
"dotnet build returned an error or warning - do you want to rollback all changes?")
)
{
_executor.RollbackGit();
_git.RollbackAllChanges();
_runtime.Abort();
}
});
}
}

void stageAllChanges() => _executor.Git("add .");

void updatePaket()
{
if (isPaketUsed && !_configuration.DontRunPaketInstall) _executor.DotNet("paket install");
if (isPaketUsed && !_configuration.DontRunPaketInstall) _dotnet.PaketInstall();
}

void updatePaketReference()
Expand All @@ -193,24 +185,19 @@ bool isPaketReference(string line)

void gitMove()
{
Directory.CreateDirectory(Path.GetDirectoryName(newDir));
_executor.Git($"mv {oldDir} {newDir}");
_filesystem.EnsureDirectoryExists(Path.GetDirectoryName(newDir));
_git.Move(oldDir, newDir);
var oldPath = Path.GetFileName(oldProjectPath).ToAbsolutePath(newDir);
if (oldPath != newProjectPath) _executor.Git($"mv {oldPath} {newProjectPath}");
if (oldPath != newProjectPath) _git.Move(oldPath, newProjectPath);
}

void addToSolution()
{
var solutionFolderArgument = string.IsNullOrWhiteSpace(solutionFolderPath)
? string.Empty
: $"-s \"{solutionFolderPath}\"";
solutionCommand($"add {solutionFolderArgument}", newProjectPath);
if (string.IsNullOrWhiteSpace(solutionFolderPath)) _dotnet.AddToSolution(newProjectPath);
else _dotnet.AddToSolution(newProjectPath, solutionFolderPath);
}

void removeFromSolution() => solutionCommand("remove", oldProjectPath);

void solutionCommand(string command, string projectPath) =>
_executor.DotNet($"sln {command} {projectPath.EscapeForShell()}");
void removeFromSolution() => _dotnet.RemoveFromSolution(oldProjectPath);

(bool wasFound, string projectPath, string solutionFolder) findProject()
{
Expand Down Expand Up @@ -244,26 +231,10 @@ string[] allProjects()

return all.Except(excluded).ToArray();

string[] filesIn(string directory) =>
Directory
.EnumerateFiles(directory, $"*{Constants.ProjectFileExtension}",
SearchOption.AllDirectories)
.ToArray();
string[] filesIn(string directory) => _filesystem.FindProjectFiles(directory, true);
}
}

void EnsureGitIsClean()
{
run("update-index -q --refresh");
run("diff-index --quiet --cached HEAD --");
run("diff-files --quiet");
run("ls-files --exclude-standard --others");

void run(string arguments) =>
_executor.Git(arguments,
() => _executor.Error("git does not seem to be clean, check git status"));
}

static string CurrentDirectoryAbsolute => Path.GetFullPath(Directory.GetCurrentDirectory());
string CurrentDirectoryAbsolute => Path.GetFullPath(_filesystem.CurrentDirectory);
}
}
3 changes: 3 additions & 0 deletions ModernRonin.ProjectRenamer/CommonExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ public static class CommonExtensions
{
public static string AsText(this bool self) => self ? "yes" : "no";

public static string EnsurePlatformDirectorySeparators(this string self) =>
self.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);

public static string EscapeForShell(this string self) => $"\"{self}\"";

public static bool IsDirectorySeparator(this char self) =>
Expand Down
20 changes: 12 additions & 8 deletions ModernRonin.ProjectRenamer/ConfigurationSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,26 @@ namespace ModernRonin.ProjectRenamer
public class ConfigurationSetup : IConfigurationSetup
{
readonly ILogger _console;
readonly IExecutor _executor;
readonly IErrorHandler _errors;
readonly IFilesystem _filesystem;
readonly IRuntime _runtime;

public ConfigurationSetup(IExecutor executor, ILogger console, IRuntime runtime)
public ConfigurationSetup(ILogger console,
IRuntime runtime,
IErrorHandler errors,
IFilesystem filesystem)
{
_executor = executor;
_console = console;
_runtime = runtime;
_errors = errors;
_filesystem = filesystem;
}

public (Configuration configuration, string solutionPath) Get(string[] commandLineArguments)
{
var solutionFiles =
Directory.EnumerateFiles(".", "*.sln", SearchOption.TopDirectoryOnly).ToArray();
var solutionFiles = _filesystem.FindSolutionFiles(".", false);
if (1 != solutionFiles.Length)
_executor.Error("Needs to be run from a directory with exactly one *.sln file in it.");
_errors.Handle("Needs to be run from a directory with exactly one *.sln file in it.");
var solutionPath = Path.GetFullPath(solutionFiles.First());
switch (ParseCommandLine(commandLineArguments))
{
Expand All @@ -36,7 +40,7 @@ public ConfigurationSetup(IExecutor executor, ILogger console, IRuntime runtime)
case (var helpOverview, Configuration configuration):
if (configuration.OldProjectName.Any(CommonExtensions.IsDirectorySeparator))
{
_executor.Error(
_errors.Handle(
$"Do not specify paths for input/'old' project names, please.{Environment.NewLine}{Environment.NewLine}{helpOverview}");
}

Expand All @@ -45,7 +49,7 @@ public ConfigurationSetup(IExecutor executor, ILogger console, IRuntime runtime)

return (configuration, solutionPath);
default:
_executor.Error(
_errors.Handle(
"Something went seriously wrong. Please create an issue at https://github.com/ModernRonin/ProjectRenamer with as much detail as possible.");
break;
}
Expand Down
1 change: 1 addition & 0 deletions ModernRonin.ProjectRenamer/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
public static class Constants
{
public const string ProjectFileExtension = ".csproj";
public const string SolutionFileExtension = ".sln";
}
}
Loading

0 comments on commit cf9fa65

Please sign in to comment.