172 lines
7.5 KiB
C#
172 lines
7.5 KiB
C#
using Explorer.Application;
|
|
using Explorer.Domain;
|
|
using Explorer.Domain.Abstractions;
|
|
using Explorer.Windows;
|
|
|
|
namespace Explorer.Application.Tests;
|
|
|
|
public class GitCommandIntegrationTests : IDisposable
|
|
{
|
|
private readonly string _root;
|
|
private readonly WindowsGitStatusProvider _git;
|
|
|
|
public GitCommandIntegrationTests()
|
|
{
|
|
_root = Path.Combine(Path.GetTempPath(), "ew-git", Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(_root);
|
|
_git = new WindowsGitStatusProvider(new UiPreferencesStore(new GitTestEnv(_root)));
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
try { Directory.Delete(_root, true); } catch { /* ignore */ }
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Diff_stage_commit_and_merge_resolution()
|
|
{
|
|
if (GitLocator.Find(null) is null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var repo = Path.Combine(_root, "repo");
|
|
Directory.CreateDirectory(repo);
|
|
await Git(repo, "init", "-b", "main").ConfigureAwait(true);
|
|
await Git(repo, "config", "user.email", "test@example.com").ConfigureAwait(true);
|
|
await Git(repo, "config", "user.name", "Test").ConfigureAwait(true);
|
|
await Git(repo, "config", "core.autocrlf", "false").ConfigureAwait(true);
|
|
File.WriteAllText(Path.Combine(repo, "note.txt"), "base\n");
|
|
await Git(repo, "add", "note.txt").ConfigureAwait(true);
|
|
await Git(repo, "commit", "-m", "base").ConfigureAwait(true);
|
|
|
|
File.WriteAllText(Path.Combine(repo, "note.txt"), "base\nedited\n");
|
|
var status = await _git.StatusAsync(repo).ConfigureAwait(true);
|
|
Assert.NotNull(status);
|
|
var unstaged = Assert.Single(status.Changes, c => c.State == GitChangeState.Unstaged);
|
|
var diff = await _git.DiffAsync(repo, unstaged).ConfigureAwait(true);
|
|
Assert.Null(diff.Error);
|
|
Assert.Contains(diff.Lines, l => l.Kind == GitDiffLineKind.Added && l.Text.Contains("edited"));
|
|
|
|
var staged = await _git.StageAsync(repo, "note.txt").ConfigureAwait(true);
|
|
Assert.True(staged.Succeeded, staged.DisplayMessage);
|
|
var committed = await _git.CommitAsync(repo, "edit", ["note.txt"]).ConfigureAwait(true);
|
|
Assert.True(committed.Succeeded, committed.DisplayMessage);
|
|
|
|
await Git(repo, "checkout", "-b", "topic").ConfigureAwait(true);
|
|
File.WriteAllText(Path.Combine(repo, "note.txt"), "base\nedited\ntopic\n");
|
|
await Git(repo, "add", "note.txt").ConfigureAwait(true);
|
|
await Git(repo, "commit", "-m", "topic").ConfigureAwait(true);
|
|
await Git(repo, "checkout", "main").ConfigureAwait(true);
|
|
File.WriteAllText(Path.Combine(repo, "note.txt"), "base\nedited\nmain\n");
|
|
await Git(repo, "add", "note.txt").ConfigureAwait(true);
|
|
await Git(repo, "commit", "-m", "main").ConfigureAwait(true);
|
|
|
|
var merge = await GitExpectFail(repo, "merge", "topic").ConfigureAwait(true);
|
|
Assert.False(string.IsNullOrWhiteSpace(merge));
|
|
status = await _git.StatusAsync(repo).ConfigureAwait(true);
|
|
Assert.NotNull(status);
|
|
Assert.Equal(GitOperationKind.Merge, status.Operation);
|
|
Assert.Contains(status.Changes, c => c.State == GitChangeState.Unmerged);
|
|
|
|
var ours = await _git.CheckoutConflictAsync(repo, "note.txt", GitConflictSide.Ours).ConfigureAwait(true);
|
|
Assert.True(ours.Succeeded, ours.DisplayMessage);
|
|
status = await _git.StatusAsync(repo).ConfigureAwait(true);
|
|
Assert.NotNull(status);
|
|
Assert.False(status.HasUnmerged);
|
|
|
|
var continued = await _git.ContinueOperationAsync(repo).ConfigureAwait(true);
|
|
Assert.True(continued.Succeeded, continued.DisplayMessage);
|
|
status = await _git.StatusAsync(repo).ConfigureAwait(true);
|
|
Assert.NotNull(status);
|
|
Assert.Equal(GitOperationKind.None, status.Operation);
|
|
Assert.True(status.WorkingTreeClean);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Abort_restores_the_pre_merge_tree()
|
|
{
|
|
if (GitLocator.Find(null) is null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var repo = Path.Combine(_root, "abort");
|
|
Directory.CreateDirectory(repo);
|
|
await Git(repo, "init", "-b", "main").ConfigureAwait(true);
|
|
await Git(repo, "config", "user.email", "test@example.com").ConfigureAwait(true);
|
|
await Git(repo, "config", "user.name", "Test").ConfigureAwait(true);
|
|
await Git(repo, "config", "core.autocrlf", "false").ConfigureAwait(true);
|
|
File.WriteAllText(Path.Combine(repo, "a.txt"), "one\n");
|
|
await Git(repo, "add", "a.txt").ConfigureAwait(true);
|
|
await Git(repo, "commit", "-m", "one").ConfigureAwait(true);
|
|
await Git(repo, "checkout", "-b", "other").ConfigureAwait(true);
|
|
File.WriteAllText(Path.Combine(repo, "a.txt"), "two\n");
|
|
await Git(repo, "add", "a.txt").ConfigureAwait(true);
|
|
await Git(repo, "commit", "-m", "two").ConfigureAwait(true);
|
|
await Git(repo, "checkout", "main").ConfigureAwait(true);
|
|
File.WriteAllText(Path.Combine(repo, "a.txt"), "three\n");
|
|
await Git(repo, "add", "a.txt").ConfigureAwait(true);
|
|
await Git(repo, "commit", "-m", "three").ConfigureAwait(true);
|
|
await GitExpectFail(repo, "merge", "other").ConfigureAwait(true);
|
|
|
|
var aborted = await _git.AbortOperationAsync(repo).ConfigureAwait(true);
|
|
Assert.True(aborted.Succeeded, aborted.DisplayMessage);
|
|
var status = await _git.StatusAsync(repo).ConfigureAwait(true);
|
|
Assert.NotNull(status);
|
|
Assert.Equal(GitOperationKind.None, status.Operation);
|
|
Assert.Equal("three\n", File.ReadAllText(Path.Combine(repo, "a.txt")).Replace("\r\n", "\n"));
|
|
}
|
|
|
|
private static async Task Git(string repo, params string[] args)
|
|
{
|
|
var result = await RunGit(repo, args).ConfigureAwait(true);
|
|
Assert.True(result.Exit == 0, result.Text);
|
|
}
|
|
|
|
private static async Task<string> GitExpectFail(string repo, params string[] args)
|
|
{
|
|
var result = await RunGit(repo, args).ConfigureAwait(true);
|
|
Assert.NotEqual(0, result.Exit);
|
|
return result.Text;
|
|
}
|
|
|
|
private static async Task<(int Exit, string Text)> RunGit(string repo, string[] args)
|
|
{
|
|
var git = GitLocator.Find(null) ?? throw new InvalidOperationException("git.exe missing");
|
|
using var process = new System.Diagnostics.Process();
|
|
process.StartInfo.FileName = git;
|
|
process.StartInfo.WorkingDirectory = repo;
|
|
process.StartInfo.UseShellExecute = false;
|
|
process.StartInfo.CreateNoWindow = true;
|
|
process.StartInfo.RedirectStandardOutput = true;
|
|
process.StartInfo.RedirectStandardError = true;
|
|
foreach (var arg in args)
|
|
{
|
|
process.StartInfo.ArgumentList.Add(arg);
|
|
}
|
|
|
|
process.Start();
|
|
var stdout = await process.StandardOutput.ReadToEndAsync().ConfigureAwait(true);
|
|
var stderr = await process.StandardError.ReadToEndAsync().ConfigureAwait(true);
|
|
await process.WaitForExitAsync().ConfigureAwait(true);
|
|
return (process.ExitCode, stdout + stderr);
|
|
}
|
|
}
|
|
|
|
file sealed class GitTestEnv : IAppEnvironment
|
|
{
|
|
public GitTestEnv(string dir)
|
|
{
|
|
DataDirectory = Path.Combine(dir, "data");
|
|
Directory.CreateDirectory(DataDirectory);
|
|
DatabasePath = Path.Combine(DataDirectory, "index.db");
|
|
LogDirectory = Path.Combine(DataDirectory, "logs");
|
|
Directory.CreateDirectory(LogDirectory);
|
|
}
|
|
|
|
public string DataDirectory { get; }
|
|
public string DatabasePath { get; }
|
|
public string LogDirectory { get; }
|
|
}
|